diff --git a/.gitattributes b/.gitattributes index 902683783..8a66ddc87 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,3 +6,8 @@ tests/WebPlatformSubset/upstream/** text eol=lf -whitespace third-party/v8-patches/*.txt text eol=lf packaging/WebScene.NativeEngine.Runtime/patches/*.txt text eol=lf +eng/graphics/** text eol=lf +tests/GraphicsCompatibility/*.mjs text eol=lf +tests/GraphicsCompatibility/fixtures/*.json text eol=lf +tests/GraphicsCompatibility/fixtures/Kestrel-LICENSE.txt -text +tests/GraphicsCompatibility/fixtures/Kestrel-CAD.zip -text diff --git a/.github/actions/graphics-sdk/action.yml b/.github/actions/graphics-sdk/action.yml new file mode 100644 index 000000000..6d1b8d69e --- /dev/null +++ b/.github/actions/graphics-sdk/action.yml @@ -0,0 +1,77 @@ +name: Verified graphics SDK +description: Restore or build an exact pinned SDK and verify its sealed inventory. +inputs: + rid: + required: true + description: Target runtime identifier + component: + required: true + description: dawn or angle + clean: + default: 'false' + description: Ignore the exact cache for an explicit rebuild +runs: + using: composite + steps: + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - id: sdk-identity + name: Resolve graphics SDK cache identity + shell: bash + env: + SDK_INPUTS: ${{ hashFiles('eng/graphics/dependencies.lock.json', 'eng/graphics/windows-runtime.json', 'eng/graphics/build.py', 'eng/graphics/DawnSymbolBoundary.cmake', 'eng/graphics/dawn_exports.py', 'eng/graphics/install-windows-sdk.ps1', '.github/actions/graphics-sdk/action.yml') }} + run: | + echo "key=webscene-graphics-sdk-v1-${{ inputs.component }}-${{ inputs.rid }}-${ImageVersion:?Runner image version is required}-$SDK_INPUTS" >> "$GITHUB_OUTPUT" + - id: sdk-cache + name: Restore pinned graphics SDK + if: inputs.clean != 'true' + uses: actions/cache/restore@v4 + with: + path: artifacts/graphics-sdk/${{ inputs.rid }}/${{ inputs.component }} + key: ${{ steps.sdk-identity.outputs.key }} + # No prefix fallback: a different build recipe or toolchain must rebuild. + - name: Install pinned Windows SDK for graphics runtime packaging + if: inputs.rid == 'win-x64' && steps.sdk-cache.outputs.cache-hit != 'true' + shell: pwsh + run: ./eng/graphics/install-windows-sdk.ps1 + - name: Initialize Windows x64 compiler environment + if: inputs.rid == 'win-x64' + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $before = @{} + Get-ChildItem Env: | ForEach-Object { $before[$_.Name] = $_.Value } + $vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/Installer/vswhere.exe' + if (!(Test-Path $vswhere)) { throw 'Visual Studio Installer/vswhere is required.' } + $installation = & $vswhere -latest -version '[17.1,)' -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath + if ($LASTEXITCODE -ne 0 -or !$installation) { throw 'An x64 Visual C++ toolchain is required.' } + $developerShell = Join-Path $installation 'Common7/Tools/Launch-VsDevShell.ps1' + & $developerShell -Arch amd64 -HostArch amd64 -SkipAutomaticLocation + if (!(Get-Command cl.exe -ErrorAction SilentlyContinue)) { throw 'Visual C++ initialization did not expose cl.exe.' } + if ($env:VSCMD_ARG_TGT_ARCH -ne 'x64') { throw 'Expected an x64 compiler target.' } + Get-ChildItem Env: | Where-Object { !$before.ContainsKey($_.Name) -or $before[$_.Name] -ne $_.Value } | ForEach-Object { + if ($_.Value.Contains("`n") -or $_.Value.Contains("`r")) { throw "Unexpected multiline environment variable: $($_.Name)" } + "$($_.Name)=$($_.Value)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + } + - name: Install Linux development prerequisites + if: inputs.rid == 'linux-x64' + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y ninja-build clang lld pkg-config libvulkan-dev libx11-dev libx11-xcb-dev libxcb1-dev libxext-dev libxi-dev libxrandr-dev libxinerama-dev libxcursor-dev libxcomposite-dev libxdamage-dev libxfixes-dev libdrm-dev libgbm-dev libwayland-dev libudev-dev + - name: Build pinned SDK + if: steps.sdk-cache.outputs.cache-hit != 'true' + shell: bash + run: python eng/graphics/build.py ${{ inputs.component }} --rid ${{ inputs.rid }} --jobs 2 + - name: Verify installed SDK + shell: bash + run: python eng/graphics/verify-sdk.py artifacts/graphics-sdk/${{ inputs.rid }}/${{ inputs.component }} --component ${{ inputs.component }} --rid ${{ inputs.rid }} + # Save immediately after verification: a later probe failure must not throw + # away a successful expensive dependency build. Hits still run all probes. + - name: Save verified graphics SDK + if: steps.sdk-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: artifacts/graphics-sdk/${{ inputs.rid }}/${{ inputs.component }} + key: ${{ steps.sdk-identity.outputs.key }} diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5e9ffe6e2..b173b399c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -31,6 +31,12 @@ jobs: node-version: 22 - name: Restore run: dotnet restore WebScene.sln + - name: Verify immutable GPU compatibility fixture + run: python tests/GraphicsCompatibility/prepare-kestrel.py + - name: Graphics evidence boundary checks + run: python -m unittest discover -s eng/graphics/tests -v + - name: Graphics reference workload and trace checks + run: node --test tests/GraphicsCompatibility/reference-tests.mjs - name: Build Release run: dotnet build WebScene.sln -c Release --no-restore - name: Portable Core contracts @@ -74,6 +80,64 @@ jobs: -B artifacts/native-engine-portability -DCMAKE_BUILD_TYPE=Release -DWEBSCENE_NATIVE_ENGINE_ENABLE_V8=OFF + -DWEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA=ON -DWEBSCENE_NATIVE_ENGINE_CERTIFICATION=OFF - name: Compile native C++ portability target run: cmake --build artifacts/native-engine-portability --config Release --parallel + + - name: Native media decode contracts + run: ctest --test-dir artifacts/native-engine-portability -C Release -R "webscene_(media_decode|audio_graph)_tests" --output-on-failure + + native-aot: + name: NativeAOT contracts ${{ matrix.rid }} Avalonia ${{ matrix.avalonia }} + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + rid: osx-arm64 + executable: WebScene.GpuHost.Probe + avalonia: 11 + avalonia12Sample: false + - os: windows-latest + rid: win-x64 + executable: WebScene.GpuHost.Probe.exe + avalonia: 11 + avalonia12Sample: false + - os: macos-latest + rid: osx-arm64 + executable: WebScene.GpuHost.Probe + avalonia: 12 + avalonia12Sample: true + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-dotnet@v5 + with: + global-json-file: global.json + - name: Publish NativeAOT with reflection JSON disabled + shell: bash + run: | + set -o pipefail + dotnet publish experiments/WebScene.GpuHost.Probe -c Release -r '${{ matrix.rid }}' \ + -p:PublishAot=true -p:JsonSerializerIsReflectionEnabledByDefault=false \ + -p:WebSceneAvalonia12Sample=${{ matrix.avalonia12Sample }} \ + -o artifacts/aot-contracts 2>&1 | tee artifacts-aot-publish.log + python scripts/verify-aot-warnings.py artifacts-aot-publish.log + - name: Publish Aureon NativeAOT sample + if: matrix.avalonia == 12 + shell: bash + run: | + set -o pipefail + dotnet publish experiments/WebScene.AureonStudio -c Release -r '${{ matrix.rid }}' \ + -p:PublishAot=true -p:WebSceneAvalonia12Sample=true \ + -o artifacts/aureon-aot 2>&1 | tee artifacts-aureon-publish.log + python scripts/verify-aot-warnings.py artifacts-aureon-publish.log + - name: Exercise checkpoint and archive contracts in native executable + shell: bash + run: ./artifacts/aot-contracts/${{ matrix.executable }} --aot-serialization-probe + - uses: actions/upload-artifact@v4 + if: always() + with: + name: aot-contracts-${{ matrix.rid }}-avalonia-${{ matrix.avalonia }} + path: artifacts*-publish.log diff --git a/.github/workflows/graphics-prerequisites.yml b/.github/workflows/graphics-prerequisites.yml new file mode 100644 index 000000000..d148cea6a --- /dev/null +++ b/.github/workflows/graphics-prerequisites.yml @@ -0,0 +1,120 @@ +name: GPU prerequisite hardware evidence + +on: + workflow_dispatch: + inputs: + rid: + description: Native GPU runner profile (runner must be enrolled with these labels) + required: true + type: choice + options: [win-x64, osx-arm64, linux-x64] + +permissions: + contents: read + +concurrency: + group: gpu-prerequisites-${{ inputs.rid }} + cancel-in-progress: false + +jobs: + prerequisites: + # These labels are an enrollment contract, not evidence that runners already exist. + # No untrusted pull_request trigger on persistent hardware machines. + runs-on: [self-hosted, webscene-gpu, '${{ inputs.rid }}'] + timeout-minutes: 180 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - uses: actions/setup-node@v4 + with: + node-version: '22' + - name: Initialize Windows x64 compiler environment + if: inputs.rid == 'win-x64' + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $before = @{} + Get-ChildItem Env: | ForEach-Object { $before[$_.Name] = $_.Value } + $vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio/Installer/vswhere.exe' + if (!(Test-Path $vswhere)) { throw 'Visual Studio Installer/vswhere is required.' } + $installation = & $vswhere -latest -version '[17.1,)' -products '*' -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath + if ($LASTEXITCODE -ne 0 -or !$installation) { throw 'An x64 Visual C++ toolchain is required.' } + $developerShell = Join-Path $installation 'Common7/Tools/Launch-VsDevShell.ps1' + & $developerShell -Arch amd64 -HostArch amd64 -SkipAutomaticLocation + if (!(Get-Command cl.exe -ErrorAction SilentlyContinue)) { throw 'Visual C++ initialization did not expose cl.exe.' } + if ($env:VSCMD_ARG_TGT_ARCH -ne 'x64') { throw 'Expected an x64 compiler target.' } + Get-ChildItem Env: | Where-Object { !$before.ContainsKey($_.Name) -or $before[$_.Name] -ne $_.Value } | ForEach-Object { + if ($_.Value.Contains("`n") -or $_.Value.Contains("`r")) { throw "Unexpected multiline environment variable: $($_.Name)" } + "$($_.Name)=$($_.Value)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + } + - name: Verify Kestrel input and evidence boundaries + id: verified-inputs + run: | + python tests/GraphicsCompatibility/prepare-kestrel.py + python -m unittest discover -s eng/graphics/tests -v + node --test tests/GraphicsCompatibility/reference-tests.mjs + - name: Verify enrolled Windows SDK prerequisite + if: inputs.rid == 'win-x64' + shell: pwsh + run: | + # Persistent runners are provisioned by their administrator; do not install + # machine-wide SDKs implicitly during a hardware qualification run. + $sdkRoot = Join-Path ${env:ProgramFiles(x86)} 'Windows Kits/10' + $sdkVersion = '10.0.28000.0' + foreach ($relative in @( + "Include/$sdkVersion/um/Windows.h", + "Include/$sdkVersion/shared/sdkddkver.h", + "Include/$sdkVersion/ucrt/stdio.h", + "Lib/$sdkVersion/um/x86/kernel32.lib", + "Lib/$sdkVersion/um/x64/kernel32.lib", + "Lib/$sdkVersion/ucrt/x86/ucrt.lib", + "Lib/$sdkVersion/ucrt/x64/ucrt.lib", + "bin/$sdkVersion/x64/rc.exe" + )) { + if (!(Test-Path (Join-Path $sdkRoot $relative))) { + throw "Missing $relative. Provision this runner using eng/graphics/install-windows-sdk.ps1 before qualification." + } + } + - name: Build pinned Dawn + run: python eng/graphics/build.py dawn --rid ${{ inputs.rid }} + - name: Build pinned ANGLE + run: python eng/graphics/build.py angle --rid ${{ inputs.rid }} + - name: Verify SDK relocation and mismatch rejection + run: python eng/graphics/check-sdk-integrity.py --sdk artifacts/graphics-sdk/${{ inputs.rid }}/dawn --rid ${{ inputs.rid }} --output artifacts/graphics-evidence/sdk-integrity.json + - name: Configure and build hardware probes + run: | + cmake -S eng/graphics/probes -B artifacts/graphics-probes -G Ninja -DCMAKE_BUILD_TYPE=Release -DWEBSCENE_GRAPHICS_SDK_ROOT=${{ github.workspace }}/artifacts/graphics-sdk/${{ inputs.rid }} + cmake --build artifacts/graphics-probes --parallel 6 + - name: Capture real hardware results + # Do not use CTest's successful process exit when all hardware tests were skipped. + # The evidence runner exits 77 on unavailable hardware, leaving this job non-successful. + run: python eng/graphics/run-probes.py --rid ${{ inputs.rid }} --sdk artifacts/graphics-sdk/${{ inputs.rid }} --probes artifacts/graphics-probes --output artifacts/graphics-evidence/native-probes.json + - name: Capture unchanged Kestrel hardware Chrome references + # Independent evidence remains useful after native build/probe failure. + # The earlier failure still fails the job; this does not waive a GPU gate. + if: ${{ !cancelled() && steps.verified-inputs.outcome == 'success' }} + # Requires an interactive desktop and installed Chrome (or CHROME_BIN). + # Unknown Chrome revisions retain traces but cannot qualify presentation timing. + run: node tests/GraphicsCompatibility/capture-chrome-reference.mjs --output artifacts/chrome-reference + - uses: actions/upload-artifact@v4 + if: always() + with: + name: graphics-prerequisites-${{ inputs.rid }} + path: | + artifacts/graphics-evidence/ + artifacts/graphics-sdk/**/webscene-graphics-package.json + artifacts/graphics-sdk/**/build-info/ + if-no-files-found: warn + - uses: actions/upload-artifact@v4 + if: always() + with: + name: graphics-chrome-reference-${{ inputs.rid }} + path: | + artifacts/chrome-reference/reference.json + artifacts/chrome-reference/*.png + artifacts/chrome-reference/*.json.gz + compression-level: 0 + retention-days: 90 + if-no-files-found: warn diff --git a/.github/workflows/graphics-sdk-build.yml b/.github/workflows/graphics-sdk-build.yml new file mode 100644 index 000000000..baabd619a --- /dev/null +++ b/.github/workflows/graphics-sdk-build.yml @@ -0,0 +1,72 @@ +name: Graphics SDK build validation + +on: + pull_request: + paths: + - 'eng/graphics/**' + - '.github/actions/graphics-sdk/**' + - '.github/workflows/graphics-sdk-build.yml' + workflow_dispatch: + inputs: + clean: + description: Rebuild SDKs without restoring the cache + type: boolean + default: false + push: + branches: [main] + paths: + - 'eng/graphics/**' + - '.github/actions/graphics-sdk/**' + - '.github/workflows/graphics-sdk-build.yml' + +permissions: + contents: read + +concurrency: + group: graphics-sdk-build-${{ github.event.pull_request.number || github.ref }} + # Preserve evidence from long clean SDK builds; the latest pending commit + # runs after the active build completes. + cancel-in-progress: false + +jobs: + build: + name: Build ${{ matrix.component }} ${{ matrix.rid }} (no hardware qualification) + strategy: + fail-fast: false + max-parallel: 4 + matrix: + component: [dawn, angle] + include: + - rid: win-x64 + os: windows-2022 + - rid: linux-x64 + os: ubuntu-22.04 + rid: [win-x64, linux-x64] + runs-on: ${{ matrix.os }} + timeout-minutes: 120 + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/graphics-sdk + with: + rid: ${{ matrix.rid }} + component: ${{ matrix.component }} + clean: ${{ inputs.clean || false }} + - name: Compile and link diagnostic probe + run: | + cmake -S eng/graphics/probes -B artifacts/graphics-probes -G Ninja -DCMAKE_BUILD_TYPE=Release -DWEBSCENE_GRAPHICS_COMPONENTS=${{ matrix.component }} -DWEBSCENE_GRAPHICS_SDK_ROOT=${{ github.workspace }}/artifacts/graphics-sdk/${{ matrix.rid }} + cmake --build artifacts/graphics-probes --parallel 2 + - name: Verify shared-handle ownership (no GPU required) + run: ctest --test-dir artifacts/graphics-probes -R "^webscene_nt_handle_tests$" --output-on-failure + # Hosted compilation proves no hardware, pixels, driver support, or conformance. + # GPU execution belongs to the dedicated graphics-prerequisites hardware workflow. + - uses: actions/upload-artifact@v4 + if: always() + with: + name: graphics-build-only-${{ matrix.component }}-${{ matrix.rid }} + path: | + artifacts/graphics-sdk/${{ matrix.rid }}/${{ matrix.component }}/ + artifacts/graphics-probes/CMakeCache.txt + retention-days: 14 + if-no-files-found: warn + # ANGLE installs .clang-format files covered by the sealed SDK inventory. + include-hidden-files: true diff --git a/.github/workflows/native-runtime-packages.yml b/.github/workflows/native-runtime-packages.yml index f02e7f214..368844add 100644 --- a/.github/workflows/native-runtime-packages.yml +++ b/.github/workflows/native-runtime-packages.yml @@ -19,6 +19,8 @@ on: - release/* paths: - '.github/workflows/native-runtime-packages.yml' + - '.github/actions/graphics-sdk/**' + - 'eng/graphics/**' - 'experiments/WebScene.NativeEngine.Probe/**' - 'packaging/WebScene.NativeEngine.Runtime/**' - 'scripts/build-native-engine-runtime.sh' @@ -267,7 +269,21 @@ jobs: rm -rf "$root" fi fi + - name: Prepare pinned Dawn runtime dependency + if: matrix.rid != 'linux-x64' + uses: ./.github/actions/graphics-sdk + with: + rid: ${{ matrix.rid }} + component: dawn + - name: Prepare pinned Windows ANGLE composition dependency + if: matrix.rid == 'win-x64' + uses: ./.github/actions/graphics-sdk + with: + rid: ${{ matrix.rid }} + component: angle - name: Build, pack, and test macOS runtime + env: + WEBSCENE_NATIVE_SKIP_HARDWARE_TESTS: '1' if: matrix.rid == 'osx-arm64' shell: bash run: | @@ -278,6 +294,7 @@ jobs: fi scripts/build-native-engine-runtime.sh \ --rid '${{ matrix.rid }}' \ + --graphics-sdk "$GITHUB_WORKSPACE/artifacts/graphics-sdk/${{ matrix.rid }}" \ --package-version '${{ needs.metadata.outputs.package-version }}' \ --v8-root "$v8_root" \ --v8-revision '${{ matrix.v8_revision }}' \ @@ -315,6 +332,8 @@ jobs: --output /workspace/artifacts/nuget-packages " - name: Build, pack, and test Windows runtime + env: + WEBSCENE_NATIVE_SKIP_HARDWARE_TESTS: '1' if: matrix.script == 'windows' shell: pwsh run: | @@ -325,6 +344,7 @@ jobs: } ./scripts/build-native-engine-runtime.ps1 ` -Rid '${{ matrix.rid }}' ` + -GraphicsSdk "$env:GITHUB_WORKSPACE/artifacts/graphics-sdk/${{ matrix.rid }}" ` -PackageVersion '${{ needs.metadata.outputs.package-version }}' ` -V8Root $v8Root ` -V8Revision '${{ matrix.v8_revision }}' ` diff --git a/Directory.Build.props b/Directory.Build.props index b55b6501f..75746bb47 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -22,6 +22,9 @@ 11.3.4 + + 12.1.1 + $(DefineConstants);WEBSCENE_AVALONIA12 + + + diff --git a/Directory.Packages.props b/Directory.Packages.props index 4baa1a872..cf4c6b36e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -28,4 +28,10 @@ + + + + + + diff --git a/benchmarks/WebScene.NativeEngine.Benchmarks/NativeResizeCadenceProbe.cs b/benchmarks/WebScene.NativeEngine.Benchmarks/NativeResizeCadenceProbe.cs index 679812bb2..2b0caefe9 100644 --- a/benchmarks/WebScene.NativeEngine.Benchmarks/NativeResizeCadenceProbe.cs +++ b/benchmarks/WebScene.NativeEngine.Benchmarks/NativeResizeCadenceProbe.cs @@ -1,4 +1,7 @@ using System.Diagnostics; +using System.Collections.Concurrent; +using Avalonia; +using Avalonia.Rendering; using System.Text.Json; using Avalonia.Controls; using Avalonia.Threading; @@ -115,6 +118,15 @@ internal static int Run(string[] args) window.Show(); Dispatcher.UIThread.RunJobs(); + // Avalonia 11.3.4 hides the service locator from its reference assembly. + // Read it only for this benchmark; do not replace or drive the timer. + var locator = typeof(AvaloniaLocator).GetProperty("Current")?.GetValue(null); + var renderTimer = locator?.GetType().GetMethod("GetService", [typeof(Type)]) + ?.Invoke(locator, [typeof(IRenderTimer)]) as IRenderTimer; + var timerTicks = new ConcurrentQueue(); + Action observeRenderTick = _ => timerTicks.Enqueue(Stopwatch.GetTimestamp()); + var tickEvent = typeof(IRenderTimer).GetEvent("Tick"); + if (renderTimer is not null) tickEvent?.AddEventHandler(renderTimer, observeRenderTick); try { Pump(view.LoadAsync(source, library)); @@ -134,7 +146,11 @@ internal static int Run(string[] args) var measurementStarted = Stopwatch.GetTimestamp(); var submitted = RunCadence( window, seconds, frequency, baseWidth, baseHeight, widthSpan, heightSpan); - var measurementElapsed = Stopwatch.GetElapsedTime(measurementStarted); + var measurementEnded = Stopwatch.GetTimestamp(); + var measurementElapsed = Stopwatch.GetElapsedTime(measurementStarted, measurementEnded); + var measuredTimerTicks = timerTicks.Where(t => t >= measurementStarted && t <= measurementEnded).ToArray(); + var timerIntervals = measuredTimerTicks.Zip(measuredTimerTicks.Skip(1), + static (a, b) => (b - a) * 1000d / Stopwatch.Frequency).ToArray(); WaitForResizeDrain(view, TimeSpan.FromSeconds(3)); process.Refresh(); var cpu = process.TotalProcessorTime - cpuBefore; @@ -191,7 +207,16 @@ internal static int Run(string[] args) var json = JsonSerializer.Serialize( new { - schema = "webscene-native-resize-cadence-v1", + schema = "webscene-native-resize-cadence-v2", + measurementScope = "headless-cpu-draw-callback", + physicalPresentationVerified = false, + headlessRenderTimer = new + { + available = renderTimer is not null && tickEvent is not null, + tickCount = measuredTimerTicks.Length, + ticksPerSecond = measuredTimerTicks.Length / measurementElapsed.TotalSeconds, + intervalMilliseconds = Summary(timerIntervals) + }, sourceKind = ReadOption(args, "--url") is null ? "deterministic-fixture" : "url", composition, certificationTelemetryEnabled = !certificationDiagnostics.StartsWith( @@ -213,9 +238,9 @@ internal static int Run(string[] args) snapshot.ResizeFrames.SubmittedPairs - snapshot.ResizeFrames.AppliedPairs, baseline.ResizeFrames.SubmittedPairs - baseline.ResizeFrames.AppliedPairs), renderedFrames = delta.RenderedScenes, - presentations = presentations.Length, + drawCallbackCompletions = presentations.Length, renderedFramesPerSecond = renderedFps, - presentationFramesPerSecond = presentationFps, + drawCallbackCompletionsPerSecond = presentationFps, layoutPasses = delta.LayoutPasses, layoutPassesPerAppliedResize = delta.LayoutPasses / (double)Math.Max( 1UL, @@ -264,8 +289,8 @@ internal static int Run(string[] args) publicationToRenderLatencies), renderLatencyMilliseconds = Summary(renderLatencies), renderIntervalMilliseconds = Summary(renderIntervals), - presentationIntervalMilliseconds = Summary(presentationIntervals), - practicalVsyncGate = new + drawCallbackIntervalMilliseconds = Summary(presentationIntervals), + cpuCadenceGate = new { maximumP95LatencyMilliseconds = 16.7, minimumFramesPerSecond = 58, @@ -276,6 +301,7 @@ internal static int Run(string[] args) ? new { identity = reference.Identity, + comparisonScope = "headless-draw-callback-versus-reference; not physical presentation", referenceFramesPerSecond = reference.FramesPerSecond, nativeFramesPerSecond = presentationFps, framesPerSecondDelta = @@ -306,6 +332,7 @@ internal static int Run(string[] args) } finally { + if (renderTimer is not null) tickEvent?.RemoveEventHandler(renderTimer, observeRenderTick); Pump(view.DisposeAsync().AsTask()); window.Close(); Dispatcher.UIThread.RunJobs(); diff --git a/docs/graphics/avalonia-gpu-host-boundary.md b/docs/graphics/avalonia-gpu-host-boundary.md new file mode 100644 index 000000000..b4a1cba52 --- /dev/null +++ b/docs/graphics/avalonia-gpu-host-boundary.md @@ -0,0 +1,319 @@ +# Avalonia 11.3.4 GPU host boundary + +Source review, 2026-09-07. No framework GPU execution pass is claimed. +WebScene pins Avalonia 11.3.4 and currently draws through ISkiaSharpApiLeaseFeature. +The standalone Graphite probe does not change that host device. + +Avalonia already has a cross-platform GPU import contract, exposed through +ICompositionGpuInterop. Supported image/semaphore handle types and synchronization +capabilities must be queried at runtime; device LUID/UUID are also exposed. A +platform's existence is not evidence that a particular backend imports our handle. +Use the installed 11.3.4 API rather than importing another presentation framework. + +[CompositionDrawingSurface](https://github.com/AvaloniaUI/Avalonia/blob/11.3.4/src/Avalonia.Base/Rendering/Composition/CompositionDrawingSurface.cs) +provides UpdateAsync, UpdateWithKeyedMutexAsync and UpdateWithSemaphoresAsync. Its +public contract says completion permits the caller to destroy/dispose the source +image. These are distinct from merely completing import, and from our generic +native queue completion notification. An adapter must follow the chosen backend's +synchronization contract before releasing/reusing a leased source allocation. + +The [server implementation](https://github.com/AvaloniaUI/Avalonia/blob/11.3.4/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionDrawingSurface.cs) +checks import completion/context validity and obtains a snapshot from the imported +image. The [OpenGL/Skia implementation](https://github.com/AvaloniaUI/Avalonia/blob/11.3.4/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaExternalObjectsFeature.cs) +wraps the native texture in a Skia surface, snapshots it, and flushes Skia/OpenGL. +The keyed-mutex and semaphore paths surround this with acquire/release or wait/signal. +No CPU bitmap loop appears in this reviewed path. Whether snapshot performs a +GPU-local copy must be verified against the concrete Skia backend and traced; +source inspection alone does not prove zero-copy or completion correctness. + +## Integration direction + +Reuse Avalonia's import/update machinery for a supported host backend. Keep native +platform code limited to producing/importing the external allocation and exchanging +the synchronization primitives actually supported by that contract. Do not assume +our new D3D12 fence helper is required by Avalonia, whose selected backend may +instead expose keyed mutex or semaphore synchronization. + +Before implementing the adapter, obtain capabilities from a running 11.3.4 host +on this machine, including actual image handle strings, synchronization flags and +device identity. Then select a supported route and test import, update, source +retention, resize and context loss. An unsupported result must stay explicit. +Graphite shared-device composition reduces internal interop, but a final external +image boundary still exists when Avalonia owns a different GPU device/context. + +Preserve HTML stacking, clips, opacity and hit testing when placing the composition +surface. A separate uncomposited native window does not satisfy the scene contract. +Repeat the boundary analysis for Uno; Avalonia's API cannot be assumed to apply to it. + +## Runtime capability evidence + +The new `experiments/WebScene.GpuHost.Probe` ran successfully on the current M4 +macOS desktop with Avalonia 11.3.4. Default platform selection returned a non-lost +interop object but empty SupportedImageHandleTypes and SupportedSemaphoreTypes. +The probe exits cleanly after posting shutdown to the dispatcher (synchronous +shutdown during startup initially triggered a lifetime initialization error). + +This rules out selecting an external-handle import route from this host's reported +capabilities. It does not prove that all Avalonia macOS configurations lack interop. +Next inspect the shared-context import contract and available host backend options; +do not implement an IOSurface handle adapter assuming this host will accept it. + +The probe now queries IOpenGlTextureSharingRenderInterfaceContextFeature through +Compositor.TryGetRenderInterfaceFeature. It reports CanCreateSharedContext=true on +the same default macOS host, with external handle lists still empty. This is a +capability result, not a successful import. The public feature creates a compatible +GL context and composition texture, avoiding arbitrary user-supplied texture wrappers. +Avalonia's Skia importer specifically verifies the context share group. + +Next exercise context creation, drawing into its composition texture, import and +surface update. Then determine the supported Metal/Dawn-to-GL allocation bridge; +shared GL context availability alone does not make a Dawn Metal texture importable. + +The shared-context import/update sequence now executes successfully in the probe: +a 32x32 shared GL texture is cleared through a complete framebuffer, flushed, +imported, and copied/snapshotted through an awaited drawing-surface update. Async +import disposal precedes texture/context teardown. No CPU pixel upload/readback +is used by the probe. This establishes successful API execution, not displayed +pixel correctness: the surface is not yet attached to a visual, and the source +is GL-produced rather than Dawn/Metal-produced. These remain separate gates. + +The host probe also attaches the updated surface to a composition visual and awaits +its render-thread commit, then detaches and commits before releasing resources. +Both commits complete on M4. Per Avalonia's API contract, this is render-thread +state application, not a display/GPU-completion fence or a pixel correctness test. +The next boundary test still needs independent displayed-pixel verification and +connection of the Dawn-produced allocation to the host-compatible source. + +## Visible output evidence + +A targeted capture of the exact probe window during `--inspect` shows the expected +blue composition surface and white remaining background. See +[evidence](evidence/avalonia-host/shared-gl-window.png). This independently confirms +visible output for the shared-OpenGL host route. It is not exact color validation; +window capture/display color management differs from raw texture verification. +The Dawn/Metal-to-host allocation bridge remains unimplemented and unverified. + +### Production-source IOSurface import operation + +NativeMacOSGpuImageImport now imports a checked native consumer's BGRA8 IOSurface +into a rectangle texture already bound in the host's current CGL context. GL +texture/state creation remains with the host; this operation uses only Apple's +CGL/IOSurface entrypoints, avoiding ambiguous GL symbol lookup alongside ANGLE. +It borrows through NativeGpuImageConsumerV3 and does not complete the consumer, +wait on producer work or enable GPU scene acquisition. + +The separate native fixture creates an accelerated CGL 3.2 context and rectangle +texture. The managed test imports a native IOSurface lease and checks its GL +level dimensions (17x4), then deletes GL references before completing the lease. +All six NativeGpuSceneInteropTests passed without skips on net8.0 and net10.0. + +This verifies the managed-to-native storage import operation. It submits no +draws and proves no rendered pixels, adapter pairing, producer synchronization +or final Avalonia presentation. The retained renderer still needs the complete +import/cache/completion path before it can advertise GPU scene capability. + +### Dawn-produced pixels through the managed import + +The CGL import fixture now optionally initializes its versioned IOSurface using +the pinned Dawn Metal backend. It requires an integrated/discrete adapter and +IOSurface/shared-event features, imports via dawn_shared_image, clears on the +GPU, ends shared access and waits for diagnostic queue completion before +publishing the ready lease. Callback state remains owned with the device. + +The managed test obtains that lease through the real native ABI, imports it +using NativeMacOSGpuImageImport, and reads all 68 pixels from the CGL framebuffer. +RGBA [51,102,153,255] matched within one channel unit, including BGRA storage +conversion. The six-test interop suite passed on net8.0 and net10.0 without skips. + +The fixture's waits and GL readback are explicit diagnostics, not an ordinary +presentation implementation. Constant-color output does not verify orientation, +transparent edges or compositing. Asynchronous production readiness, GL fences, +host texture conversion and retained scene rendering still need integration. + +### GL fence retirement component + +NativeMacOSGpuConsumerFence obtains GL procedures from a caller-supplied host +resolver (intended to be GlInterface.GetProcAddress), inserts a GPU completion +fence and flushes. TryComplete uses glClientWaitSync with zero timeout. It retains +the consumer on pending/failed results, requires the original thread/current CGL +context, deletes a signaled fence and completes the native consumer once. +Retired polling is idempotent. The host must retain and poll this component; +there is no GC-based completion or automatic render scheduling. + +The real CGL import test now exercises this path, including wrong-thread polling +rejection and duplicate consumer completion rejection. All six interop tests +passed on net8.0 and net10.0. The fixture now queues a GPU framebuffer blit from +its imported rectangle into independent 2D texture storage before fence insertion. +The native provider remains alive before polling and expires after successful +retirement. Only then does diagnostic readback verify all 68 destination pixels. +This measures one GPU-local copy, with no CPU pixel transport between APIs; it +is not a forced-delayed-GPU stress test. Wrong-thread rejection uses a dedicated +thread because a queued Task can execute inline on a waiting worker thread. +Fixture-only failure cleanup drains GL before releasing native image ownership. +Production context-loss cleanup and renderer scheduling remain unfinished. + +### Direct pinned Ganesh sampling of the IOSurface rectangle + +The .NET interop fixture now exercises `NativeMacOSGpuImageImport.TryWrapRectangleTexture` +with the repository's SkiaSharp 2.88.9 Ganesh GL backend. It imports the Dawn-written +17×4 IOSurface into a CGL rectangle texture, wraps that borrowed texture as a +texture-backed SKImage, and draws it into a GPU SKSurface. The image is clipped +and blended at alpha 128 over opaque blue. All 192 destination pixels are checked +against the expected clipped extent and channel values after Skia submission and +native consumer fence retirement. Both net8.0 and net10.0 pass all seven interop +tests without skips on the macOS arm64 fixture host. + +The route uses the existing pinned Ganesh API and no explicit intermediate texture +blit or CPU upload. The only explicit readback is diagnostic destination validation +after the source fence. Internal driver/Skia copy counts still require a trace; +this is not a claim that every underlying operation is copy-free. The native source +owner is observed released after the fence, independently of the destination. +The wrapper borrows the caller's GL texture and consumer; SKImage disposal is not +GPU completion. The caller supplies negotiated origin and alpha interpretation. + +This is an actual GPU Skia composition test, not yet an Avalonia-window retained +scene test. Uniform source color cannot qualify texture orientation or transparent +source edges. Host context acquisition, import caching, retained replay lifetime, +ordered DOM/canvas composition, and device-loss handling remain integration work. +Graphite migration is not required by this demonstrated direct Ganesh route. + +### Actual Avalonia window using public pinned graphics leases + +`WebScene.GpuHost.Probe --ganesh-window` uses an ICustomDrawOperation in a real +Avalonia 11.3.4 macOS window. It acquires ISkiaSharpApiLease.GrContext and +TryLeasePlatformGraphicsApi, checks for IGlContext, and imports/wraps the native +Dawn IOSurface with the production-source helpers. The pinned framework flushes +Skia when entering the platform lease and resets its cached GL state on leaving +it (verified in tag 11.3.4 DrawingContextImpl.ApiLease.PlatformApiLease). No +framework patch is needed for these operations. + +The window draws the same imported SKImage 32 times with clipping and alpha, +using one GL import. It disposes the retained SKImage after its last draw, enters +the platform lease to flush Skia, inserts the host GL fence, polls on subsequent +render callbacks, completes the native consumer and deletes the GL texture. +The fixture's synchronous producer setup runs before the window opens. Ordinary +window rendering has no explicit texture copy or pixel readback. + +Run with the already-built enabled native runtime and test fixture: + +```sh +WEBSCENE_TEST_NATIVE_LIBRARY="$PWD/artifacts/graphics-build/native-v8-enabled/libwebscene_native_engine.dylib" \ +WEBSCENE_TEST_GPU_FIXTURE_LIBRARY="$PWD/artifacts/graphics-build/native-v8-enabled/libwebscene_graphics_iosurface_fixture.dylib" \ +dotnet run --project experiments/WebScene.GpuHost.Probe -- --ganesh-window +``` + +Add `--verify-window-pixels` for two explicit diagnostic destination reads inside +the actual host callback: a blended interior pixel and an exterior clip pixel. +Both executions completed 32 frames and fence retirement on the Apple M4 macOS +arm64 host; raw result JSON is in `evidence/ganesh-host`. This proves host render +surface pixels, not physical scanout. Internal driver copies still need tracing. + +This is a diagnostic control, not WebScene's retained DOM renderer. Production +scene acquisition, paint ordering, live readiness delivery, resizing, teardown +failure recovery and device recreation remain open. The diagnostic fails/exits on +unsupported or lost host contexts; that is not qualified production loss recovery. + +### Retained import owner and host thread migration + +`NativeMacOSRetainedGpuImage` now owns the GL import, SKImage and native consumer +in the Avalonia backend. Import admission preserves the scene lease on +backpressure. Draw reuses the imported image under the owning Skia/CGL context; +Retire prevents further draws and flushes through the host platform lease before +creating a fence. TryComplete polls without a CPU GPU wait and releases the GL +texture after native consumer completion. Failed fence creation keeps ownership +and permits retirement retry. This object deliberately has no GC/Dispose-based +GPU completion: the scene cache must retain it until retirement succeeds. + +The real-window probe now uses this backend owner rather than local import and +fence fields. A first run exposed an incorrect fixed-thread ownership assumption: +the first callback ran on managed thread 1 and the next on thread 4, with identical +native Skia and CGL handles. Avalonia's active drawing lease serializes the +GRContext while allowing that migration. The owner therefore checks the leased +Skia/CGL context identity. NativeMacOSGpuConsumerFence also offers a host-platform- +lease polling route for migration; its standalone polling still rejects a foreign +thread. No context identity check was removed. + +Both real-window modes again completed 32 redraws, one import and retirement; +the diagnostic mode verified both host pixels. General compositor loss recovery +and automatic retirement scheduling are still unfinished, and the retained DOM +renderer still needs to consume this owner. + +### Opt-in ordered retained GPU paint replay + +NativeCanvasSceneRenderer now accepts `orderedGpuImages: true` when applying a +scene. It compiles contiguous static DOM commands into retained SKPictures and +keeps GPU image command 256 as a dynamic slot between them. Clip, scale, rotation +and opacity scopes replay around both picture segments and image slots. Scope +pairs are validated before applying the diff; replay restores the host canvas +state even if an image draw throws. GPU image changes can therefore replay with +new slot contents without recompiling static DOM pictures. + +The actual Avalonia window now feeds a constructed scene command stream through +this shared renderer instead of manually drawing its image. That stream places +DOM behind the image and a yellow DOM rectangle over it, with clip and group +opacity around the GPU slot. The 32-frame run with one import verified three +host-surface pixels (blend, exterior clip and foreground DOM), then retired its +GPU consumer. Result: `evidence/ganesh-host/ordered-scene-pixels.json`. + +Validation: 20 focused ordered-renderer/culling/native-interop tests pass on both +net8.0 and net10.0; the complete Avalonia net10.0 suite passes 270 tests with no +skips; the Uno backend builds without warnings/errors. CPU-only callers retain +the existing rendering path. + +This opt-in integration is not yet enabled by ordinary native scene acquisition. +The diagnostic constructs its command stream; V8 GPU canvas publication and v3 +image-slot binding remain unfinished. Legacy Canvas2D layers have no ordered +placement marker yet, so this mode rejects scenes with those layers instead of +silently painting them in the old global split. Full mixed Canvas2D/SVG/GPU DOM +coverage, transformed bounds/culling qualification, and generation cache updates +remain required before advertising the GPU scene capability in production. + +### Explicit retained Canvas2D placements in mixed scenes + +The ordered path now recognizes command 257 as a Canvas2D layer placement, using +node_id to resolve the current retained layer and its layout/bitmap dimensions. +Its native contract requires the separate ORDERED_CANVAS capability bit (2); +ordinary callers still advertise neither ordered-canvas nor GPU-image capability. +Static DOM pictures, GPU slots and Canvas2D slots replay in command order under +the same clip/transform/opacity state. Canvas isolation uses the existing layer +semantics, shared with the legacy renderer. + +Before applying a diff, the renderer checks that every visible Canvas2D layer has +exactly one placement and that every placement names a visible retained layer. +Offscreen source canvases do not require a paint placement. Layer-only layout +updates preserve the compiled paint list; removing a layer without updating its +placement is rejected before changing live state. This supersedes the earlier +blanket rejection of all Canvas2D layers in the opt-in path. + +Tests cover GPU→Canvas2D→GPU→DOM interleaving, layer-only reposition/scale and stale +placement rejection. Four ordered-renderer tests pass on net8.0/net10.0; all 271 +Avalonia net10.0 tests pass without skips, and Uno builds without warnings/errors. +The real window now includes a retained Canvas2D layer and verifies four host +pixels across the mixed scene, with one GPU import across 32 draws and fence +retirement (`evidence/ganesh-host/mixed-canvas-pixels.json`). + +Native DOM generation does not yet emit these placements or acquire GPU images +through the ordinary scene path. The window continues to construct its diagnostic +scene. Full SVG/text/destructive Canvas2D fixtures, transform bounds, native slot +binding and production lifecycle handling remain required for epic completion. + +### Native document generation of mixed paint order + +The engine now collects published GPU images before building the DOM paint stream +and requests ordered Canvas2D markers when that image set is nonempty. The native +DOM traversal emits command 257 at the Canvas2D element's content position, +including recursive, elevated and fixed-position traversal. GPU command 256 and +scene-local image-slot resolution already existed in the source; earlier progress +notes saying all native GPU command generation was absent were inaccurate. The +new work supplies the previously missing native Canvas2D placements in GPU scenes. + +The default native_document::build_scene call remains CPU-compatible without +ordered Canvas2D markers. The V8 runtime test creates a real 2D canvas sibling, +checks GPU→Canvas2D→DOM order from native traversal, verifies default output has no +257 commands, and checks a fixed-position canvas emits exactly one marker. +`webscene_graphics_v8_runtime_tests` passes in the macOS enabled native build. + +This does not implement navigator.gpu or canvas.getContext('webgpu'). The existing +internal publication fixture supplies the GPU image. Ordinary managed v3 scene +acquisition/slot binding, live producer scheduling and browser API coverage remain +unfinished; Kestrel has not yet been qualified on this rendering path. diff --git a/docs/graphics/coherent-gpu-scene-publication.md b/docs/graphics/coherent-gpu-scene-publication.md new file mode 100644 index 000000000..e87dfbfe9 --- /dev/null +++ b/docs/graphics/coherent-gpu-scene-publication.md @@ -0,0 +1,905 @@ +# Coherent GPU scene publication + +Status: initial bounded scene capture/commit integration implemented; failure +recovery and end-to-end coherence/performance remain unqualified. This preserves +epic #22 scope and its Dawn/Skia GPU-resident route. + +## Required invariant + +A scene captured after an application rendering opportunity must retain both its +CPU/2D display lists and the exact GPU image outputs requested during that +opportunity. Publish that immutable scene only after all of those outputs are +ready. Do not combine the captured lists with whichever GPU image is newest at +publication time. Keep replaying the previously accepted complete scene meanwhile. + +Evidence: `evidence/kestrel/split-gpu-overlay-publications.json`. In particular, +revisions 16–18 advance the overlay and GPU image separately. The two counters +are independent; equal numeric values are not a general correctness condition. + +## Changes to implement + +1. Add an internal completion ticket to `dawn_iosurface_submission.h`. A ticket + retains one exact image allocation/generation/content serial and shares the + submission's queue-completion and validation state. Capturing a ticket must + not expose a consumer handle before both completion signals succeed. Tickets + must remain resolvable if the provider has already drained its ordinary ready + queue. Destructive `take_ready()` alone cannot provide this ownership model. + Retention is metadata/lease retention, with no pixel readback, blit or copy. +2. In `dawn_iosurface_canvas_host.h`, associate the latest submitted output with + its ticket. Capture tickets at the rendering-opportunity boundary. Do not + substitute a subsequently submitted image. A configured canvas without new + work reuses its previously complete image. Reset/unconfigure/detach must + invalidate obsolete captured generations without prematurely releasing + allocations still in producer or consumer use. +3. Extend the runtime/document snapshot boundary to provide image placeholders + and tickets for outputs that are not ready yet. Today `build_scene()` emits a + GPU command only for a completed image, which cannot represent the first + pending frame. Each placeholder must carry stable canvas identity and exact + target version, later mapped to a scene-local image index. +4. Split `webscene_native_engine_scene.inc::publish_scene()` into capture and + commit phases. Capture freezes command/string/layer arrays, dimensions, + generation identifiers and GPU dependencies together. Commit resolves only + those dependencies, finalizes image tables and their hash, and exposes the + immutable scene to C ABI v3. Allocate public revision/base relationships at + commit against the consumer's actual predecessor; a superseded capture must + not leave a diff targeting a revision the consumer never received. +5. Bound staged captures and image tickets by the existing three-image budget. + Select/coalesce complete opportunities as whole scenes, never per-canvas + fragments. On pressure retain the last displayed scene and defer producer + RAF admission using existing host wakes. Continue servicing input, promise + completion and device-loss callbacks. Do not add an unbounded snapshot queue, + a GPU wait on the UI/engine thread, or a one-frame-only GPU allocation policy. +6. Preserve dirty work that arrives after capture. Committing a frozen scene + must not clear the worker's `scene_pending` flag for later DOM/2D mutations. + Audit the `starting_scene_generation`/`scene_pending` loop in + `webscene_native_engine_worker.inc`. Completion wakeups should retry commit; + no polling timer should become necessary for ordinary producer completion. +7. On failure, cancel the entire staged opportunity and retain the prior complete + scene. Request a coherent replacement/checkpoint where necessary. Never + declare a failed producer ready or let a failed canvas strand all future + scenes indefinitely. Device loss, navigation, resize and hidden-window + retirement need explicit state transitions and tests. + +## Verification before accepting the fix + +- Deterministic delayed producer: draw geometry and an overlay marker at A, + capture B with its producer unresolved, then mutate live state to C. While B + is pending, presentation stays at complete A. Completing B presents B/B, + never C/B or B/A. Then complete C and verify C/C. +- Two canvases completing out of order: no partial scene is visible. Include a + canvas with unchanged content and a first-ever pending GPU image. +- Fill all bounded slots, supersede/cancel a capture and verify exact lease + release only after relevant GPU reads/writes complete. Check fixed memory + limits and continued input service, without synchronous GPU waits. +- Cover generation changes, reset at unchanged bitmap size, detach/navigation, + unconfigure, validation failure, queue/device loss and shutdown during pending + capture. No stale output can enter a replacement generation. +- Verify diff predecessor integrity when several captures are superseded before + a consumer acknowledgement. Replay the resulting stream into the real retained + renderer and check pixels/order, not only revision arithmetic. +- Run unchanged Kestrel native wheel and right-button pan plus live resize. + Capture presented frames with aligned geometry/overlay landmarks; producer + counters alone do not certify display coherence or smoothness. Compare timing + distributions against the browser and report instrumentation overhead. +- Re-run existing GPU lease/retirement, ordered canvas paint, native runtime and + ordinary-view GPU tests. Confirm zero explicit presentation pixel copies. + +Do not count these requirements as passing until implemented and evidenced. The +current CSS fixes and startup success do not qualify this presentation change. + + +## Completion-ticket foundation (2026-09-08) + +`dawn_iosurface_submission::capture_snapshot()` retains the exact output while +sharing the submission completion state. The snapshot exposes only metadata +until both queue completion and handoff validation succeed. Its one-shot +`take_ready()` also accepts the provider-consumed state, so draining the original +ready queue does not invalidate a captured scene's ownership. Retention pressure +returns no ticket; failed or discarded submissions cannot yield an image. + +The Dawn fixture captures before its completion waits, drains the ordinary +reference, and verifies the snapshot still resolves the identical allocation and +content serial exactly once. The Ganesh window fixture passes 32 frames, two +imports, eight diagnostic pixel readbacks and GPU retirement, with zero explicit +transport copies. Native GPU runtime regressions also pass. Evidence: +`evidence/kestrel/gpu-completion-ticket-fixture.json`. + +This fixture does not deterministically delay completion, and does not prove +pending-scene coherence or physical display timing. The provider/runtime/scene +capture integration and delayed-producer tests above remain mandatory. + + +## Provider capture boundary (2026-09-08) + +`dawn_iosurface_canvas_host::capture_latest_submission()` exposes the latest +submitted ticket before ordinary ready-queue draining. It rejects capture while +an acquired texture still belongs to an open rendering opportunity, and discard +retirement clears the lookup. A weak submission lookup adds no hidden allocation +retention; only the captured ticket retains the output. Capture must therefore +happen before the ordinary ready queue consumes the provider reference. + +The fixture now captures through the provider, drains its ready queue and returns +the captured image for the real Ganesh pixel/retirement test, verifying allocation +and content serial identity and one-shot transfer. Discarded work yields no +capture. The 32-frame fixture and native GPU runtime test pass; evidence is in +`evidence/kestrel/gpu-provider-ticket-fixture.json`. Runtime snapshot capture, +first-pending-image commands, atomic scene commit and delayed-completion tests +remain unimplemented; this provider API alone does not fix presentation. + + +## Deterministic completion-phase coverage (2026-09-08) + +The production Dawn submission now uses `producer_completion_gate` under its +existing mutex. The gate requires queue completion and validation, even when +one reports failure; a validation error alone cannot certify finished GPU writes. +Duplicate phase delivery is ignored, preventing a terminal consumed state from +being overwritten by a repeated completion signal. + +The platform-independent completion tests withhold either phase, check that +polling cannot make it ready, and cover all success/failure combinations and +duplicate delivery before/after completion. Completion and V8 GPU runtime tests +pass (0.92s together). The real Ganesh fixture also passes 32 frames and retirement +with zero explicit transport copies. Evidence: +`evidence/kestrel/producer-completion-gate.json`. + +This deterministic test covers the shared production gate, not an artificially +stalled hardware queue or whole captured scene. Runtime snapshot integration and +the A/B/C delayed-scene regression remain outstanding. + + +## Runtime output capture (2026-09-08) + +The runtime now captures a backend-neutral `webscene_gpu_image_snapshot` at the +end of each submitted rendering opportunity, before `publish_ready_gpu_canvases` +drains provider outputs. Canvas state holds the latest dependency; a scene can +retain that shared dependency independently of future canvas changes. The Dawn +adapter caches successful resolution, preserving stable image identity without +pixel copying. Bitmap reset and configure/unconfigure clear the canvas's current +reference; existing captures keep ownership of their exact image. + +The native runtime regression resolves the captured output after normal image +publication, checks allocation/serial identity and repeated resolution, then +verifies unconfigure clears current canvas state without destroying an existing +capture. Both native suites pass (12.44s). Unchanged Kestrel handles all forty +wheel events with no bitmap mutations or app errors; results are in +`evidence/kestrel/runtime-output-snapshot.json`. + +The scene builder still consumes completed `gpu_image` state. Pending-image +command representation, frozen CPU scene capture and atomic commit remain +mandatory. Failed/pressure-rejected capture also needs explicit staged-scene +failure handling at that integration point; no coherence claim is made yet. + + +## Pending image scene representation (2026-09-08) + +`native_document::build_gpu_canvas_bindings()` captures node identity, immutable +image metadata and either its completion snapshot or an existing completed lease. +The binding resolves its captured dependency without consulting live canvas state. +`build_scene(..., ordered_canvas=true, capture_gpu_outputs=true)` emits GPU paint +placeholders for pending first images, propagating the capture mode through normal, +fixed, modal and elevated paint traversal. The default published-scene path is +unchanged until engine capture/commit integration is complete. + +A controlled delayed-image regression verifies that a first pending image has a +paint placeholder but no resolvable image, then clears the live canvas reference +and completes the captured dependency: it must resolve the original allocation. +The pre-existing pool-release assertion remains intact. Native engine tests pass +(11.77s); GPU runtime tests pass after scoping the test capture to release its +owned reference (0.75s). This tests dependency capture, not atomic publication of +the full A/B/C scene sequence. The engine still needs bounded staging, failure and +generation invalidation, dirty-work preservation and atomic commit. + + +## Initial engine capture/commit integration (2026-09-08) + +The engine now freezes scene commands, layer arrays, input sequence, viewport and +exact GPU bindings together. One staged capture waits for all captured images; +commit validates canvas identity, bitmap generation/content floor, image version, +viewport and the consumer predecessor. It rechecks mailbox capacity before +publication. Later live document changes remain pending after a frozen capture +commits. Open GPU rendering opportunities defer capture. Resolution retains GPU +leases without pixel copies or a synchronous GPU wait. + +Revision numbers are reserved at capture, rather than commit as originally +proposed. Discarded captures can leave gaps; predecessor validation prevents a +diff from targeting an unpublished capture. Both native suites pass after the +identity/capacity hardening (14.57 seconds). A forty-wheel Kestrel run had no app +errors, but manual input contaminated its timing counters, so it is not +performance evidence. + +Remaining acceptance work includes controlled full capture/presentation tests, +multi-canvas completion order, explicit missing-ticket handling under retention +pressure, failure recovery, navigation/detach retirement, controlled right-button +pan and resize, and browser-comparable displayed-frame measurements. A failed +snapshot currently discards its captured scene; recovery from that state is not +yet qualified. No claim that physical flicker or panning latency is fixed. + +The production commit helper now has deterministic white-box coverage for a +pending dependency retaining scene A, frozen CPU commands B surviving a newer +capture C, dirty-generation preservation, mailbox capacity, failed output, stale +predecessor, viewport change and same-size bitmap reset. Both native suites pass +with these regressions (14.16 seconds). These tests reuse a controlled image lease +and vary CPU commands; they do not establish full multi-version rendered A/B/C +coherence or exercise the runtime capture checkpoint. Consumer predecessor +validation and queue insertion now share one lock to prevent a checkpoint reset +from intervening between them. + + +## Missing output and native right-button pan (2026-09-08) + +A submitted output that cannot produce a retention ticket now installs an +explicit failed snapshot carrying the current canvas version. Scene capture +cannot fall back to the older completed image. Submitted output capture also +marks the document generation changed, including first pending images and failed +outputs that will never reach the ordinary ready queue. The commit regression +checks failed dependency selection in the presence of an old image, whole-scene +rejection and successful replacement. Its canvas now allocates its own backing +before metadata capture; this corrects an earlier fixture identity mistake. +Native engine tests pass (12.55s); GPU runtime tests pass after that fixture +correction (0.89s). Device-loss recovery remains a broader outstanding gate. + +The unchanged Kestrel probe adds `--pan-kestrel`, driving eighty right-button +moves through the native input queue, out and back, with requested 16ms spacing. +The recorded run delivered 74 moves and coalesced six, entered Kestrel's own +panning state for every delivered move and exited on release, with no dropped +inputs or application errors. It invoked 43 app animation callbacks but rendered +66 scenes and performed 119 layout passes, with nine blocked publications. +Median observed move spacing was 20.53ms; maximum was 37.04ms. These include host +scheduling/coalescing and are not GPU execution or physical presentation timings. + +Evidence: `evidence/kestrel/native-right-button-pan.json`. No pointer capture +events were observed, so capture-event conformance remains unqualified. The next +performance investigation should separate input/RAF/layout scheduling, producer +completion and consumer retirement. Browser timings, presented-frame coherence +and resize qualification remain required. + + +## Completion invalidation and full A/B/C capture (2026-09-08) + +Ordinary completion of the exact already-captured GPU output no longer marks the +document changed a second time. Submission remains the content invalidation; +completion wakes and resolves the existing dependency. A completed image without +a matching valid snapshot still invalidates normally. Native suites pass with +that regression (12.18 seconds together). + +A further white-box regression uses the production document and scene builder, +three distinct content serials and CPU background colors, and real ABI v3 scene +acknowledgements. It verifies A/A remains published while B is pending, changing +live state to C does not alter frozen B, completing B publishes B/B, and completing +C publishes C/C. This addresses the earlier commit-only test limitation; it does +not yet cover multiple canvases or physical presentation. The expanded GPU +runtime suite passes (0.65 seconds). + +The same eighty-move native pan probe completed without errors or dropped inputs. +This run produced one no-damage build (previously ten), 62 rendered scenes +(previously 66), and zero blocked publication attempts (previously nine). It still +performed 117 layouts for 42 application RAF callbacks. Counts vary with scheduling +and coalescing, so these single runs do not qualify an FPS or latency improvement. +Evidence: `evidence/kestrel/completion-invalidation-and-full-capture.json`. + + +## Multiple canvas completion order and failure preflight (2026-09-08) + +The production scene-builder regression now introduces a second canvas whose +first image is pending. It checks both producer completion orders: the previous +complete scene remains published until both exact outputs are ready. Published +GPU command indices must map to each canvas identity and expected content serial; +the CPU marker must belong to the same capture. + +A failing second output initially exposed a bug: the commit loop returned at the +first pending dependency before inspecting later failures. Commit now validates +every captured dependency before trying to resolve any pending output. The test +verifies failed and bitmap-reset second-canvas captures are discarded promptly +while the first producer remains pending. Previous published content stays intact. +The native engine suite passes (12.55s), and the expanded GPU runtime suite passes +(0.67s). Evidence: `evidence/kestrel/two-canvas-publication.json`. + +These tests use controlled native completion snapshots; they do not stall a +hardware queue or prove physical presentation timing. Browser-comparable pan +performance, live resize timing, device-loss recovery, cross-platform interop and +all other epic gates remain required. + + +## Retry allocation and callback timing (2026-09-08) + +Scene allocation and initial command reservation now happen only after GPU +opportunity/staged-capture checks and consumer-mailbox admission. A deferred +commit no longer creates and discards a second scene on each retry. Native engine +and GPU runtime suites pass (14.76 seconds together). + +The native pan probe temporarily wraps requestAnimationFrame to record callback +wall time while preserving callback receiver, return value and cancellation ID. +Cleanup restores the original function. The observed run had 0.83ms median and +5.30ms p95 callback duration, but only 30 callbacks inside the native counter +interval. Instrumentation recorded 31 samples including setup/settle. This directs +subsequent investigation toward frame admission, producer completion and consumer +retirement; it does not identify GPU execution as the bottleneck. Evidence: +`evidence/kestrel/native-pan-callback-timing.json`. The existing interactive demo +remained open, so this is investigative evidence rather than an isolated benchmark. + + +## Frame admission ownership trace (2026-09-08) + +A thread-safe native image-pool occupancy snapshot now distinguishes occupied +images, unfinished producers, retained-reference images and outstanding-consumer +images. Roles can overlap; counts are images, not reference counts. The owned +pool and IOSurface provider expose the same read-only snapshot. Tests cover an +image with simultaneous producer/retained/consumer ownership, completed producer +with retained ownership only, and full release to idle. Image lease and GPU +runtime suites pass (0.95 seconds together). + +Temporary admission tracing recorded 37 admitted and 17 blocked host frame +signals during a controlled eighty-move pan (66 delivered, 14 coalesced). Every +blocked signal had three busy images, zero unfinished producers, two retained +images and two images with outstanding consumer leases. This does not prove +consumer GPU execution was still running: the lease may instead await polling +of an already-signaled GL fence. The next investigation must measure that +distinction in the Skia/GL retirement path. + +Trace logging was removed and the native library rebuilt. Evidence: +`evidence/kestrel/frame-admission-ownership.json`. An earlier trace contained +extra manual input and is excluded from controlled counts. No frame-rate or +physical presentation qualification is inferred from this diagnostic run. + + +## Consumer retirement after drawing (2026-09-08) + +Fence tracing showed most initial zero-timeout checks were unsignaled and the +next check collected completion around 20ms later. A trial polling through a +separately acquired host context before producer-frame submission reduced that +age but processed fewer frames/moves; it was removed. + +The retained implementation instead checks retiring groups again after recording +the current frame, under the existing Skia graphics lease. It reuses the same +retirement/fence logic, never waits for GPU completion, and leaves the current +image group intact. The traced trial's median collection age was 3.57ms with 41 +application RAF callbacks, compared with 20.40ms and 30 callbacks in the initial +trace. A final run after removing trace code invoked 52 callbacks, delivered 71 +moves and coalesced nine, with zero app errors. These are individual investigative +runs, not a statistically qualified speedup or browser comparison. + +Eleven native GPU interop tests pass on each of .NET 8 and .NET 10, with no skips. +The Ganesh fixture completes 32 frames and retirement with zero explicit transport +copies (eight diagnostic readbacks; physical presentation not certified). +Evidence: `evidence/kestrel/consumer-retirement-after-draw.json`. No temporary +trace code or separate-context polling experiment remains in the implementation. + +### Vsync/mailbox integration audit (2026-09-08) + +The macOS Avalonia path already sends its compositor frame timestamp through +`NativeSceneComposition.OnAnimationFrameUpdate` to native frame input. +`v8_dom_runtime::signal_animation_frame` admits the RAF batch only when configured +GPU canvases have available storage. Finishing that rendering opportunity submits +current textures and captures versioned image dependencies. Scene publication +uses the existing bounded acknowledgement mailbox, including the staged scene's +CPU/GPU coherence checks. GPU completion wakes the native worker; it does not +constitute a new display vsync or permission to invoke another RAF batch. + +There is still a separate producer timing gate: the worker permits ordinary scene +publication only after `next_scene_publication`, advanced on a fixed 16ms cadence. +Only paired resize/host-frame boundaries currently bypass that gate. This is an +observed architectural mismatch with display-driven scheduling, not yet proof of +the remaining pan bottleneck. A ready GPU dependency may encounter this gate +before entering the compositor mailbox. Completion-to-publication delay must be +measured separately from actual GPU execution and consumer fence collection. + +Next scheduling verification must cover completed ordinary host-frame batches, +asynchronous GPU completion after that batch, full-mailbox acknowledgement, +multiple canvases, resize, and idle behavior. Publication eligibility should +follow completed rendering opportunities and mailbox capacity without adding an +independent display clock. Completion must never run future RAF callbacks, expose +partial CPU/GPU scenes, introduce unbounded queued frames, or block the compositor. +Retain a bounded fallback for non-frame-driven document updates. Qualify against +actual presented-frame intervals on the user's monitors; application RAF counts +and the nominal 16ms interval do not establish sustained 60fps. + +Follow-up control-flow inspection narrows the suspected gate: a deferred capture +or commit does not advance `next_scene_publication`. An ordinary staged scene was +therefore captured after the gate had already opened, and its later GPU completion +normally retries against that same expired deadline. Simply exempting staged +scenes from the gate would not remove the ordinary pan delay; that trial was +removed before retention. The remaining question is delay *before initial scene +capture*, and whether ordinary host-frame completion should bypass the producer +phase as paired resize already does. This correction supersedes any inference +above that every GPU-completion retry incurs another 16ms wait. + +The next unchanged-Kestrel run completed with zero application errors, but +recorded 185 pointer moves for the scheduled 80-move workload. It is not a valid +controlled baseline. Both attached LG displays report 3840x2160, logical +1920x1080 at 60Hz. The run observed 89 blocked publication attempts and 56 +published scenes over 1.859s including settling; neither counter measures physical +presentation. Evidence is retained in +`evidence/kestrel/vsync-current-pan-investigation.json`. Before comparing scheduling +changes, the probe must distinguish its injected workload from other routed input +and invalidate contaminated runs automatically. Existing interactive windows were +preserved. Native engine and graphics runtime tests passed (11.58s and 1.82s). + +The pan probe now clears warmup observations immediately before measurement and +validates the gesture boundaries and delivered moves as an ordered subsequence of +the 80 injected right-button moves. Coalesced moves are allowed; extra, reordered, +or wrong-button events fail the probe before startup verification can report an +overall successful run. Matching coordinates cannot establish provenance for an +identical external event. A rebuilt probe completed with 51 delivered moves, +37 application callbacks, 43 rendered scenes, one blocked publication and zero +script errors. See `evidence/kestrel/validated-pan-workload.json`. This establishes +a usable workload trace only, not presentation timing or sustained performance. + +An ordinary-host-frame publication bypass was built and run against the validated +pan workload. The run passed workload/startup checks with zero script errors, +28 application callbacks and 32 rendered scenes, versus 37/43 in the preceding +validated run. This is not a statistical regression finding, but provides no +support for retaining the change. The experiment was removed and the original +scheduler restored. Evidence: `evidence/kestrel/host-frame-gate-experiment.json`. +A same-iteration host-frame flag also cannot represent an RAF batch that finishes +in a later worker iteration; any eventual scheduling change must track the actual +completed rendering opportunity. Next profiling should timestamp host-frame +admission, RAF completion, scene capture/commit and compositor drawing separately. + +The pan probe now emits the existing per-view publication, rendered revision and +end-of-draw timestamps, filtered to the measurement window. No new rendering-path +instrumentation was added. The property's legacy `PresentationTimestamps` name is +reported as `drawCallbackCompletions`: its implementation records OnRender, not +physical presentation. The rebuilt probe passed workload/startup validation and +recorded 64 publications, 64 rendered scenes and 65 draw callbacks. Matching +revision timestamps yields median publication-to-draw latency 33.37ms. This directs +further investigation toward compositor consumption/retirement; it does not prove +a GPU execution bottleneck or a physical frame rate. Evidence: +`evidence/kestrel/pan-composition-timeline.json`. + +A bounded consumer-drain prototype now applies at most a second queued GPU diff +before invalidating/drawing, preserving diff order and combining both damage +regions. Manual frame certification remains one diff per frame. The existing +presenter capacity/retirement checks can reject the second acquisition without +advancing its acknowledgement. A validated trial recorded 50 publications, 30 +rendered scenes, 48 RAF callbacks and median publication-to-draw latency 28.42ms. +Intermediate revisions need not be drawn, but every accepted diff is applied. +Existing interop/frame-policy/damage tests pass: 18 each on net8/net10, no skips. +The prototype remains under evaluation and needs dedicated combined-damage and +multi-scene lifetime coverage before acceptance. Evidence: +`evidence/kestrel/bounded-mailbox-drain-trial.json`. No physical 60fps claim follows. + +Dedicated damage regressions now cover separated changes, unchanged-following +scenes, and full invalidation in either order. A native IOSurface ownership test +retains four undrawn scene groups, accepts only the bounded current-plus-two +retiring groups, and verifies that the rejected group remains caller-owned until +explicit discard. This checks undrawn ownership, not imported GPU fence execution. +The focused suite passes 23 tests each on net8/net10 with no skips. The bounded +consumer change is retained for further performance and physical-render testing; +its single-run latency result remains unqualified. + +Post-change GPU fixture verification completes 32 frames, two imports and GPU +retirement with zero explicit transport copies (eight diagnostic readbacks). +A repeated validated pan measured 34.06ms median publication-to-draw latency, +so the earlier 28.42ms sample must not be treated as an established speedup. +Kestrel resize verification now fails on application errors, nonpositive/nonfinite +CSS size or DPR, bitmap/DPR mismatch exceeding one pixel, or viewport/workbench +height disagreement. All four settled checkpoints pass in the rebuilt probe. +This adds an unchanged-application regression gate alongside the existing grid +WPT contracts; it does not qualify physical resize smoothness. Evidence: +`evidence/kestrel/mailbox-resize-verification.json`. + +Render samples now include an optional acceptance timestamp in the same Stopwatch +clock as publication and draw timestamps. It is sampled after applying and +acknowledging the revision, only while performance instrumentation is enabled; +zero means unavailable. Existing positional construction/deconstruction remains +unchanged. A rebuilt, validated pan run matched 16 rendered revisions and verified +publication <= acceptance <= draw for each. Median publication-to-acceptance was +59.77ms; acceptance-to-end-of-draw was 2.99ms. Single-run variability remains high, +but this trace locates most observed latency before acceptance, not inside drawing. +Next investigation should distinguish presenter retirement backpressure from +missed compositor acquisition opportunities. Evidence: +`evidence/kestrel/acceptance-to-draw-timeline.json`. No physical presentation claim. + +Undrawn intermediate scene groups no longer occupy GPU retirement slots when +ImportedCount is zero. Replacement discards only their CPU source leases; any +partially or fully imported group still requires the bounded fence-retirement +path. Admission uses the same condition before mutating the renderer. The native +IOSurface regression now verifies four consecutive unimported replacements leave +no retirement queue and release the final source on discard. All 23 focused tests +pass on net8/net10, with no skips; Ganesh completes 32 frames and retirement with +zero explicit transport copies. A validated Kestrel run measured 63.41ms median +publication-to-draw latency, so this resource-lifetime simplification is not an +established performance fix. Evidence: `evidence/kestrel/unimported-scene-release.json`. + +Temporary acquisition-reason tracing in a validated pan observed 35 native +acquisitions and 35 successful presenter applications during the measured window. +There were no recorded empty/backpressure outcomes or outstanding-draw gate +rejections. Draw callback intervals had median 78.76ms. Synchronous trace output +can affect timing, so the value is not a performance qualification; the reason +counts nevertheless do not support presenter admission failure as this run's +explanation. Investigate compositor callback cadence and host scheduling next. +All temporary tracing was removed and the probe rebuilt successfully. Evidence: +`evidence/kestrel/acquisition-reasons-trace.json`. + +Callback-cadence audit corrects the inference from draw intervals: the traced run +had 74 compositor animation callbacks over 1.853s (~39.94 callbacks/s), but only +20 rendered scenes. Slow draw intervals therefore do not establish an equally +slow native vsync timer. Across four captured runs compositor rates vary roughly +33–52 callbacks/s; application and draw rates differ further. These intervals +include settling and are not FPS qualifications. The probe uses the ordinary +Avalonia UsePlatformDetect configuration without a custom render timer override. +Before altering that configuration, correlate individual compositor ticks with +native publication availability and frame demand. Evidence: +`evidence/kestrel/callback-cadence-audit.json`. + +A temporary per-compositor-callback trace recorded 70 measured callbacks in a +validated pan. At callback entry none had accepted work awaiting drawing or +pending GPU retirements. Thirty-three callbacks had one pending mailbox signal; +37 had none. Of 33 matched rendered revisions, 18 had one callback between +publication and acceptance and 15 had none; none spanned multiple observed +callbacks before acceptance. Demand was caret-only (bit 4) on 45 callbacks, zero +on 24, and RAF/current-texture demand (bit 1) on one. Thus this run does not show +ready scenes being repeatedly ignored at compositor boundaries. Investigate +input-to-publication timing and callback delivery without assuming every tick +has application GPU work. Trace logging was removed and the probe rebuilt. +Evidence: `evidence/kestrel/compositor-demand-trace.json`; timing remains affected +by tracing and does not qualify physical presentation. + +The pan probe now records each injected move's native sequence, monotonic +submission timestamp and coordinates in its composition timeline. Both initial +and retry runs captured exactly 80 strictly increasing input sequences with +ordered timestamps, but failed workload validation because of additional input. +Neither run is used for a latency comparison. This also exercises the probe's +nonzero failure exit instead of allowing startup success to mask contamination. +Future sequence-to-scene correlation must be described as consumed-input progress, +not proof that each coalesced move was individually drawn. Evidence: +`evidence/kestrel/input-sequence-trace-validation.json`. + +User recording (8.27s, 60Hz capture) shows the drawing/grid disappearing during +window resize while surrounding HTML remains. Kestrel's ResizeObserver resets the +bitmap and queues invalidate() through RAF. The native engine could publish the +cleared canvas between that observer and its next rendering opportunity. Bitmap +reset now records a one-opportunity hold, effective only while configured and a +RAF callback is waiting. Host-frame admission clears the hold; normal GPU output +capture/completion takes over. A callback that draws nothing cannot indefinitely +retain the preceding scene, nor can unconfigure or absence of RAF. +Runtime regressions for these boundaries pass, and all four unchanged Kestrel +resize geometry checkpoints pass. This is a candidate fix for the recorded +flicker; a new physical capture still needs to verify it and HTML/canvas 60fps. +Evidence: `evidence/kestrel/resize-redraw-boundary.json`. + +A new window-scoped 8.99s capture of the rebuilt original Kestrel shows the drawing +remaining visible in inspected 2Hz overview and 15Hz transition samples across +stepped resize checkpoints. `resize-redraw-check.mp4` is a resized viewing copy; +the source capture reports 60Hz, which is not application FPS. This supports the +blank-frame fix but does not certify every captured frame or continuous native +window dragging. The capture also shows transient uncovered host area when the +window grows before layout catches up. Continuous-drag visual checks and separate +HTML/sidebar cadence measurements remain required. The probe's optional +`--capture-resize-kestrel` flag adds a five-second attachment delay before its +existing resize sequence; it does not alter application source. + +Continuous-resize verification attempt: a window-scoped capture around synthetic +native edge-drag events did not show changing window dimensions in the inspected +frames. The action therefore did not exercise the intended live-resize path and +is not a pass. Do not substitute its absence of blank frames for continuous-drag +qualification. No FPS result is derived. Existing stepped-resize evidence remains +limited to that workload; real continuous dragging still requires verification. + +The unchanged-application sidebar probe uses native left-button input on the real +`.left-resizer`, moving 120px across 60 steps, and asserts final width. It passes +222→342px with zero script errors. The first run records 92 compositor callbacks, +47 RAF callbacks, 154 layouts and only 11 rendered scenes; median matched +publication-to-draw latency is 32.62ms. Input contamination is not yet checked in +this probe and counters include settling, so this is investigative evidence, not +FPS qualification. It directs the next check toward scene publication/captured +output invalidation during repeated bitmap resizes, rather than assuming low +compositor callback frequency. Evidence: `evidence/kestrel/sidebar-drag-baseline.json`. + +Sidebar publication tracing identifies a scheduling cost of the resize hold. +The detailed run records 137 resize-awaiting-RAF deferrals, 30 pending-output +retries and 19 publications including startup, with no stale-binding rejection. +Thus the current evidence does not support repeated invalidation of frozen GPU +dependencies as the primary cause; repeated new layout/ResizeObserver changes +before a queued redraw can publish are the next ordering concern. Preserve input +sequence barriers and bounded coherent capture when correcting this; simply +bypassing the hold would restore intermediate blank canvases. Temporary logging +was removed. Evidence: `evidence/kestrel/sidebar-publication-deferrals.json`. + +A reset performed after host-frame admission could leave its hold active even +when the same RAF batch submitted the replacement output. Finishing a submitted +canvas now clears that hold; the captured GPU dependency governs publication. +The regression resets inside RAF, obtains a replacement texture, queues another +RAF, and verifies publication is not held for that unrelated next callback. +Runtime tests pass. Original sidebar geometry/startup checks pass with 13 rendered +scenes, 51 RAF callbacks and 94 compositor callbacks, so the broader continuous +resize starvation remains unresolved. Evidence: +`evidence/kestrel/within-frame-resize-hold.json`. +The WebGPU canvas reference confirms configure clears the drawing buffer; this +change preserves reset semantics and changes only readiness tracking: +https://gpuweb.github.io/types/interfaces/GPUCanvasContext + +Live Chrome reference audit confirms WebGPU is active. The existing reference tab +was 792x878 CSS pixels at DPR2 with a 175px explorer, unlike the native 1280x800, +DPR2, 222px explorer. A temporary fresh comparison tab with a 1280x800 viewport +override reported DPR1, so it still did not match native pixel load; backend was +WebGPU and application errors were zero. The override was reset and temporary tab +closed, preserving the user's original reference tab. Do not derive a native / +Chrome performance ratio from these mismatched conditions. A comparison harness +must verify settled canvas dimensions as well as viewport, DPR and application +state before measuring identical interaction workloads. + +### Rejected pre-RAF resize layout experiment + +Moving layout and ResizeObserver delivery before the admitted GPU RAF batch produced 26 scene draws over a 1.577-second sidebar workload (including settling), compared with 13 in the preceding run. This is neither an FPS measurement nor a controlled speedup claim. Both native test suites passed, but the experiment changes observable rendering phase ordering: ResizeObserver delivery belongs after animation callbacks. The production experiment was removed. Evidence: `evidence/kestrel/rejected-pre-raf-resize-layout.json`. + +The retained resize redraw hold passed the stepped resize checks, but continuous window-edge resizing and physical 60fps presentation remain unqualified. The hold can still defer too many scenes under continuous sidebar resizing; resolving that requires preserving browser scheduling and coherent CPU/GPU scene boundaries. + +### Validated sidebar input baseline + +The sidebar probe now records all submitted move sequences and timestamps, observes delivered pointer events, and rejects unexpected boundaries, buttons, or moves outside the ordered submitted path. Coalesced omissions are allowed. Temporary observers are removed in `finally`; the Kestrel fixture remains unchanged. Eight managed regression cases cover both pan and sidebar validation on net8.0 and net10.0. + +The validated baseline in `evidence/kestrel/validated-sidebar-publication-baseline.json` reproduced the publication deficit: 94 compositor callbacks, 49 application RAF callbacks, and 14 scene draws in 1.580 seconds including 500ms settling. Geometry reached 342px from 222px with no application errors. These counts are not physical FPS or a sustained-rate measurement. Next work remains the resize reset/publication boundary, without promoting ResizeObserver ahead of RAF or allowing mismatched CPU/GPU generations. + +### Commit completed captures independently of newer GPU opportunities + +`publish_scene` now commits an existing staged CPU/GPU capture before checking whether the current runtime opportunity is open. The layout/ResizeObserver checkpoint remains in its existing position. The capture still passes viewport, canvas generation/content, producer completion, predecessor, and mailbox capacity validation. New capture creation remains gated until the newer rendering opportunity ends. + +The production scene lease regression opens a newer runtime GPU opportunity while frozen B completes: it failed at B publication before the change, then passed after the change. It also verifies that C cannot be newly captured while the opportunity remains open. Both native engine and graphics runtime suites passed. A validated unchanged-Kestrel sidebar smoke run reached the expected width without errors, with 15 scene draws and 49 RAF callbacks; it overlapped a test-target build and is not a controlled performance comparison. The principal continuous-resize publication deficit remains unresolved, and physical 60fps remains unqualified. + +### Headless non-GPU resize measurement scope + +The existing native resize cadence probe uses Avalonia.Headless. Its timestamps previously named presentation timestamps are CPU draw callback completions, not scanout. Report schema v2 now names these fields `drawCallbackCompletions`, `drawCallbackCompletionsPerSecond`, and `drawCallbackIntervalMilliseconds`, labels the measurement headless, and reports `physicalPresentationVerified: false`. The former practical-vsync threshold is now explicitly `cpuCadenceGate`. The comparison script accepts v2, exposes `--require-cpu-cadence`, and always rejects `--require-vsync` for this measurement source. Legacy v1 artifacts are not silently promoted. Two regression tests protect legacy rejection and failure of physical qualification even when CPU cadence passes. + +The initial current-library non-GPU run reported about 30 draw callbacks per second, then aborted during cleanup with `mutex lock failed: Invalid argument` (exit 134). `evidence/kestrel/non-gpu-headless-resize-failed.json` records the command, native binary hash, partial metrics and failure. This is not a passed baseline or a physical presentation result. Headless scheduling and the cleanup failure require investigation before qualifying this benchmark. + +### Revoke compositor engine access before native destruction + +The shutdown crash report identified `std::mutex::lock` inside `webscene_engine_acquire_next_scene`. Surface detachment previously queued an asynchronous Stop and allowed engine destruction before the handler consumed it; queued wake/live-resize/manual/render callbacks could still acquire scenes. The surface now retains its handler and revokes engine access synchronously before queuing Stop on engine replacement or visual detachment. A shared callback gate joins an in-flight message, animation, or render callback. Later callbacks cannot touch the engine; Stop can still retire resources. Revoked capture requests fail explicitly rather than hanging. Stop is terminal for an individual handler; reattachment creates a new one. No GPU completion wait or pixel transport is introduced. + +An attempted compositor-commit wait was removed because a detached headless visual could stop servicing commits. The retained implementation does not depend on another compositor tick. Seven capture/lifecycle regression cases pass on net8.0 and net10.0, including late messages and rendering after both Stop and synchronous revocation. The short and repeated five-second headless resize runs exited zero after revocation, including the final render-callback guard. See `evidence/kestrel/non-gpu-resize-cleanup-fixed.json`; the CPU cadence gate remains false and this is not physical presentation evidence. + +### Observe the headless clock separately from scene draws + +Avalonia 11.3.4 configures its headless render timer for 60Hz, but the current benchmark run observed 150 actual timer events during the five-second measurement interval (about 30Hz), alongside 150 scene draws. Thus this run does not establish that the native desktop renderer discards half of its display opportunities. The requested input cadence remains 60Hz. The root of the headless timer cadence itself is not yet established. + +The resize benchmark now subscribes to the existing timer without replacing or forcing it, reports availability, event count, rate and intervals, and unsubscribes in `finally`. Observation is restricted to the timed workload, excluding warmup and drain. Access is benchmark-only reflection against the pinned framework because Avalonia removes these private APIs from its reference assembly. `evidence/kestrel/headless-render-clock-observed.json` contains the clean-exit run and source reference. The physical 60fps gate remains unqualified; the separate native-window Kestrel publication deficit remains actionable. + +### Complete ordinary host RAF batches before unrelated work + +Ordinary host frames now use the same complete admitted animation-callback drain as paired resize frames. Previously the general task scheduler could yield after its task/time cap and process a host evaluation while a RAF batch remained partially executed. The expanded native callback-list regression reproduced this: the host saw only the first callback. After the change it sees the complete cancelable list; nested callbacks retain the next-frame deadline. Resize-specific metrics remain conditional on actual resize. ResizeObserver ordering is unchanged. + +Both native suites pass. The new project-owned WPT-style candidate `contracts/animation-frame-batch-boundary.html` passes 5/5 subtests through `webscene-animation-frame-batch-profile.json`; this is not an upstream or hardware qualification. The validated original-Kestrel sidebar runs yielded 11 scene draws before and 16 after, with 49 and 48 RAF callbacks respectively. These single runs do not establish a speedup or physical frame rate. Evidence: `evidence/kestrel/complete-host-raf-batch.json`. Continuous resize remains below the target. + +### Current graphics-disabled verification + +The current macOS Release V8 engine also builds with graphics OFF. Its native engine suite passes, including the expanded RAF batch regression, and the project-owned RAF candidate passes all five subtests against that disabled binary. `otool -L` lists no Dawn or ANGLE dynamic dependency. `evidence/kestrel/current-graphics-disabled-verification.json` records the binary hash and local worktree qualifications. This incremental check does not complete the clean-build, relocation, Windows/Linux or performance gates of #23. + +### Align browser and native sidebar geometry + +The native document probe accepts positive-integer `--document-width` and `--document-height` options and records initial viewport, DPR, sidebar and canvas dimensions in sidebar timelines. A 792×878 run at DPR 2 matched the current Chrome viewport and sidebar endpoints (175→295px). Original Kestrel input validation and startup passed, with 11 scene draws and 51 RAF callbacks in the native run. Chrome endpoint inspection showed the drawing and no application errors after a CUA drag; its initial sidebar width was restored and the temporary tab closed. This does not classify intermediate flicker, establish matched gesture timing, or qualify physical FPS. Browser restored local workspace state, so full document-state equivalence is also unverified. Evidence: `evidence/kestrel/browser-native-sidebar-geometry.json`. + +### Preserve the cross-library DOM footprint budget + +Linux package CI failed the existing `sizeof(dom_node) <= 1024` assertion. Moving `xml_mode` beside the byte-sized node kind removes padding without changing defaults or increasing the budget. A Linux x64 Ubuntu GCC 13.3/libstdc++ container reproduced the original-header failure and compiled the changed header with a measured 1024-byte node. Both macOS native suites also pass after rebuilding. Evidence: `evidence/kestrel/linux-dom-footprint-fix.json`. This is emulated compile/layout verification, not Linux GPU hardware or complete package qualification. The separate CI failure for the explicit inert-style harnessBlocked entry remains open. + +### Remove the inert-style harness block through native navigation + +The WPT-style runner now supports opt-in native navigation for harness/contract entries. It loads prepared HTML with the existing product resource loader, allowing the native parser to retain script raw text, comments and inert template contents. It does not activate styles by extracting regex matches. The unchanged `html-script-style-text-is-inert.html` case passes its assertion through this route and is now a candidate rather than harnessBlocked. The main profile’s existing empty-harnessBlocked architecture assertion is unchanged; all six release-compatibility guard tests pass. The default prepared-document RAF contract also remains 5/5 passing. + +Evidence: `evidence/kestrel/inert-style-native-navigation.json`. This is a local candidate pass, not upstream or multi-platform qualification. Temporary prepared files are deleted after engine destruction; no source fixture or original Kestrel code was altered. + +The native-navigation inert-style candidate also passes against the rebuilt graphics-disabled V8 library (1/1 document, 1/1 subtest). `evidence/kestrel/inert-style-navigation-graphics-disabled.json` records the independent native binary identity. This verifies the parsing regression without a WebGPU dependency; it does not extend platform or performance qualification. + +### Retain reference capture sources and generated inputs + +The local historical Chrome matrix contains 32 runs and 224 referenced image/trace files; all referenced SHA-256 values match the files. However, its recorded capture-script hash matches neither the current script nor a version found in that file’s Git history. That historical source provenance remains incomplete. Hash integrity alone does not establish reproducibility or durable archival qualification. + +Future captures now retain the exact four harness source files and both generated 10k/100k project inputs alongside their hashes, relative paths and byte lengths. A regression verifies that archived source bytes and hashes remain consistent after the original source is changed. All six reference unit tests pass. This improves future capture evidence; no new hardware matrix was captured, and it does not repair the historical missing source or qualify physical 60fps. + +### Attempt publication at every host frame boundary + +Ordinary host RAF boundaries now bypass the producer-only 16ms publication timer, as paired native resize frames already did. GPU completion, immutable capture validation and mailbox admission remain unchanged. Both native suites pass. The unchanged original Kestrel sidebar workload validates at 792×878, DPR 2, but produced only 12 scene draws in this run. This does not establish a performance improvement or physical 60fps; resize redraw starvation remains unresolved. Full probe evidence: `evidence/kestrel/all-host-frame-publication.json`. + +### Diagnose remaining sidebar publication holds + +Temporary branch tracing during the validated original Kestrel sidebar workload recorded 206 open-output deferrals, all caused by bitmap reset awaiting RAF redraw, and 34 unresolved immutable GPU capture deferrals. No mailbox-full, invalidated capture or failed-producer branch was observed. Counts include startup and do not measure time spent; logging perturbs timing. The instrumentation was removed. This narrows further work to resize/redraw scheduling rather than mailbox capacity, without establishing a fix or physical cadence. Evidence: `evidence/kestrel/sidebar-publication-gate-diagnosis.json`. + +### Chromium comparison for panning handoff + +The current original-Kestrel pan workload validates all 80 submitted move watermarks. Across 35 matched scene samples, publication-to-acceptance median is 23.54ms (p95 32.27ms), versus acceptance-to-draw-callback-end median 2.44ms (p95 3.36ms). These measures include settling and do not establish physical FPS or GPU execution duration. See `evidence/kestrel/current-pan-handoff-latency.json`. + +Chromium `WebGPUSwapBufferProvider::ExportCurrentSharedImage` ends texture access, exports the resulting sync token, and retains the swap buffer through a release callback. `PrepareTransferableResource` packages that shared image and token for composition. The inspected export path contains no CPU completion wait. Source: https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc . This supports investigating asynchronous handoff rather than treating queue completion as the only publication boundary; it does not demonstrate that WebScene can omit synchronization or lifetime tracking. + +WebScene currently acquires before invalidation, and its invalidation gate prevents another acquisition until the pending draw. Ordinary publication wakes are suppressed after startup. A newer publication can therefore wait for a subsequent animation callback. Investigate these phases with timestamped evidence before changing wake policy; acquiring inside a clipped draw without expanding damage would be incorrect. + +The additional project-owned ResizeObserver/RAF ordering contract passes: the complete RAF batch and its microtask precede observer delivery, and RAF requested by the observer waits for a later opportunity. Combined profile: 2/2 documents, 7/7 subtests. This preserves HTML rendering order while further scheduling work remains open. + +Inspected Chromium source SHA-256: `fadd272dd6df5bad58dbef192383f51d5fab80d176521ca66d12f518766c18e5` (retrieved 2026-09-08; main URL is mutable). + +### Reject ordinary publication UI wake experiment + +Temporarily enabling a coalesced normal-priority UI-to-compositor wake for every ordinary publication did not materially reduce original-Kestrel pan handoff latency: median publication-to-acceptance 24.12ms, compared with 23.54ms in the preceding run; draw-callback-end latency 26.68ms versus 27.25ms. The workload validated 80 input watermarks, with 37 matched draws. These single runs cannot establish a speedup. The policy change was removed, avoiding an extra UI wake on each publication without demonstrated benefit. Evidence: `evidence/kestrel/rejected-publication-wake-pan.json`. Physical 60fps remains unqualified. + +### Implementing the Chromium-style dependency handoff + +User requested adopting Chromium's approach. Current macOS `NativeMacOSRetainedGpuImage.Import` explicitly requires a CGL host; `dawn_iosurface_submission` waits for queue completion and handoff validation before exposing the image. Removing that admission check alone is unsafe. The pinned Dawn SDK exposes `SharedFenceMTLSharedEventExportInfo`, while the pinned Avalonia.Native 11.3.4 package lists Metal as a rendering mode. These establish an implementation direction, not verified interoperability. + +Next implementation must qualify the host Metal context/queue lease, retain exported producer shared-event/value dependencies with immutable scene images, encode consumer GPU waits before Skia reads, and retain allocation ownership until consumer completion. Keep the existing completion-certified CGL route for unsupported hosts until the Metal route is verified. Test delayed producers, multiple dependencies, reset/unconfigure, device loss, and delayed consumer release before switching the original Kestrel probe. Performance acceptance still requires physical 60fps measurement; source-level similarity is insufficient. + +### Qualify the active Avalonia Metal host lease + +The opt-in `--metal-host` native-window probe forces the pinned Avalonia Metal rendering mode and verifies a non-null Metal device, command queue and Skia GPU context through an active platform drawing lease. It passed with `Avalonia.Native.MetalDevice`, exit zero. The runtime interface is marked PrivateApi and unavailable in reference assemblies, so the diagnostic uses reflection against the pinned interface. An initial probe attempted Skia drawing before releasing the platform lease; Avalonia rejected that misuse, and the corrected run releases the platform lease before drawing. Evidence: `evidence/kestrel/metal-host-lease.json`. This does not import a Dawn image, encode producer waits, prove physical presentation or enable Metal for Kestrel. Those remain required next steps. + +### Verify delayed Metal producer dependency on hardware + +The new macOS graphics hardware test uses two Metal command queues. The producer waits behind a deliberately unsignaled shared event, writes a buffer, and signals value 7 on a second event. The consumer submits a GPU wait for that value before a diagnostic read. CPU submission returns while the producer remains gated; the consumer remains incomplete until release, then all 4096 bytes match. CTest passed in 0.43s. The test uses a diagnostic GPU buffer copy and CPU inspection; neither is introduced into production pixel transport. It does not yet exercise Dawn fence export, the Avalonia queue, Skia texture import, or physical presentation. Evidence: `evidence/kestrel/metal-event-handoff.json`. + +### Retain Dawn producer dependency ownership + +Both IOSurface submission routes now move the complete Dawn EndAccess output into the submission object. Captured snapshots expose a const view and retain submission ownership, so fence/value arrays survive the publishing stack. The readiness gate remains unchanged. The hardware fixture verifies initialized output, nonempty matching fence/value arrays, and successful non-null Metal shared-event export from every retained fence after return. The existing CGL/Ganesh window verification passes 32 frames, two imports, zero explicit transport copies and eight diagnostic readbacks; both native suites pass. Evidence: `evidence/kestrel/retained-dawn-handoff.json`. This is retained dependency plumbing, not yet early scene publication or a Metal consumer path. + +### Encode all Metal producer dependencies + +Added `submit_metal_producer_waits`, which validates the complete event list, encodes every event/value wait into a command buffer on the caller-owned host queue, and commits without waiting on the CPU. Its return value certifies submission only. The hardware test now uses two dependencies and a separate following consumer command buffer: the first producer finishing does not release the consumer while the second event remains unsignaled. After both signals, all diagnostic bytes match. Null dependencies reject before encoding. CTest passes in 0.39s. An initial same-device check was rejected by the hardware run and removed: MTLSharedEvent supports cross-device synchronization (https://developer.apple.com/documentation/metal/mtlsharedevent). Evidence: `evidence/kestrel/metal-multiple-dependencies.json`. The helper is not yet called by the production presenter; exported Dawn events and retained image lifetime must be wired into it next. + +### Connect Dawn fence export to Metal queue waits + +`submit_dawn_metal_producer_waits` validates fence/value cardinality, verifies each exported fence type, retains exported Metal events in the dependency list, and submits all waits through the Metal queue helper. No command is submitted for an invalid list. The hardware test now imports two controlled shared events into Dawn fence objects and exercises this export bridge; it rejects mismatched counts and verifies the consumer remains held until both event values signal. CTest passes in 0.44s. Evidence: `evidence/kestrel/dawn-metal-fence-bridge.json`. These are controlled events imported into Dawn, not an end-to-end Dawn-produced texture rendered by Skia; production presenter wiring remains incomplete. + +### Wrap native Metal textures with pinned SkiaSharp + +Added a managed bridge to the pinned native `gr_backendtexture_new_metal` entry point, following the approach Avalonia uses for its Metal render-target constructor. The backend wrapper owns only its native Skia descriptor; callers retain the Metal texture through image use and GPU retirement. Argument checks precede native calls, and constructor failure deletes the native descriptor. The active Metal host probe allocated a 16×16 texture on the leased host device, wrapped it successfully, checked validity/dimensions, disposed the wrapper and released the texture. Build and probe exit zero. Evidence: `evidence/kestrel/metal-backend-texture-wrapper.json`. This verifies backend texture wrapping, not sampling a Dawn-produced texture or production presenter integration. + +### Import Dawn IOSurface on the Avalonia Metal device + +Added `NativeMetalIOSurfaceTexture` to create a BGRA8 shader-read Metal texture view of a retained native consumer IOSurface. It validates dimensions/format and owns the Objective-C texture reference, while the caller separately retains consumer ownership. The `--metal-host --metal-fixture` probe created the completion-certified Dawn fixture before opening its window, imported it on the active Avalonia Metal device, and verified a valid Skia backend wrapper. Build and probe exit zero. No pixel copy or GPU read is submitted by this import test, so consumer completion follows wrapper/texture disposal without a GPU retirement fence. Evidence: `evidence/kestrel/dawn-iosurface-metal-import.json`. Actual sampling, asynchronous producer admission and read retirement remain required before production use. + +### Sample the Dawn image with Skia Metal + +The opt-in Metal fixture probe now wraps the imported IOSurface as an SKImage, draws it into a GPU surface on the active Avalonia Metal GRContext, and verifies every readback pixel against the Dawn clear color (51,102,153,255), allowing one RGB quantization step. It passes with one diagnostic readback. Import takes place inside the platform lease; Skia work begins only after that lease is released. Diagnostic cleanup synchronously flushes GPU work before releasing texture/consumer ownership, including readback failure. This synchronous cleanup is explicitly not the production retirement strategy. Evidence: `evidence/kestrel/dawn-metal-skia-sampling.json`. Asynchronous producer admission, consumer retirement, original Kestrel integration and physical FPS remain incomplete. + +### Nonblocking Metal consumer retirement marker + +Added `NativeMetalConsumerFence`: after the caller submits Skia work on the leased host queue, it commits a retained Metal command-buffer marker. Polling returns pending without CPU waits, releases the marker only on completion, and treats GPU error as failure without certifying image reuse. The fixture probe now uses a non-synchronous Skia flush and this marker before releasing imported texture/consumer ownership. It additionally inserts deliberately gated GPU work before the marker: polling remains pending before signal, completes after signal, and remains idempotently complete. The diagnostic always releases the gate on failure. The final build and hardware run pass; evidence: `evidence/kestrel/metal-consumer-retirement.json`. Bounded sleeps/polling occur only in the diagnostic. Production integration must retain resources across later compositor callbacks rather than run this diagnostic loop. + +### Integrate retained Metal images into scene ownership + +The immutable scene image group now uses a shared retained-image contract and selects Metal import for an active Metal host. The Metal owner retains texture, SKImage and native consumer across redraws, inserts a nonblocking queue completion marker at retirement, and releases the allocation only when that marker completes. Detached retirement acquires the host context before the Skia lock. Existing CGL ownership remains available. The opt-in `--ganesh-metal` native-window fixture passes 32 frames, two imports and all eight diagnostic readbacks, including a separate detached-retirement run. The CGL regression run also passes. Evidence: `evidence/kestrel/retained-metal-presenter.json`. This path still accepts completion-certified producer images; asynchronous Dawn dependency admission and original Kestrel Metal qualification remain incomplete. + +### Original Kestrel on opt-in Metal: pan passes, resize run fails + +Added `--webgpu-metal` to force Metal for the unchanged document probe. The pan workload passes all 80 input watermarks. Across 33 matched scenes, publication-to-acceptance median is 25.98ms, acceptance-to-draw-end 0.79ms, total 27.03ms. Faster draw callbacks do not resolve handoff delay or qualify FPS. Evidence: `evidence/kestrel/original-kestrel-metal-pan.json`. + +The stepped resize run reported all four correct CSS×DPR canvas geometries, then aborted during shutdown (exit 134). The OS crash stack reaches `gr_direct_context_flush_and_submit`, Metal command-buffer commit and the assertion "commit command buffer with uncommitted encoder". Thus this is a failed run, not resize qualification. Evidence: `evidence/kestrel/original-kestrel-metal-resize-failure.json`. Metal remains opt-in; resolve the flush/retirement lifecycle failure before treating original Kestrel Metal as ready. + +### Seal detached Metal retirement on the composition owner + +The pinned Avalonia drawing context releases its GRContext monitor before disposing the rendering session; taking only that monitor from background retirement does not serialize the session finalization flush. Stop now initiates presenter retirement on its composition owner before starting background polling. A Metal image with an inserted completion marker only polls that marker during detached retirement; it never flushes Skia again from the background task. Two original-Kestrel stepped resize runs now complete all four geometry checks and exit zero. The seven capture/lifecycle tests pass for both target frameworks. Evidence: `evidence/kestrel/metal-retirement-owner-fix.json`. This removes the observed failure in these runs; continuous resize, broader stress and physical 60fps remain unqualified. + +### Preserve producer dependencies through image and consumer leases + +Dawn resolved snapshots now attach an owned producer-dependency object to their native image lease. Image retain and consumer acquisition propagate that owner, allowing Metal event/value lookup while the originating scene is gone. This is internal opaque-handle plumbing; no early-ready policy or public ABI hook is enabled yet. A production C-API regression releases the source, retains a clone, acquires a consumer, releases the clone, and verifies dependency destruction occurs only after consumer completion. Both native suites pass (11.88s total). The consumer-side native lookup and managed wait submission are the next required wiring steps. + +### Expose retained producer dependencies through the native ABI + +Added optional v3 dependency-count and Metal-event lookup exports. A consumer retains the backend dependency owner; event pointers are borrowed until consumer completion. The count API distinguishes invalid input from a successful empty list, and empty does not certify GPU readiness. Event lookup validates version, struct size and index, and clears valid output records on failure. The C layout test checks pointer/value offsets and the 64-bit wire size. The lifetime regression uses synthetic opaque event pointers to verify values, invalid inputs and lookup after original image release; it does not dereference them or claim hardware synchronization. Both native suites and the C ABI test pass, and `nm` confirms both symbols in the built dylib. Managed borrowing and wait submission remain unwired; early producer publication stays disabled. + +### Submit borrowed producer waits from the managed Metal importer + +The native consumer wrapper now borrows the full Metal event list under its existing completion-deferral ownership model. Lookup failure throws before wait submission; a successful empty list submits no barrier. The Metal importer submits all event/value waits on the leased host queue before wrapping the texture for Skia. Consumer ownership remains retained through read retirement. A new fixture regression verifies concurrent completion is deferred until the borrow returns, callback failure does not release ownership, and subsequent borrowing can retry; it passes on net8.0 and net10.0. The fixture library was rebuilt for the changed opaque native handle implementation. Original Kestrel Metal pan validates and exits zero. Evidence: `evidence/kestrel/managed-metal-producer-waits.json`. Producer publication remains completion-certified; asynchronous readiness/capability negotiation is still required before this can remove the producer-side completion gate. + +### Negotiate images requiring producer GPU waits + +Added scene capability bit 2 for producer GPU waits. Each image can carry an immutable wait requirement, preserved by native retain; scene acquisition computes the capability from retained image requirements and rejects consumers lacking it. A native regression verifies clone preservation, rejection without the bit and success with it. The managed presenter advertises the bit only after inspecting an active Metal host lease; uninspected and CGL hosts do not claim it. Both native suites and the C ABI test pass (12.55s); the managed probe builds, and the private fixture is rebuilt for the changed opaque handle layout. No backend image is marked early by this change, so the producer completion gate remains active until readiness and publication negotiation are wired. + +### Publish validated images with producer GPU dependencies + +A consumer advertising producer GPU waits now allows captured Dawn snapshots to resolve after successful handoff validation, without requiring queue completion. The snapshot exports and validates every Metal dependency before exposing an immutable image marked as requiring waits. Other consumers retain the completion gate. Validation readiness wakes publication; producer ownership still lasts through queue completion, and consumer ownership lasts through GPU read retirement. A controlled scene regression verifies that capability alone and validation alone cannot publish the pending image; both are required. Completion tests cover both callback orders and failures. + +The three targeted native suites pass. Original Kestrel starts and validates the 80-move pan workload. Across 33 matched scenes, publication-to-acceptance median is 26.55ms and acceptance-to-draw-end median is 0.80ms. This does not demonstrate a panning improvement or physical 60fps. Evidence: `evidence/kestrel/early-gpu-publication.json`. The ordinary workload also does not prove that a real Dawn image reached the Metal consumer while producer work was still pending. A deliberately delayed end-to-end Dawn texture test, device-loss handling and continuous resize qualification remain required. + +### Demand-observation ordering experiment + +The early-publication run recorded 107 compositor animation ticks, 53 submitted native frames, 54 skipped empty-demand ticks and 33 rendered scenes over 1.93 seconds including settling. A possible demand transition exists when observing the compositor wakes the pointer worker before the host samples demand. Sampling demand first was tested in unchanged Kestrel: 102 ticks, 53 submitted frames and 33 rendered scenes, with publication-to-acceptance median 27.75ms versus 26.55ms baseline. Both workloads validated, but this experiment does not establish an improvement or the cause of missed frames. The code change was reverted. Evidence: `evidence/kestrel/demand-order-experiment.json`. Per-tick mailbox availability and acquisition/retirement backpressure are needed next; aggregate counts cannot attribute each skipped frame, and settling prevents interpreting these counts as active-pan FPS. + +### Bounded compositor scheduling trace + +Added optional instrumentation samples for compositor ticks, native acquisition outcomes and presenter application outcomes, including pending mailbox count, revision and retirement presence. The observer stores at most 4096 samples, exposes snapshots through the surface, and the pan/sidebar probe includes them in its timeline. Disabled instrumentation retains no samples and avoids outcome-string allocation. Tests for disabled capture and bounded retention pass on net8.0 and net10.0 (two each). + +Two unchanged-Kestrel runs produced scheduling samples but failed drag validation because additional real pointer events contaminated the injected gesture. Neither run qualifies performance comparison. The first mixed-input trace reported no acquisition or presenter backpressure; this cannot exclude backpressure in a clean pan workload. Evidence: `evidence/kestrel/scheduling-trace-validation.json`. A clean trace remains necessary before attributing the measured handoff latency. + +### CPU scene application dominates measured handoff + +A clean scheduling run validates and shows no acquisition/presenter backpressure. Additional samples inside presenter application separate retention, CPU renderer application, replacement and acknowledgement. In a validated baseline, CPU renderer application has median 16.92ms; retention 0.0096ms, replacement 0.0027ms and acknowledgement 0.0071ms. The prior publication-to-acceptance measurement therefore includes substantial CPU work rather than solely mailbox waiting. + +Ordered DOM compilation now shares text shapers across its paint segments for that single compilation, disposing them in a finally block after the full ordered stream is compiled. Ordinary DOM compilation retains its own shaper ownership. No cross-scene font cache is introduced. A validated unchanged-Kestrel run reports median CPU application 15.41ms. This is one run per variant, not statistical qualification. Ordered GPU paint, culling and instrumentation regressions pass: 16 tests each on net8.0 and net10.0. Evidence: `evidence/kestrel/cpu-scene-application.json`. CPU compilation still nearly consumes the full 60Hz budget, so further profiling and retained DOM work remain necessary; no physical 60fps claim is made. + +### DOM compilation versus canvas layers + +A validated unchanged-Kestrel run with temporary renderer timing hooks splits CPU application into DOM compilation (52 samples, median 12.10ms) and canvas-layer compilation (median 2.45ms). The hooks were removed after capture to keep Avalonia observer types out of the renderer shared with Uno. Evidence: `evidence/kestrel/dom-canvas-compilation-split.json`. This identifies the DOM work as the larger target but does not attribute it to a particular paint command or establish physical FPS. + +Added a pixel regression comparing text in separate ordered paint segments around a GPU marker against an unsegmented reference. Both are replayed after compilation shapers have been disposed; the test checks pixel equality and nonempty glyph output. All five ordered-paint tests pass on net8.0 and net10.0. The manual Kestrel window remains available; no automated input was sent to it. + +### Sidebar shell resize profile from the user recording + +Inspected frames from the supplied 6.26-second recording: the left sidebar changes width while the outer window remains fixed. A validated unchanged-Kestrel sidebar run moves width 222 to 342 across 60 submitted movements over 1004ms. It records 157 layout passes, 14 publications and 14 drawn scenes. The largest gap between draw callbacks is about 513ms; CPU scene application median is 15.57ms. No acquisition/presenter backpressure appears in the trace. Evidence: `evidence/kestrel/sidebar-shell-profile.json`. The long gap requires native publication-deferral attribution; the trace does not itself prove which native gate caused it. CPU compilation remains an additional per-frame cost. Physical presentation remains unqualified. + +An earlier sidebar run failed its final-width check; a five-second manual process stack sample mostly captured waiting threads. Neither is treated as active-resize performance evidence. The probe now emits timeline and diagnostic data before width/gesture validation, preserving failed-run evidence while retaining failure exit status. + +### Attribute sidebar deferrals to bitmap reset hold + +A validated unchanged-Kestrel sidebar drag with temporary `has_open_gpu_output` reason logging recorded 224 `bitmap-reset-awaiting-frame` holds and no `rendering-opportunity` or `current-texture` holds. Evidence: `evidence/kestrel/sidebar-bitmap-reset-gate.json`. The probe traces only that gate, not every publication-deferral branch. Logging can perturb timing; its counts identify the active gate but do not qualify a performance comparison. Temporary source instrumentation was removed after capture. + +The hold is set when a canvas bitmap is resized and a future RAF is pending; frame admission and a submitted replacement clear it. Repeated ResizeObserver resets after RAF can renew the hold across successive rendering opportunities. Removing the gate outright would lose the existing blank-canvas regression protection. The required follow-up is to coordinate resize observation, canvas redraw and coherent scene capture so continuous input cannot starve publication, while retaining RAF/ResizeObserver ordering and generation validation. + +### Chromium resize separates drawing storage from compositor content + +Source inspection resolves a design mismatch in the resize hold. [GPUCanvasContext::Reshape](https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc) replaces current drawing storage without destroying the provider. [WebGPUSwapBufferProvider](https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc) returns no new resource when no current buffer exists. [TextureLayer](https://chromium.googlesource.com/chromium/src/+/main/cc/layers/texture_layer.cc) replaces its retained resource only when preparation succeeds. The inferred model is independent retained compositor content, not whole-scene deferral. Source hashes and observations: `evidence/kestrel/chromium-resize-front-buffer.json`. This is source evidence, not browser pixel qualification. + +Implementation requirements for WebScene: + +- Keep the last compositor-accepted GPU image as an explicit presentation resource, separate from the current canvas backing generation and pending producer snapshot. Do not revive an invalidated backing image or relax ordinary generation checks. +- On bitmap resize, invalidate current drawing storage and API-visible content normally, while allowing a new DOM scene to refer to the retained presentation resource at current CSS placement until a replacement is accepted. Preserve exact allocation/content identity and consumer retirement; no pixel copying. +- Pin the presentation resource through immutable scene bindings. Validate it against its explicit presentation ownership, rather than falsely treating it as content of the new backing generation. Update scene paint selection, image indexing and capture together. +- Replacement must be atomic. Failed or pending output must not discard the retained presentation resource; unconfigure, context/device loss, navigation and node removal need explicit release/invalidation behavior and tests. Avoid retaining an extra unbounded image history. +- Test repeated bitmap resets while a RAF redraw remains pending: shell scenes must advance, no blank intermediate GPU image, no wrong image index, no reuse before retirement. Also test a reset without redraw and canvas API-visible reset behavior independently of compositor retention. +- Compare unchanged Kestrel against Chrome for sidebar resize, including changed CSS dimensions and bitmap dimensions. Measure native publication progress and physical presentation; retaining an image alone does not establish 60fps. + +The existing whole-scene bitmap-reset hold remains in place until this presentation-resource model and its lifetime/visual tests are implemented. This avoids replacing the measured stall with an unqualified blank-frame workaround. + +### Track compositor-accepted image ownership independently + +The native acknowledgement state now retains the most recently accepted scene image set independently of its DOM comparison snapshot. Ordered acknowledgement updates this set even for image-only diffs; stale or premature acknowledgements cannot replace it. Removal replaces it with an empty set, and checkpoint reset releases it. Retained scene/image/consumer leases remain independent owners. This is the ownership prerequisite for resize presentation retention; no fallback image selection or removal of the whole-scene resize gate is enabled yet. + +Both targeted native suites pass (13.88s). The scene lease regression verifies accepted-image tracking across image-only diffs while retaining the preceding DOM snapshot, rejected acknowledgement stability, removal/reset, and continued validity of outstanding scene leases. + +### Publish resized shell with an explicitly retained presentation image + +Bitmap resize now records a pending presentation replacement. Before capturing a new scene, the runtime can select the matching canvas image from the compositor-accepted image set when there is no new drawing output. The scene uses a tagged presentation binding with the exact retained image and the current backing generation; ordinary backing-content validation remains strict. A subsequent bitmap reset invalidates that captured tag. New output and configuration invalidation clear fallback state, and a configuration-generation floor rejects images from an earlier configuration. The bitmap redraw hold is bypassed only when the explicit presentation resource exists. + +Both native suites pass, including real Dawn runtime retention/unconfigure/reconfiguration checks and binding generation/ownership regression. The graphics-disabled build passes. One validated sidebar run reaches width 342, publishes 49 scenes and draws 28; maximum draw-callback gap is 56.62ms versus about 513ms in the prior baseline. Four stepped native-window resize geometries also pass. Probe runs preceded the added configuration-generation guard; its final build and native tests pass. Evidence: `evidence/kestrel/retained-image-sidebar-resize.json`. This is not physical 60fps or continuous visual qualification; CPU scene compilation and device-loss/lifecycle stress remain outstanding. + +### Continuous scripted window resize probe + +Added `--continuous-resize-kestrel`: 80 small window-size changes outward and back, with publication/draw timelines and final CSS×DPR/ancestor geometry validation. It requires intermediate viewport publication and drawing during updates, and restores the initial size. A complete run exits zero: 103 publications, 56 drawn scenes, 3436ms actual update span, and maximum draw-callback gap 88.04ms. Evidence: `evidence/kestrel/continuous-window-resize.json`. Requested 16ms delays do not establish actual 60Hz input; resizing takes longer in this run. This verifies progress under continuous scripted updates, not native mouse-drag smoothness, absence of visual flicker or physical presentation. + +Two probe-development failures were corrected before the passing run: enabling performance capture and including ancestor geometry required by the existing validator. Neither earlier run is counted as qualification. CPU DOM compilation and presentation timing remain optimization targets. + +### Attribute DOM compilation to text preparation + +Three validated unchanged-Kestrel sidebar runs used temporary command timers. The first recorded 617.17ms total text work across 45 compilations (median 13.10ms), versus 9.60ms total SVG work. Splitting text routes measured ordinary text around 10.02ms median and spaced text 3.52ms. A third run measured ordinary text total 9.60ms median, with its final DrawShapedText call only 0.27ms; preparation dominates this path. Evidence: `evidence/kestrel/dom-text-command-profile.json`. Draw timing is a subset, not an additional bucket. Measurements include startup, and diagnostic logging may perturb time. All temporary timers were removed. + +The next optimization target is repeated text preparation (parsing, font resolution, fallback checking, shaping, positioning and metrics), with font/text invalidation and typography regressions preserved. These samples do not identify one preparation operation as solely responsible, nor qualify physical FPS. + +### Cache repeated system font-name probes + +UsesMacSystemUiMetrics walked CSS font lists and called SKTypeface.FromFamilyName repeatedly for preceding installed/missing families. Added a case-insensitive cache of those boolean system-family checks, limited to 256 entries with serialized admission. Web-font registries and global registered fonts remain checked first on every call, so a cached system miss cannot conceal a newly loaded web font. This follows the existing process system-font resolution policy; it adds no OS font-install notification handling or native font ownership. + +A new regression warms a missing-family result, registers a document web font under that name, and verifies case-insensitive precedence and isolation from another document. Text/ordered-paint/font tests pass: 68 ordinary tests and two native integration cases per framework (net8.0 and net10.0). The initial native integration skip was explicitly rerun with the native library configured. + +One validated unchanged-Kestrel sidebar run reports CPU scene application median 2.93ms, 61 drawn scenes and maximum draw-callback gap 39.09ms. A validated pan run matches 64 scenes and publication-to-draw-callback median 15.21ms. Evidence: `evidence/kestrel/system-font-probe-cache.json`. This is a substantial single-run improvement over the prior roughly 15.6ms CPU application baseline, not statistical or physical 60fps qualification. + +### Continuous resize after system-font probe caching + +The complete continuous-window-resize probe passes after the font lookup optimization: 106 publications, 101 drawn scenes, 2813ms active update span, median callback gap 33.14ms and maximum 50.56ms. The earlier scripted run had maximum gap 88.04ms. Evidence: `evidence/kestrel/font-cache-continuous-resize.json`. This remains one scripted run with settling, not a statistical comparison, physical FPS measurement or native mouse-drag qualification. The callback spacing still leaves a cadence question after the CPU cost reduction; actual input-update timestamps must be distinguished from compositor and presentation timing. + +The authoritative GitHub #23 was rechecked and remains open with its hardware/reproducibility acceptance boxes unchecked. These local macOS performance improvements do not satisfy missing cross-RID native probe, reference or package gates. + +### Separate resize-driver cadence from rendering cadence + +Analysis of the previous continuous-resize trace found median input-update spacing 33.45ms while compositor ticks were 16.24ms. The probe previously awaited a new 16ms delay after each update. It now targets absolute 60Hz deadlines, records timestamps before and after setters, and yields without busy-waiting when late. + +The updated validated run completes its active loop in 1353ms: update interval median 15.73ms, compositor interval median 16.24ms, and active draw interval median 30.00ms (maximum 51.37ms). Setters themselves took only 0.020ms median, contradicting a hypothesis that synchronous setters consumed a display slot; delay/UI scheduling caused the earlier slow driver cadence. Evidence: `evidence/kestrel/deadline-paced-window-resize.json`. This is a probe correction, not a production rendering speedup. The remaining draw-cadence gap persists with faster input and still requires investigation. Native user-drag and physical presentation remain unqualified. + +### Native counters separate property updates from engine resize input + +Fixed continuous-probe JSON serialization to include native metric fields; previous baseline/after native structs serialized as empty objects, although their separate timing arrays were present. A validated run now records 80 scripted size assignments but only 44 submitted native resize/frame pairs. All 44 were applied and published; engine resize coalescing stayed zero. Their aggregate submission-to-publication time is 337.48ms (about 7.67ms mean), maximum 23.81ms. Evidence: `evidence/kestrel/continuous-resize-native-counters.json`. + +The next attribution boundary is before engine submission: Avalonia property/layout updates versus actual native resize notifications. These counts do not prove a native-drag 30fps limit or that the engine drops half its resize requests. Earlier metrics-free traces remain usable only for their explicitly captured publication/draw/input arrays. Physical presentation remains unqualified. + +### Locate resize reduction before the WebScene surface + +The continuous probe now records bounded window Resized notifications (including reason), surface SizeChanged notifications and existing native resize submissions. Handlers are detached in finally. A validated run records 80 property assignments, 38 window notifications, 38 surface changes and 38 native submissions; all window notifications have Layout reason. Evidence: `evidence/kestrel/host-resize-event-boundaries.json`. + +Thus this scripted workload combines size changes before they reach the WebScene surface/engine. It cannot establish native mouse-drag throughput or justify a renderer change aimed solely at matching all 80 property assignments. A native window-edge drag trace, with the same stage measurements and actual presentation evidence, is the remaining relevant qualification. Build and complete scripted workload pass; physical presentation remains unqualified. + +### Explicit OpenGL host resize regression + +Added `--webgpu-opengl` to force Avalonia OpenGl as the sole rendering mode, removing reliance on platform defaults for host regression evidence. The unchanged Kestrel WebGPU document passes four stepped resize geometries and exits zero under this option. Evidence: `evidence/kestrel/explicit-opengl-resize.json`. A preceding explicit-host sidebar run failed gesture validation and is not counted as passing. This is Dawn WebGPU displayed through the OpenGL compositor host; it does not qualify ANGLE/WebGL API fallback, native drag smoothness, visual flicker or physical FPS. + +### Manual performance feedback and explicit OpenGL sidebar recheck + +On 2026-09-08, after launching the latest Metal WebGPU Kestrel build, the user reported “performance is now excellent!”. This records qualitative manual feedback only; no physical frame timing or specific monitor/interaction qualification was supplied. The manual application remains open. + +A fresh explicit OpenGL compositor-host sidebar run passes the strict gesture validator and startup check with zero application errors (exit zero): 60 submitted moves, width 222 to 342, 55 publications and 53 drawn-scene samples. Full captured timeline and reproduction command: `evidence/kestrel/explicit-opengl-sidebar.json`. This resolves the missing successful explicit-host sidebar workload evidence without treating the previous failed gesture run as passing. It does not establish ANGLE WebGL API fallback or physical 60fps. Epic #23 remains open pending its mandatory cross-platform and baseline gates. diff --git a/docs/graphics/cross-platform-reuse-review.md b/docs/graphics/cross-platform-reuse-review.md new file mode 100644 index 000000000..88b2492d8 --- /dev/null +++ b/docs/graphics/cross-platform-reuse-review.md @@ -0,0 +1,70 @@ +# Cross-platform rendering and interop reuse review + +Status: source review, not a working integration or acceptance pass. Recorded +2026-09-07 after the user questioned expansion of platform-specific code. +The epic's native Dawn/Tint, ANGLE and GPU-resident Skia requirements remain intact. +Further bespoke platform expansion should follow a demonstrated composition path. + +## Findings from current code + +WebScene's Avalonia project targets net8.0 and net10.0. Directory.Packages.props +pins SkiaSharp 2.88.9. NativeSceneDrawOperation.Render obtains Avalonia's +ISkiaSharpApiLeaseFeature; WebScene does not currently own that renderer's device. +The versioned native image leases exist, but this path does not compose them yet. +The new D3D12 helpers are preliminary, largely Windows-unverified infrastructure. +They are not evidence that the final presenter needs every helper. + +ProGPU revision reviewed: `102e39e5088b462624da6296ff70a43ed2c5d8b4`. +Its [Dawn package](https://github.com/wieslawsoltes/ProGPU/blob/102e39e5088b462624da6296ff70a43ed2c5d8b4/src/ProGPU.Backend.Dawn/ProGPU.Backend.Dawn.csproj) +targets net10.0, references WebGPUSharp (0.5.5 in central dependencies) and +ProGPU.Backend. The latter includes Silk.NET WebGPU/wgpu-native and GLFW +windowing/input dependencies. Direct consumption therefore requires runtime, +packaging and net8 compatibility work, not just an additional namespace import. + +Its [external texture contract](https://github.com/wieslawsoltes/ProGPU/blob/102e39e5088b462624da6296ff70a43ed2c5d8b4/src/ProGPU.Backend/ExternalGpuTextureInterop.cs) +abstracts IOSurface, DXGI, AHardwareBuffer and DMA-BUF. Its +[Dawn sharing implementation](https://github.com/wieslawsoltes/ProGPU/blob/102e39e5088b462624da6296ff70a43ed2c5d8b4/src/ProGPU.Backend.Dawn/DawnSharedTextureMemory.cs) +wraps the same native shared-memory and fence mechanisms as our helpers. Platform +interop exists in ProGPU too. Its SkiaSharp compatibility shim and WebGPU renderer +are a different integration choice from retaining our existing native Skia host. +No source review here establishes ProGPU's hardware or standards qualification. + +Skia Graphite's [Dawn backend context](https://skia.googlesource.com/skia/+/refs/heads/main/include/gpu/graphite/dawn/DawnBackendContext.h) +accepts a caller-supplied Dawn instance, device and queue. Its +[Dawn backend texture API](https://skia.googlesource.com/skia/+/refs/heads/main/include/gpu/graphite/dawn/DawnGraphiteTypes.h) +can wrap a WGPUTexture. The backend texture itself does not retain that texture; +wrapping SkImage/SkSurface objects do. GPU completion still requires lease tracking. +These moving-main APIs must be pinned with a compatible Dawn revision before build. +This gives a concrete cross-platform route for internal WebScene composition, but +does not make an independent Avalonia/Uno Skia device consume the result automatically. + +## Decision and bounded next implementation + +Keep the native versioned lease ABI, Dawn execution service and ANGLE architecture. +Do not replace native Skia with ProGPU's compatibility shim as an incidental change. +Do not add more native platform adapters merely because a platform issue exists. +First test shared-device Skia Graphite composition against the actual ownership +boundary. Treat ProGPU as a reusable implementation candidate and reference; +direct adoption remains unproven due to its runtime and renderer dependencies. + +The next spike must: + +1. Pin compatible Skia Graphite and Dawn revisions and confirm an isolated build + can use the same Dawn runtime as WebScene (no cross-library object pointers). +2. Create a Graphite context using WebScene's Dawn device/queue; wrap a retained + canvas texture and compose it with ordinary Skia content, clip, opacity and + transforms. Preserve the lease until GPU completion. +3. Verify output with diagnostic readback only in the test, and instrument the + production composition path to establish no CPU pixel transfer. Exercise resize + and delayed consumers; texture references alone do not authorize pool reuse. +4. Identify the host presentation API for both Avalonia and Uno. Prove whether + their existing renderers can consume this result or require one external image + adapter. Do not call an offscreen Graphite test end-to-end presentation. +5. Record which existing platform helpers are required, redundant or replaceable + by a library. Integrate only the demonstrated boundary. Preserve public .NET + target compatibility and avoid changing the application's renderer implicitly. + +A JavaScript WebGPU triangle inside composed WebScene remains the useful visible +milestone, followed by full bindings, ANGLE fallback, unchanged Kestrel and the +remaining conformance/platform/performance gates. This review closes no sub-issue +and does not change the epic's completion criteria or numbered issue ordering. diff --git a/docs/graphics/evidence/2026-09-07-chrome-metal/README.md b/docs/graphics/evidence/2026-09-07-chrome-metal/README.md new file mode 100644 index 000000000..363187fda --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-chrome-metal/README.md @@ -0,0 +1,22 @@ +# Partial G01: hardware Chrome reference matrix + +Captured on 2026-09-07 with Chrome 152.0.7977.77, revision `529d9a34b491745086b59458f58a5aae8292adaa`, Apple M4, Metal, hardware WebGPU and GPU composition enabled. All 32 runs confirmed a non-fallback WebGPU adapter. Kestrel source files came from the byte-verified original archive and were not modified. + +The courtyard, supplied mechanical fixture, seeded 10,000-line and 100,000-line inputs were each captured twice in dark/light themes at DPR 1/2. All 16 comparisons have byte-identical before and after PNGs, separately for the composited viewport, GPU canvas and overlay canvas. The document viewport was 1920×1080 CSS pixels; the CAD canvas was 1446×743 CSS pixels (2892×1486 physical pixels at DPR 2). + +Each timed run performs 180 camera pans. Chromium's platform presentation-feedback-derived reporter cadence ranged from 59.329 to 60.337 reports per second; p95 intervals ranged from 16.667 to 33.333 ms. This counts unique reported feedback timestamps, not independently observed monitor scanouts. Near-coincident feedback timestamps can occur. CPU render/submission samples and rAF intervals are recorded separately and are not GPU execution timings. No WebScene performance claim follows from these Chrome reference results. + +`reference.json.gz` contains the complete matrix metadata, per-frame timing samples, hardware identity, camera state, repeat comparisons, input/harness hashes and all artifact hashes. `files.json` hashes the files retained here. Two representative images and one raw compressed trace are committed for inspection and analyzer reproduction. The remaining raw PNGs/traces are retained locally in `artifacts/chrome-reference-matrix-02`; they are **not included in this repository evidence subset**. Full durable archival of those files remains outstanding. The earlier `matrix-01` attempt is also retained locally: its app canvas layers matched, but command suggestions obscured some composite images. The harness now dismisses that UI through normal app input handlers before capture. + +Reproduce the full matrix from the repository root: + +```sh +node --test tests/GraphicsCompatibility/reference-tests.mjs +node tests/GraphicsCompatibility/capture-chrome-reference.mjs --output artifacts/chrome-reference-new +``` + +The base commit was `d42a715628738c67c696e9d805d98f9f8697220f`; the then-uncommitted harness is identified by its exact file hashes in the result. Subsequent changes to documentation do not alter those hashes. Unknown Chrome revisions deliberately produce unavailable presentation analysis until the Chromium source contract is verified. + +This evidence does not close #23. Other GPU hardware targets, remaining native integration/package validation, and complete durable reference artifact storage are still required. No WebGPU/WebGL browser API or GPU-resident presentation feature is claimed implemented here. + +The non-graphics CI at that base commit passed Windows, macOS and Linux. Native package workflow [34110232771](https://github.com/wieslawsoltes/WebScene/actions/runs/34110232771) also passed all three runtime builds and package consumers after rerunning a Linux detached-DOM GC test failure. The initial failure is not suppressed or counted as a successful first attempt. diff --git a/docs/graphics/evidence/2026-09-07-chrome-metal/courtyard-dpr1-dark-run1-trace.json.gz b/docs/graphics/evidence/2026-09-07-chrome-metal/courtyard-dpr1-dark-run1-trace.json.gz new file mode 100644 index 000000000..457bc9f15 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-chrome-metal/courtyard-dpr1-dark-run1-trace.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-chrome-metal/files.json b/docs/graphics/evidence/2026-09-07-chrome-metal/files.json new file mode 100644 index 000000000..2177b51bc --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-chrome-metal/files.json @@ -0,0 +1,18 @@ +{ + "courtyard-dpr1-dark-run1-trace.json.gz": { + "sha256": "b6cf1707bf3bdf8a5329e890a009e0a5d715b65679e1aea5ee8dd78a80f46608", + "bytes": 1834293 + }, + "reference.json.gz": { + "sha256": "2fc4c7bf48d96c9cf5569b550dff0c52d1abec8af638657ecae55775721886ab", + "bytes": 248569 + }, + "fixture-dpr1-dark-run1-before.png": { + "sha256": "d169cdfbf2717a168b6db504032dd92ac06bbccab08061e4caa9a6f797dd4d15", + "bytes": 336025 + }, + "lines-100000-dpr2-light-run1-before.png": { + "sha256": "de652a6c5e0b9db1b6533237d4c84d1ee46a7146908fd2869ddc866fcda2b900", + "bytes": 1723103 + } +} diff --git a/docs/graphics/evidence/2026-09-07-chrome-metal/fixture-dpr1-dark-run1-before.png b/docs/graphics/evidence/2026-09-07-chrome-metal/fixture-dpr1-dark-run1-before.png new file mode 100644 index 000000000..2ced4b7ad Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-chrome-metal/fixture-dpr1-dark-run1-before.png differ diff --git a/docs/graphics/evidence/2026-09-07-chrome-metal/lines-100000-dpr2-light-run1-before.png b/docs/graphics/evidence/2026-09-07-chrome-metal/lines-100000-dpr2-light-run1-before.png new file mode 100644 index 000000000..2925a7c48 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-chrome-metal/lines-100000-dpr2-light-run1-before.png differ diff --git a/docs/graphics/evidence/2026-09-07-chrome-metal/reference.json.gz b/docs/graphics/evidence/2026-09-07-chrome-metal/reference.json.gz new file mode 100644 index 000000000..04ea81d3b Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-chrome-metal/reference.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-clean-angle/README.md b/docs/graphics/evidence/2026-09-07-clean-angle/README.md new file mode 100644 index 000000000..6c9681e80 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-clean-angle/README.md @@ -0,0 +1,9 @@ +# Clean ANGLE and combined SDK hardware verification + +ANGLE compiled all 1,322 build steps in a separate output directory and installed into the clean SDK root. The builder now honors `--build` for ANGLE as it already did for Dawn. GN configuration outside the source checkout was verified before this change; no previous compilation objects were reused. + +The clean Dawn and ANGLE SDKs then passed all three Metal hardware probes on Apple M4: Dawn and ANGLE ES 2/ES 3, 68 verified pixels each. `native-probes.json.gz` includes both complete SDK manifests, hardware identity, executable hashes and strict adjacent-library verification. Logs retain the clean build and probe configure/link commands. + +Reproduce with `python3 eng/graphics/build.py angle --rid osx-arm64 --jobs 6 --build --sdk `, using the same SDK root as the clean Dawn build. Configure a new `eng/graphics/probes` build with `WEBSCENE_GRAPHICS_COMPONENTS=dawn;angle` and that SDK root, build, and run `eng/graphics/run-probes.py` for `osx-arm64`. + +This completes the local clean SDK compilation/probe check. Windows/Linux hosted builds and hardware gates remain outstanding; #23 is not complete. diff --git a/docs/graphics/evidence/2026-09-07-clean-angle/angle-clean-build.log.gz b/docs/graphics/evidence/2026-09-07-clean-angle/angle-clean-build.log.gz new file mode 100644 index 000000000..6feae4300 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-clean-angle/angle-clean-build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-clean-angle/clean-all-probes-build.log.gz b/docs/graphics/evidence/2026-09-07-clean-angle/clean-all-probes-build.log.gz new file mode 100644 index 000000000..a1c4e2f9a Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-clean-angle/clean-all-probes-build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-clean-angle/clean-all-probes-configure.log.gz b/docs/graphics/evidence/2026-09-07-clean-angle/clean-all-probes-configure.log.gz new file mode 100644 index 000000000..537f30097 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-clean-angle/clean-all-probes-configure.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-clean-angle/native-probes.json.gz b/docs/graphics/evidence/2026-09-07-clean-angle/native-probes.json.gz new file mode 100644 index 000000000..1d98e7465 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-clean-angle/native-probes.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-clean-dawn/README.md b/docs/graphics/evidence/2026-09-07-clean-dawn/README.md new file mode 100644 index 000000000..6b6c780c8 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-clean-dawn/README.md @@ -0,0 +1,7 @@ +# Clean shared Dawn build: macOS ARM64 + +A new `artifacts/graphics-clean-build` directory compiled all 932 build steps using the verified pinned source tree and the updated dependency lock. It installed to a separate `artifacts/graphics-clean-sdk` directory. No objects from the previous static/shared experiment were reused. + +A new probe build using only this Dawn SDK passed the Metal hardware test on Apple M4, verifying all 68 pixels. Compressed configure/build logs, the SDK manifest and probe result are retained here. This is Dawn-only clean-build evidence; it does not qualify ANGLE or another RID. + +Reproduce with `python3 eng/graphics/build.py dawn --rid osx-arm64 --jobs 6 --build --sdk `. Configure `eng/graphics/probes` in a new directory with `WEBSCENE_GRAPHICS_COMPONENTS=dawn` and `WEBSCENE_GRAPHICS_SDK_ROOT=/osx-arm64`, build, and run `webscene_dawn_probe metal`. diff --git a/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-probe-build.log.gz b/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-probe-build.log.gz new file mode 100644 index 000000000..09d5643f2 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-probe-build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-probe-configure.log.gz b/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-probe-configure.log.gz new file mode 100644 index 000000000..d52b9dba7 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-probe-configure.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-probe.json.gz b/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-probe.json.gz new file mode 100644 index 000000000..62c334a9b Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-probe.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-shared-build.log.gz b/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-shared-build.log.gz new file mode 100644 index 000000000..d3b2b3501 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-clean-dawn/dawn-clean-shared-build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-clean-dawn/sdk-manifest.json.gz b/docs/graphics/evidence/2026-09-07-clean-dawn/sdk-manifest.json.gz new file mode 100644 index 000000000..419803f55 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-clean-dawn/sdk-manifest.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-g02-completion-scheduling.md b/docs/graphics/evidence/2026-09-07-g02-completion-scheduling.md new file mode 100644 index 000000000..d60e06328 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-g02-completion-scheduling.md @@ -0,0 +1,376 @@ +# G02 completion scheduling and native mapping evidence + +This is partial evidence for issue #24, not completion of G02 or epic #22. + +On macOS ARM64 (Apple M4), the graphics-enabled V8 build passed all 12 CTests +in 13.51 seconds after adding pending-operation scheduling. After adding the +native mapping cases, all eight graphics CTests passed in 0.47 seconds. + +Commands: + +```sh +cmake --build artifacts/graphics-build/native-v8-enabled --parallel 4 +ctest --test-dir artifacts/graphics-build/native-v8-enabled --output-on-failure +ctest --test-dir artifacts/graphics-build/native-v8-enabled -R graphics --output-on-failure +``` + +The completion mailbox tracks pending reservations under its existing mutex. +Publishing or cancelling a pending slot removes it from the pending count; +ready records retain their bounded slot until engine-thread delivery. An idle +Dawn instance with no pending operations does not force a periodic worker wake. +Outstanding operations retain the one-millisecond ProcessEvents polling bound. +Ready cancellation records request immediate delivery even after service close. +Future device-loss notifications must use an explicit event/wake strategy; +this does not qualify idle behavior of future persistent notification bindings. + +The hardware Dawn test requests a native hardware adapter/device and submits a +real command buffer. It also maps a 4096-byte buffer without RAF or a presenter, +checks its initialized contents, then separately destroys a buffer immediately +after MapAsync and before ProcessEvents. The latter must deliver Aborted, and +both paths leave no pending or ready mailbox records. This readback is an API +mapping test, not a presentation path or a claim of zero-copy composition. + +The V8 task path explicitly enters the owning context for graphics delivery and +runs a microtask checkpoint when completions are delivered. Initialization +rejects a different runtime thread. The existing runtime suite passes, but no +JavaScript WebGPU binding exists yet to prove end-to-end promise dispatch. + +Still outstanding: full native device/resource management and queue integration, +navigation and promise cancellation integration, finalizer release routing, +device loss, multi-engine stress, detailed diagnostics and performance gates, +and Windows/Linux hardware evidence. No issue is closed by these results. + +## Native device ownership follow-up + +The graphics service now owns generation-bearing Dawn device handles. Each +native device wrapper retains its adapter and device, creates a distinct device +owner identity, and closes on its engine thread. Destroy cancels that owner's +mailbox records before calling native Device.Destroy. The registry rejects +foreign-engine and stale handles and disallows destruction during an active +device execution scope. The internal adoption hook requires a freshly requested +device from this service's instance, supplied once with its originating adapter; +it is not a JavaScript-facing arbitrary device import API. + +The extended hardware test destroys an owned device while MapAsync is pending. +Its cancellation record is delivered once, the actual late native callback runs +but cannot republish, and a separate owner's mailbox record remains successful. +This proves mailbox isolation, not yet two independent hardware devices under +load. The full suite passed 12 tests in 12.75 seconds, followed by eight graphics +tests after adding the pending-device-map case. Backend resource retention is +still distinct from submission-table fence completion; the device owner does +not substitute Destroy for a GPU completion fence. + +## Two-device hardware isolation and completion diagnostics + +The hardware test now requests two distinct adapters and creates one device +from each. Dawn consumes an adapter on successful device creation, so reusing +the first adapter is deliberately avoided. Both devices are owned by one +engine service with different device identities. After destroying the first +with a pending map, the surviving device uploads 4096 bytes and maps them +successfully. The CPU upload array is overwritten immediately after WriteBuffer; +every mapped word must retain the original pattern. Both device handles are +then destroyed, leaving zero live service devices and no pending/ready records. +This qualifies the native Dawn call-time upload behavior, not yet a JavaScript +typed-array detachment path or the future native command queue. + +Completion snapshots report pending/ready counts, high-water storage occupancy, +admitted/delivered records, saturated reservations and rejected publications. +Admission-to-delivery latency samples, total and maximum nanoseconds are opt-in; +default operation does not read the clock for this instrumentation. The service +exposes completion snapshots alongside live native device/context counts and +propagates its timing option through lazy Dawn initialization. Work-queue byte +and presenter copy/readback counters still require later integration. + +The completion unit test checks counters across saturation, cancellation, +duplicate publication and slot reuse, with timing disabled and enabled. It also +passes under Clang ThreadSanitizer using `-fsanitize=thread -pthread`. + +## Bounded command channel integration + +```mermaid +flowchart LR + P[Native producers / finalizers] --> Q[Shared bounded command channel] + Q --> W[Latched engine wake] + W --> E[Engine worker / graphics service] + E --> D[Dawn device calls] + E --> A[ANGLE context scope] + D --> M[Native completion mailbox] + M --> W + E --> V[Owning V8 context delivery] +``` + +The channel retains no engine pointer. Native producers enqueue static noexcept +dispatch functions, fixed-size value/handle arguments and copied upload bytes. +Only the graphics service can consume or close the channel. It drains bounded +batches before native completion processing; queued work makes the existing +runtime readiness and idle-wait paths runnable. Shutdown closes admission and +drains all accepted commands before destroying devices/contexts. A retained +endpoint rejects admission after engine teardown. Framework Skia objects remain +outside this native execution scope and must stay on their presenter thread. + +The ANGLE service test fills a two-slot queue from another thread, overwrites +the source data, verifies saturation without execution, then drains one command +at a time and checks the original red/blue GL state in FIFO order. It verifies +queue depth/high-water/copied-upload counters and execution of accepted work +during close. Another endpoint is retained across service destruction and must +return closed. The Dawn hardware test enqueues duplicate deferred device release +records from a finalizer-like thread: the device stays resident until the engine +pump, then releases once without a stale-handle failure. + +These are native dispatch primitives, not completed JS binding integration. +Dispatchers must validate/report errors without throwing, retain referenced +resources as required by their backend operation and never capture borrowed V8 +memory. Full queues require caller retention/retry, including finalizer releases; +actual V8 finalizer registration and that retry policy remain outstanding. The +command arena is lazy and fixed-capacity once created. GPU completion fences +still govern submitted resource lifetimes independently of command consumption. + +## Cancellation versus native callback retirement + +Logical cancellation no longer makes a completion slot immediately reusable. +Each reservation also tracks its one outstanding native publication. A cancelled +record can be delivered to the engine while its slot remains occupied until the +actual callback calls publish. That call retires the native operation, rejects +duplicate logical delivery and wakes capacity waiters. Generations cannot advance +while an old native operation still owns the slot. Metrics now expose native +pending and total occupied counts separately from logical pending/ready counts. + +The open graphics service keeps its ProcessEvents polling demand while these +native callbacks are outstanding, even after all cancellation records are +consumed. Once the last native callback retires, idle polling stops. Reservation +callers must publish exactly once for native completion (or synchronous failure +before issuing the operation); cancellation is not a substitute for retirement. +Full-service shutdown still closes publication and destroys the native instance; +this change does not claim completed navigation/promise shutdown integration. + +A capacity-one test cancels and drains a record, proves another reservation is +rejected until a delayed callback retires it on another thread, then reuses the +slot with a new generation and rejects the old callback. The service test checks +continued bounded polling followed by return to its normal idle wait. The real +pending-map destruction test uses service readiness/idle recommendations while +waiting for native retirement. All 12 local tests passed in 13.56 seconds; after +adding the scheduling assertions, all eight graphics tests passed in 0.43 seconds. +The completion test also passed with Clang ThreadSanitizer. + +## V8 runtime completion integration test + +A separate `webscene_graphics_v8_runtime_tests` executable compiles the production +engine source graph with its include paths, definitions, dependencies and +compiler/toolchain settings. It avoids exporting test hooks from the production +C ABI and is enabled only for graphics + V8 test builds. + +On the runtime's owning worker thread, the fixture initializes V8, creates a JS +promise and schedules RAF, then hides the runtime. A separate native thread +publishes a completion record without entering V8. The normal runtime readiness +and task pump deliver the record; the dispatcher verifies the current isolate, +context and thread, resolves the promise, and the runtime checkpoint runs its +continuation while RAF remains unexecuted. A second case publishes a record and +uses `execute()` to reach task draining after script execution, proving explicit +context entry works on that route too. Wrong-thread graphics initialization is +rejected and completion storage returns to zero occupied slots. + +This closes the earlier lack of direct runtime pump evidence. It still does not +prove JavaScript WebGPU bindings, automatic binding-triggered initialization, +navigation cancellation or full device-loss promise behavior; those are not +implemented by the fixture. No adapter/hardware qualification is claimed by this +test, which uses a native completion record and a real V8 promise. + +```sh +ctest --test-dir artifacts/graphics-build/native-v8-enabled -R graphics_v8_runtime --output-on-failure +``` + +## Top-level document navigation retirement + +After a replacement document loads successfully, but before its location/content +is installed, the runtime retires the old graphics service inside the existing +V8 context scope. It detaches the service/dispatcher from the runtime, closes +native admission, drains cancellation records and performs a microtask checkpoint. +A transition guard rejects reentrant graphics initialization or navigation during +this delivery. The retired service stays alive until dispatch has finished; old +command endpoints and native publication remain closed after it is destroyed. +A failed document load leaves the existing graphics service intact. + +The runtime fixture verifies a pending promise receives the cancellation outcome +before the new document takes effect, late publication is rejected, the old +command endpoint is closed, and new initialization creates a distinct graphics +identity. It also verifies reentrant initialization/navigation rejection. The +full 13-test local suite passed in 12.85 seconds before the final reentrancy +assertion; the focused runtime test is rerun after that assertion. + +This covers top-level load_url navigation with the native completion dispatcher. +Iframe-specific ownership, JavaScript WebGPU promise/error types and explicit +engine-disposal promise sequencing remain separate outstanding work. The runtime +still reuses its existing top-level V8 context as before; this change retires its +graphics lifetime and does not claim a general navigation-context redesign. + +## Normal engine disposal sequencing + +The runtime exposes an engine-thread-only shutdown_graphics operation, called +by the worker's normal shutdown path before runtime reset. It permanently closes +graphics initialization, enters the owning isolate/context and reuses document +graphics retirement to deliver cancellation records and run a microtask +checkpoint before releasing the service. Repeated shutdown is harmless. A +transition guard rejects shutdown during cancellation delivery. + +The V8 fixture creates another pending promise after navigation, calls shutdown, +verifies its cancellation dispatcher runs in the owning V8 context/thread, and +checks the continuation has run before context disposal. A late native +publication is rejected, and graphics reinitialization after shutdown fails. +The raw destructor remains a no-JS fallback for bootstrap/terminal-failure cleanup; +this does not claim graceful promise delivery after an unrecoverable runtime +failure. Actual WebGPU binding promise types and iframe resource ownership are +still outstanding. + +## Reserved finalizer-release capacity + +The native service now has a separate fixed-capacity release channel. A wrapper +must reserve a generation-bearing slot before it becomes GC-visible; if no slot +is available, wrapper creation must handle backpressure before exposing the +object. A finalizer publishes that existing slot without allocation or competing +for command-queue space. The record captures the accepted command prefix, and +the engine dispatches release only after that prefix has executed. Later queued +commands cannot indefinitely postpone an eligible release. This CPU execution +barrier does not substitute for GPU submission fences. + +The channel retains native command/wake endpoints, not an engine pointer. Duplicate +and stale tickets are rejected. Shutdown closes publication, drains published +releases after accepted commands, and leaves unpublished registrations to the +service's owner-wide resource teardown. Registration occupancy is exposed in +service metrics. The older ordinary-command release helpers remain available; +GC bindings should use reserved slots to avoid retrying a full command queue. + +The hardware service test reserves one slot, saturates a two-command queue, +publishes release from another thread, and verifies the context stays alive after +one command but is released after both. It checks duplicate/stale publication, +slot reuse and shutdown of an unpublished registration. All 13 local CTests +passed in 13.62 seconds. The service test also passed under Clang ThreadSanitizer; +Dawn/ANGLE SDK binaries themselves were not rebuilt with sanitizer instrumentation. +Actual V8 weak-handle registration is the next integration step, not claimed here. + +## V8 weak-wrapper release registry + +The binding utility `graphics/v8_release_registry.h` owns bounded weak wrapper +entries and reserves native release slots before registration succeeds. It follows +the pinned V8 15.3.10 weak-callback contract: the first pass resets the triggering +persistent handle and schedules a second pass; the second pass publishes native +release work without executing JavaScript or GPU APIs. The engine later drains +that release through the command-prefix barrier. Registry disposal in its owning +isolate scope resets remaining handles and publishes their reserved releases. + +The real V8 runtime fixture registers an unreachable object, triggers collection +through notify_low_memory, verifies no native release ran inside GC, then pumps +the runtime and observes exactly one release and zero occupied release slots. +It verifies bounded wrapper registration, reuses the collected entry, and disposes +the registry while a new wrapper is still held by a local handle. That disposal +also queues release instead of executing it inline. The focused runtime test +passed in 0.58 seconds with both cases. + +This utility is exercised with real V8 objects and native dispatch records. Future +WebGPU/WebGL bindings must own a registry, attach each resource wrapper before +exposure and dispose the registry in its isolate scope. Those browser API wrapper +classes are not introduced here. The explicit low-memory call is test stimulus, +not a new production GC policy. + +## Failed resource insertion preserves ownership + +Resource-table insertion now accepts an rvalue reference and transfers the +unique pointer only after thread/owner/capacity validation succeeds. A rejected +wrong-thread call previously destroyed its by-value argument during unwinding, +which could release a thread-confined native object on the wrong thread. Tests +now retain a named pointer across wrong-thread, wrong-owner and full-table +failures and verify successful insertion transfers it exactly once. The resource +test passed under AddressSanitizer and UndefinedBehaviorSanitizer. + +The full local CTest run passed 12 of 13 tests, including every graphics test. +The existing native-engine DOM activation case failed with: +`duplicate activation was not coalesced while save was pending: {"activations":0,"requests":0,"pending":false,"label":"Save"}`. +An isolated rerun of webscene_native_engine_tests passed in 11.16 seconds. +This is an unresolved intermittent test failure, not a clean full-suite pass or +proof that its cause is unrelated. No test expectation was weakened. + +## Completion-delivery reentrancy guard + +The graphics service now rejects recursive completion pumping and close during +an active pump. The runtime separately guards the entire graphics delivery and +microtask-checkpoint scope. Synchronous navigation/shutdown from a native +completion dispatcher is rejected before moving or destroying the active service; +nested task draining skips graphics delivery until the outer scope finishes. +This prevents a dispatcher from deleting the service whose pump is on the stack. +Normal later navigation/shutdown remains supported. + +The service test attempts recursive pump and close inside delivery, then throws +from the dispatcher and verifies the guard unwinds so later close succeeds. +The real V8 fixture attempts destructive navigation and shutdown from its native +dispatcher, verifies rejection, and continues normal promise delivery. All nine +graphics CTests passed in 1.37 seconds. This focused result does not erase the +previously recorded intermittent non-graphics DOM activation failure. + +The service fixture also passed with AddressSanitizer/UndefinedBehaviorSanitizer; +the prebuilt Dawn/ANGLE libraries remained uninstrumented. + +## Native device-loss signal and engine delivery + +Device requests can install a shared loss signal in their Dawn descriptor before +creation and pass it to native device adoption. Dawn's spontaneous loss callback +only stores an atomic flag and signals the retained engine wake endpoint. It +captures neither V8 nor a raw engine pointer. Waiting for device loss does not +reserve a permanently pending completion slot or force periodic idle polling. +Explicit Destroy and callback cancellation remain separate from unexpected loss. + +Service readiness observes pending loss; its engine-thread pump applies the lost +state and cancels records belonging to that device with device_lost status. +Subsequent native device access rejects the lost device, while close preserves an +already applied loss outcome. Resource-table visitation stays on its owning +execution thread and does not mutate the table during traversal. + +The Metal hardware test installs the descriptor observer, invokes Dawn ForceLoss, +waits for its native signal, verifies service readiness, pumps a pending record +to device_lost, rejects native access and rejects its late success publication. +All nine graphics CTests passed in 2.17 seconds. The pending record in this loss +case is synthetic; actual pending-map destruction is covered separately. Complete +loss reason/message propagation, browser promise semantics, simultaneous live- +device loss isolation and recovery/recreation remain outstanding. This is native +loss-path evidence, not a hardware driver-reset or full browser conformance pass. + +## Pending-map loss and asynchronous pipeline coverage + +The forced-loss hardware case now issues a real MapAsync before ForceLoss, +replacing its earlier synthetic pending record. The service delivers one logical +device_lost record and continues pumping via its readiness/idle-wait API until +the actual mapping callback retires. That callback cannot publish a second +outcome; both occupied completion storage and native-pending counts return to +zero before deferred device release. + +The same hardware fixture now compiles a WGSL compute shader through native Dawn +and calls CreateComputePipelineAsync. It verifies a successful, non-null pipeline +arrives through the service's bounded completion mailbox and headless event pump. +No presentation or RAF is involved. This proves native asynchronous compilation +progress for the fixture, not compute correctness or WebGPU CTS conformance. +The focused hardware test passed in 0.56 seconds on Apple M4 / Metal. + +## Native error-scope completion + +The hardware fixture now pushes a validation scope, creates a deliberately invalid +buffer, and pops the scope asynchronously through the native completion mailbox. +It requires a successful scope operation with a Validation error and nonempty +native message. A following scope creates a valid buffer and must complete with +NoError, proving the captured error does not contaminate the next scope. Both +operations use service readiness and idle recommendations without RAF; occupied +completion storage returns to zero after each. The Metal hardware fixture passed +in 0.48 seconds. This verifies native error-scope progress and isolation, not yet +JavaScript GPUError object construction or browser error-scope conformance. + +## Repeated stale-finalizer/native-context reuse + +The hardware service fixture now runs 64 iterations with a one-context resource +table. Each iteration reserves a finalizer release, explicitly destroys the old +ANGLE context, creates a replacement in the same slot with a new generation, +then publishes and drains the old release. The replacement must remain alive and +its GL state must remain usable. After explicit replacement destruction, live +contexts, release registrations and command depth return to zero on every cycle. +The focused Metal service fixture passed in 0.44 seconds. This is bounded native +context lifetime stress, not a general leak/performance qualification. + +G02 implementation continues to have qualification/integration gaps recorded +above. Work can now proceed on G03's scene lease layer without closing G01/G02 +or treating unavailable hardware, missing bindings or baseline gates as passes. diff --git a/docs/graphics/evidence/2026-09-07-graphics-package/README.md b/docs/graphics/evidence/2026-09-07-graphics-package/README.md new file mode 100644 index 000000000..75a02192d --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-graphics-package/README.md @@ -0,0 +1,20 @@ +# G01 graphics-enabled macOS runtime package + +The opt-in runtime wrapper built and packaged the verified shared Dawn/ANGLE dependencies with the native V8 runtime. Its complete verification run passed four native suites, the existing required WPT subset (149 documents / 509 subtests), extracted-package runtime probes, and the clean package-consumer build. This subset is existing WebScene compatibility coverage, not WebGPU/WebGL conformance. + +The package contains all three graphics libraries with matching SDK hashes, full SDK manifests, and unchanged license bytes mapped from all 1,010 original license paths. Content-hash license filenames avoid path-length problems (maximum package entry length: 92 characters). Publish output contains matching libraries and loads native ABI 3. Removing Dawn from the disposable consumer's package cache caused the expected missing-asset build error; the file was restored afterward. Staging also rejected a mismatched adjacent Dawn library before creating output. + +Reproduction command (substitute matching local SDK paths): + +```sh +bash scripts/build-native-engine-runtime.sh --rid osx-arm64 \ + --v8-root /path/to/patched-v8-15.3.10 \ + --graphics-sdk /path/to/graphics-sdk/osx-arm64 \ + --output /new/local/package-directory --package-version 1.0.33-gpu-g01.3 +``` + +The recorded run used the matching `native-engine-v8-latest` SDK identified in the previous integration investigation. Logs preserve the full build and consumer paths. `package-verification.json` records the implementation commit, package hash and all package entry hashes; `graphics-runtime.json` records dependency revisions and license provenance. The local experimental package is in `artifacts/graphics-runtime-packages-03` and was not published to a feed. + +Earlier attempts exposed duplicated license paths and writes over a deduplicated read-only license file; both were corrected before the recorded successful run. An initial running shell script also failed after it was edited in place; the final complete run used stable script contents. No failed attempt is counted as successful. + +Windows/Linux graphics package builds, their GPU/driver/compiler dependencies, and hardware qualification remain incomplete. #23 stays open; this package contains graphics prerequisites without claiming browser API support or Kestrel execution inside WebScene. diff --git a/docs/graphics/evidence/2026-09-07-graphics-package/graphics-missing-library-rejection.log.gz b/docs/graphics/evidence/2026-09-07-graphics-package/graphics-missing-library-rejection.log.gz new file mode 100644 index 000000000..2bad65fa4 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-graphics-package/graphics-missing-library-rejection.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-graphics-package/graphics-publish-smoke.log.gz b/docs/graphics/evidence/2026-09-07-graphics-package/graphics-publish-smoke.log.gz new file mode 100644 index 000000000..ca24cddb0 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-graphics-package/graphics-publish-smoke.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-graphics-package/graphics-runtime-package-build-03.log.gz b/docs/graphics/evidence/2026-09-07-graphics-package/graphics-runtime-package-build-03.log.gz new file mode 100644 index 000000000..218f19c7f Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-graphics-package/graphics-runtime-package-build-03.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-graphics-package/graphics-runtime.json b/docs/graphics/evidence/2026-09-07-graphics-package/graphics-runtime.json new file mode 100644 index 000000000..fef8ef62f --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-graphics-package/graphics-runtime.json @@ -0,0 +1,1032 @@ +{ + "schemaVersion": 1, + "rid": "osx-arm64", + "scope": "native graphics dependencies; browser APIs not qualified", + "components": { + "dawn": { + "revision": "2ca8cbfe0f8275aa0f739e7b6b4345a16e2f0378", + "lockSha256": "d8df42a73525165f6e041fb99b4cefa8a0c34ffa10ea39f326fe213a59799afd" + }, + "angle": { + "revision": "082d85ba19efba24d3c25108dc1f0cad9cf149f9", + "lockSha256": "d8df42a73525165f6e041fb99b4cefa8a0c34ffa10ea39f326fe213a59799afd" + } + }, + "libraries": { + "libwebgpu_dawn.dylib": "ba985ff167a82defc17db7ee2c1bd41ef9551ec9a78070e2622eccc4212023b6", + "libEGL.dylib": "7028670d2e3c2c2f8a6032668144035e2caca54afc71559a789b9759a5670830", + "libGLESv2.dylib": "edcce24792bc722a2ce5fc3080c13a317740ea5e1954f74efd97e04c95dfdf17" + }, + "licenses": { + "dawn/LICENSE": "licenses/dawn/0493f897193af1796d5054659f45ec7d4c5af648fa67a99f01d30e55cc805abc.txt", + "dawn/src/emdawnwebgpu/pkg/webgpu_cpp/LICENSE": "licenses/dawn/7e1efc85a78732a13d7ddfc8b52912da7c8f8d3c6d334624b20e3f3a96297de0.txt", + "dawn/third_party/EGL-Registry/LICENSE": "licenses/dawn/4782253b8777b2c679544b31b18a52616d3c2ba0515dcf695e262bd318b356f0.txt", + "dawn/third_party/EGL-Registry/src/sdk/docs/man/copyright.xml": "licenses/dawn/3a528aae8731663f7b2b02ee709b1ba1fd4f9bbd8935941b2a93981c5ab78bc4.txt", + "dawn/third_party/EGL-Registry/src/sdk/docs/man/xhtml/copyright.inc.xsl": "licenses/dawn/609ae74144cd07a61653f733a0e11abf241a10c7dff4acd07dacda9fbffae22e.txt", + "dawn/third_party/OpenGL-Registry/LICENSE": "licenses/dawn/d20809e7f3c8116615249bdefdc826c29b0b8332e7ac7ac249fd3949b716e8c5.txt", + "dawn/third_party/abseil-cpp/LICENSE": "licenses/dawn/c79a7fea0e3cac04cd43f20e7b648e5a0ff8fa5344e644b0ee09ca1162b62747.txt", + "dawn/third_party/agility-sdk/LICENSE": "licenses/dawn/caf3f489e3959df3605fec3c1f921fe72456c5d3640d998a5e635e8a9505cec5.txt", + "dawn/third_party/benchmark_shaders/unity_boat_attack/LICENSE.md": "licenses/dawn/7ff3a8e0e49a0141989e4dea4fa92e18f908fe462a19d4b6cb2f1225c857bfa1.txt", + "dawn/third_party/directx-headers/LICENSE": "licenses/dawn/7c77a44a8acd9b41fdc209864a8016b3d430b5d0e09309818d5b7444336df744.txt", + "dawn/third_party/directx-headers/src/LICENSE": "licenses/dawn/903df5512f7d02609fed0c780a9b704f5a3eeb6e4d84ebe42a29845c81899a3c.txt", + "dawn/third_party/directx-shader-compiler/LICENSE": "licenses/dawn/27a49e35d1da96eba18fba54bc882667ff0ff8c0254f16f2b6e165d605ba7df8.txt", + "dawn/third_party/directx-shader-compiler/src/LICENSE.TXT": "licenses/dawn/27a49e35d1da96eba18fba54bc882667ff0ff8c0254f16f2b6e165d605ba7df8.txt", + "dawn/third_party/directx-shader-compiler/src/lib/DxilCompression/LICENSE.TXT": "licenses/dawn/6f20fa7672b00e2e975c291df737cf227addf3ad32e36fef3fe0f416e4664d3d.txt", + "dawn/third_party/directx-shader-compiler/src/lib/Support/COPYRIGHT.regex": "licenses/dawn/0424e57d4303164dc59a8509c20dae0518b853692e5c2b0e98b11816fdbc97c7.txt", + "dawn/third_party/directx-shader-compiler/src/test/YAMLParser/LICENSE.txt": "licenses/dawn/d0d8b09800a45cd982e9568fc7669d9c1a4c330e275a821bbe24d54366d16fe9.txt", + "dawn/third_party/directx-shader-compiler/src/tools/clang/lib/Headers/hlsl/LICENSE.txt": "licenses/dawn/eb425408dc2905e3506310bfdc33fdf00c1dfc2f2f01de95fb01809b29765be2.txt", + "dawn/third_party/directx-shader-compiler/src/utils/unittest/googlemock/LICENSE.txt": "licenses/dawn/9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138.txt", + "dawn/third_party/directx-shader-compiler/src/utils/unittest/googletest/LICENSE.TXT": "licenses/dawn/9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138.txt", + "dawn/third_party/emdawnwebgpu/LICENSE": "licenses/dawn/2f79bf3699b0870251255b381670237f73f21a04a38c094f791eba39c5fd1df7.txt", + "dawn/third_party/emdawnwebgpu/pkg/webgpu/src/LICENSE": "licenses/dawn/2f79bf3699b0870251255b381670237f73f21a04a38c094f791eba39c5fd1df7.txt", + "dawn/third_party/glfw3/LICENSE": "licenses/dawn/149704059b5d0bf551637e50042dd4de9c2cae921021f6636298911e3a5f9462.txt", + "dawn/third_party/glfw3/src/LICENSE.md": "licenses/dawn/149704059b5d0bf551637e50042dd4de9c2cae921021f6636298911e3a5f9462.txt", + "dawn/third_party/glslang/LICENSE": "licenses/dawn/23353f4505b1c8ce4f8f72fc3b11dc74b4a8a7bf95921d93ff77f227c171a710.txt", + "dawn/third_party/glslang/src/LICENSE.txt": "licenses/dawn/17e70c676e1521ff3e4686f04a2053d93a7e28a33be8de7ec37ab0ff72feb677.txt", + "dawn/third_party/glslang/src/license-checker.cfg": "licenses/dawn/0b7c936ff1270fb5089750e326f732ce2f08b18e804dc8847aa44561d0a7a277.txt", + "dawn/third_party/google_benchmark/src/LICENSE": "licenses/dawn/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "dawn/third_party/googletest/src/LICENSE": "licenses/dawn/9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138.txt", + "dawn/third_party/jinja2/LICENSE.rst": "licenses/dawn/3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b.txt", + "dawn/third_party/libprotobuf-mutator/src/LICENSE": "licenses/dawn/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "dawn/third_party/markupsafe/LICENSE": "licenses/dawn/0bbe88228fd63d20ec097f64e58d5a0a465123ae139140a18d406c60b48824b5.txt", + "dawn/third_party/protobuf/LICENSE": "licenses/dawn/6e5e117324afd944dcf67f36cf329843bc1a92229a8cd9bb573d7a83130fea7d.txt", + "dawn/third_party/protobuf/src/google/protobuf/compiler/notices.h": "licenses/dawn/b47ca1ea743623d50c6e02faa136dea4f6574a98e200666f859dbf57fb491721.txt", + "dawn/third_party/protobuf/third_party/utf8_range/LICENSE": "licenses/dawn/02de69b64fc36d9e938f418e52723e42f0b2b226d58a9cb3c8dcbdf7059f5074.txt", + "dawn/third_party/renderdoc/LICENSE.md": "licenses/dawn/b921912e9e433291f6010631a0dd41cec76c4a877966ecc22ba86151b2e66718.txt", + "dawn/third_party/spirv-headers/LICENSE": "licenses/dawn/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "dawn/third_party/spirv-headers/src/LICENSE": "licenses/dawn/ea43b1de38a6f90c488800d66dec1ed671e68cda530266bc96951fb5b6307613.txt", + "dawn/third_party/spirv-tools/LICENSE": "licenses/dawn/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "dawn/third_party/spirv-tools/src/LICENSE": "licenses/dawn/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "dawn/third_party/spirv-tools/src/utils/vscode/src/lsp/LICENSE": "licenses/dawn/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "dawn/third_party/vulkan-headers/LICENSE.txt": "licenses/dawn/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "dawn/third_party/vulkan-headers/src/LICENSE.md": "licenses/dawn/95ad366d23fadf701d355bc45fb8b82ae2d700239471d35d41286ac3b08ff903.txt", + "dawn/third_party/vulkan-loader/src/LICENSE.txt": "licenses/dawn/43c0a37e6a0fa7ff3c843b3ec5a4fac84b712558ddac103fbd4c1649662a9ece.txt", + "dawn/third_party/vulkan-utility-libraries/src/LICENSE.md": "licenses/dawn/69760673abf91cfd0280ae73739a29c078f493804d9016a122b3b189b48ad6e6.txt", + "dawn/third_party/webgpu-headers/LICENSE": "licenses/dawn/17420d366df90c474bd70ad474694956cdb7fc64be70387a49a45458c4152d22.txt", + "dawn/third_party/webgpu-headers/src/LICENSE": "licenses/dawn/17420d366df90c474bd70ad474694956cdb7fc64be70387a49a45458c4152d22.txt", + "dawn/tools/nocompile/LICENSE": "licenses/dawn/368cca1106be99d39ecd32a38d8305585d802a475effb66380b91ffc9bcf709b.txt", + "angle/LICENSE": "licenses/angle/bf4da21bd20bcfb5b60b7ecc67fa864a79be049e21d6178076887f178dd6c71a.txt", + "angle/build/android/incremental_install/third_party/AndroidHiddenApiBypass/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/buildtools/LICENSE": "licenses/angle/ff11d445fb41a1087c7630e120ab15f1a2cb67c1b707173cb494141805fca35e.txt", + "angle/buildtools/reclient/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/buildtools/reclient/NOTICE": "licenses/angle/a90a4f374e17d62b1725bd687eca71ae7110ded0c2fe3e2f06c9ba7176169b5c.txt", + "angle/buildtools/third_party/mold/LICENSE": "licenses/angle/c98a2858469bd3b231c8865c5b65f80f6ffbf25e850d5d575967e3d9ee080755.txt", + "angle/src/common/third_party/xxhash/LICENSE": "licenses/angle/6ffedbc0f7878612d2b23589f1ff2ab15633e1df7963a5d9fc750ec5500c7e7a.txt", + "angle/src/libANGLE/renderer/vulkan/shaders/src/third_party/etc_decoder/LICENSE": "licenses/angle/118be5792e5126839694ca2209c62c71d2d7cd49e7bbb43bbdee9b016fb06094.txt", + "angle/src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/LICENSE": "licenses/angle/09a7c3fbc0b4ae6a9ccc4ffdcbfa511c14b8647a24f24783838862cf6c226d4e.txt", + "angle/src/tests/test_utils/third_party/LICENSE": "licenses/angle/0e64c1e9cd62f47682caeb545d2943fb4c38a9b2e5d9fd7e3b456973bb430d1b.txt", + "angle/src/third_party/ceval/LICENSE": "licenses/angle/0dd71b8a6d6db0db4dc38a983749e1b2f4bb57aba30141a168042042c4a7b6f8.txt", + "angle/src/third_party/libXNVCtrl/LICENSE": "licenses/angle/31346421254a3e6e12687cf17f19f6357ee73a617fa7b3d3ccefdcbabe49bdd3.txt", + "angle/src/third_party/volk/LICENSE.md": "licenses/angle/336f505f8d5aa73ea40b4d798dde86953e9c1f6525757f1d7f18120fea09bb1d.txt", + "angle/third_party/EGL-Registry/src/sdk/docs/man/copyright.xml": "licenses/angle/3a528aae8731663f7b2b02ee709b1ba1fd4f9bbd8935941b2a93981c5ab78bc4.txt", + "angle/third_party/EGL-Registry/src/sdk/docs/man/xhtml/copyright.inc.xsl": "licenses/angle/609ae74144cd07a61653f733a0e11abf241a10c7dff4acd07dacda9fbffae22e.txt", + "angle/third_party/OpenCL-Docs/src/LICENSE": "licenses/angle/01db48fbe12f95dfd63b92dc7c29afcf8783f96f8ca0061a9fb7e869f5a08512.txt", + "angle/third_party/OpenCL-Docs/src/config/copyright-ccby.txt": "licenses/angle/62fbcd9ebefaa5dd4245b241d36655e83391bb196c873c6023e1296fbc447ab8.txt", + "angle/third_party/OpenCL-Docs/src/copyrights-ccby.txt": "licenses/angle/f7ef3add54eda59b0a8b882154c8d1e98d1192f3d0287d1a37b1c65a95ec2ff3.txt", + "angle/third_party/OpenCL-Docs/src/copyrights.txt": "licenses/angle/18a3e8b3d7d0adf096d0b28183f6c808749877829cc53b8ac9ed79e2bd01ac15.txt", + "angle/third_party/Python-Markdown/LICENSE.md": "licenses/angle/6f1193cb634718e65c3a537d6e25ebd614820ec0ef693cfc12248112638d64da.txt", + "angle/third_party/SwiftShader/LICENSE.txt": "licenses/angle/3ddf9be5c28fe27dad143a5dc76eea25222ad1dd68934a047064e56ed2fa40c5.txt", + "angle/third_party/SwiftShader/third_party/SPIRV-Headers/LICENSE": "licenses/angle/ea43b1de38a6f90c488800d66dec1ed671e68cda530266bc96951fb5b6307613.txt", + "angle/third_party/SwiftShader/third_party/SPIRV-Tools/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/SwiftShader/third_party/SPIRV-Tools/utils/vscode/src/lsp/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/SwiftShader/third_party/astc-encoder/LICENSE.txt": "licenses/angle/494accc32e50eb523a0e384d0ae6d4b702db867a89d6971216760e92b240ee12.txt", + "angle/third_party/SwiftShader/third_party/llvm-10.0/llvm/include/llvm/Support/LICENSE.TXT": "licenses/angle/a012d664e4e01df52a65b2eeafdfb8aeb856fec0e6c372265d01b0109c3f5e2a.txt", + "angle/third_party/SwiftShader/third_party/llvm-10.0/llvm/lib/Support/COPYRIGHT.regex": "licenses/angle/0424e57d4303164dc59a8509c20dae0518b853692e5c2b0e98b11816fdbc97c7.txt", + "angle/third_party/SwiftShader/third_party/llvm-16.0/llvm/include/llvm/Support/LICENSE.TXT": "licenses/angle/54cbc326a78b9400065bfc5830a57fdcdaf808286d4ac35d8a9e324aa77b7241.txt", + "angle/third_party/SwiftShader/third_party/llvm-16.0/llvm/lib/Support/BLAKE3/LICENSE": "licenses/angle/6a94bedb8b707ed97f6e310d0d015ab14e0683ffa0a612b02958581b9cc9fc0e.txt", + "angle/third_party/SwiftShader/third_party/llvm-16.0/llvm/lib/Support/COPYRIGHT.regex": "licenses/angle/0424e57d4303164dc59a8509c20dae0518b853692e5c2b0e98b11816fdbc97c7.txt", + "angle/third_party/SwiftShader/third_party/llvm-subzero/LICENSE.TXT": "licenses/angle/9c9a05118ed1b6d96781a2e52335f7d4ec3dd6e7139340a8aa95fbf7eb4f199a.txt", + "angle/third_party/SwiftShader/third_party/marl/LICENSE": "licenses/angle/58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd.txt", + "angle/third_party/SwiftShader/third_party/marl/license-checker.cfg": "licenses/angle/398974c0415d06f88df08dc434cf58a0d0038afaf95d100799f42bae973ea945.txt", + "angle/third_party/SwiftShader/third_party/subzero/LICENSE.TXT": "licenses/angle/c55ce1e876843853a8a2e5c936df6dc8dd3d185f83d85e6d113143b8c24f542e.txt", + "angle/third_party/VK-GL-CTS/src/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/VK-GL-CTS/src/NOTICE": "licenses/angle/ca382aa537f8923d6c0991fb976d184a2009eb76080313bf10dcecdc9311f0dd.txt", + "angle/third_party/VK-GL-CTS/src/external/graphicsfuzz/data/gles3/graphicsfuzz/LICENSE": "licenses/angle/8e95cc3fc83600845b44bd2f763d8edc48cfffe0feb3abd59d30810aef1119c7.txt", + "angle/third_party/VK-GL-CTS/src/external/vulkancts/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/abseil-cpp/LICENSE": "licenses/angle/c79a7fea0e3cac04cd43f20e7b648e5a0ff8fa5344e644b0ee09ca1162b62747.txt", + "angle/third_party/android_system_sdk/LICENSE": "licenses/angle/8f1bd8841582bdee098eeae9eeb3862d9e7af011e94e54c14aef0568e816be19.txt", + "angle/third_party/astc-encoder/src/LICENSE.txt": "licenses/angle/494accc32e50eb523a0e384d0ae6d4b702db867a89d6971216760e92b240ee12.txt", + "angle/third_party/astc-encoder/src/Test/Images/HDRIHaven/LICENSE.txt": "licenses/angle/f7230d5a427449430ec09e331f751ae8a7e26cafdc0af89c02e5312e4320de9b.txt", + "angle/third_party/astc-encoder/src/Test/Images/Khronos/LICENSE.txt": "licenses/angle/edc930ca714966b56089c5a3e9a790366f9bf37e1749fb17e6dcf40b8783251f.txt", + "angle/third_party/catapult/LICENSE": "licenses/angle/f0df289ba9d03d857ad1c2f5918861376b1510b71588ffc60eff5c7a7bfedb09.txt", + "angle/third_party/catapult/common/py_vulcanize/third_party/rcssmin/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/catapult/common/py_vulcanize/third_party/rcssmin/bench/LICENSE.cssmin": "licenses/angle/65d4ed698fb5cbcd1d44c78bc6a02c5bf1da00df5395d2d6ac43bdafe6bc20dc.txt", + "angle/third_party/catapult/common/py_vulcanize/third_party/rjsmin/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/catapult/experimental/trace_on_tap/LICENSE": "licenses/angle/3b38d48befd0af70b892e13d10c9e34679416c24a9277f962629951c64d71f4c.txt", + "angle/third_party/catapult/experimental/trace_on_tap/third_party/pako/LICENSE": "licenses/angle/3bc404ffa7888053253eedcc5a667619aa08dc1be0bea400e5ff28c51602180f.txt", + "angle/third_party/catapult/systrace/profile_chrome/third_party/COPYING": "licenses/angle/8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643.txt", + "angle/third_party/catapult/systrace/systrace/LICENSE": "licenses/angle/ef5b39dfcafe08323262e3f51a3a9de649978a55ed8ef8eef3c451f2c1e78a53.txt", + "angle/third_party/catapult/telemetry/third_party/altgraph/LICENSE": "licenses/angle/348dfecdd95ac4de096f7495674c9e90c778f8795d3faf5cd880b0a25bcbdd15.txt", + "angle/third_party/catapult/telemetry/third_party/altgraph/doc/license.rst": "licenses/angle/e21ff4f2af8698b4e8f44d333bf2c8b59523488357ce26513afc7404092c1884.txt", + "angle/third_party/catapult/telemetry/third_party/chromite/LICENSE": "licenses/angle/212c5a071f61512786b5e5840b3d70c85e017f3f82939ad4d4a870fc48b33477.txt", + "angle/third_party/catapult/telemetry/third_party/flot/LICENSE.txt": "licenses/angle/e09d954054165670b6a669e6da59673d9e85f343b9983e92a220623ff0198f8c.txt", + "angle/third_party/catapult/telemetry/third_party/modulegraph/LICENSE": "licenses/angle/c70d07bf7a3d935e05c62e80bc0fd30292d7182cd9ab695f7c7ddd7bcac39256.txt", + "angle/third_party/catapult/telemetry/third_party/mox3/COPYING.txt": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/catapult/telemetry/third_party/png/LICENSE": "licenses/angle/8ebde739ff734d4ed18082965e83dbab9673a37199d2af9cfc3fb390398b35b8.txt", + "angle/third_party/catapult/telemetry/third_party/pyfakefs/pyfakefs/COPYING": "licenses/angle/09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b.txt", + "angle/third_party/catapult/telemetry/third_party/websocket-client/LICENSE": "licenses/angle/f3834b4a6b6e7c112207c84a11e87d4255bee0310b90338b5aaccd849fab1afb.txt", + "angle/third_party/catapult/third_party/apiclient/LICENSE": "licenses/angle/43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1.txt", + "angle/third_party/catapult/third_party/beautifulsoup4/COPYING.txt": "licenses/angle/424336c2b3446b3c179f07217271bb914dc881a65c2bf7021da98c77e776d2c9.txt", + "angle/third_party/catapult/third_party/beautifulsoup4-4.9.3/COPYING.txt": "licenses/angle/a47ea51236098464fe0b4f559743590b533056d9e00f49ecbf80299fab47e231.txt", + "angle/third_party/catapult/third_party/beautifulsoup4-4.9.3/LICENSE": "licenses/angle/ca7227ddb9eed6cc809e157f67b020e78dde063240001d11856f85c49cb6e423.txt", + "angle/third_party/catapult/third_party/cachetools/LICENSE": "licenses/angle/7dd496262c0ba3787f7eebf02663c50c305ff575a81f69208e1645738da3cffc.txt", + "angle/third_party/catapult/third_party/chai/LICENSE": "licenses/angle/17afb4516438c26ee15213c5a082206340d976a68472b8eab2499d7bce4debec.txt", + "angle/third_party/catapult/third_party/chardet/LICENSE": "licenses/angle/6095e9ffa777dd22839f7801aa845b31c9ed07f3d6bf8a26dc5d2dec8ccc0ef3.txt", + "angle/third_party/catapult/third_party/click/LICENSE": "licenses/angle/9a8ad106a394e853bfe21f42f4e72d592819a22805d991b5f3275029292b658d.txt", + "angle/third_party/catapult/third_party/cloudstorage/COPYING": "licenses/angle/50e6751797c50dedd75ef1b8a0d9e42f5f8472e9fbce91f34718e9f97b0c780a.txt", + "angle/third_party/catapult/third_party/coverage/LICENSE.txt": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/catapult/third_party/coverage/NOTICE.txt": "licenses/angle/55f703486573f73b00920a8d46fc551debc4d1fa35ff4c18784363b09b3bf780.txt", + "angle/third_party/catapult/third_party/d3/LICENSE": "licenses/angle/7a3cb0e5055874e67db9aa2d5fe26de23204fa994ffbad198901ffe9c812a717.txt", + "angle/third_party/catapult/third_party/d3/v5/LICENSE": "licenses/angle/7a3cb0e5055874e67db9aa2d5fe26de23204fa994ffbad198901ffe9c812a717.txt", + "angle/third_party/catapult/third_party/depot_tools/depot_tools/third_party/schema/LICENSE-MIT": "licenses/angle/f4360ca8f779e6a673cd2882f73419bc2c5f74184fd9db91d2e86a368cc04e0b.txt", + "angle/third_party/catapult/third_party/flask/LICENSE": "licenses/angle/489a8e1108509ed98a37bb983e11e0f7e1d31f0bd8f99a79c8448e7ff37d07ea.txt", + "angle/third_party/catapult/third_party/flot/LICENSE.txt": "licenses/angle/52cb566b16d84314b92b91361ed072eaaf166e8d3dfa3d0fd3577613925f205c.txt", + "angle/third_party/catapult/third_party/google-auth/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/catapult/third_party/graphy/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/catapult/third_party/gsutil/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/catapult/third_party/gsutil/gslib/vendored/boto/LICENSE": "licenses/angle/e3248f259a211f4d9ed06cfd07bc64373376c92a192152e37ec3420d6036dd4e.txt", + "angle/third_party/catapult/third_party/gsutil/gslib/vendored/oauth2client/LICENSE": "licenses/angle/d6a43f0bae029b0cea5bd0fffd87f05659dc599a763886027614ad210be1ba3d.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/apitools/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/argcomplete/LICENSE.rst": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/argcomplete/NOTICE": "licenses/angle/2c889c721ec8ae6d7664680afaefbb4c7620976f434b57a506ecd92f0649b6a0.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/cachetools/LICENSE": "licenses/angle/23c4eff7a1c027a977a0b79c4497e17582c334c5f17ef6ac8ca0b52d1e7d8417.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/certifi/LICENSE": "licenses/angle/e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/chardet/LICENSE": "licenses/angle/dc626520dcd53a22f727af3ee42c770e56c97a64fe3adb063799d8ab032fe551.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/charset_normalizer/LICENSE": "licenses/angle/6d0d41bfe170ac6c7dc248c9a63e254d0fb45a60d50a8257d0af92c6e249b887.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/charset_normalizer/data/NOTICE.md": "licenses/angle/0cb3efcfd8f7a02a337e98dc3de4b0b57424d7208a332b26e7deb8cb94c13922.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/crcmod/LICENSE": "licenses/angle/89480768826f408daea1f3caff0509c2cc9606e10f6bb0ccfd12a3d604842c35.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/crcmod_osx/LICENSE": "licenses/angle/89480768826f408daea1f3caff0509c2cc9606e10f6bb0ccfd12a3d604842c35.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/fasteners/LICENSE": "licenses/angle/d2de2f566d2d0e0b509fb0ea1fa3669f49064ab1de21c57453cab3750a234e8f.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/gcs-oauth2-boto-plugin/LICENSE": "licenses/angle/8c6db340475136df3c1201d458fa5755698eace76e510471ecc9d857d6083dac.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/google-auth-library-python/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/google-auth-library-python-httplib2/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/google-reauth-python/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/httplib2/LICENSE": "licenses/angle/589eec38f72df2be203711d3b8cbece9b908c5e7ff00bc3cab7f63bae9e366b4.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/idna/LICENSE.md": "licenses/angle/b7a336abf3b04e180ec065cdd16e705d079e1cc7a14f910aa6e9187f36b9cd87.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/monotonic/LICENSE": "licenses/angle/cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/pyasn1/LICENSE.rst": "licenses/angle/2aad5fc00f705c4a1addb83eed10a6a75d286a3779f0cf8519d87e62bc4735fd.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/pyasn1-modules/LICENSE.txt": "licenses/angle/70bb0e4c89f4e41a11950365d98a13e2e6ad6ee4aed80cd1ecffc93d98d44e8c.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/pyparsing/LICENSE": "licenses/angle/10d5120a16805804ffda8b688c220bfb4e8f39741b57320604d455a309e01972.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/pyu2f/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/requests/LICENSE": "licenses/angle/09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/requests/NOTICE": "licenses/angle/f5110972dedad2b4e9d314518daf3b7d72d6e02e499acd802181de6f74571dcc.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/requests/ext/LICENSE": "licenses/angle/3172d399cbd8f10609e73fec73d0e0b33eecd3c572a68b0722229d8c7059f725.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/retry-decorator/LICENSE.txt": "licenses/angle/c3710b8fc15eee9d2de041c0302116dc30fcb370ae5cc3969e746d8f08b869fd.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/rsa/LICENSE": "licenses/angle/073f28b7d389c8fe74f607e17c27f81eaa5ace69edc43a884f23f41b41c5c726.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/six/LICENSE": "licenses/angle/4375ba20e2b9c6c4e7cad2940a628fd90e95cc3d50ee92aae755715d8ba1fbd0.txt", + "angle/third_party/catapult/third_party/gsutil/third_party/urllib3/LICENSE.txt": "licenses/angle/130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686.txt", + "angle/third_party/catapult/third_party/html5lib-1.1/LICENSE": "licenses/angle/16a39991619e92f18680932da2a9199fdf7d95df3ecaedc52ea06218aabafd6f.txt", + "angle/third_party/catapult/third_party/html5lib-1.1/html5lib/tests/testdata/LICENSE": "licenses/angle/ff512aac9ef231d504be5afaf4429005024e4b2aaf257be39524f37b8402aaf2.txt", + "angle/third_party/catapult/third_party/idb/LICENSE": "licenses/angle/873a2f333fda393ec3464f4579209b019d98e97c3bf498b10e85f630162fd708.txt", + "angle/third_party/catapult/third_party/idna/LICENSE.rst": "licenses/angle/0d4bc7abd48dcfb14e24254ee404066737ff0167144e222914a2113b8794683e.txt", + "angle/third_party/catapult/third_party/ijson/LICENSE.txt": "licenses/angle/3cdb5f5be14a92dec127561fc90d8f7127aa59d81522938ffc91952615ea13eb.txt", + "angle/third_party/catapult/third_party/itsdangerous/LICENSE": "licenses/angle/a6c1acff7e7b7918ae5122700fe2da1e127dd459cc5b04271ff8f62d6a6f9e17.txt", + "angle/third_party/catapult/third_party/jinja2/LICENSE": "licenses/angle/3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b.txt", + "angle/third_party/catapult/third_party/jquery/LICENSE.txt": "licenses/angle/a078a8f80016416042c2e5f04dbb7f499f0f6deebb086511f3a3b72633a2d761.txt", + "angle/third_party/catapult/third_party/jszip/LICENSE.markdown": "licenses/angle/b7804b570c31c8491352bd4e0b123a9652edb72d778554986ec51f22e6c2b70b.txt", + "angle/third_party/catapult/third_party/markupsafe/LICENSE": "licenses/angle/489a8e1108509ed98a37bb983e11e0f7e1d31f0bd8f99a79c8448e7ff37d07ea.txt", + "angle/third_party/catapult/third_party/mocha/LICENSE": "licenses/angle/1f194a987fa1dc60e4bcf5e04e0fc03fff8f2ee587c52136adb2cebb397250b8.txt", + "angle/third_party/catapult/third_party/mox3/COPYING.txt": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/catapult/third_party/oauth2client/LICENSE": "licenses/angle/e3aefad6cbfecc174ce6a7628e8f2fb58d1c2928d9d4f9d531d125177ab23324.txt", + "angle/third_party/catapult/third_party/polymer/LICENSE.polymer": "licenses/angle/24699c6858472311aa9acc6c2b7112ff9de6e7792569158ba9e439deb0529ef6.txt", + "angle/third_party/catapult/third_party/polymer/components/google-apis/LICENSE": "licenses/angle/4149f7427385d27e5915e68129cff9706f424353783a958919f99e80cb6fcc63.txt", + "angle/third_party/catapult/third_party/polymer/components/google-signin/LICENSE": "licenses/angle/328cb74a9f2c5b67be2f63da900f09363060feac3c01ee42e3f441c2cab1eec3.txt", + "angle/third_party/catapult/third_party/polymer/components/polymer/LICENSE.txt": "licenses/angle/984fb04a16a9f1e0145ffd891125dc366a01cd921f58c9b0369be400c720790d.txt", + "angle/third_party/catapult/third_party/polymer/components/promise-polyfill/LICENSE": "licenses/angle/453a712c58161b74efa998578aaf10fd7ad8204120730de38ca04d7f47f3ea46.txt", + "angle/third_party/catapult/third_party/polymer/components/shadycss/LICENSE.md": "licenses/angle/10ae82b5a349c1ac15015d2c50e5adaf6413538f69be961cf0140cfc152b97e3.txt", + "angle/third_party/catapult/third_party/polymer/components/web-animations-js/COPYING": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/catapult/third_party/polymer-svg-template/LICENSE": "licenses/angle/737070ec67c0feed5e767af9c774d159c4132604812ac29736ee5e7a917c998d.txt", + "angle/third_party/catapult/third_party/pyasn1_modules/LICENSE.txt": "licenses/angle/22c5cc6922ab5d69fba32d8c5ee4cdd14981508cb53afc0ebd85593847fd95a5.txt", + "angle/third_party/catapult/third_party/pyfakefs/COPYING": "licenses/angle/09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b.txt", + "angle/third_party/catapult/third_party/pyparsing/LICENSE": "licenses/angle/10d5120a16805804ffda8b688c220bfb4e8f39741b57320604d455a309e01972.txt", + "angle/third_party/catapult/third_party/redux/LICENSE.md": "licenses/angle/f2da73c752c6b87624755edacf927cbd915fa76555d383e74494bad4a5155ab3.txt", + "angle/third_party/catapult/third_party/requests/LICENSE": "licenses/angle/c15544050f84cf503e47d60299a7c119e751f1d81ac617a8a13e706581cc05bc.txt", + "angle/third_party/catapult/third_party/six/LICENSE": "licenses/angle/8bb850c565aa389fdc16f3a46965ad23d82adff60f2393fc2762b63185e8e6c9.txt", + "angle/third_party/catapult/third_party/snap-it/LICENSE": "licenses/angle/b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1.txt", + "angle/third_party/catapult/third_party/tsproxy/LICENSE": "licenses/angle/b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1.txt", + "angle/third_party/catapult/third_party/typ/LICENSE": "licenses/angle/6dc0e068dcf3a5bc8e054205b85b7720e1d49265bbc64bf515d2cf79197df69a.txt", + "angle/third_party/catapult/third_party/uritemplate/LICENSE": "licenses/angle/2d1f6074aca5e089e1cd580c0a5a925fc508ad542aec8bfe804c0031cf717667.txt", + "angle/third_party/catapult/third_party/vinn/LICENSE": "licenses/angle/842d692fdbb8b4dd8e22461d5091e29c1c8725dd7618fcd5d59436c1c10f8804.txt", + "angle/third_party/catapult/third_party/vinn/third_party/parse5/LICENSE": "licenses/angle/d0a435e5f6a4943a2c3927c3932e7baee9c2231caa977ffeba748f3712ae437e.txt", + "angle/third_party/catapult/third_party/vinn/third_party/v8/LICENSE": "licenses/angle/f9db6a9bcfcc0644975526b4f9a21af61473ac2767e3c4764ff14c48fbff4000.txt", + "angle/third_party/catapult/third_party/vinn/third_party/v8/LICENSE.strongtalk": "licenses/angle/6a585a9f466654abc8fc0829d56b1bc987e3a073d31faa03bba37d33640a23cd.txt", + "angle/third_party/catapult/third_party/vinn/third_party/v8/LICENSE.v8": "licenses/angle/4af93c12062c58058378de2397dc1c92bbff9ddfb1d583a01c84127557ce97ca.txt", + "angle/third_party/catapult/third_party/vinn/third_party/v8/LICENSE.valgrind": "licenses/angle/cae8c00ca6e90a682c321ec11e7a5a345d0d317aa0b8f038e03ef03a18095b2f.txt", + "angle/third_party/catapult/third_party/webapp2/LICENSE": "licenses/angle/5359da685feee46d7e22acc5b8fcc496c5ca176fc46986eb640aa21aaedfaf1d.txt", + "angle/third_party/catapult/third_party/webencodings-0.5.1/LICENSE": "licenses/angle/f23bae6ada76095610a77137fb92aec7342723900211c5826d54b4c57907ca56.txt", + "angle/third_party/catapult/third_party/werkzeug/LICENSE": "licenses/angle/3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b.txt", + "angle/third_party/catapult/tracing/LICENSE": "licenses/angle/f77133324f35589f9f170473456321fe76aa35b750293cb8a475e26afa8f2bac.txt", + "angle/third_party/catapult/tracing/third_party/chai/LICENSE": "licenses/angle/17afb4516438c26ee15213c5a082206340d976a68472b8eab2499d7bce4debec.txt", + "angle/third_party/catapult/tracing/third_party/d3/LICENSE": "licenses/angle/1920d2326ebbad34dcbd9681b4fe4926f113aa5e7dc9a92fceb456d859ee142e.txt", + "angle/third_party/catapult/tracing/third_party/gl-matrix/LICENSE.md": "licenses/angle/e8b80a53d0f95a3cf0f992f8cfc6b3911a7f32f47e0e4a8d4fd66582eeae9484.txt", + "angle/third_party/catapult/tracing/third_party/jpeg-js/LICENSE": "licenses/angle/24604018b3d42b92eb3a0ee55a9e8d3bde92f95a0809f9ef22c06ce32f627940.txt", + "angle/third_party/catapult/tracing/third_party/jszip/LICENSE.markdown": "licenses/angle/602ef1d5d3db1b23ada0b61d4230ef336012de7bc3b773d565f2b27a2757f51d.txt", + "angle/third_party/catapult/tracing/third_party/mannwhitneyu/LICENSE": "licenses/angle/6aa99913137a7f9b212e53e8768871fe178e4ee01d8da0b267dbcbee314c527a.txt", + "angle/third_party/catapult/tracing/third_party/mocha/LICENSE": "licenses/angle/1f194a987fa1dc60e4bcf5e04e0fc03fff8f2ee587c52136adb2cebb397250b8.txt", + "angle/third_party/catapult/tracing/third_party/pako/LICENSE": "licenses/angle/a04665b3b2de56c66730c1f720f528175739e4104f79073614aa611da1e85539.txt", + "angle/third_party/cherry/LICENSE": "licenses/angle/04c35849b20d927d99f1f498cfc3b4e0050cd726875be6ad5392a9f75f93bb03.txt", + "angle/third_party/cherry/third_party/angular/LICENSE": "licenses/angle/fc0c17466a53b104d5b0d907b97bbf5f7ab7031581be924001ab15e42f9d893a.txt", + "angle/third_party/cherry/third_party/angular/docs/components/google-code-prettify-1.0.1/COPYING": "licenses/angle/deec98192f710d6e6aa8aba33f67087199e62c2db7f9d793b0ad465248ca3d05.txt", + "angle/third_party/cherry/third_party/angular-spinner/LICENSE": "licenses/angle/d5334c1ff1f71deabc8eec66ee26105d6919718b73f2eb300d641db43ee722d6.txt", + "angle/third_party/cherry/third_party/angular-tree-control/LICENSE": "licenses/angle/2e61cef458cfa3b764eadb2d0b6cbfe557ba70f2b39433d85fa41361450c50c4.txt", + "angle/third_party/cherry/third_party/bootstrap/LICENSE": "licenses/angle/9293c072b4854fa961b21291637532cf5ba97c6eeab48241aa60c873c711773c.txt", + "angle/third_party/cherry/third_party/go-sqlite3/LICENSE": "licenses/angle/afa48e5e64dc610298d80b010ae7a3450f61a79500a9f1d1697ff6dcbbfa1f72.txt", + "angle/third_party/cherry/third_party/jquery/LICENSE": "licenses/angle/f980a306a01e5881cc8004115f7e6dde44e7f5296477237d37d169a86ce7c094.txt", + "angle/third_party/cherry/third_party/sax/LICENSE": "licenses/angle/21425a6ffc6c2a9dc2a091fcab8f815afdcef6f0fdf2748c1043904bf38bdae1.txt", + "angle/third_party/cherry/third_party/sax/LICENSE-W3C.html": "licenses/angle/066b84cfd245e2ba8c6940aba7d63465c027550906301d8104be07cbb8398c46.txt", + "angle/third_party/cherry/third_party/spin/LICENSE": "licenses/angle/90981a279fd882ae1966d063e002115842fe607bfe3a119c9a12b056ceed3db2.txt", + "angle/third_party/cherry/third_party/ui-bootstrap/LICENSE": "licenses/angle/d3a1ecfb2804d0b4da300870c7c2914fd4edbd6b1b5fe4f7eb7b5d7db766b19b.txt", + "angle/third_party/cherry/third_party/ui-router/LICENSE": "licenses/angle/824db5eb5d83d8415d09f3b1ef0753ec7cfb2453b34eb767f4a200252fc299fb.txt", + "angle/third_party/cherry/third_party/underscore/LICENSE": "licenses/angle/c1b900aa1f61291ccd0160a351f40d217bc0080cd057aaf320257a589fd7d220.txt", + "angle/third_party/cherry/third_party/websocket/LICENSE": "licenses/angle/2be1b548b0387ca8948e1bb9434e709126904d15f622cc2d0d8e7f186e4d122d.txt", + "angle/third_party/colorama/LICENSE": "licenses/angle/15137d6c822e3ab097093a33c3a39a9df699f373f6438867ad534ff60762a947.txt", + "angle/third_party/cpython3/host/lib/python3.11/LICENSE.txt": "licenses/angle/3b2f81fe21d181c499c59a256c8e1968455d6689d269aa85373bfb6af41da3bf.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE": "licenses/angle/cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE.APACHE": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE.BSD": "licenses/angle/b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/cachecontrol/LICENSE.txt": "licenses/angle/86eeee87be2a43f3ff1f56496f451f69243926f025fedbb033666c304c4c161b.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/certifi/LICENSE": "licenses/angle/e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/distlib/LICENSE.txt": "licenses/angle/808e10c8a6ab8deb149ff9b3fb19f447a808094606d712a9ca57fead3552599d.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/distro/LICENSE": "licenses/angle/cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/idna/LICENSE.md": "licenses/angle/1a9a4f0e3d479a27240ddd59a9137a66ab4a0f9dfdc8ca6188cc0bfd85187f04.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/msgpack/COPYING": "licenses/angle/492dedba85da5872f78e6091bcd1fea474d660d35acb4dee964b8aab3f007427.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE": "licenses/angle/cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE.APACHE": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE.BSD": "licenses/angle/b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/pkg_resources/LICENSE": "licenses/angle/86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/platformdirs/LICENSE": "licenses/angle/29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/pygments/LICENSE": "licenses/angle/a9d66f1d526df02e29dce73436d34e56e8632f46c275bbdffc70569e882f9f17.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/LICENSE": "licenses/angle/1b22b049b5267d6dfc23a67bf4a84d8ec04b9fdfb1a51d360e42b4342c8b4154.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/requests/LICENSE": "licenses/angle/09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/resolvelib/LICENSE": "licenses/angle/f388fd38cad13112c1dc0f669bbe80e7f84541edbafb72f3030d2ca7642c3c9d.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/rich/LICENSE": "licenses/angle/deed7c17a4318158190a3ea239cc879a5a50271cebb98ae7025f48fbe58dca15.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/tomli/LICENSE": "licenses/angle/b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/tomli_w/LICENSE": "licenses/angle/b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/truststore/LICENSE": "licenses/angle/33be7b7e8fa4fd19b1760e1a8ed8a668bdab852c91b692dd41424bcb725a9fca.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/urllib3/LICENSE.txt": "licenses/angle/130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/LICENSE.txt": "licenses/angle/634300a669d49aeae65b12c6c48c924c51a4cdf3d1ff086dc3456dc8bcaa2104.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt": "licenses/angle/86eeee87be2a43f3ff1f56496f451f69243926f025fedbb033666c304c4c161b.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE": "licenses/angle/e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt": "licenses/angle/808e10c8a6ab8deb149ff9b3fb19f447a808094606d712a9ca57fead3552599d.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE": "licenses/angle/cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md": "licenses/angle/1a9a4f0e3d479a27240ddd59a9137a66ab4a0f9dfdc8ca6188cc0bfd85187f04.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING": "licenses/angle/492dedba85da5872f78e6091bcd1fea474d660d35acb4dee964b8aab3f007427.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE": "licenses/angle/cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD": "licenses/angle/b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE": "licenses/angle/86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE": "licenses/angle/29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE": "licenses/angle/a9d66f1d526df02e29dce73436d34e56e8632f46c275bbdffc70569e882f9f17.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE": "licenses/angle/1b22b049b5267d6dfc23a67bf4a84d8ec04b9fdfb1a51d360e42b4342c8b4154.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE": "licenses/angle/09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE": "licenses/angle/f388fd38cad13112c1dc0f669bbe80e7f84541edbafb72f3030d2ca7642c3c9d.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE": "licenses/angle/deed7c17a4318158190a3ea239cc879a5a50271cebb98ae7025f48fbe58dca15.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE": "licenses/angle/b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE": "licenses/angle/b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE": "licenses/angle/33be7b7e8fa4fd19b1760e1a8ed8a668bdab852c91b692dd41424bcb725a9fca.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt": "licenses/angle/130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE": "licenses/angle/ade78d04982d69972d444a8e14a94f87a2334dd3855cc80348ea8e240aa0df2d.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE": "licenses/angle/86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/licenses/LICENSE": "licenses/angle/458502e12d97bbf64438606a20044aa85eb05fb0a8a807bb35dbec253fd1fc04.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/LICENSE": "licenses/angle/86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/licenses/LICENSE": "licenses/angle/9755a18519666e5f0f4cae3daad3d7012bcae48a600b31237d75e9fe134e6683.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/licenses/LICENSE": "licenses/angle/5a57cb4db85e2a2dd88c290628908add57e3451449e0a9a71fdfb38776fd759d.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/licenses/LICENSE": "licenses/angle/09f1c8c9e941af3e584d59641ea9b87d83c0cb0fd007eb5ef391a7e2643c1a46.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE": "licenses/angle/cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.APACHE": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.BSD": "licenses/angle/b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/licenses/LICENSE": "licenses/angle/29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/licenses/LICENSE": "licenses/angle/b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/licenses/LICENSE.txt": "licenses/angle/30c23618679108f3e8ea1d2a658c7ca417bdfc891c98ef1a89fa4ff0c9828654.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/licenses/LICENSE": "licenses/angle/5a57cb4db85e2a2dd88c290628908add57e3451449e0a9a71fdfb38776fd759d.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/config/NOTICE": "licenses/angle/2dddf08818297a3b89d43d95ff659d8da85741108c9136dfa3a4d856c0623bd8.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/NOTICE": "licenses/angle/09c9bcea95ca086f8bc5bed174e40bc835b297d40fb5f86bbbb570fe0a5581a7.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/setuptools-83.0.0.dist-info/licenses/LICENSE": "licenses/angle/86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741.txt", + "angle/third_party/cpython3/host/lib/python3.11/site-packages/wheel-0.47.0.dist-info/licenses/LICENSE.txt": "licenses/angle/30c23618679108f3e8ea1d2a658c7ca417bdfc891c98ef1a89fa4ff0c9828654.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/LICENSE.txt": "licenses/angle/3b2f81fe21d181c499c59a256c8e1968455d6689d269aa85373bfb6af41da3bf.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE": "licenses/angle/cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE.APACHE": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE.BSD": "licenses/angle/b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/cachecontrol/LICENSE.txt": "licenses/angle/86eeee87be2a43f3ff1f56496f451f69243926f025fedbb033666c304c4c161b.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/certifi/LICENSE": "licenses/angle/e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/distlib/LICENSE.txt": "licenses/angle/808e10c8a6ab8deb149ff9b3fb19f447a808094606d712a9ca57fead3552599d.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/distro/LICENSE": "licenses/angle/cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/idna/LICENSE.md": "licenses/angle/1a9a4f0e3d479a27240ddd59a9137a66ab4a0f9dfdc8ca6188cc0bfd85187f04.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/msgpack/COPYING": "licenses/angle/492dedba85da5872f78e6091bcd1fea474d660d35acb4dee964b8aab3f007427.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE": "licenses/angle/cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE.APACHE": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE.BSD": "licenses/angle/b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/pkg_resources/LICENSE": "licenses/angle/86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/platformdirs/LICENSE": "licenses/angle/29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/pygments/LICENSE": "licenses/angle/a9d66f1d526df02e29dce73436d34e56e8632f46c275bbdffc70569e882f9f17.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/LICENSE": "licenses/angle/1b22b049b5267d6dfc23a67bf4a84d8ec04b9fdfb1a51d360e42b4342c8b4154.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/requests/LICENSE": "licenses/angle/09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/resolvelib/LICENSE": "licenses/angle/f388fd38cad13112c1dc0f669bbe80e7f84541edbafb72f3030d2ca7642c3c9d.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/rich/LICENSE": "licenses/angle/deed7c17a4318158190a3ea239cc879a5a50271cebb98ae7025f48fbe58dca15.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/tomli/LICENSE": "licenses/angle/b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/tomli_w/LICENSE": "licenses/angle/b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/truststore/LICENSE": "licenses/angle/33be7b7e8fa4fd19b1760e1a8ed8a668bdab852c91b692dd41424bcb725a9fca.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/urllib3/LICENSE.txt": "licenses/angle/130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/LICENSE.txt": "licenses/angle/634300a669d49aeae65b12c6c48c924c51a4cdf3d1ff086dc3456dc8bcaa2104.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt": "licenses/angle/86eeee87be2a43f3ff1f56496f451f69243926f025fedbb033666c304c4c161b.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE": "licenses/angle/e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt": "licenses/angle/808e10c8a6ab8deb149ff9b3fb19f447a808094606d712a9ca57fead3552599d.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE": "licenses/angle/cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md": "licenses/angle/1a9a4f0e3d479a27240ddd59a9137a66ab4a0f9dfdc8ca6188cc0bfd85187f04.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING": "licenses/angle/492dedba85da5872f78e6091bcd1fea474d660d35acb4dee964b8aab3f007427.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE": "licenses/angle/cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD": "licenses/angle/b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE": "licenses/angle/86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE": "licenses/angle/29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE": "licenses/angle/a9d66f1d526df02e29dce73436d34e56e8632f46c275bbdffc70569e882f9f17.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE": "licenses/angle/1b22b049b5267d6dfc23a67bf4a84d8ec04b9fdfb1a51d360e42b4342c8b4154.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE": "licenses/angle/09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE": "licenses/angle/f388fd38cad13112c1dc0f669bbe80e7f84541edbafb72f3030d2ca7642c3c9d.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE": "licenses/angle/deed7c17a4318158190a3ea239cc879a5a50271cebb98ae7025f48fbe58dca15.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE": "licenses/angle/b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE": "licenses/angle/b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE": "licenses/angle/33be7b7e8fa4fd19b1760e1a8ed8a668bdab852c91b692dd41424bcb725a9fca.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt": "licenses/angle/130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE": "licenses/angle/ade78d04982d69972d444a8e14a94f87a2334dd3855cc80348ea8e240aa0df2d.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE": "licenses/angle/86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/licenses/LICENSE": "licenses/angle/458502e12d97bbf64438606a20044aa85eb05fb0a8a807bb35dbec253fd1fc04.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/LICENSE": "licenses/angle/86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/licenses/LICENSE": "licenses/angle/9755a18519666e5f0f4cae3daad3d7012bcae48a600b31237d75e9fe134e6683.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/licenses/LICENSE": "licenses/angle/5a57cb4db85e2a2dd88c290628908add57e3451449e0a9a71fdfb38776fd759d.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/licenses/LICENSE": "licenses/angle/09f1c8c9e941af3e584d59641ea9b87d83c0cb0fd007eb5ef391a7e2643c1a46.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE": "licenses/angle/cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.APACHE": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.BSD": "licenses/angle/b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/licenses/LICENSE": "licenses/angle/29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/licenses/LICENSE": "licenses/angle/b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/licenses/LICENSE.txt": "licenses/angle/30c23618679108f3e8ea1d2a658c7ca417bdfc891c98ef1a89fa4ff0c9828654.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/licenses/LICENSE": "licenses/angle/5a57cb4db85e2a2dd88c290628908add57e3451449e0a9a71fdfb38776fd759d.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/config/NOTICE": "licenses/angle/2dddf08818297a3b89d43d95ff659d8da85741108c9136dfa3a4d856c0623bd8.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/NOTICE": "licenses/angle/09c9bcea95ca086f8bc5bed174e40bc835b297d40fb5f86bbbb570fe0a5581a7.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools-83.0.0.dist-info/licenses/LICENSE": "licenses/angle/86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741.txt", + "angle/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/wheel-0.47.0.dist-info/licenses/LICENSE.txt": "licenses/angle/30c23618679108f3e8ea1d2a658c7ca417bdfc891c98ef1a89fa4ff0c9828654.txt", + "angle/third_party/depot_tools/LICENSE": "licenses/angle/20b1e32e55821d109bcd17e1e150cfe242b54590d3e8083648d1276f88d33d16.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/gslib/vendored/boto/LICENSE": "licenses/angle/e3248f259a211f4d9ed06cfd07bc64373376c92a192152e37ec3420d6036dd4e.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/gslib/vendored/oauth2client/LICENSE": "licenses/angle/d6a43f0bae029b0cea5bd0fffd87f05659dc599a763886027614ad210be1ba3d.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/apitools/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/argcomplete/LICENSE.rst": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/argcomplete/NOTICE": "licenses/angle/2c889c721ec8ae6d7664680afaefbb4c7620976f434b57a506ecd92f0649b6a0.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/cachetools/LICENSE": "licenses/angle/2f4d2ff05f05c5da3879f40292b7600332d775dc7ed320d43dd42f3cd7d92c9b.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/certifi/LICENSE": "licenses/angle/e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/chardet/LICENSE": "licenses/angle/dc626520dcd53a22f727af3ee42c770e56c97a64fe3adb063799d8ab032fe551.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/charset_normalizer/LICENSE": "licenses/angle/eb31a0c5a4fb09b8a4e32055d25c1e5f9c358a2752fef3cd720213d1ccfee241.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/charset_normalizer/data/NOTICE.md": "licenses/angle/0cb3efcfd8f7a02a337e98dc3de4b0b57424d7208a332b26e7deb8cb94c13922.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/crcmod/LICENSE": "licenses/angle/89480768826f408daea1f3caff0509c2cc9606e10f6bb0ccfd12a3d604842c35.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/crcmod_osx/LICENSE": "licenses/angle/89480768826f408daea1f3caff0509c2cc9606e10f6bb0ccfd12a3d604842c35.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/fasteners/LICENSE": "licenses/angle/d2de2f566d2d0e0b509fb0ea1fa3669f49064ab1de21c57453cab3750a234e8f.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/funcsigs/LICENSE": "licenses/angle/559229b4b693d80fe087d517f7c79d4857c965add18031512d0981efc28755f0.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/gcs-oauth2-boto-plugin/LICENSE": "licenses/angle/8c6db340475136df3c1201d458fa5755698eace76e510471ecc9d857d6083dac.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/google-auth-library-python/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/google-auth-library-python-httplib2/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/google-reauth-python/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/httplib2/LICENSE": "licenses/angle/589eec38f72df2be203711d3b8cbece9b908c5e7ff00bc3cab7f63bae9e366b4.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/idna/LICENSE.md": "licenses/angle/a59f0b0ef3635874109a4461ca44ff7a70d50696e814767bfaf721d4c9b0db0f.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/mock/LICENSE.txt": "licenses/angle/5831ee149d3850b28df8ff02fb7bd07cecda81e85cc8435c20827d3922202d34.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/monotonic/LICENSE": "licenses/angle/cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/pyasn1/LICENSE.rst": "licenses/angle/2aad5fc00f705c4a1addb83eed10a6a75d286a3779f0cf8519d87e62bc4735fd.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/pyasn1/docs/source/license.rst": "licenses/angle/2fd7257410c4d7d9c8d8d85cb7f9f4ef9eee34126a96a993245c71577997c345.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/pyasn1-modules/LICENSE.txt": "licenses/angle/70bb0e4c89f4e41a11950365d98a13e2e6ad6ee4aed80cd1ecffc93d98d44e8c.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/pyparsing/LICENSE": "licenses/angle/10d5120a16805804ffda8b688c220bfb4e8f39741b57320604d455a309e01972.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/pyu2f/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/requests/LICENSE": "licenses/angle/09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/requests/NOTICE": "licenses/angle/f5110972dedad2b4e9d314518daf3b7d72d6e02e499acd802181de6f74571dcc.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/requests/docs/_themes/LICENSE": "licenses/angle/6afc9d58f919ab52f4806a895a37aefe4d263f8e52278e6a1e87c5d7ec82299c.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/requests/ext/LICENSE": "licenses/angle/3172d399cbd8f10609e73fec73d0e0b33eecd3c572a68b0722229d8c7059f725.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/retry-decorator/LICENSE.txt": "licenses/angle/c3710b8fc15eee9d2de041c0302116dc30fcb370ae5cc3969e746d8f08b869fd.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/rsa/LICENSE": "licenses/angle/073f28b7d389c8fe74f607e17c27f81eaa5ace69edc43a884f23f41b41c5c726.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/six/LICENSE": "licenses/angle/4375ba20e2b9c6c4e7cad2940a628fd90e95cc3d50ee92aae755715d8ba1fbd0.txt", + "angle/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/urllib3/LICENSE.txt": "licenses/angle/130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686.txt", + "angle/third_party/depot_tools/metadata/LICENSE_OWNERS": "licenses/angle/c203ade846c159e17bb36214eef81b55866645a3ece3cf4f10d9fcff110e444a.txt", + "angle/third_party/depot_tools/metadata/fields/custom/license.py": "licenses/angle/8dabd9a3478fabeb5ecf0ee2624ed7b41a8c346fdcb3d24a9fc4098a30ba4f54.txt", + "angle/third_party/depot_tools/metadata/fields/custom/license_allowlist.py": "licenses/angle/ef8e5604a137b1eb920336bad1a4948ab8dc71f2e1a4cb765178964d3598c434.txt", + "angle/third_party/depot_tools/metadata/fields/custom/license_file.py": "licenses/angle/4ab59682a096a2c31ab9ae42f2666dc6d5e72bac0071942a2e28d6d57d6732b6.txt", + "angle/third_party/depot_tools/metadata/tests/data/LICENSE": "licenses/angle/8bc55eba9da5911fd65d6b9dddfd56d94e30ff7fc2a9a30a5782bfc47cdf9c35.txt", + "angle/third_party/depot_tools/metadata/tests/data/src/LICENSE.txt": "licenses/angle/19ad13f8d801c13b5dde35625d2a57a0c3e1e4cb4d073dd2aff72be3925940e6.txt", + "angle/third_party/depot_tools/third_party/colorama/LICENSE.txt": "licenses/angle/cac35c02686e5d04a5a7140bfb3b36e73aed496656e891102e428886d7930318.txt", + "angle/third_party/depot_tools/third_party/repo/COPYING": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/depot_tools/third_party/schema/LICENSE-MIT": "licenses/angle/f4360ca8f779e6a673cd2882f73419bc2c5f74184fd9db91d2e86a368cc04e0b.txt", + "angle/third_party/flatbuffers/LICENSE": "licenses/angle/7ec9661a8afafab1eee3523d6f1a193eff76314a5ab10b4ce96aefd87621b0c3.txt", + "angle/third_party/glmark2/src/COPYING": "licenses/angle/8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903.txt", + "angle/third_party/glmark2/src/COPYING.SGI": "licenses/angle/16fbc228292bd774b263b212ae422c524cbf3b2078bcf21b22f8bdd4373be617.txt", + "angle/third_party/glmark2/src/src/libjpeg-turbo/LICENSE.md": "licenses/angle/fffd497be5f4ae0a10b8258e191125fb58b90250ecbf3c79398d79604dd00b7d.txt", + "angle/third_party/glmark2/src/src/libmatrix/COPYING": "licenses/angle/79d3f64f22269a86ce0e25a62f6a391f1e07e2735909bd8de710b3e4c51bf196.txt", + "angle/third_party/glmark2/src/src/libpng/LICENSE": "licenses/angle/cb7ac9e8ff6f939378b777feb2615598c16380b69f604845799e462f29ab6e90.txt", + "angle/third_party/glslang/LICENSE": "licenses/angle/23353f4505b1c8ce4f8f72fc3b11dc74b4a8a7bf95921d93ff77f227c171a710.txt", + "angle/third_party/glslang/src/LICENSE.txt": "licenses/angle/17e70c676e1521ff3e4686f04a2053d93a7e28a33be8de7ec37ab0ff72feb677.txt", + "angle/third_party/glslang/src/license-checker.cfg": "licenses/angle/0b7c936ff1270fb5089750e326f732ce2f08b18e804dc8847aa44561d0a7a277.txt", + "angle/third_party/googletest/src/LICENSE": "licenses/angle/9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138.txt", + "angle/third_party/jinja2/LICENSE.rst": "licenses/angle/3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b.txt", + "angle/third_party/jsoncpp/LICENSE": "licenses/angle/76c45ece83a26117f86f4e349e7df118708e061e87225328fb478ce1e8b3eb86.txt", + "angle/third_party/jsoncpp/source/LICENSE": "licenses/angle/cec0db5f6d7ed6b3a72647bd50aed02e13c3377fd44382b96dc2915534c042ad.txt", + "angle/third_party/jsoncpp/source/devtools/licenseupdater.py": "licenses/angle/81d0fc4498e695444090a0ba9a74398f8738cd1ae18c788734be93fec2dc3515.txt", + "angle/third_party/libc++/src/LICENSE.TXT": "licenses/angle/539dd7aed86e8a4f12cbdd0e6c50c189c7d74847e4fecc64ce2c6ee3a01da38b.txt", + "angle/third_party/libc++abi/src/LICENSE.TXT": "licenses/angle/e2b35be49f7284a45b7baca8fc7b3ab7440e7902392b2528a457816b5bb2a15c.txt", + "angle/third_party/libjpeg_turbo/LICENSE.md": "licenses/angle/96f5b328adbb78eeaaec6980d73fd558cb1e4d62560ed615646bc3cf5e532430.txt", + "angle/third_party/libjpeg_turbo/LICENSE.md.chromium": "licenses/angle/152a0f78d9c3a3afc09470cba1e66eabb915515cb121252d6c85c4ed6352a073.txt", + "angle/third_party/libpng/src/LICENSE": "licenses/angle/7317e078e2d3b5d7ba5a6159e650945153262b44b76f6700f8e9edb261c5143e.txt", + "angle/third_party/libpng/src/ci/LICENSE_MIT.txt": "licenses/angle/508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1.txt", + "angle/third_party/libpng/src/contrib/gregbook/COPYING": "licenses/angle/d6cb0e9e560f51085556949a84af12b79a00f10ab8b66c752537faf7cd665572.txt", + "angle/third_party/libpng/src/contrib/gregbook/LICENSE": "licenses/angle/b6a03c1803eb58ffb1f1278d5c7d4096c4c116e66dce8a7553e8c77d163c3438.txt", + "angle/third_party/libpng/src/contrib/pngexif/LICENSE_MIT.txt": "licenses/angle/508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1.txt", + "angle/third_party/libpng/src/contrib/pngminus/LICENSE.txt": "licenses/angle/eeb50cca0bf0537aeeef00874e1e22f0de50cb035f5db37e36036dc9b8218e8d.txt", + "angle/third_party/libunwind/src/LICENSE.TXT": "licenses/angle/b5efebcaca80879234098e52d1725e6d9eb8fb96a19fce625d39184b705f7b6d.txt", + "angle/third_party/llvm-libc/src/LICENSE.TXT": "licenses/angle/ebcd9bbf783a73d05c53ba4d586b8d5813dcdf3bbec50265860ccc885e606f47.txt", + "angle/third_party/lunarg-vulkantools/src/LICENSE.txt": "licenses/angle/400635d6ddaa1efc61cc38c6a737b8bbb975f4424f4727cd3983f53110eafe67.txt", + "angle/third_party/markupsafe/LICENSE": "licenses/angle/0bbe88228fd63d20ec097f64e58d5a0a465123ae139140a18d406c60b48824b5.txt", + "angle/third_party/nasm/LICENSE": "licenses/angle/7436a7c46b6e4d969b41e1ce387885ae4ced25710662189ff2983665253729ac.txt", + "angle/third_party/nasm/zlib/LICENSE": "licenses/angle/845efc77857d485d91fb3e0b884aaa929368c717ae8186b66fe1ed2495753243.txt", + "angle/third_party/ninja/COPYING": "licenses/angle/eb7e9ab9690124c5c9f42bdc81383d886a3dede26345b6ed15bbad7caf81f7ea.txt", + "angle/third_party/perfetto/LICENSE": "licenses/angle/9a682a56cffc9524dfa9b0b1c0dca9cb81a19e96d5bd0793aaf02c08a95ee7ca.txt", + "angle/third_party/perfetto/python/LICENSE": "licenses/angle/80f13607677e9932bf08e5f0bc025f8d77bde813d62bf3d5465c709025710d3d.txt", + "angle/third_party/perfetto/ui/src/plugins/dev.perfetto.TraceInfoPage/tabs/notices.ts": "licenses/angle/72e7fdaba087f43ae01cd304f4e654c78b9265906727efd75429b4c0d6bcf08c.txt", + "angle/third_party/proguard/LICENSE": "licenses/angle/294f58267c6f473c4ce7270bf5c8d34b2003cb43804552459654c36553431276.txt", + "angle/third_party/protobuf/LICENSE": "licenses/angle/6e5e117324afd944dcf67f36cf329843bc1a92229a8cd9bb573d7a83130fea7d.txt", + "angle/third_party/protobuf/src/google/protobuf/compiler/notices.h": "licenses/angle/b47ca1ea743623d50c6e02faa136dea4f6574a98e200666f859dbf57fb491721.txt", + "angle/third_party/protobuf/third_party/utf8_range/LICENSE": "licenses/angle/02de69b64fc36d9e938f418e52723e42f0b2b226d58a9cb3c8dcbdf7059f5074.txt", + "angle/third_party/r8/LICENSE": "licenses/angle/68834f116f8ff545f05d14753357b620748156d60ee36b26beab4cb3f317efe4.txt", + "angle/third_party/rapidjson/src/bin/jsonschema/LICENSE": "licenses/angle/837402bd25fad9b704265801ca3f92566a98157c1f9a7acd6f446299ba1c305a.txt", + "angle/third_party/rapidjson/src/contrib/natvis/LICENSE": "licenses/angle/394faaedb93c1da8ecbd61322518834908fee64381117e01a611bf9fac20baa6.txt", + "angle/third_party/rapidjson/src/license.txt": "licenses/angle/a140e5d46fe734a1c78f1a3c3ef207871dd75648be71fdda8e309b23ab8b1f32.txt", + "angle/third_party/re2/LICENSE": "licenses/angle/6040cda75d90b1738292a631d89934c411ef7ffd543c4d6a1b7edfc8edf29449.txt", + "angle/third_party/re2/src/LICENSE": "licenses/angle/6040cda75d90b1738292a631d89934c411ef7ffd543c4d6a1b7edfc8edf29449.txt", + "angle/third_party/re2/src/python/LICENSE": "licenses/angle/6040cda75d90b1738292a631d89934c411ef7ffd543c4d6a1b7edfc8edf29449.txt", + "angle/third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/LICENSE-MIT": "licenses/angle/e99d88d232bf57d70f0fb87f6b496d44b6653f99f8a63d250a54c61ea4bcde40.txt", + "angle/third_party/rust/chromium_crates_io/vendor/adler2-v2/LICENSE-0BSD": "licenses/angle/861399f8c21c042b110517e76dc6b63a2b334276c8cf17412fc3c8908ca8dc17.txt", + "angle/third_party/rust/chromium_crates_io/vendor/adler2-v2/LICENSE-APACHE": "licenses/angle/8ada45cd9f843acf64e4722ae262c622a2b3b3007c7310ef36ac1061a30f6adb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/adler2-v2/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ahash-v0_8/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ahash-v0_8/LICENSE-MIT": "licenses/angle/0444c6991eead6822f7b9102e654448d51624431119546492e8b231db42c48bb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/aho-corasick-v1/COPYING": "licenses/angle/01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/aho-corasick-v1/LICENSE-MIT": "licenses/angle/0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/android_system_properties-v0_1/LICENSE-APACHE": "licenses/angle/216486f29671a4262efe32af6d84a75bef398127f8c5f369b5c8305983887a06.txt", + "angle/third_party/rust/chromium_crates_io/vendor/android_system_properties-v0_1/LICENSE-MIT": "licenses/angle/80f275e90d799911ed3830a7f242a2ef5a4ade2092fe0aa07bfb2d2cf2f2b95e.txt", + "angle/third_party/rust/chromium_crates_io/vendor/anstyle-v1/LICENSE-APACHE": "licenses/angle/c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08.txt", + "angle/third_party/rust/chromium_crates_io/vendor/anstyle-v1/LICENSE-MIT": "licenses/angle/6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6.txt", + "angle/third_party/rust/chromium_crates_io/vendor/antlr4rust-v0_5/LICENSE.txt": "licenses/angle/3e1f197b6b221b918470078665608303aeebb27c1f54cd8241873b35f3affa62.txt", + "angle/third_party/rust/chromium_crates_io/vendor/anyhow-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/anyhow-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/arbitrary-int-v1/LICENSE.txt": "licenses/angle/6982f0cd109b04512cbb5f0e0f0ef82154f33a57d2127afe058ecc72039ab88c.txt", + "angle/third_party/rust/chromium_crates_io/vendor/arbitrary-int-v2/LICENSE.txt": "licenses/angle/6982f0cd109b04512cbb5f0e0f0ef82154f33a57d2127afe058ecc72039ab88c.txt", + "angle/third_party/rust/chromium_crates_io/vendor/array-init-v2/LICENSE-APACHE": "licenses/angle/c8d9a0d15dd76ca3bf277b6bf6da56799e266eac60bdc321a97ebc6d76d5153c.txt", + "angle/third_party/rust/chromium_crates_io/vendor/array-init-v2/LICENSE-MIT": "licenses/angle/e27fb2953c088c71285a4f2f54a0ac53323460ee7c2b1b838d563bd2687a38af.txt", + "angle/third_party/rust/chromium_crates_io/vendor/autocfg-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/autocfg-v1/LICENSE-MIT": "licenses/angle/27995d58ad5c1145c1a8cd86244ce844886958a35eb2b78c6b772748669999ac.txt", + "angle/third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/LICENSE-MIT": "licenses/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt", + "angle/third_party/rust/chromium_crates_io/vendor/base64-v0_22/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/base64-v0_22/LICENSE-MIT": "licenses/angle/0dd882e53de11566d50f8e8e2d5a651bcf3fabee4987d70f306233cf39094ba7.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bincode-v2/LICENSE.md": "licenses/angle/90d7e062634054e6866d3c81e6a2b3058a840e6af733e98e80bdfe1a7dec6912.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bincode_derive-v2/LICENSE.md": "licenses/angle/90d7e062634054e6866d3c81e6a2b3058a840e6af733e98e80bdfe1a7dec6912.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bit-set-v0_8/LICENSE-APACHE": "licenses/angle/8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bit-set-v0_8/LICENSE-MIT": "licenses/angle/f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bit-vec-v0_8/LICENSE-APACHE": "licenses/angle/8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bit-vec-v0_8/LICENSE-MIT": "licenses/angle/f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bitbybit-v1/LICENSE": "licenses/angle/0a73de6c78c0743aef49c275563c9486fd3e55d61611044cecc5620f4dfe772d.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bitflags-v2/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bitflags-v2/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bytemuck-v1/LICENSE-APACHE": "licenses/angle/e3ba223bb1423f0aad8c3dfce0fe3148db48926d41e6fbc3afbbf5ff9e1c89cb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bytemuck-v1/LICENSE-MIT": "licenses/angle/9df9ba60a11af705f2e451b53762686e615d86f76b169cf075c3237730dbd7e2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bytemuck-v1/LICENSE-ZLIB": "licenses/angle/84b34dd7608f7fb9b17bd588a6bf392bf7de504e2716f024a77d89f1b145a151.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bytemuck_derive-v1/LICENSE-APACHE": "licenses/angle/e3ba223bb1423f0aad8c3dfce0fe3148db48926d41e6fbc3afbbf5ff9e1c89cb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bytemuck_derive-v1/LICENSE-MIT": "licenses/angle/9df9ba60a11af705f2e451b53762686e615d86f76b169cf075c3237730dbd7e2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bytemuck_derive-v1/LICENSE-ZLIB": "licenses/angle/84b34dd7608f7fb9b17bd588a6bf392bf7de504e2716f024a77d89f1b145a151.txt", + "angle/third_party/rust/chromium_crates_io/vendor/byteorder-lite-v0_1/LICENSE-MIT": "licenses/angle/0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/byteorder-v1/COPYING": "licenses/angle/01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/byteorder-v1/LICENSE-MIT": "licenses/angle/0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/bytes-v1/LICENSE": "licenses/angle/45f522cacecb1023856e46df79ca625dfc550c94910078bd8aec6e02880b3d42.txt", + "angle/third_party/rust/chromium_crates_io/vendor/calendrical_calculations-v0_2/LICENSE": "licenses/angle/192ea857d1bff2b87c174de36cbae5c173234726c6b8eceab9790a535d7dbc95.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cfg-if-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cfg-if-v1/LICENSE-MIT": "licenses/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cfg_aliases-v0_2/LICENSE": "licenses/angle/31b94860253d8ec7b4529f51901044d3b459d6292d996504a36b1bae3a36a812.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cfg_aliases-v0_2/NOTICES.md": "licenses/angle/1e2b7ade3fb228130408b9990cae6a7618eb314c75aa0b164bfe485d9d9756ee.txt", + "angle/third_party/rust/chromium_crates_io/vendor/chrono-v0_4/LICENSE.txt": "licenses/angle/946c9835d8034d24404f8cfec5f4654cee5dad17e944afc3d06d742cf2882831.txt", + "angle/third_party/rust/chromium_crates_io/vendor/clap-v4/LICENSE-APACHE": "licenses/angle/c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08.txt", + "angle/third_party/rust/chromium_crates_io/vendor/clap-v4/LICENSE-MIT": "licenses/angle/6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6.txt", + "angle/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/LICENSE-APACHE": "licenses/angle/c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08.txt", + "angle/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/LICENSE-MIT": "licenses/angle/6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6.txt", + "angle/third_party/rust/chromium_crates_io/vendor/clap_lex-v1/LICENSE-APACHE": "licenses/angle/c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08.txt", + "angle/third_party/rust/chromium_crates_io/vendor/clap_lex-v1/LICENSE-MIT": "licenses/angle/6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cobs-v0_3/LICENSE-APACHE": "licenses/angle/c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cobs-v0_3/LICENSE-MIT": "licenses/angle/e0cfa1006a64520633de6bfbf563f5b1bea04ef0c5b73f049681931fa297dda3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/codespan-reporting-v0_13/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/rust/chromium_crates_io/vendor/const_format-v0_2/LICENSE-ZLIB.md": "licenses/angle/c1e018d60dd011b335b5280b919bd3a75dbba81c6fbe24e2fc90cb235bdb6883.txt", + "angle/third_party/rust/chromium_crates_io/vendor/const_format_proc_macros-v0_2/LICENSE-ZLIB.md": "licenses/angle/c1e018d60dd011b335b5280b919bd3a75dbba81c6fbe24e2fc90cb235bdb6883.txt", + "angle/third_party/rust/chromium_crates_io/vendor/const_panic-v0_2/LICENSE-ZLIB.md": "licenses/angle/573e362dc50a6d9eb444cea38ef61587e16a0645cb8098ba13a2c42fdde72acd.txt", + "angle/third_party/rust/chromium_crates_io/vendor/core-foundation-sys-v0_8/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/core-foundation-sys-v0_8/LICENSE-MIT": "licenses/angle/62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/core_maths-v0_1/LICENSE": "licenses/angle/9ebf8c4cc0b735ca13a766451f7b8097db3185975ceb2ba94b5abf439156a91f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/crc32fast-v1/LICENSE-APACHE": "licenses/angle/c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08.txt", + "angle/third_party/rust/chromium_crates_io/vendor/crc32fast-v1/LICENSE-MIT": "licenses/angle/61d383b05b87d78f94d2937e2580cce47226d17823c0430fbcad09596537efcf.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ctor-proc-macro-v0_0_7/LICENSE-APACHE": "licenses/angle/a8ad31b1c3f40dca5a84119351b8fa8ddc868edd77fad8a8ebf6d8f2d16fa4ae.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ctor-proc-macro-v0_0_7/LICENSE-MIT": "licenses/angle/bccaa8b6c09f94e81f06696e179dbe058464bbdfbc823b6d49cada1d71e84ac3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ctor-v0_6/LICENSE-APACHE": "licenses/angle/a8ad31b1c3f40dca5a84119351b8fa8ddc868edd77fad8a8ebf6d8f2d16fa4ae.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ctor-v0_6/LICENSE-MIT": "licenses/angle/bccaa8b6c09f94e81f06696e179dbe058464bbdfbc823b6d49cada1d71e84ac3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cxx-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cxx-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cxxbridge-cmd-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cxxbridge-cmd-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cxxbridge-flags-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cxxbridge-flags-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cxxbridge-macro-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/cxxbridge-macro-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/derivre-v0_3/LICENSE": "licenses/angle/c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383.txt", + "angle/third_party/rust/chromium_crates_io/vendor/diplomat-runtime-v0_15/LICENSE-APACHE": "licenses/angle/639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666.txt", + "angle/third_party/rust/chromium_crates_io/vendor/diplomat-runtime-v0_15/LICENSE-MIT": "licenses/angle/3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4.txt", + "angle/third_party/rust/chromium_crates_io/vendor/diplomat-v0_15/LICENSE-APACHE": "licenses/angle/639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666.txt", + "angle/third_party/rust/chromium_crates_io/vendor/diplomat-v0_15/LICENSE-MIT": "licenses/angle/3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4.txt", + "angle/third_party/rust/chromium_crates_io/vendor/diplomat-v0_16/LICENSE-APACHE": "licenses/angle/639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666.txt", + "angle/third_party/rust/chromium_crates_io/vendor/diplomat-v0_16/LICENSE-MIT": "licenses/angle/3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4.txt", + "angle/third_party/rust/chromium_crates_io/vendor/diplomat_core-v0_15/LICENSE-APACHE": "licenses/angle/639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666.txt", + "angle/third_party/rust/chromium_crates_io/vendor/diplomat_core-v0_15/LICENSE-MIT": "licenses/angle/3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4.txt", + "angle/third_party/rust/chromium_crates_io/vendor/diplomat_core-v0_16/LICENSE-APACHE": "licenses/angle/639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666.txt", + "angle/third_party/rust/chromium_crates_io/vendor/diplomat_core-v0_16/LICENSE-MIT": "licenses/angle/3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4.txt", + "angle/third_party/rust/chromium_crates_io/vendor/displaydoc-v0_2/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/displaydoc-v0_2/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/dtor-proc-macro-v0_0_6/LICENSE-APACHE": "licenses/angle/a8ad31b1c3f40dca5a84119351b8fa8ddc868edd77fad8a8ebf6d8f2d16fa4ae.txt", + "angle/third_party/rust/chromium_crates_io/vendor/dtor-proc-macro-v0_0_6/LICENSE-MIT": "licenses/angle/bccaa8b6c09f94e81f06696e179dbe058464bbdfbc823b6d49cada1d71e84ac3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/dtor-v0_1/LICENSE-APACHE": "licenses/angle/a8ad31b1c3f40dca5a84119351b8fa8ddc868edd77fad8a8ebf6d8f2d16fa4ae.txt", + "angle/third_party/rust/chromium_crates_io/vendor/dtor-v0_1/LICENSE-MIT": "licenses/angle/bccaa8b6c09f94e81f06696e179dbe058464bbdfbc823b6d49cada1d71e84ac3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/either-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/either-v1/LICENSE-MIT": "licenses/angle/7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545.txt", + "angle/third_party/rust/chromium_crates_io/vendor/equivalent-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/equivalent-v1/LICENSE-MIT": "licenses/angle/7365cc8878a1d7ce155a58c4ca09c3d7a6be413efa5334a80ea842912b669349.txt", + "angle/third_party/rust/chromium_crates_io/vendor/erased-serde-v0_4/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/erased-serde-v0_4/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/extended-v0_1/LICENSE.txt": "licenses/angle/25a0874d15e7c834a47c3adc80901edb2219759254992023ef1010c3065413d5.txt", + "angle/third_party/rust/chromium_crates_io/vendor/fastbloom-v0_14/LICENSE-APACHE": "licenses/angle/7cde763ba32b3ec2a84eddd8beb0dcb895fd6436aeb18491ab9572a7eb8de996.txt", + "angle/third_party/rust/chromium_crates_io/vendor/fastbloom-v0_14/LICENSE-MIT": "licenses/angle/7c86aec715e38bf01c316a69de917c1245bf9945a5dc0329eecc774cdb4f26c2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/fdeflate-v0_3/LICENSE-APACHE": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/rust/chromium_crates_io/vendor/fdeflate-v0_3/LICENSE-MIT": "licenses/angle/c77a4cf9da729987d0fe7ccd811e3bd27393914ddf3d23467c18cc22954513b3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/fend-core-v1/LICENSE.md": "licenses/angle/d39a21ed70fb553856f6d7e74fee4332261069502ae32ab9ac13b49d147696f7.txt", + "angle/third_party/rust/chromium_crates_io/vendor/fixed_decimal-v0_7/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/flate2-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/flate2-v1/LICENSE-MIT": "licenses/angle/025436edff4cfcdde17a5811fdea78892d8482efd1abdec5a17872d07a4f2112.txt", + "angle/third_party/rust/chromium_crates_io/vendor/foldhash-v0_2/LICENSE": "licenses/angle/b1181a40b2a7b25cf66fd01481713bc1005df082c53ef73e851e55071b102744.txt", + "angle/third_party/rust/chromium_crates_io/vendor/font-types-v0_12/LICENSE-APACHE": "licenses/angle/eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/font-types-v0_12/LICENSE-MIT": "licenses/angle/7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427.txt", + "angle/third_party/rust/chromium_crates_io/vendor/fs2-v0_4/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/fs2-v0_4/LICENSE-MIT": "licenses/angle/7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/getrandom-v0_3/LICENSE-APACHE": "licenses/angle/aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf.txt", + "angle/third_party/rust/chromium_crates_io/vendor/getrandom-v0_3/LICENSE-MIT": "licenses/angle/29e9fe5074bd27e0e5d5d110394fbbcd841baee2651a3c4b4560a632702cede4.txt", + "angle/third_party/rust/chromium_crates_io/vendor/getrandom-v0_4/LICENSE-APACHE": "licenses/angle/aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf.txt", + "angle/third_party/rust/chromium_crates_io/vendor/getrandom-v0_4/LICENSE-MIT": "licenses/angle/523a42c25d245dde9c015f882cec7f4555aad883382a6cf19b4b7d9b2cd5419b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/gimli-v0_32/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/gimli-v0_32/LICENSE-MIT": "licenses/angle/7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/harfrust-v0_13/LICENSE": "licenses/angle/3a7c3f0b887abb7c638faf022d29257418458614c6724b120ceeffb279f9c7d2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/hashbrown-v0_16/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/hashbrown-v0_16/LICENSE-MIT": "licenses/angle/ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/hashbrown-v0_17/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/hashbrown-v0_17/LICENSE-MIT": "licenses/angle/ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/heck-v0_5/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/heck-v0_5/LICENSE-MIT": "licenses/angle/7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/hex-v0_4/LICENSE-APACHE": "licenses/angle/c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08.txt", + "angle/third_party/rust/chromium_crates_io/vendor/hex-v0_4/LICENSE-MIT": "licenses/angle/f7bdb3426d045cd50efd4953026e3eb5a83d0199f458a075602611b9344da5b9.txt", + "angle/third_party/rust/chromium_crates_io/vendor/hmac-sha256-v1/LICENSE": "licenses/angle/6f4e2de03c87fde1f0d4481b5a6358f9d2ba1f4bf8ed331d8f2d2fc4579b4747.txt", + "angle/third_party/rust/chromium_crates_io/vendor/hostname-v0_4/LICENSE": "licenses/angle/2e4213e573312c8c75e6e7e2c55a45283427e759432b56014afaf3c7d950a568.txt", + "angle/third_party/rust/chromium_crates_io/vendor/iana-time-zone-v0_1/LICENSE-APACHE": "licenses/angle/696759d65dfe558ff7d9f031c76db19ec5c0767470fb67c4e8d990820d1e99c9.txt", + "angle/third_party/rust/chromium_crates_io/vendor/iana-time-zone-v0_1/LICENSE-MIT": "licenses/angle/da28ccc6b158fc2d8cccc74e99794b1cff1d29bd7bbeb019442fcf0c04c6cad9.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_calendar-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_calendar_data-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_capi-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_casemap-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_casemap_data-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_collections-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_decimal-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_decimal_data-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_experimental-v0_5/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_experimental_data-v0_5/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_list-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_list_data-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_locale-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_locale_core-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_locale_data-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_normalizer-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_normalizer_data-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_pattern-v0_4/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_plurals-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_plurals_data-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_properties-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_properties_data-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_provider-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/icu_provider_adapters-v2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/image-v0_25/LICENSE-APACHE": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/rust/chromium_crates_io/vendor/image-v0_25/LICENSE-MIT": "licenses/angle/c77a4cf9da729987d0fe7ccd811e3bd27393914ddf3d23467c18cc22954513b3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/incremental-font-transfer-v0_7/LICENSE-APACHE": "licenses/angle/eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/incremental-font-transfer-v0_7/LICENSE-MIT": "licenses/angle/7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427.txt", + "angle/third_party/rust/chromium_crates_io/vendor/indexmap-v2/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/indexmap-v2/LICENSE-MIT": "licenses/angle/ecc269ef87fd38a1d98e30bfac9ba964a9dbd9315c3770fed98d4d7cb5882055.txt", + "angle/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-MIT": "licenses/angle/7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545.txt", + "angle/third_party/rust/chromium_crates_io/vendor/itoa-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/itoa-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ixdtf-v0_6/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/jpeg-encoder-v0_7/LICENSE-APACHE": "licenses/angle/85a884980abd6032fc6b5439ba918ab16db9ac9051b5cf18db9c27c587df07c0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/jpeg-encoder-v0_7/LICENSE-IJG": "licenses/angle/a21e92ec88aefc6823f0c51994783571a849e8d9b43d9ade61ec9d886776721e.txt", + "angle/third_party/rust/chromium_crates_io/vendor/jpeg-encoder-v0_7/LICENSE-MIT": "licenses/angle/4d022727ea392ebba2fe5e9a5f78c610a0eae506b491f3db29c78b6d430d6503.txt", + "angle/third_party/rust/chromium_crates_io/vendor/jxl-v0_6/LICENSE": "licenses/angle/8405932022a556380c2d8c272eff154a923feb197233f348ce5f7334fb0a5ede.txt", + "angle/third_party/rust/chromium_crates_io/vendor/jxl_macros-v0_6/LICENSE": "licenses/angle/8405932022a556380c2d8c272eff154a923feb197233f348ce5f7334fb0a5ede.txt", + "angle/third_party/rust/chromium_crates_io/vendor/jxl_simd-v0_6/LICENSE": "licenses/angle/8405932022a556380c2d8c272eff154a923feb197233f348ce5f7334fb0a5ede.txt", + "angle/third_party/rust/chromium_crates_io/vendor/jxl_transforms-v0_6/LICENSE": "licenses/angle/8405932022a556380c2d8c272eff154a923feb197233f348ce5f7334fb0a5ede.txt", + "angle/third_party/rust/chromium_crates_io/vendor/konst-v0_2/LICENSE-ZLIB.md": "licenses/angle/573e362dc50a6d9eb444cea38ef61587e16a0645cb8098ba13a2c42fdde72acd.txt", + "angle/third_party/rust/chromium_crates_io/vendor/konst_macro_rules-v0_2/LICENSE-ZLIB.md": "licenses/angle/573e362dc50a6d9eb444cea38ef61587e16a0645cb8098ba13a2c42fdde72acd.txt", + "angle/third_party/rust/chromium_crates_io/vendor/lazy_static-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/lazy_static-v1/LICENSE-MIT": "licenses/angle/0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720.txt", + "angle/third_party/rust/chromium_crates_io/vendor/libafl-v0_15/LICENSE-APACHE": "licenses/angle/43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1.txt", + "angle/third_party/rust/chromium_crates_io/vendor/libafl-v0_15/LICENSE-MIT": "licenses/angle/30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652.txt", + "angle/third_party/rust/chromium_crates_io/vendor/libafl_bolts-v0_15/LICENSE-APACHE": "licenses/angle/43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1.txt", + "angle/third_party/rust/chromium_crates_io/vendor/libafl_bolts-v0_15/LICENSE-MIT": "licenses/angle/30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652.txt", + "angle/third_party/rust/chromium_crates_io/vendor/libafl_derive-v0_15/LICENSE-APACHE": "licenses/angle/43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1.txt", + "angle/third_party/rust/chromium_crates_io/vendor/libafl_derive-v0_15/LICENSE-MIT": "licenses/angle/30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652.txt", + "angle/third_party/rust/chromium_crates_io/vendor/libc-v0_2/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/libc-v0_2/LICENSE-MIT": "licenses/angle/123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e.txt", + "angle/third_party/rust/chromium_crates_io/vendor/libm-v0_2/LICENSE.txt": "licenses/angle/3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7.txt", + "angle/third_party/rust/chromium_crates_io/vendor/litemap-v0_8/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/llguidance-v1/LICENSE": "licenses/angle/c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383.txt", + "angle/third_party/rust/chromium_crates_io/vendor/lock_api-v0_4/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/lock_api-v0_4/LICENSE-MIT": "licenses/angle/c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5.txt", + "angle/third_party/rust/chromium_crates_io/vendor/log-v0_4/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/log-v0_4/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/mach2-v0_5/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/mach2-v0_5/LICENSE-BSD": "licenses/angle/044983df14c97f2f9570766aaf977b3cdfc4a06cf1f36b776331c5ff89b4fb89.txt", + "angle/third_party/rust/chromium_crates_io/vendor/mach2-v0_5/LICENSE-MIT": "licenses/angle/3f9f0f7e5a5911a8042e32c83ff5d061ce1ffd02e8a207ec2135a44ad73b4191.txt", + "angle/third_party/rust/chromium_crates_io/vendor/memchr-v2/COPYING": "licenses/angle/01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/memchr-v2/LICENSE-MIT": "licenses/angle/0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/meminterval-v0_4/LICENSE-APACHE": "licenses/angle/43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1.txt", + "angle/third_party/rust/chromium_crates_io/vendor/meminterval-v0_4/LICENSE-MIT": "licenses/angle/30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652.txt", + "angle/third_party/rust/chromium_crates_io/vendor/memo-map-v0_3/LICENSE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/memoffset-v0_9/LICENSE": "licenses/angle/3234ac55816264ee7b6c7ee27efd61cf0a1fe775806870e3d9b4c41ea73c5cb1.txt", + "angle/third_party/rust/chromium_crates_io/vendor/minijinja-v2/LICENSE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/minijinja-v2/src/vendor/self_cell/LICENSE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_8/LICENSE": "licenses/angle/4108245a1f2df9d4e94df8abed5b4ba0759bb2f9b40a6b939f1be141077ae50b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_8/LICENSE-APACHE.md": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_8/LICENSE-MIT.md": "licenses/angle/799e9ca9d179295ef372f25d3769cdda7d25bb2668add6a6a1e22d1e4c678b8d.txt", + "angle/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_8/LICENSE-ZLIB.md": "licenses/angle/0a54e647fe54104658b5e563c04c6f9edf251710e47bce692e0bd990a4ddaa39.txt", + "angle/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_9/LICENSE": "licenses/angle/4108245a1f2df9d4e94df8abed5b4ba0759bb2f9b40a6b939f1be141077ae50b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_9/LICENSE-APACHE.md": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_9/LICENSE-MIT.md": "licenses/angle/799e9ca9d179295ef372f25d3769cdda7d25bb2668add6a6a1e22d1e4c678b8d.txt", + "angle/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_9/LICENSE-ZLIB.md": "licenses/angle/0a54e647fe54104658b5e563c04c6f9edf251710e47bce692e0bd990a4ddaa39.txt", + "angle/third_party/rust/chromium_crates_io/vendor/moxcms-v0_8/LICENSE-APACHE.md": "licenses/angle/90bf2d659c43045111b65c733ab2a6d4cbcb422a098368c8c58a9ba3db4ed0c5.txt", + "angle/third_party/rust/chromium_crates_io/vendor/moxcms-v0_8/LICENSE.md": "licenses/angle/2aa92cada6431e75615e3fe6cb1a9082c98f777d48ae1c087c0da0e37f7b8bff.txt", + "angle/third_party/rust/chromium_crates_io/vendor/murmur3-v0_4/LICENSE-APACHE": "licenses/angle/c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08.txt", + "angle/third_party/rust/chromium_crates_io/vendor/murmur3-v0_4/LICENSE-MIT": "licenses/angle/d24a2b82b5d96fd64c84cdae1b1f6250d76c16f0660a593d4ac8127177054b5f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/nix-v0_30/LICENSE": "licenses/angle/66e3ee1fa7f909ad3c612d556f2a0cdabcd809ad6e66f3b0605015ac64841b70.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-bigint-v0_4/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-bigint-v0_4/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-complex-v0_4/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-complex-v0_4/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-derive-v0_4/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-derive-v0_4/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-integer-v0_1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-integer-v0_1/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-rational-v0_4/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-rational-v0_4/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-traits-v0_2/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num-traits-v0_2/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num_enum-v0_7/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num_enum-v0_7/LICENSE-BSD": "licenses/angle/0be96d891d00e0ae0df75d7f3289b12871c000a1f5ac744f3b570768d4bb277c.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num_enum-v0_7/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num_enum_derive-v0_7/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num_enum_derive-v0_7/LICENSE-BSD": "licenses/angle/0be96d891d00e0ae0df75d7f3289b12871c000a1f5ac744f3b570768d4bb277c.txt", + "angle/third_party/rust/chromium_crates_io/vendor/num_enum_derive-v0_7/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/object-v0_37/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/object-v0_37/LICENSE-MIT": "licenses/angle/0b74dfa0bcee5c420c6b7f67b4b2658f9ab8388c97b8e733975f2cecbdd668a6.txt", + "angle/third_party/rust/chromium_crates_io/vendor/once_cell-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/once_cell-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ordered-float-v5/LICENSE-MIT": "licenses/angle/f7715d38a3fa1b4ac97c5729740752505a39cb92ee83ab5b102aeb5eaa7cdea4.txt", + "angle/third_party/rust/chromium_crates_io/vendor/parking_lot-v0_12/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/parking_lot-v0_12/LICENSE-MIT": "licenses/angle/c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5.txt", + "angle/third_party/rust/chromium_crates_io/vendor/parking_lot_core-v0_9/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/parking_lot_core-v0_9/LICENSE-MIT": "licenses/angle/c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5.txt", + "angle/third_party/rust/chromium_crates_io/vendor/png-v0_18/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/png-v0_18/LICENSE-MIT": "licenses/angle/eaf40297c75da471f7cda1f3458e8d91b4b2ec866e609527a13acfa93b638652.txt", + "angle/third_party/rust/chromium_crates_io/vendor/postcard-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/postcard-v1/LICENSE-MIT": "licenses/angle/177540cad091a40e8071db310bc3b6115c4e329a92a234609b60c154b008a888.txt", + "angle/third_party/rust/chromium_crates_io/vendor/potential_utf-v0_1/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/primal-check-v0_3/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/primal-check-v0_3/LICENSE-MIT": "licenses/angle/6d3a9431e65e69c73a8923e6517b889d17549b23db406b9ec027710d16af701f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/prost-derive-v0_14/LICENSE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/prost-v0_14/LICENSE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/pxfm-v0_1/LICENSE-APACHE.md": "licenses/angle/90bf2d659c43045111b65c733ab2a6d4cbcb422a098368c8c58a9ba3db4ed0c5.txt", + "angle/third_party/rust/chromium_crates_io/vendor/pxfm-v0_1/LICENSE.md": "licenses/angle/2aa92cada6431e75615e3fe6cb1a9082c98f777d48ae1c087c0da0e37f7b8bff.txt", + "angle/third_party/rust/chromium_crates_io/vendor/qr_code-v2/LICENSE-APACHE.txt": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/qr_code-v2/LICENSE-MIT.txt": "licenses/angle/7f865e72ab3644ea5887aa1f352aa435b36d139c35a964f47091e7dc02722e9a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/quote-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/quote-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/rand_core-v0_9/COPYRIGHT": "licenses/angle/90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5.txt", + "angle/third_party/rust/chromium_crates_io/vendor/rand_core-v0_9/LICENSE-APACHE": "licenses/angle/6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51.txt", + "angle/third_party/rust/chromium_crates_io/vendor/rand_core-v0_9/LICENSE-MIT": "licenses/angle/209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/read-fonts-v0_43/LICENSE-APACHE": "licenses/angle/eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/read-fonts-v0_43/LICENSE-MIT": "licenses/angle/7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ref-cast-impl-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ref-cast-impl-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ref-cast-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ref-cast-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/regex-automata-v0_4/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/regex-automata-v0_4/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/regex-lite-v0_1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/regex-lite-v0_1/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/regex-syntax-v0_8/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/regex-syntax-v0_8/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/regex-syntax-v0_8/src/unicode_tables/LICENSE-UNICODE": "licenses/angle/74db5baf44a41b1000312c673544b3374e4198af5605c7f9080a402cec42cfa3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/regex-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/regex-v1/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/resb-v0_1/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/rustc-demangle-capi-v0_1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/rustc-demangle-capi-v0_1/LICENSE-MIT": "licenses/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt", + "angle/third_party/rust/chromium_crates_io/vendor/rustc-demangle-v0_1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/rustc-demangle-v0_1/LICENSE-MIT": "licenses/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt", + "angle/third_party/rust/chromium_crates_io/vendor/rustfft-v6/LICENSE-APACHE": "licenses/angle/2e54cd84a645bea25943c75dd8ae67cb291e66a47a11578333c9b4b3b6b86c85.txt", + "angle/third_party/rust/chromium_crates_io/vendor/rustfft-v6/LICENSE-MIT": "licenses/angle/8f5442dfa8e9169045697e386bc91d19f393c939635741fa2a665ec36ca6f0ad.txt", + "angle/third_party/rust/chromium_crates_io/vendor/rustversion-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/rustversion-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ryu-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/ryu-v1/LICENSE-BOOST": "licenses/angle/c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566.txt", + "angle/third_party/rust/chromium_crates_io/vendor/safe_arch-v0_7/LICENSE-APACHE.md": "licenses/angle/e3ba223bb1423f0aad8c3dfce0fe3148db48926d41e6fbc3afbbf5ff9e1c89cb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/safe_arch-v0_7/LICENSE-MIT.md": "licenses/angle/e57011537d230b14e790f6666dc00816f7b371ebbd7da8a12491e51086fec278.txt", + "angle/third_party/rust/chromium_crates_io/vendor/safe_arch-v0_7/LICENSE-ZLIB.md": "licenses/angle/c43b9a9b1387ed53d2c49263838261129a010e280e3a174a792242c3e2c98db9.txt", + "angle/third_party/rust/chromium_crates_io/vendor/scopeguard-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/scopeguard-v1/LICENSE-MIT": "licenses/angle/fb77f0a9c53e473abe5103c8632ef9f0f2874d4fb3f17cb2d8c661aab9cee9d7.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serde-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serde-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serde_core-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serde_core-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serde_derive-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serde_derive-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serde_json-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serde_json-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serde_json_lenient-v0_2/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serde_json_lenient-v0_2/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serial_test-v3/LICENSE": "licenses/angle/ac7e05bd11cc1cfc3f9452c1b9986a9b1d54e180fa88e44e69caf955f95dc8a6.txt", + "angle/third_party/rust/chromium_crates_io/vendor/serial_test_derive-v3/LICENSE": "licenses/angle/ac7e05bd11cc1cfc3f9452c1b9986a9b1d54e180fa88e44e69caf955f95dc8a6.txt", + "angle/third_party/rust/chromium_crates_io/vendor/sfv-v0_15/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/sfv-v0_15/LICENSE-MIT": "licenses/angle/5318787a14e32720b1652f08a24408aaea67fdb5154ceda0f46a6069a0c5e5e3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/shared-brotli-patch-decoder-v0_1/LICENSE-APACHE": "licenses/angle/eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/shared-brotli-patch-decoder-v0_1/LICENSE-MIT": "licenses/angle/7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427.txt", + "angle/third_party/rust/chromium_crates_io/vendor/simd-adler32-v0_3/LICENSE.md": "licenses/angle/42a35170233e83e18856792e748de4c1ce4a63b2afce9a370c89ef3fe23f9f2d.txt", + "angle/third_party/rust/chromium_crates_io/vendor/siphasher-v1/COPYING": "licenses/angle/c962ee4d1d05ddc138b202b2540219ebc57893fcf97b364852094a9a94ce1365.txt", + "angle/third_party/rust/chromium_crates_io/vendor/siphasher-v1/LICENSE-APACHE": "licenses/angle/58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd.txt", + "angle/third_party/rust/chromium_crates_io/vendor/skera-v0_6/LICENSE-APACHE": "licenses/angle/eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/skera-v0_6/LICENSE-MIT": "licenses/angle/7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427.txt", + "angle/third_party/rust/chromium_crates_io/vendor/skrifa-v0_46/LICENSE-APACHE": "licenses/angle/eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/skrifa-v0_46/LICENSE-MIT": "licenses/angle/7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427.txt", + "angle/third_party/rust/chromium_crates_io/vendor/small_ctor-v0_1/LICENSE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/smallvec-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/smallvec-v1/LICENSE-MIT": "licenses/angle/0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9.txt", + "angle/third_party/rust/chromium_crates_io/vendor/stable_deref_trait-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/stable_deref_trait-v1/LICENSE-MIT": "licenses/angle/5e05b024f653a5ce199e77cbbbd42fb5553562ec714b819421ed0c3e552a75d7.txt", + "angle/third_party/rust/chromium_crates_io/vendor/static_assertions-v1/LICENSE-APACHE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/static_assertions-v1/LICENSE-MIT": "licenses/angle/ea084a2373ebc1f0902c09266e7bf25a05ab3814c1805bb017ffa7308f90c061.txt", + "angle/third_party/rust/chromium_crates_io/vendor/strck-v1/LICENSE": "licenses/angle/7e24648e3d082d4f8026cdedb21d084d91ef13223bff6b76cb8bf19a744b7d28.txt", + "angle/third_party/rust/chromium_crates_io/vendor/strength_reduce-v0_2/LICENSE-APACHE": "licenses/angle/2e54cd84a645bea25943c75dd8ae67cb291e66a47a11578333c9b4b3b6b86c85.txt", + "angle/third_party/rust/chromium_crates_io/vendor/strength_reduce-v0_2/LICENSE-MIT": "licenses/angle/8f5442dfa8e9169045697e386bc91d19f393c939635741fa2a665ec36ca6f0ad.txt", + "angle/third_party/rust/chromium_crates_io/vendor/strsim-v0_11/LICENSE": "licenses/angle/1e697ce8d21401fbf1bddd9b5c3fd4c4c79ae1e3bdf51f81761c85e11d5a89cd.txt", + "angle/third_party/rust/chromium_crates_io/vendor/strum-v0_28/LICENSE": "licenses/angle/8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42.txt", + "angle/third_party/rust/chromium_crates_io/vendor/strum_macros-v0_28/LICENSE": "licenses/angle/8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42.txt", + "angle/third_party/rust/chromium_crates_io/vendor/subtle-v2/LICENSE": "licenses/angle/d1fc1bc0d155df60b2e7705b6b2ae02a05c96f948e1cec6e2fb86360b09f346b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-bundle-flac-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-bundle-mp3-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-codec-pcm-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-codec-vorbis-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-common-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-core-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-format-isomp4-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-format-mkv-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-format-ogg-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-format-riff-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-metadata-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/symphonia-v0_6/LICENSE": "licenses/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/syn-v2/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/syn-v2/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/syn-v3/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/syn-v3/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/synstructure-v0_13/LICENSE": "licenses/angle/219920e865eee70b7dcfc948a86b099e7f4fe2de01bcca2ca9a20c0a033f2b59.txt", + "angle/third_party/rust/chromium_crates_io/vendor/temporal_capi-v0_2/LICENSE-Apache": "licenses/angle/4e6bdc19db64d455dbddc0ee2f53ecba556f0226d4558954b5f02673c7358e59.txt", + "angle/third_party/rust/chromium_crates_io/vendor/temporal_capi-v0_2/LICENSE-MIT": "licenses/angle/073d3574ac6389e263360572b331e045dda7ac5cdba239ce5bdb02f299ef47bb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/temporal_rs-v0_2/LICENSE-Apache": "licenses/angle/4e6bdc19db64d455dbddc0ee2f53ecba556f0226d4558954b5f02673c7358e59.txt", + "angle/third_party/rust/chromium_crates_io/vendor/temporal_rs-v0_2/LICENSE-MIT": "licenses/angle/073d3574ac6389e263360572b331e045dda7ac5cdba239ce5bdb02f299ef47bb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/termcolor-v1/COPYING": "licenses/angle/01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/termcolor-v1/LICENSE-MIT": "licenses/angle/0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/thiserror-impl-v2/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/thiserror-impl-v2/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/thiserror-v2/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/thiserror-v2/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/timezone_provider-v0_2/LICENSE-Apache": "licenses/angle/4e6bdc19db64d455dbddc0ee2f53ecba556f0226d4558954b5f02673c7358e59.txt", + "angle/third_party/rust/chromium_crates_io/vendor/timezone_provider-v0_2/LICENSE-MIT": "licenses/angle/073d3574ac6389e263360572b331e045dda7ac5cdba239ce5bdb02f299ef47bb.txt", + "angle/third_party/rust/chromium_crates_io/vendor/tinystr-v0_8/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/toktrie-v1/LICENSE": "licenses/angle/c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383.txt", + "angle/third_party/rust/chromium_crates_io/vendor/transpose-v0_2/LICENSE-APACHE": "licenses/angle/8797ef61538ec5ee9222ebef7ca4e0f3ec5761b145ca9943d358c450efb644dd.txt", + "angle/third_party/rust/chromium_crates_io/vendor/transpose-v0_2/LICENSE-MIT": "licenses/angle/5080149357fd0be590bdc10cf92165412bb4d61ce496284d56f2d12874ae3121.txt", + "angle/third_party/rust/chromium_crates_io/vendor/tuple_list-v0_1/LICENSE": "licenses/angle/fdd3b4e30f42d35402ee9c33a6b72ad202f9658374b61b97becde4603551b95d.txt", + "angle/third_party/rust/chromium_crates_io/vendor/typed-arena-v2/LICENSE": "licenses/angle/9ed5e982274d54d0cf94f0e9f9fd889182b6f1f50a012f0be41ce7c884347ab6.txt", + "angle/third_party/rust/chromium_crates_io/vendor/typed-builder-macro-v0_22/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/typed-builder-macro-v0_22/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/typed-builder-v0_22/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/typed-builder-v0_22/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/typed-path-v0_12/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/typed-path-v0_12/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/typeid-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/typeid-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/typewit-v1/LICENSE-ZLIB.md": "licenses/angle/6db6d36c8aae8c2f6ebec32965bab9b4769128caed09a55b8696afce4301838a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/uds-v0_4/LICENSE-APACHE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/uds-v0_4/LICENSE-MIT": "licenses/angle/f234d44d4afa9dad03246705dcedb1a70a7562bf595fdbfafa93aa73c8839d57.txt", + "angle/third_party/rust/chromium_crates_io/vendor/unicode-ident-v1/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust/chromium_crates_io/vendor/unicode-ident-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/unicode-ident-v1/LICENSE-UNICODE": "licenses/angle/f7db81051789b729fea528a63ec4c938fdcb93d9d61d97dc8cc2e9df6d47f2a1.txt", + "angle/third_party/rust/chromium_crates_io/vendor/unicode-width-v0_2/COPYRIGHT": "licenses/angle/23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d.txt", + "angle/third_party/rust/chromium_crates_io/vendor/unicode-width-v0_2/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/unicode-width-v0_2/LICENSE-MIT": "licenses/angle/7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/unicode-xid-v0_2/COPYRIGHT": "licenses/angle/23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d.txt", + "angle/third_party/rust/chromium_crates_io/vendor/unicode-xid-v0_2/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/unicode-xid-v0_2/LICENSE-MIT": "licenses/angle/7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/unty-v0_0_4/LICENSE-APACHE": "licenses/angle/fa84f04c495f2533ba036606acc8644b50077752f17bc2e943235459dda1c12c.txt", + "angle/third_party/rust/chromium_crates_io/vendor/unty-v0_0_4/LICENSE-MIT": "licenses/angle/ac1e6e437cd571f6b450abd33dc055cf26dfb0fe2a72952c3ef6ae3844549c12.txt", + "angle/third_party/rust/chromium_crates_io/vendor/utf16_iter-v1/COPYRIGHT": "licenses/angle/b84efe109a420fa3ca98be33f4227327af7ffa426195812c270feb1268bc2426.txt", + "angle/third_party/rust/chromium_crates_io/vendor/utf16_iter-v1/LICENSE-APACHE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/utf16_iter-v1/LICENSE-MIT": "licenses/angle/3fa4ca83dcc9237839b1bdeb2e6d16bdfb5ec0c5ce42b24694d8bbf0dcbef72c.txt", + "angle/third_party/rust/chromium_crates_io/vendor/utf8_iter-v1/COPYRIGHT": "licenses/angle/c30152c94a6d75e021adbc52b3a52470366a46edb917e17deae3259251af244c.txt", + "angle/third_party/rust/chromium_crates_io/vendor/utf8_iter-v1/LICENSE-APACHE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/utf8_iter-v1/LICENSE-MIT": "licenses/angle/3fa4ca83dcc9237839b1bdeb2e6d16bdfb5ec0c5ce42b24694d8bbf0dcbef72c.txt", + "angle/third_party/rust/chromium_crates_io/vendor/uuid-v1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/uuid-v1/LICENSE-MIT": "licenses/angle/436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d.txt", + "angle/third_party/rust/chromium_crates_io/vendor/version_check-v0_9/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/version_check-v0_9/LICENSE-MIT": "licenses/angle/b7e650f3fce5c53249d1cdc608b54df156a97edd636cf9d23498d0cfe7aec63e.txt", + "angle/third_party/rust/chromium_crates_io/vendor/virtue-v0_0_18/LICENSE.md": "licenses/angle/ddcbb6914b62d5bebc3cb58ebe8d2738ffa9a48555469bbcbe65159979b878cf.txt", + "angle/third_party/rust/chromium_crates_io/vendor/wait-timeout-v0_2/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/wait-timeout-v0_2/LICENSE-MIT": "licenses/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt", + "angle/third_party/rust/chromium_crates_io/vendor/wide-v0_7/LICENSE-ZLIB.md": "licenses/angle/c43b9a9b1387ed53d2c49263838261129a010e280e3a174a792242c3e2c98db9.txt", + "angle/third_party/rust/chromium_crates_io/vendor/winapi-util-v0_1/COPYING": "licenses/angle/01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f.txt", + "angle/third_party/rust/chromium_crates_io/vendor/winapi-util-v0_1/LICENSE-MIT": "licenses/angle/cb3c929a05e6cbc9de9ab06a4c57eeb60ca8c724bef6c138c87d3a577e27aa14.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows-link-v0_2/license-apache-2.0": "licenses/angle/c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows-link-v0_2/license-mit": "licenses/angle/c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows-sys-v0_52/license-apache-2.0": "licenses/angle/c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows-sys-v0_52/license-mit": "licenses/angle/c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows-targets-v0_52/license-apache-2.0": "licenses/angle/c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows-targets-v0_52/license-mit": "licenses/angle/c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows_aarch64_msvc-v0_52/license-apache-2.0": "licenses/angle/c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows_aarch64_msvc-v0_52/license-mit": "licenses/angle/c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows_i686_msvc-v0_52/license-apache-2.0": "licenses/angle/c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows_i686_msvc-v0_52/license-mit": "licenses/angle/c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows_x86_64_msvc-v0_52/license-apache-2.0": "licenses/angle/c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/windows_x86_64_msvc-v0_52/license-mit": "licenses/angle/c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383.txt", + "angle/third_party/rust/chromium_crates_io/vendor/write-fonts-v0_52/LICENSE-APACHE": "licenses/angle/eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/write-fonts-v0_52/LICENSE-MIT": "licenses/angle/7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427.txt", + "angle/third_party/rust/chromium_crates_io/vendor/write16-v1/COPYRIGHT": "licenses/angle/3210be7332b5bdf48eb24a945258b9f38616a2cceb0dfc06e3c3c7e9740475a0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/write16-v1/LICENSE-APACHE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/write16-v1/LICENSE-MIT": "licenses/angle/3fa4ca83dcc9237839b1bdeb2e6d16bdfb5ec0c5ce42b24694d8bbf0dcbef72c.txt", + "angle/third_party/rust/chromium_crates_io/vendor/writeable-v0_6/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/xml-v1/LICENSE": "licenses/angle/0dc18d924dc0a5f41172a393012843a5eaaef338e795b3645da9cc3b6068220b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/xxhash-rust-v0_8/LICENSE": "licenses/angle/c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566.txt", + "angle/third_party/rust/chromium_crates_io/vendor/yoke-derive-v0_8/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/yoke-v0_8/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zerocopy-v0_8/LICENSE-APACHE": "licenses/angle/9d185ac6703c4b0453974c0d85e9eee43e6941009296bb1f5eb0b54e2329e9f3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zerocopy-v0_8/LICENSE-BSD": "licenses/angle/83c1763356e822adde0a2cae748d938a73fdc263849ccff6b27776dff213bd32.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zerocopy-v0_8/LICENSE-MIT": "licenses/angle/1a2f5c12ddc934d58956aa5dbdd3255fe55fd957633ab7d0d39e4f0daa73f7df.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zerofrom-derive-v0_1/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zerofrom-v0_1/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zeroize-v1/LICENSE-APACHE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zeroize-v1/LICENSE-MIT": "licenses/angle/8c7516d4b27b1e495be5e38b612298b63de48d05f49cdac94f70f3cd70f8864b.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zeroize_derive-v1/LICENSE-APACHE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zeroize_derive-v1/LICENSE-MIT": "licenses/angle/b8c6939380a400f53e11923d50fcc4dd2fa1ba8339fd9d04cda38a0251b6c9b0.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zerotrie-v0_2/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zerovec-derive-v0_11/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zerovec-v0_11/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zip-v8/LICENSE": "licenses/angle/58545fed1565e42d687aecec6897d35c6d37ccb71479a137c0deb2203e125c79.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zmij-v1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zoneinfo64-v0_3/LICENSE": "licenses/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zune-core-v0_5/LICENSE-APACHE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zune-core-v0_5/LICENSE-MIT": "licenses/angle/d30047bca3b516639339a3c279bb84c3483124fb5a9dafe3c75056a85090e745.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zune-core-v0_5/LICENSE-ZLIB": "licenses/angle/d201d14804d3bcd3b944147173175e4abfbd838b7c8069b6bd3452496bf13e6c.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zune-jpeg-v0_5/LICENSE-APACHE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zune-jpeg-v0_5/LICENSE-MIT": "licenses/angle/d30047bca3b516639339a3c279bb84c3483124fb5a9dafe3c75056a85090e745.txt", + "angle/third_party/rust/chromium_crates_io/vendor/zune-jpeg-v0_5/LICENSE-ZLIB": "licenses/angle/d201d14804d3bcd3b944147173175e4abfbd838b7c8069b6bd3452496bf13e6c.txt", + "angle/third_party/rust-toolchain/lib/rustlib/rustc-src/rust/compiler/rustc_codegen_cranelift/LICENSE-APACHE": "licenses/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt", + "angle/third_party/rust-toolchain/lib/rustlib/rustc-src/rust/compiler/rustc_codegen_cranelift/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust-toolchain/lib/rustlib/rustc-src/rust/compiler/rustc_codegen_gcc/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust-toolchain/lib/rustlib/rustc-src/rust/compiler/rustc_codegen_gcc/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/backtrace/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/backtrace/LICENSE-MIT": "licenses/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/compiler-builtins/LICENSE.txt": "licenses/angle/ab6eec6caf0fa5775e411c7a8bc6a45c4ef2956b0980b157ab74fc5cd62a928b.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/compiler-builtins/libm/LICENSE.txt": "licenses/angle/3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/portable-simd/LICENSE-APACHE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/portable-simd/LICENSE-MIT": "licenses/angle/eb07d497d26e6d68fbc76e793f5e5c9cfa197df2a580e47383569c287a55edf9.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/portable-simd/crates/core_simd/LICENSE-APACHE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/portable-simd/crates/core_simd/LICENSE-MIT": "licenses/angle/eb07d497d26e6d68fbc76e793f5e5c9cfa197df2a580e47383569c287a55edf9.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/LICENSE-MIT": "licenses/angle/29662666b44dff84977b46e05642cdef910bc3a93a17b5fd86e632bafa59cf21.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/core_arch/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/core_arch/LICENSE-MIT": "licenses/angle/29662666b44dff84977b46e05642cdef910bc3a93a17b5fd86e632bafa59cf21.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/intrinsic-test/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/intrinsic-test/LICENSE-MIT": "licenses/angle/8bc20184c0ddf3006df05e89fdf7193b33dbe4c751ae59d6bb1835f71bbe70da.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/addr2line-0.27.1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/addr2line-0.27.1/LICENSE-MIT": "licenses/angle/e99d88d232bf57d70f0fb87f6b496d44b6653f99f8a63d250a54c61ea4bcde40.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/adler2-2.0.1/LICENSE-0BSD": "licenses/angle/861399f8c21c042b110517e76dc6b63a2b334276c8cf17412fc3c8908ca8dc17.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/adler2-2.0.1/LICENSE-APACHE": "licenses/angle/8ada45cd9f843acf64e4722ae262c622a2b3b3007c7310ef36ac1061a30f6adb.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/adler2-2.0.1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/cc-1.4.3/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/cc-1.4.3/LICENSE-MIT": "licenses/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/cfg-if-1.0.4/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/cfg-if-1.0.4/LICENSE-MIT": "licenses/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/dlmalloc-0.2.14/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/dlmalloc-0.2.14/LICENSE-MIT": "licenses/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/find-msvc-tools-0.1.11/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/find-msvc-tools-0.1.11/LICENSE-MIT": "licenses/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/foldhash-0.2.0/LICENSE": "licenses/angle/b1181a40b2a7b25cf66fd01481713bc1005df082c53ef73e851e55071b102744.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/getopts-0.2.24/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/getopts-0.2.24/LICENSE-MIT": "licenses/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/gimli-0.34.0/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/gimli-0.34.0/LICENSE-MIT": "licenses/angle/7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.17.1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.17.1/LICENSE-MIT": "licenses/angle/ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hermit-abi-0.5.3/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hermit-abi-0.5.3/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/libc-0.2.189/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/libc-0.2.189/LICENSE-MIT": "licenses/angle/123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/memchr-2.8.3/COPYING": "licenses/angle/01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/memchr-2.8.3/LICENSE-MIT": "licenses/angle/0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/miniz_oxide-0.9.1/LICENSE": "licenses/angle/4108245a1f2df9d4e94df8abed5b4ba0759bb2f9b40a6b939f1be141077ae50b.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/miniz_oxide-0.9.1/LICENSE-APACHE.md": "licenses/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/miniz_oxide-0.9.1/LICENSE-MIT.md": "licenses/angle/799e9ca9d179295ef372f25d3769cdda7d25bb2668add6a6a1e22d1e4c678b8d.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/miniz_oxide-0.9.1/LICENSE-ZLIB.md": "licenses/angle/0a54e647fe54104658b5e563c04c6f9edf251710e47bce692e0bd990a4ddaa39.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/moto-rt-0.17.4/LICENSE-APACHE": "licenses/angle/69fef7b0f322a65554156141f2bc6256ed0bb78cba7e49f0d8829d7ec4ee62cd.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/moto-rt-0.17.4/LICENSE-MIT": "licenses/angle/a7c936ff1ed8fa340172d42a98185afee078f818e907da69f3a8e336b1623b4b.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/object-0.39.1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/object-0.39.1/LICENSE-MIT": "licenses/angle/0b74dfa0bcee5c420c6b7f67b4b2658f9ab8388c97b8e733975f2cecbdd668a6.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand-0.9.5/COPYRIGHT": "licenses/angle/90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand-0.9.5/LICENSE-APACHE": "licenses/angle/35242e7a83f69875e6edeff02291e688c97caafe2f8902e4e19b49d3e78b4cab.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand-0.9.5/LICENSE-MIT": "licenses/angle/209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_core-0.9.5/COPYRIGHT": "licenses/angle/90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_core-0.9.5/LICENSE-APACHE": "licenses/angle/6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_core-0.9.5/LICENSE-MIT": "licenses/angle/209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_xorshift-0.4.0/COPYRIGHT": "licenses/angle/90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_xorshift-0.4.0/LICENSE-APACHE": "licenses/angle/35242e7a83f69875e6edeff02291e688c97caafe2f8902e4e19b49d3e78b4cab.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_xorshift-0.4.0/LICENSE-MIT": "licenses/angle/209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rustc-demangle-0.1.28/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rustc-demangle-0.1.28/LICENSE-MIT": "licenses/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rustc-literal-escaper-0.0.8/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rustc-literal-escaper-0.0.8/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/shlex-2.0.1/LICENSE-APACHE": "licenses/angle/553fffcd9b1cb158bc3e9edc35da85ca5c3b3d7d2e61c883ebcfa8a65814b583.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/shlex-2.0.1/LICENSE-MIT": "licenses/angle/4455bf75a91154108304cb283e0fea9948c14f13e20d60887cf2552449dea3b1.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/unwinding-0.2.10/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/unwinding-0.2.10/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip2-1.0.4+wasi-0.2.12/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip2-1.0.4+wasi-0.2.12/LICENSE-Apache-2.0_WITH_LLVM-exception": "licenses/angle/268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip2-1.0.4+wasi-0.2.12/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip3-0.7.1+wasi-0.3.0/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip3-0.7.1+wasi-0.3.0/LICENSE-Apache-2.0_WITH_LLVM-exception": "licenses/angle/268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip3-0.7.1+wasi-0.3.0/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wit-bindgen-0.57.1/LICENSE-APACHE": "licenses/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wit-bindgen-0.57.1/LICENSE-Apache-2.0_WITH_LLVM-exception": "licenses/angle/268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wit-bindgen-0.57.1/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust-toolchain/lib/rustlib/src/rust/src/llvm-project/libunwind/LICENSE.TXT": "licenses/angle/b5efebcaca80879234098e52d1725e6d9eb8fb96a19fce625d39184b705f7b6d.txt", + "angle/third_party/rust-toolchain/lib/third_party/crubit/LICENSE": "licenses/angle/d136abc3388ab9b16879d6dcb9c8c4a5d70ddf821bab80b6454843abe358d34a.txt", + "angle/third_party/rust-toolchain/share/doc/cargo/LICENSE-APACHE": "licenses/angle/8ada45cd9f843acf64e4722ae262c622a2b3b3007c7310ef36ac1061a30f6adb.txt", + "angle/third_party/rust-toolchain/share/doc/cargo/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust-toolchain/share/doc/cargo/LICENSE-THIRD-PARTY": "licenses/angle/cbc759b1f17a2ac38fe3eb9e9563b1a08ba0f900611c49faaf68b46907b6d898.txt", + "angle/third_party/rust-toolchain/share/doc/clippy/LICENSE-APACHE": "licenses/angle/d8b56cd45661bfc7ccf4ce5722388cab275f9dabb9e471c922cc80e36bbf9caa.txt", + "angle/third_party/rust-toolchain/share/doc/clippy/LICENSE-MIT": "licenses/angle/8d07f0c9c9966be0aaec4196d7863b56ade114e9714dbc31ebba576c0446d2fc.txt", + "angle/third_party/rust-toolchain/share/doc/rust-analyzer/LICENSE-APACHE": "licenses/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt", + "angle/third_party/rust-toolchain/share/doc/rust-analyzer/LICENSE-MIT": "licenses/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt", + "angle/third_party/rust-toolchain/share/doc/rustc/COPYRIGHT-library.html": "licenses/angle/07ca08d0838ccbbfb79bf292741c7c4c45311f90784431d09af9a1e0b90a9469.txt", + "angle/third_party/rust-toolchain/share/doc/rustc/COPYRIGHT.html": "licenses/angle/9ff34fe87a89242afd776a07dbe055151121fead277acd30fee56e42b41cab93.txt", + "angle/third_party/rust-toolchain/share/doc/rustfmt/LICENSE-APACHE": "licenses/angle/092c8e82c47fb859d7385253c1b04052dbd65a8bcf0bbd8b6f7dba58a9e8753d.txt", + "angle/third_party/rust-toolchain/share/doc/rustfmt/LICENSE-MIT": "licenses/angle/ae0f1f791e3b4faccf981c0b530199235a8b8a021c7ef0c3c85c6c676ea4d27f.txt", + "angle/third_party/spirv-cross/src/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/spirv-cross/src/LICENSES/LicenseRef-KhronosFreeUse.txt": "licenses/angle/fbeaca472f4f70e276dd1106ca5097435967a22ad6c1d8200aef7ad9f70aaf3f.txt", + "angle/third_party/spirv-headers/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/spirv-headers/src/LICENSE": "licenses/angle/ea43b1de38a6f90c488800d66dec1ed671e68cda530266bc96951fb5b6307613.txt", + "angle/third_party/spirv-tools/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/spirv-tools/src/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/spirv-tools/src/utils/vscode/src/lsp/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/turbine/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/vulkan-deps/LICENSE": "licenses/angle/845022e0c1db1abb41a6ba4cd3c4b674ec290f3359d9d3c78ae558d4c0ed9308.txt", + "angle/third_party/vulkan-deps/glslang/LICENSE": "licenses/angle/23353f4505b1c8ce4f8f72fc3b11dc74b4a8a7bf95921d93ff77f227c171a710.txt", + "angle/third_party/vulkan-deps/spirv-headers/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/vulkan-deps/spirv-tools/LICENSE": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/vulkan-deps/vulkan-headers/LICENSE.txt": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/vulkan-headers/LICENSE.txt": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/vulkan-headers/src/LICENSE.md": "licenses/angle/95ad366d23fadf701d355bc45fb8b82ae2d700239471d35d41286ac3b08ff903.txt", + "angle/third_party/vulkan-loader/src/LICENSE.txt": "licenses/angle/43c0a37e6a0fa7ff3c843b3ec5a4fac84b712558ddac103fbd4c1649662a9ece.txt", + "angle/third_party/vulkan-tools/src/LICENSE.txt": "licenses/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt", + "angle/third_party/vulkan-utility-libraries/src/LICENSE.md": "licenses/angle/69760673abf91cfd0280ae73739a29c078f493804d9016a122b3b189b48ad6e6.txt", + "angle/third_party/vulkan-validation-layers/src/LICENSE.txt": "licenses/angle/db3010170b904cb7212ef6abd2336f316bf735060eeeca23f1a737f459cc73e4.txt", + "angle/third_party/vulkan_memory_allocator/LICENSE.txt": "licenses/angle/cdb520614db3ec62e667ece01e64e6afa21948fa51e85e748b1494597a7be907.txt", + "angle/third_party/wayland/LICENSE": "licenses/angle/778a9c936b9fa24f3842b6071e3cc5c794d3f7cc6d6fddbf356b6f2202afb6a0.txt", + "angle/third_party/wayland-protocols/LICENSE": "licenses/angle/f1a2b233e8a9a71c40f4aa885be08a0842ac85bb8588703c1dd7e6e6502e3124.txt", + "angle/third_party/zlib/LICENSE": "licenses/angle/e32ff4e00d9d94930537635291da39e7e612703334bf6fde8c7f1686fe8a45a2.txt", + "angle/tools/flex-bison/third_party/m4sugar/LICENSE": "licenses/angle/ab15fd526bd8dd18a9e77ebc139656bf4d33e97fc7238cd11bf60e2b9b8666c6.txt", + "angle/tools/flex-bison/third_party/skeletons/LICENSE": "licenses/angle/8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903.txt", + "angle/tools/valgrind/asan/third_party/LICENSE.TXT": "licenses/angle/1a8f1058753f1ba890de984e48f0242a3a5c29a6a8f2ed9fd813f36985387e8d.txt", + "angle/util/android/third_party/LICENSE": "licenses/angle/58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd.txt", + "angle/util/windows/third_party/StackWalker/LICENSE": "licenses/angle/bfec18debedcb337f8af53f143ccf0b1575d0b7c30deaee137f10397eca0d353.txt" + } +} diff --git a/docs/graphics/evidence/2026-09-07-graphics-package/package-verification.json b/docs/graphics/evidence/2026-09-07-graphics-package/package-verification.json new file mode 100644 index 000000000..ff5c798d3 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-graphics-package/package-verification.json @@ -0,0 +1,484 @@ +{ + "implementationCommit": "6a828946cded90bf6c6c2bde3658b5c7a47edc1d", + "rid": "osx-arm64", + "package": "WebScene.NativeEngine.Runtime.osx-arm64.1.0.33-gpu-g01.3.nupkg", + "sha256": "a2e4f8d5d342b813d9ffd03dbd245a68027ceb33c26283d937c26314e51b7c2a", + "bytes": 23701969, + "packagePublished": false, + "checks": { + "nativeSuites": 4, + "wptDocuments": 149, + "wptSubtests": 509, + "graphicsLibraries": 3, + "originalLicensePaths": 1010, + "publishHashes": "passed", + "publishNativeAbi": 3, + "missingConsumerLibrary": "rejected", + "mismatchedStagingLibrary": "rejected" + }, + "entries": { + "_rels/.rels": "0192bee3a9ece4d02676ac7ea428150b8d57084f5a0d1358f9e8035a4dc9acc3", + "WebScene.NativeEngine.Runtime.osx-arm64.nuspec": "dcb4588dc01b2966732a0b32e5a675580803ef749987620dd45d1eaaf4dc1eaf", + "README.md": "45de29a6186fb8bc1df4920bce757730730fae0d2ba394a6b826fe12d1b60555", + "LICENSE": "1e1b33f10bda103ea949dd646d43fa3e76f5534f635a873a8e0e551056f816b0", + "buildTransitive/WebScene.NativeEngine.Runtime.osx-arm64.props": "e8fba620211c885300e5a0686551c49d1171b3b70f39e345bbe380f7cadbacf2", + "buildTransitive/WebScene.NativeEngine.Runtime.osx-arm64.targets": "62a82741a7d7fb0cdd0a18613c61a65db135439a175d37eb4ede905fc7050448", + "buildTransitive/graphics/WebScene.NativeEngine.Graphics.targets": "64b88bb54655afa9a76115d7abc504481e03b0860b3227d03af4c201989c7660", + "graphics/angle-sdk.json": "97464550068b16f59a055b56d881445da63a80afffc57bbc8ea2a9226b75000d", + "graphics/dawn-sdk.json": "aba01f2ab19ec0ba33cae460f134d82da8f01dde9370b91ffd62abaff934e747", + "licenses/HTML-PARSER-THIRD-PARTY-NOTICES.md": "a6763548578267420cde3544279a6d69b723839b18989328dbc2986bb2df522b", + "licenses/ICU-LICENSE.txt": "e55522d81edc687a341a4411e0776e54ca654e90147f354a90458aaced4116af", + "licenses/IXWebSocket-LICENSE.txt": "9cc46d4265f18ab959b75c44102285dc04bb04ea2bb696b1858f5c38f7ebaca0", + "licenses/V8-LICENSE.txt": "6ab33af8774a0f396ee3aeeb761e3229057682d6f9fa7f572e390c2cb3a6e509", + "licenses/graphics/angle/01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f.txt": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f", + "licenses/graphics/angle/01db48fbe12f95dfd63b92dc7c29afcf8783f96f8ca0061a9fb7e869f5a08512.txt": "01db48fbe12f95dfd63b92dc7c29afcf8783f96f8ca0061a9fb7e869f5a08512", + "licenses/graphics/angle/025436edff4cfcdde17a5811fdea78892d8482efd1abdec5a17872d07a4f2112.txt": "025436edff4cfcdde17a5811fdea78892d8482efd1abdec5a17872d07a4f2112", + "licenses/graphics/angle/02de69b64fc36d9e938f418e52723e42f0b2b226d58a9cb3c8dcbdf7059f5074.txt": "02de69b64fc36d9e938f418e52723e42f0b2b226d58a9cb3c8dcbdf7059f5074", + "licenses/graphics/angle/0424e57d4303164dc59a8509c20dae0518b853692e5c2b0e98b11816fdbc97c7.txt": "0424e57d4303164dc59a8509c20dae0518b853692e5c2b0e98b11816fdbc97c7", + "licenses/graphics/angle/0444c6991eead6822f7b9102e654448d51624431119546492e8b231db42c48bb.txt": "0444c6991eead6822f7b9102e654448d51624431119546492e8b231db42c48bb", + "licenses/graphics/angle/044983df14c97f2f9570766aaf977b3cdfc4a06cf1f36b776331c5ff89b4fb89.txt": "044983df14c97f2f9570766aaf977b3cdfc4a06cf1f36b776331c5ff89b4fb89", + "licenses/graphics/angle/04c35849b20d927d99f1f498cfc3b4e0050cd726875be6ad5392a9f75f93bb03.txt": "04c35849b20d927d99f1f498cfc3b4e0050cd726875be6ad5392a9f75f93bb03", + "licenses/graphics/angle/0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720.txt": "0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720", + "licenses/graphics/angle/066b84cfd245e2ba8c6940aba7d63465c027550906301d8104be07cbb8398c46.txt": "066b84cfd245e2ba8c6940aba7d63465c027550906301d8104be07cbb8398c46", + "licenses/graphics/angle/073d3574ac6389e263360572b331e045dda7ac5cdba239ce5bdb02f299ef47bb.txt": "073d3574ac6389e263360572b331e045dda7ac5cdba239ce5bdb02f299ef47bb", + "licenses/graphics/angle/073f28b7d389c8fe74f607e17c27f81eaa5ace69edc43a884f23f41b41c5c726.txt": "073f28b7d389c8fe74f607e17c27f81eaa5ace69edc43a884f23f41b41c5c726", + "licenses/graphics/angle/07ca08d0838ccbbfb79bf292741c7c4c45311f90784431d09af9a1e0b90a9469.txt": "07ca08d0838ccbbfb79bf292741c7c4c45311f90784431d09af9a1e0b90a9469", + "licenses/graphics/angle/092c8e82c47fb859d7385253c1b04052dbd65a8bcf0bbd8b6f7dba58a9e8753d.txt": "092c8e82c47fb859d7385253c1b04052dbd65a8bcf0bbd8b6f7dba58a9e8753d", + "licenses/graphics/angle/09a7c3fbc0b4ae6a9ccc4ffdcbfa511c14b8647a24f24783838862cf6c226d4e.txt": "09a7c3fbc0b4ae6a9ccc4ffdcbfa511c14b8647a24f24783838862cf6c226d4e", + "licenses/graphics/angle/09c9bcea95ca086f8bc5bed174e40bc835b297d40fb5f86bbbb570fe0a5581a7.txt": "09c9bcea95ca086f8bc5bed174e40bc835b297d40fb5f86bbbb570fe0a5581a7", + "licenses/graphics/angle/09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b.txt": "09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b", + "licenses/graphics/angle/09f1c8c9e941af3e584d59641ea9b87d83c0cb0fd007eb5ef391a7e2643c1a46.txt": "09f1c8c9e941af3e584d59641ea9b87d83c0cb0fd007eb5ef391a7e2643c1a46", + "licenses/graphics/angle/0a54e647fe54104658b5e563c04c6f9edf251710e47bce692e0bd990a4ddaa39.txt": "0a54e647fe54104658b5e563c04c6f9edf251710e47bce692e0bd990a4ddaa39", + "licenses/graphics/angle/0a73de6c78c0743aef49c275563c9486fd3e55d61611044cecc5620f4dfe772d.txt": "0a73de6c78c0743aef49c275563c9486fd3e55d61611044cecc5620f4dfe772d", + "licenses/graphics/angle/0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9.txt": "0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9", + "licenses/graphics/angle/0b74dfa0bcee5c420c6b7f67b4b2658f9ab8388c97b8e733975f2cecbdd668a6.txt": "0b74dfa0bcee5c420c6b7f67b4b2658f9ab8388c97b8e733975f2cecbdd668a6", + "licenses/graphics/angle/0b7c936ff1270fb5089750e326f732ce2f08b18e804dc8847aa44561d0a7a277.txt": "0b7c936ff1270fb5089750e326f732ce2f08b18e804dc8847aa44561d0a7a277", + "licenses/graphics/angle/0bbe88228fd63d20ec097f64e58d5a0a465123ae139140a18d406c60b48824b5.txt": "0bbe88228fd63d20ec097f64e58d5a0a465123ae139140a18d406c60b48824b5", + "licenses/graphics/angle/0be96d891d00e0ae0df75d7f3289b12871c000a1f5ac744f3b570768d4bb277c.txt": "0be96d891d00e0ae0df75d7f3289b12871c000a1f5ac744f3b570768d4bb277c", + "licenses/graphics/angle/0cb3efcfd8f7a02a337e98dc3de4b0b57424d7208a332b26e7deb8cb94c13922.txt": "0cb3efcfd8f7a02a337e98dc3de4b0b57424d7208a332b26e7deb8cb94c13922", + "licenses/graphics/angle/0d4bc7abd48dcfb14e24254ee404066737ff0167144e222914a2113b8794683e.txt": "0d4bc7abd48dcfb14e24254ee404066737ff0167144e222914a2113b8794683e", + "licenses/graphics/angle/0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594.txt": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/graphics/angle/0dc18d924dc0a5f41172a393012843a5eaaef338e795b3645da9cc3b6068220b.txt": "0dc18d924dc0a5f41172a393012843a5eaaef338e795b3645da9cc3b6068220b", + "licenses/graphics/angle/0dd71b8a6d6db0db4dc38a983749e1b2f4bb57aba30141a168042042c4a7b6f8.txt": "0dd71b8a6d6db0db4dc38a983749e1b2f4bb57aba30141a168042042c4a7b6f8", + "licenses/graphics/angle/0dd882e53de11566d50f8e8e2d5a651bcf3fabee4987d70f306233cf39094ba7.txt": "0dd882e53de11566d50f8e8e2d5a651bcf3fabee4987d70f306233cf39094ba7", + "licenses/graphics/angle/0e64c1e9cd62f47682caeb545d2943fb4c38a9b2e5d9fd7e3b456973bb430d1b.txt": "0e64c1e9cd62f47682caeb545d2943fb4c38a9b2e5d9fd7e3b456973bb430d1b", + "licenses/graphics/angle/0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f.txt": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f", + "licenses/graphics/angle/10ae82b5a349c1ac15015d2c50e5adaf6413538f69be961cf0140cfc152b97e3.txt": "10ae82b5a349c1ac15015d2c50e5adaf6413538f69be961cf0140cfc152b97e3", + "licenses/graphics/angle/10d5120a16805804ffda8b688c220bfb4e8f39741b57320604d455a309e01972.txt": "10d5120a16805804ffda8b688c220bfb4e8f39741b57320604d455a309e01972", + "licenses/graphics/angle/118be5792e5126839694ca2209c62c71d2d7cd49e7bbb43bbdee9b016fb06094.txt": "118be5792e5126839694ca2209c62c71d2d7cd49e7bbb43bbdee9b016fb06094", + "licenses/graphics/angle/123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e.txt": "123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e", + "licenses/graphics/angle/130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686.txt": "130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686", + "licenses/graphics/angle/15137d6c822e3ab097093a33c3a39a9df699f373f6438867ad534ff60762a947.txt": "15137d6c822e3ab097093a33c3a39a9df699f373f6438867ad534ff60762a947", + "licenses/graphics/angle/152a0f78d9c3a3afc09470cba1e66eabb915515cb121252d6c85c4ed6352a073.txt": "152a0f78d9c3a3afc09470cba1e66eabb915515cb121252d6c85c4ed6352a073", + "licenses/graphics/angle/16a39991619e92f18680932da2a9199fdf7d95df3ecaedc52ea06218aabafd6f.txt": "16a39991619e92f18680932da2a9199fdf7d95df3ecaedc52ea06218aabafd6f", + "licenses/graphics/angle/16fbc228292bd774b263b212ae422c524cbf3b2078bcf21b22f8bdd4373be617.txt": "16fbc228292bd774b263b212ae422c524cbf3b2078bcf21b22f8bdd4373be617", + "licenses/graphics/angle/177540cad091a40e8071db310bc3b6115c4e329a92a234609b60c154b008a888.txt": "177540cad091a40e8071db310bc3b6115c4e329a92a234609b60c154b008a888", + "licenses/graphics/angle/17afb4516438c26ee15213c5a082206340d976a68472b8eab2499d7bce4debec.txt": "17afb4516438c26ee15213c5a082206340d976a68472b8eab2499d7bce4debec", + "licenses/graphics/angle/17e70c676e1521ff3e4686f04a2053d93a7e28a33be8de7ec37ab0ff72feb677.txt": "17e70c676e1521ff3e4686f04a2053d93a7e28a33be8de7ec37ab0ff72feb677", + "licenses/graphics/angle/18a3e8b3d7d0adf096d0b28183f6c808749877829cc53b8ac9ed79e2bd01ac15.txt": "18a3e8b3d7d0adf096d0b28183f6c808749877829cc53b8ac9ed79e2bd01ac15", + "licenses/graphics/angle/1920d2326ebbad34dcbd9681b4fe4926f113aa5e7dc9a92fceb456d859ee142e.txt": "1920d2326ebbad34dcbd9681b4fe4926f113aa5e7dc9a92fceb456d859ee142e", + "licenses/graphics/angle/192ea857d1bff2b87c174de36cbae5c173234726c6b8eceab9790a535d7dbc95.txt": "192ea857d1bff2b87c174de36cbae5c173234726c6b8eceab9790a535d7dbc95", + "licenses/graphics/angle/19ad13f8d801c13b5dde35625d2a57a0c3e1e4cb4d073dd2aff72be3925940e6.txt": "19ad13f8d801c13b5dde35625d2a57a0c3e1e4cb4d073dd2aff72be3925940e6", + "licenses/graphics/angle/1a2f5c12ddc934d58956aa5dbdd3255fe55fd957633ab7d0d39e4f0daa73f7df.txt": "1a2f5c12ddc934d58956aa5dbdd3255fe55fd957633ab7d0d39e4f0daa73f7df", + "licenses/graphics/angle/1a8f1058753f1ba890de984e48f0242a3a5c29a6a8f2ed9fd813f36985387e8d.txt": "1a8f1058753f1ba890de984e48f0242a3a5c29a6a8f2ed9fd813f36985387e8d", + "licenses/graphics/angle/1a9a4f0e3d479a27240ddd59a9137a66ab4a0f9dfdc8ca6188cc0bfd85187f04.txt": "1a9a4f0e3d479a27240ddd59a9137a66ab4a0f9dfdc8ca6188cc0bfd85187f04", + "licenses/graphics/angle/1b22b049b5267d6dfc23a67bf4a84d8ec04b9fdfb1a51d360e42b4342c8b4154.txt": "1b22b049b5267d6dfc23a67bf4a84d8ec04b9fdfb1a51d360e42b4342c8b4154", + "licenses/graphics/angle/1e2b7ade3fb228130408b9990cae6a7618eb314c75aa0b164bfe485d9d9756ee.txt": "1e2b7ade3fb228130408b9990cae6a7618eb314c75aa0b164bfe485d9d9756ee", + "licenses/graphics/angle/1e697ce8d21401fbf1bddd9b5c3fd4c4c79ae1e3bdf51f81761c85e11d5a89cd.txt": "1e697ce8d21401fbf1bddd9b5c3fd4c4c79ae1e3bdf51f81761c85e11d5a89cd", + "licenses/graphics/angle/1f194a987fa1dc60e4bcf5e04e0fc03fff8f2ee587c52136adb2cebb397250b8.txt": "1f194a987fa1dc60e4bcf5e04e0fc03fff8f2ee587c52136adb2cebb397250b8", + "licenses/graphics/angle/209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b.txt": "209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b", + "licenses/graphics/angle/20b1e32e55821d109bcd17e1e150cfe242b54590d3e8083648d1276f88d33d16.txt": "20b1e32e55821d109bcd17e1e150cfe242b54590d3e8083648d1276f88d33d16", + "licenses/graphics/angle/212c5a071f61512786b5e5840b3d70c85e017f3f82939ad4d4a870fc48b33477.txt": "212c5a071f61512786b5e5840b3d70c85e017f3f82939ad4d4a870fc48b33477", + "licenses/graphics/angle/21425a6ffc6c2a9dc2a091fcab8f815afdcef6f0fdf2748c1043904bf38bdae1.txt": "21425a6ffc6c2a9dc2a091fcab8f815afdcef6f0fdf2748c1043904bf38bdae1", + "licenses/graphics/angle/216486f29671a4262efe32af6d84a75bef398127f8c5f369b5c8305983887a06.txt": "216486f29671a4262efe32af6d84a75bef398127f8c5f369b5c8305983887a06", + "licenses/graphics/angle/219920e865eee70b7dcfc948a86b099e7f4fe2de01bcca2ca9a20c0a033f2b59.txt": "219920e865eee70b7dcfc948a86b099e7f4fe2de01bcca2ca9a20c0a033f2b59", + "licenses/graphics/angle/22c5cc6922ab5d69fba32d8c5ee4cdd14981508cb53afc0ebd85593847fd95a5.txt": "22c5cc6922ab5d69fba32d8c5ee4cdd14981508cb53afc0ebd85593847fd95a5", + "licenses/graphics/angle/23353f4505b1c8ce4f8f72fc3b11dc74b4a8a7bf95921d93ff77f227c171a710.txt": "23353f4505b1c8ce4f8f72fc3b11dc74b4a8a7bf95921d93ff77f227c171a710", + "licenses/graphics/angle/23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d.txt": "23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d", + "licenses/graphics/angle/23c4eff7a1c027a977a0b79c4497e17582c334c5f17ef6ac8ca0b52d1e7d8417.txt": "23c4eff7a1c027a977a0b79c4497e17582c334c5f17ef6ac8ca0b52d1e7d8417", + "licenses/graphics/angle/23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3.txt": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/graphics/angle/24604018b3d42b92eb3a0ee55a9e8d3bde92f95a0809f9ef22c06ce32f627940.txt": "24604018b3d42b92eb3a0ee55a9e8d3bde92f95a0809f9ef22c06ce32f627940", + "licenses/graphics/angle/24699c6858472311aa9acc6c2b7112ff9de6e7792569158ba9e439deb0529ef6.txt": "24699c6858472311aa9acc6c2b7112ff9de6e7792569158ba9e439deb0529ef6", + "licenses/graphics/angle/25a0874d15e7c834a47c3adc80901edb2219759254992023ef1010c3065413d5.txt": "25a0874d15e7c834a47c3adc80901edb2219759254992023ef1010c3065413d5", + "licenses/graphics/angle/268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5.txt": "268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5", + "licenses/graphics/angle/27995d58ad5c1145c1a8cd86244ce844886958a35eb2b78c6b772748669999ac.txt": "27995d58ad5c1145c1a8cd86244ce844886958a35eb2b78c6b772748669999ac", + "licenses/graphics/angle/294f58267c6f473c4ce7270bf5c8d34b2003cb43804552459654c36553431276.txt": "294f58267c6f473c4ce7270bf5c8d34b2003cb43804552459654c36553431276", + "licenses/graphics/angle/29662666b44dff84977b46e05642cdef910bc3a93a17b5fd86e632bafa59cf21.txt": "29662666b44dff84977b46e05642cdef910bc3a93a17b5fd86e632bafa59cf21", + "licenses/graphics/angle/29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6.txt": "29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6", + "licenses/graphics/angle/29e9fe5074bd27e0e5d5d110394fbbcd841baee2651a3c4b4560a632702cede4.txt": "29e9fe5074bd27e0e5d5d110394fbbcd841baee2651a3c4b4560a632702cede4", + "licenses/graphics/angle/2aa92cada6431e75615e3fe6cb1a9082c98f777d48ae1c087c0da0e37f7b8bff.txt": "2aa92cada6431e75615e3fe6cb1a9082c98f777d48ae1c087c0da0e37f7b8bff", + "licenses/graphics/angle/2aad5fc00f705c4a1addb83eed10a6a75d286a3779f0cf8519d87e62bc4735fd.txt": "2aad5fc00f705c4a1addb83eed10a6a75d286a3779f0cf8519d87e62bc4735fd", + "licenses/graphics/angle/2be1b548b0387ca8948e1bb9434e709126904d15f622cc2d0d8e7f186e4d122d.txt": "2be1b548b0387ca8948e1bb9434e709126904d15f622cc2d0d8e7f186e4d122d", + "licenses/graphics/angle/2c889c721ec8ae6d7664680afaefbb4c7620976f434b57a506ecd92f0649b6a0.txt": "2c889c721ec8ae6d7664680afaefbb4c7620976f434b57a506ecd92f0649b6a0", + "licenses/graphics/angle/2d1f6074aca5e089e1cd580c0a5a925fc508ad542aec8bfe804c0031cf717667.txt": "2d1f6074aca5e089e1cd580c0a5a925fc508ad542aec8bfe804c0031cf717667", + "licenses/graphics/angle/2dddf08818297a3b89d43d95ff659d8da85741108c9136dfa3a4d856c0623bd8.txt": "2dddf08818297a3b89d43d95ff659d8da85741108c9136dfa3a4d856c0623bd8", + "licenses/graphics/angle/2e4213e573312c8c75e6e7e2c55a45283427e759432b56014afaf3c7d950a568.txt": "2e4213e573312c8c75e6e7e2c55a45283427e759432b56014afaf3c7d950a568", + "licenses/graphics/angle/2e54cd84a645bea25943c75dd8ae67cb291e66a47a11578333c9b4b3b6b86c85.txt": "2e54cd84a645bea25943c75dd8ae67cb291e66a47a11578333c9b4b3b6b86c85", + "licenses/graphics/angle/2e61cef458cfa3b764eadb2d0b6cbfe557ba70f2b39433d85fa41361450c50c4.txt": "2e61cef458cfa3b764eadb2d0b6cbfe557ba70f2b39433d85fa41361450c50c4", + "licenses/graphics/angle/2f4d2ff05f05c5da3879f40292b7600332d775dc7ed320d43dd42f3cd7d92c9b.txt": "2f4d2ff05f05c5da3879f40292b7600332d775dc7ed320d43dd42f3cd7d92c9b", + "licenses/graphics/angle/2fd7257410c4d7d9c8d8d85cb7f9f4ef9eee34126a96a993245c71577997c345.txt": "2fd7257410c4d7d9c8d8d85cb7f9f4ef9eee34126a96a993245c71577997c345", + "licenses/graphics/angle/30c23618679108f3e8ea1d2a658c7ca417bdfc891c98ef1a89fa4ff0c9828654.txt": "30c23618679108f3e8ea1d2a658c7ca417bdfc891c98ef1a89fa4ff0c9828654", + "licenses/graphics/angle/30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652.txt": "30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652", + "licenses/graphics/angle/31346421254a3e6e12687cf17f19f6357ee73a617fa7b3d3ccefdcbabe49bdd3.txt": "31346421254a3e6e12687cf17f19f6357ee73a617fa7b3d3ccefdcbabe49bdd3", + "licenses/graphics/angle/3172d399cbd8f10609e73fec73d0e0b33eecd3c572a68b0722229d8c7059f725.txt": "3172d399cbd8f10609e73fec73d0e0b33eecd3c572a68b0722229d8c7059f725", + "licenses/graphics/angle/31b94860253d8ec7b4529f51901044d3b459d6292d996504a36b1bae3a36a812.txt": "31b94860253d8ec7b4529f51901044d3b459d6292d996504a36b1bae3a36a812", + "licenses/graphics/angle/3210be7332b5bdf48eb24a945258b9f38616a2cceb0dfc06e3c3c7e9740475a0.txt": "3210be7332b5bdf48eb24a945258b9f38616a2cceb0dfc06e3c3c7e9740475a0", + "licenses/graphics/angle/3234ac55816264ee7b6c7ee27efd61cf0a1fe775806870e3d9b4c41ea73c5cb1.txt": "3234ac55816264ee7b6c7ee27efd61cf0a1fe775806870e3d9b4c41ea73c5cb1", + "licenses/graphics/angle/328cb74a9f2c5b67be2f63da900f09363060feac3c01ee42e3f441c2cab1eec3.txt": "328cb74a9f2c5b67be2f63da900f09363060feac3c01ee42e3f441c2cab1eec3", + "licenses/graphics/angle/3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4.txt": "3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4", + "licenses/graphics/angle/336f505f8d5aa73ea40b4d798dde86953e9c1f6525757f1d7f18120fea09bb1d.txt": "336f505f8d5aa73ea40b4d798dde86953e9c1f6525757f1d7f18120fea09bb1d", + "licenses/graphics/angle/33be7b7e8fa4fd19b1760e1a8ed8a668bdab852c91b692dd41424bcb725a9fca.txt": "33be7b7e8fa4fd19b1760e1a8ed8a668bdab852c91b692dd41424bcb725a9fca", + "licenses/graphics/angle/348dfecdd95ac4de096f7495674c9e90c778f8795d3faf5cd880b0a25bcbdd15.txt": "348dfecdd95ac4de096f7495674c9e90c778f8795d3faf5cd880b0a25bcbdd15", + "licenses/graphics/angle/35242e7a83f69875e6edeff02291e688c97caafe2f8902e4e19b49d3e78b4cab.txt": "35242e7a83f69875e6edeff02291e688c97caafe2f8902e4e19b49d3e78b4cab", + "licenses/graphics/angle/378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397.txt": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/graphics/angle/3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7.txt": "3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7", + "licenses/graphics/angle/394faaedb93c1da8ecbd61322518834908fee64381117e01a611bf9fac20baa6.txt": "394faaedb93c1da8ecbd61322518834908fee64381117e01a611bf9fac20baa6", + "licenses/graphics/angle/398974c0415d06f88df08dc434cf58a0d0038afaf95d100799f42bae973ea945.txt": "398974c0415d06f88df08dc434cf58a0d0038afaf95d100799f42bae973ea945", + "licenses/graphics/angle/3a528aae8731663f7b2b02ee709b1ba1fd4f9bbd8935941b2a93981c5ab78bc4.txt": "3a528aae8731663f7b2b02ee709b1ba1fd4f9bbd8935941b2a93981c5ab78bc4", + "licenses/graphics/angle/3a7c3f0b887abb7c638faf022d29257418458614c6724b120ceeffb279f9c7d2.txt": "3a7c3f0b887abb7c638faf022d29257418458614c6724b120ceeffb279f9c7d2", + "licenses/graphics/angle/3b2f81fe21d181c499c59a256c8e1968455d6689d269aa85373bfb6af41da3bf.txt": "3b2f81fe21d181c499c59a256c8e1968455d6689d269aa85373bfb6af41da3bf", + "licenses/graphics/angle/3b38d48befd0af70b892e13d10c9e34679416c24a9277f962629951c64d71f4c.txt": "3b38d48befd0af70b892e13d10c9e34679416c24a9277f962629951c64d71f4c", + "licenses/graphics/angle/3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b.txt": "3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b", + "licenses/graphics/angle/3bc404ffa7888053253eedcc5a667619aa08dc1be0bea400e5ff28c51602180f.txt": "3bc404ffa7888053253eedcc5a667619aa08dc1be0bea400e5ff28c51602180f", + "licenses/graphics/angle/3cdb5f5be14a92dec127561fc90d8f7127aa59d81522938ffc91952615ea13eb.txt": "3cdb5f5be14a92dec127561fc90d8f7127aa59d81522938ffc91952615ea13eb", + "licenses/graphics/angle/3ddf9be5c28fe27dad143a5dc76eea25222ad1dd68934a047064e56ed2fa40c5.txt": "3ddf9be5c28fe27dad143a5dc76eea25222ad1dd68934a047064e56ed2fa40c5", + "licenses/graphics/angle/3e1f197b6b221b918470078665608303aeebb27c1f54cd8241873b35f3affa62.txt": "3e1f197b6b221b918470078665608303aeebb27c1f54cd8241873b35f3affa62", + "licenses/graphics/angle/3f9f0f7e5a5911a8042e32c83ff5d061ce1ffd02e8a207ec2135a44ad73b4191.txt": "3f9f0f7e5a5911a8042e32c83ff5d061ce1ffd02e8a207ec2135a44ad73b4191", + "licenses/graphics/angle/3fa4ca83dcc9237839b1bdeb2e6d16bdfb5ec0c5ce42b24694d8bbf0dcbef72c.txt": "3fa4ca83dcc9237839b1bdeb2e6d16bdfb5ec0c5ce42b24694d8bbf0dcbef72c", + "licenses/graphics/angle/400635d6ddaa1efc61cc38c6a737b8bbb975f4424f4727cd3983f53110eafe67.txt": "400635d6ddaa1efc61cc38c6a737b8bbb975f4424f4727cd3983f53110eafe67", + "licenses/graphics/angle/4108245a1f2df9d4e94df8abed5b4ba0759bb2f9b40a6b939f1be141077ae50b.txt": "4108245a1f2df9d4e94df8abed5b4ba0759bb2f9b40a6b939f1be141077ae50b", + "licenses/graphics/angle/4149f7427385d27e5915e68129cff9706f424353783a958919f99e80cb6fcc63.txt": "4149f7427385d27e5915e68129cff9706f424353783a958919f99e80cb6fcc63", + "licenses/graphics/angle/424336c2b3446b3c179f07217271bb914dc881a65c2bf7021da98c77e776d2c9.txt": "424336c2b3446b3c179f07217271bb914dc881a65c2bf7021da98c77e776d2c9", + "licenses/graphics/angle/42a35170233e83e18856792e748de4c1ce4a63b2afce9a370c89ef3fe23f9f2d.txt": "42a35170233e83e18856792e748de4c1ce4a63b2afce9a370c89ef3fe23f9f2d", + "licenses/graphics/angle/43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1.txt": "43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1", + "licenses/graphics/angle/436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d.txt": "436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d", + "licenses/graphics/angle/4375ba20e2b9c6c4e7cad2940a628fd90e95cc3d50ee92aae755715d8ba1fbd0.txt": "4375ba20e2b9c6c4e7cad2940a628fd90e95cc3d50ee92aae755715d8ba1fbd0", + "licenses/graphics/angle/43c0a37e6a0fa7ff3c843b3ec5a4fac84b712558ddac103fbd4c1649662a9ece.txt": "43c0a37e6a0fa7ff3c843b3ec5a4fac84b712558ddac103fbd4c1649662a9ece", + "licenses/graphics/angle/4455bf75a91154108304cb283e0fea9948c14f13e20d60887cf2552449dea3b1.txt": "4455bf75a91154108304cb283e0fea9948c14f13e20d60887cf2552449dea3b1", + "licenses/graphics/angle/453a712c58161b74efa998578aaf10fd7ad8204120730de38ca04d7f47f3ea46.txt": "453a712c58161b74efa998578aaf10fd7ad8204120730de38ca04d7f47f3ea46", + "licenses/graphics/angle/458502e12d97bbf64438606a20044aa85eb05fb0a8a807bb35dbec253fd1fc04.txt": "458502e12d97bbf64438606a20044aa85eb05fb0a8a807bb35dbec253fd1fc04", + "licenses/graphics/angle/45f522cacecb1023856e46df79ca625dfc550c94910078bd8aec6e02880b3d42.txt": "45f522cacecb1023856e46df79ca625dfc550c94910078bd8aec6e02880b3d42", + "licenses/graphics/angle/489a8e1108509ed98a37bb983e11e0f7e1d31f0bd8f99a79c8448e7ff37d07ea.txt": "489a8e1108509ed98a37bb983e11e0f7e1d31f0bd8f99a79c8448e7ff37d07ea", + "licenses/graphics/angle/492dedba85da5872f78e6091bcd1fea474d660d35acb4dee964b8aab3f007427.txt": "492dedba85da5872f78e6091bcd1fea474d660d35acb4dee964b8aab3f007427", + "licenses/graphics/angle/494accc32e50eb523a0e384d0ae6d4b702db867a89d6971216760e92b240ee12.txt": "494accc32e50eb523a0e384d0ae6d4b702db867a89d6971216760e92b240ee12", + "licenses/graphics/angle/4ab59682a096a2c31ab9ae42f2666dc6d5e72bac0071942a2e28d6d57d6732b6.txt": "4ab59682a096a2c31ab9ae42f2666dc6d5e72bac0071942a2e28d6d57d6732b6", + "licenses/graphics/angle/4af93c12062c58058378de2397dc1c92bbff9ddfb1d583a01c84127557ce97ca.txt": "4af93c12062c58058378de2397dc1c92bbff9ddfb1d583a01c84127557ce97ca", + "licenses/graphics/angle/4d022727ea392ebba2fe5e9a5f78c610a0eae506b491f3db29c78b6d430d6503.txt": "4d022727ea392ebba2fe5e9a5f78c610a0eae506b491f3db29c78b6d430d6503", + "licenses/graphics/angle/4e6bdc19db64d455dbddc0ee2f53ecba556f0226d4558954b5f02673c7358e59.txt": "4e6bdc19db64d455dbddc0ee2f53ecba556f0226d4558954b5f02673c7358e59", + "licenses/graphics/angle/5080149357fd0be590bdc10cf92165412bb4d61ce496284d56f2d12874ae3121.txt": "5080149357fd0be590bdc10cf92165412bb4d61ce496284d56f2d12874ae3121", + "licenses/graphics/angle/508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1.txt": "508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1", + "licenses/graphics/angle/50e6751797c50dedd75ef1b8a0d9e42f5f8472e9fbce91f34718e9f97b0c780a.txt": "50e6751797c50dedd75ef1b8a0d9e42f5f8472e9fbce91f34718e9f97b0c780a", + "licenses/graphics/angle/523a42c25d245dde9c015f882cec7f4555aad883382a6cf19b4b7d9b2cd5419b.txt": "523a42c25d245dde9c015f882cec7f4555aad883382a6cf19b4b7d9b2cd5419b", + "licenses/graphics/angle/52cb566b16d84314b92b91361ed072eaaf166e8d3dfa3d0fd3577613925f205c.txt": "52cb566b16d84314b92b91361ed072eaaf166e8d3dfa3d0fd3577613925f205c", + "licenses/graphics/angle/5318787a14e32720b1652f08a24408aaea67fdb5154ceda0f46a6069a0c5e5e3.txt": "5318787a14e32720b1652f08a24408aaea67fdb5154ceda0f46a6069a0c5e5e3", + "licenses/graphics/angle/5359da685feee46d7e22acc5b8fcc496c5ca176fc46986eb640aa21aaedfaf1d.txt": "5359da685feee46d7e22acc5b8fcc496c5ca176fc46986eb640aa21aaedfaf1d", + "licenses/graphics/angle/539dd7aed86e8a4f12cbdd0e6c50c189c7d74847e4fecc64ce2c6ee3a01da38b.txt": "539dd7aed86e8a4f12cbdd0e6c50c189c7d74847e4fecc64ce2c6ee3a01da38b", + "licenses/graphics/angle/54cbc326a78b9400065bfc5830a57fdcdaf808286d4ac35d8a9e324aa77b7241.txt": "54cbc326a78b9400065bfc5830a57fdcdaf808286d4ac35d8a9e324aa77b7241", + "licenses/graphics/angle/553fffcd9b1cb158bc3e9edc35da85ca5c3b3d7d2e61c883ebcfa8a65814b583.txt": "553fffcd9b1cb158bc3e9edc35da85ca5c3b3d7d2e61c883ebcfa8a65814b583", + "licenses/graphics/angle/559229b4b693d80fe087d517f7c79d4857c965add18031512d0981efc28755f0.txt": "559229b4b693d80fe087d517f7c79d4857c965add18031512d0981efc28755f0", + "licenses/graphics/angle/55f703486573f73b00920a8d46fc551debc4d1fa35ff4c18784363b09b3bf780.txt": "55f703486573f73b00920a8d46fc551debc4d1fa35ff4c18784363b09b3bf780", + "licenses/graphics/angle/573e362dc50a6d9eb444cea38ef61587e16a0645cb8098ba13a2c42fdde72acd.txt": "573e362dc50a6d9eb444cea38ef61587e16a0645cb8098ba13a2c42fdde72acd", + "licenses/graphics/angle/5831ee149d3850b28df8ff02fb7bd07cecda81e85cc8435c20827d3922202d34.txt": "5831ee149d3850b28df8ff02fb7bd07cecda81e85cc8435c20827d3922202d34", + "licenses/graphics/angle/58545fed1565e42d687aecec6897d35c6d37ccb71479a137c0deb2203e125c79.txt": "58545fed1565e42d687aecec6897d35c6d37ccb71479a137c0deb2203e125c79", + "licenses/graphics/angle/589eec38f72df2be203711d3b8cbece9b908c5e7ff00bc3cab7f63bae9e366b4.txt": "589eec38f72df2be203711d3b8cbece9b908c5e7ff00bc3cab7f63bae9e366b4", + "licenses/graphics/angle/58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd.txt": "58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd", + "licenses/graphics/angle/5a57cb4db85e2a2dd88c290628908add57e3451449e0a9a71fdfb38776fd759d.txt": "5a57cb4db85e2a2dd88c290628908add57e3451449e0a9a71fdfb38776fd759d", + "licenses/graphics/angle/5e05b024f653a5ce199e77cbbbd42fb5553562ec714b819421ed0c3e552a75d7.txt": "5e05b024f653a5ce199e77cbbbd42fb5553562ec714b819421ed0c3e552a75d7", + "licenses/graphics/angle/602ef1d5d3db1b23ada0b61d4230ef336012de7bc3b773d565f2b27a2757f51d.txt": "602ef1d5d3db1b23ada0b61d4230ef336012de7bc3b773d565f2b27a2757f51d", + "licenses/graphics/angle/6040cda75d90b1738292a631d89934c411ef7ffd543c4d6a1b7edfc8edf29449.txt": "6040cda75d90b1738292a631d89934c411ef7ffd543c4d6a1b7edfc8edf29449", + "licenses/graphics/angle/6095e9ffa777dd22839f7801aa845b31c9ed07f3d6bf8a26dc5d2dec8ccc0ef3.txt": "6095e9ffa777dd22839f7801aa845b31c9ed07f3d6bf8a26dc5d2dec8ccc0ef3", + "licenses/graphics/angle/609ae74144cd07a61653f733a0e11abf241a10c7dff4acd07dacda9fbffae22e.txt": "609ae74144cd07a61653f733a0e11abf241a10c7dff4acd07dacda9fbffae22e", + "licenses/graphics/angle/61d383b05b87d78f94d2937e2580cce47226d17823c0430fbcad09596537efcf.txt": "61d383b05b87d78f94d2937e2580cce47226d17823c0430fbcad09596537efcf", + "licenses/graphics/angle/62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3.txt": "62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3", + "licenses/graphics/angle/62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a.txt": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/graphics/angle/62fbcd9ebefaa5dd4245b241d36655e83391bb196c873c6023e1296fbc447ab8.txt": "62fbcd9ebefaa5dd4245b241d36655e83391bb196c873c6023e1296fbc447ab8", + "licenses/graphics/angle/634300a669d49aeae65b12c6c48c924c51a4cdf3d1ff086dc3456dc8bcaa2104.txt": "634300a669d49aeae65b12c6c48c924c51a4cdf3d1ff086dc3456dc8bcaa2104", + "licenses/graphics/angle/639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666.txt": "639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666", + "licenses/graphics/angle/6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb.txt": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/graphics/angle/65d4ed698fb5cbcd1d44c78bc6a02c5bf1da00df5395d2d6ac43bdafe6bc20dc.txt": "65d4ed698fb5cbcd1d44c78bc6a02c5bf1da00df5395d2d6ac43bdafe6bc20dc", + "licenses/graphics/angle/66e3ee1fa7f909ad3c612d556f2a0cdabcd809ad6e66f3b0605015ac64841b70.txt": "66e3ee1fa7f909ad3c612d556f2a0cdabcd809ad6e66f3b0605015ac64841b70", + "licenses/graphics/angle/68834f116f8ff545f05d14753357b620748156d60ee36b26beab4cb3f317efe4.txt": "68834f116f8ff545f05d14753357b620748156d60ee36b26beab4cb3f317efe4", + "licenses/graphics/angle/696759d65dfe558ff7d9f031c76db19ec5c0767470fb67c4e8d990820d1e99c9.txt": "696759d65dfe558ff7d9f031c76db19ec5c0767470fb67c4e8d990820d1e99c9", + "licenses/graphics/angle/69760673abf91cfd0280ae73739a29c078f493804d9016a122b3b189b48ad6e6.txt": "69760673abf91cfd0280ae73739a29c078f493804d9016a122b3b189b48ad6e6", + "licenses/graphics/angle/6982f0cd109b04512cbb5f0e0f0ef82154f33a57d2127afe058ecc72039ab88c.txt": "6982f0cd109b04512cbb5f0e0f0ef82154f33a57d2127afe058ecc72039ab88c", + "licenses/graphics/angle/69fef7b0f322a65554156141f2bc6256ed0bb78cba7e49f0d8829d7ec4ee62cd.txt": "69fef7b0f322a65554156141f2bc6256ed0bb78cba7e49f0d8829d7ec4ee62cd", + "licenses/graphics/angle/6a585a9f466654abc8fc0829d56b1bc987e3a073d31faa03bba37d33640a23cd.txt": "6a585a9f466654abc8fc0829d56b1bc987e3a073d31faa03bba37d33640a23cd", + "licenses/graphics/angle/6a94bedb8b707ed97f6e310d0d015ab14e0683ffa0a612b02958581b9cc9fc0e.txt": "6a94bedb8b707ed97f6e310d0d015ab14e0683ffa0a612b02958581b9cc9fc0e", + "licenses/graphics/angle/6aa99913137a7f9b212e53e8768871fe178e4ee01d8da0b267dbcbee314c527a.txt": "6aa99913137a7f9b212e53e8768871fe178e4ee01d8da0b267dbcbee314c527a", + "licenses/graphics/angle/6afc9d58f919ab52f4806a895a37aefe4d263f8e52278e6a1e87c5d7ec82299c.txt": "6afc9d58f919ab52f4806a895a37aefe4d263f8e52278e6a1e87c5d7ec82299c", + "licenses/graphics/angle/6d0d41bfe170ac6c7dc248c9a63e254d0fb45a60d50a8257d0af92c6e249b887.txt": "6d0d41bfe170ac6c7dc248c9a63e254d0fb45a60d50a8257d0af92c6e249b887", + "licenses/graphics/angle/6d3a9431e65e69c73a8923e6517b889d17549b23db406b9ec027710d16af701f.txt": "6d3a9431e65e69c73a8923e6517b889d17549b23db406b9ec027710d16af701f", + "licenses/graphics/angle/6db6d36c8aae8c2f6ebec32965bab9b4769128caed09a55b8696afce4301838a.txt": "6db6d36c8aae8c2f6ebec32965bab9b4769128caed09a55b8696afce4301838a", + "licenses/graphics/angle/6dc0e068dcf3a5bc8e054205b85b7720e1d49265bbc64bf515d2cf79197df69a.txt": "6dc0e068dcf3a5bc8e054205b85b7720e1d49265bbc64bf515d2cf79197df69a", + "licenses/graphics/angle/6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51.txt": "6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51", + "licenses/graphics/angle/6e5e117324afd944dcf67f36cf329843bc1a92229a8cd9bb573d7a83130fea7d.txt": "6e5e117324afd944dcf67f36cf329843bc1a92229a8cd9bb573d7a83130fea7d", + "licenses/graphics/angle/6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6.txt": "6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6", + "licenses/graphics/angle/6f1193cb634718e65c3a537d6e25ebd614820ec0ef693cfc12248112638d64da.txt": "6f1193cb634718e65c3a537d6e25ebd614820ec0ef693cfc12248112638d64da", + "licenses/graphics/angle/6f4e2de03c87fde1f0d4481b5a6358f9d2ba1f4bf8ed331d8f2d2fc4579b4747.txt": "6f4e2de03c87fde1f0d4481b5a6358f9d2ba1f4bf8ed331d8f2d2fc4579b4747", + "licenses/graphics/angle/6ffedbc0f7878612d2b23589f1ff2ab15633e1df7963a5d9fc750ec5500c7e7a.txt": "6ffedbc0f7878612d2b23589f1ff2ab15633e1df7963a5d9fc750ec5500c7e7a", + "licenses/graphics/angle/70bb0e4c89f4e41a11950365d98a13e2e6ad6ee4aed80cd1ecffc93d98d44e8c.txt": "70bb0e4c89f4e41a11950365d98a13e2e6ad6ee4aed80cd1ecffc93d98d44e8c", + "licenses/graphics/angle/72e7fdaba087f43ae01cd304f4e654c78b9265906727efd75429b4c0d6bcf08c.txt": "72e7fdaba087f43ae01cd304f4e654c78b9265906727efd75429b4c0d6bcf08c", + "licenses/graphics/angle/7317e078e2d3b5d7ba5a6159e650945153262b44b76f6700f8e9edb261c5143e.txt": "7317e078e2d3b5d7ba5a6159e650945153262b44b76f6700f8e9edb261c5143e", + "licenses/graphics/angle/7365cc8878a1d7ce155a58c4ca09c3d7a6be413efa5334a80ea842912b669349.txt": "7365cc8878a1d7ce155a58c4ca09c3d7a6be413efa5334a80ea842912b669349", + "licenses/graphics/angle/737070ec67c0feed5e767af9c774d159c4132604812ac29736ee5e7a917c998d.txt": "737070ec67c0feed5e767af9c774d159c4132604812ac29736ee5e7a917c998d", + "licenses/graphics/angle/7436a7c46b6e4d969b41e1ce387885ae4ced25710662189ff2983665253729ac.txt": "7436a7c46b6e4d969b41e1ce387885ae4ced25710662189ff2983665253729ac", + "licenses/graphics/angle/74db5baf44a41b1000312c673544b3374e4198af5605c7f9080a402cec42cfa3.txt": "74db5baf44a41b1000312c673544b3374e4198af5605c7f9080a402cec42cfa3", + "licenses/graphics/angle/7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545.txt": "7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545", + "licenses/graphics/angle/76c45ece83a26117f86f4e349e7df118708e061e87225328fb478ce1e8b3eb86.txt": "76c45ece83a26117f86f4e349e7df118708e061e87225328fb478ce1e8b3eb86", + "licenses/graphics/angle/778a9c936b9fa24f3842b6071e3cc5c794d3f7cc6d6fddbf356b6f2202afb6a0.txt": "778a9c936b9fa24f3842b6071e3cc5c794d3f7cc6d6fddbf356b6f2202afb6a0", + "licenses/graphics/angle/799e9ca9d179295ef372f25d3769cdda7d25bb2668add6a6a1e22d1e4c678b8d.txt": "799e9ca9d179295ef372f25d3769cdda7d25bb2668add6a6a1e22d1e4c678b8d", + "licenses/graphics/angle/79d3f64f22269a86ce0e25a62f6a391f1e07e2735909bd8de710b3e4c51bf196.txt": "79d3f64f22269a86ce0e25a62f6a391f1e07e2735909bd8de710b3e4c51bf196", + "licenses/graphics/angle/7a3cb0e5055874e67db9aa2d5fe26de23204fa994ffbad198901ffe9c812a717.txt": "7a3cb0e5055874e67db9aa2d5fe26de23204fa994ffbad198901ffe9c812a717", + "licenses/graphics/angle/7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427.txt": "7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427", + "licenses/graphics/angle/7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0.txt": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0", + "licenses/graphics/angle/7c86aec715e38bf01c316a69de917c1245bf9945a5dc0329eecc774cdb4f26c2.txt": "7c86aec715e38bf01c316a69de917c1245bf9945a5dc0329eecc774cdb4f26c2", + "licenses/graphics/angle/7cde763ba32b3ec2a84eddd8beb0dcb895fd6436aeb18491ab9572a7eb8de996.txt": "7cde763ba32b3ec2a84eddd8beb0dcb895fd6436aeb18491ab9572a7eb8de996", + "licenses/graphics/angle/7dd496262c0ba3787f7eebf02663c50c305ff575a81f69208e1645738da3cffc.txt": "7dd496262c0ba3787f7eebf02663c50c305ff575a81f69208e1645738da3cffc", + "licenses/graphics/angle/7e24648e3d082d4f8026cdedb21d084d91ef13223bff6b76cb8bf19a744b7d28.txt": "7e24648e3d082d4f8026cdedb21d084d91ef13223bff6b76cb8bf19a744b7d28", + "licenses/graphics/angle/7ec9661a8afafab1eee3523d6f1a193eff76314a5ab10b4ce96aefd87621b0c3.txt": "7ec9661a8afafab1eee3523d6f1a193eff76314a5ab10b4ce96aefd87621b0c3", + "licenses/graphics/angle/7f865e72ab3644ea5887aa1f352aa435b36d139c35a964f47091e7dc02722e9a.txt": "7f865e72ab3644ea5887aa1f352aa435b36d139c35a964f47091e7dc02722e9a", + "licenses/graphics/angle/808e10c8a6ab8deb149ff9b3fb19f447a808094606d712a9ca57fead3552599d.txt": "808e10c8a6ab8deb149ff9b3fb19f447a808094606d712a9ca57fead3552599d", + "licenses/graphics/angle/80f13607677e9932bf08e5f0bc025f8d77bde813d62bf3d5465c709025710d3d.txt": "80f13607677e9932bf08e5f0bc025f8d77bde813d62bf3d5465c709025710d3d", + "licenses/graphics/angle/80f275e90d799911ed3830a7f242a2ef5a4ade2092fe0aa07bfb2d2cf2f2b95e.txt": "80f275e90d799911ed3830a7f242a2ef5a4ade2092fe0aa07bfb2d2cf2f2b95e", + "licenses/graphics/angle/8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90.txt": "8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90", + "licenses/graphics/angle/8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643.txt": "8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643", + "licenses/graphics/angle/81d0fc4498e695444090a0ba9a74398f8738cd1ae18c788734be93fec2dc3515.txt": "81d0fc4498e695444090a0ba9a74398f8738cd1ae18c788734be93fec2dc3515", + "licenses/graphics/angle/824db5eb5d83d8415d09f3b1ef0753ec7cfb2453b34eb767f4a200252fc299fb.txt": "824db5eb5d83d8415d09f3b1ef0753ec7cfb2453b34eb767f4a200252fc299fb", + "licenses/graphics/angle/837402bd25fad9b704265801ca3f92566a98157c1f9a7acd6f446299ba1c305a.txt": "837402bd25fad9b704265801ca3f92566a98157c1f9a7acd6f446299ba1c305a", + "licenses/graphics/angle/83c1763356e822adde0a2cae748d938a73fdc263849ccff6b27776dff213bd32.txt": "83c1763356e822adde0a2cae748d938a73fdc263849ccff6b27776dff213bd32", + "licenses/graphics/angle/8405932022a556380c2d8c272eff154a923feb197233f348ce5f7334fb0a5ede.txt": "8405932022a556380c2d8c272eff154a923feb197233f348ce5f7334fb0a5ede", + "licenses/graphics/angle/842d692fdbb8b4dd8e22461d5091e29c1c8725dd7618fcd5d59436c1c10f8804.txt": "842d692fdbb8b4dd8e22461d5091e29c1c8725dd7618fcd5d59436c1c10f8804", + "licenses/graphics/angle/845022e0c1db1abb41a6ba4cd3c4b674ec290f3359d9d3c78ae558d4c0ed9308.txt": "845022e0c1db1abb41a6ba4cd3c4b674ec290f3359d9d3c78ae558d4c0ed9308", + "licenses/graphics/angle/845efc77857d485d91fb3e0b884aaa929368c717ae8186b66fe1ed2495753243.txt": "845efc77857d485d91fb3e0b884aaa929368c717ae8186b66fe1ed2495753243", + "licenses/graphics/angle/84b34dd7608f7fb9b17bd588a6bf392bf7de504e2716f024a77d89f1b145a151.txt": "84b34dd7608f7fb9b17bd588a6bf392bf7de504e2716f024a77d89f1b145a151", + "licenses/graphics/angle/85a884980abd6032fc6b5439ba918ab16db9ac9051b5cf18db9c27c587df07c0.txt": "85a884980abd6032fc6b5439ba918ab16db9ac9051b5cf18db9c27c587df07c0", + "licenses/graphics/angle/861399f8c21c042b110517e76dc6b63a2b334276c8cf17412fc3c8908ca8dc17.txt": "861399f8c21c042b110517e76dc6b63a2b334276c8cf17412fc3c8908ca8dc17", + "licenses/graphics/angle/86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741.txt": "86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741", + "licenses/graphics/angle/86eeee87be2a43f3ff1f56496f451f69243926f025fedbb033666c304c4c161b.txt": "86eeee87be2a43f3ff1f56496f451f69243926f025fedbb033666c304c4c161b", + "licenses/graphics/angle/873a2f333fda393ec3464f4579209b019d98e97c3bf498b10e85f630162fd708.txt": "873a2f333fda393ec3464f4579209b019d98e97c3bf498b10e85f630162fd708", + "licenses/graphics/angle/8797ef61538ec5ee9222ebef7ca4e0f3ec5761b145ca9943d358c450efb644dd.txt": "8797ef61538ec5ee9222ebef7ca4e0f3ec5761b145ca9943d358c450efb644dd", + "licenses/graphics/angle/89480768826f408daea1f3caff0509c2cc9606e10f6bb0ccfd12a3d604842c35.txt": "89480768826f408daea1f3caff0509c2cc9606e10f6bb0ccfd12a3d604842c35", + "licenses/graphics/angle/8ada45cd9f843acf64e4722ae262c622a2b3b3007c7310ef36ac1061a30f6adb.txt": "8ada45cd9f843acf64e4722ae262c622a2b3b3007c7310ef36ac1061a30f6adb", + "licenses/graphics/angle/8bb850c565aa389fdc16f3a46965ad23d82adff60f2393fc2762b63185e8e6c9.txt": "8bb850c565aa389fdc16f3a46965ad23d82adff60f2393fc2762b63185e8e6c9", + "licenses/graphics/angle/8bc20184c0ddf3006df05e89fdf7193b33dbe4c751ae59d6bb1835f71bbe70da.txt": "8bc20184c0ddf3006df05e89fdf7193b33dbe4c751ae59d6bb1835f71bbe70da", + "licenses/graphics/angle/8bc55eba9da5911fd65d6b9dddfd56d94e30ff7fc2a9a30a5782bfc47cdf9c35.txt": "8bc55eba9da5911fd65d6b9dddfd56d94e30ff7fc2a9a30a5782bfc47cdf9c35", + "licenses/graphics/angle/8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42.txt": "8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42", + "licenses/graphics/angle/8c6db340475136df3c1201d458fa5755698eace76e510471ecc9d857d6083dac.txt": "8c6db340475136df3c1201d458fa5755698eace76e510471ecc9d857d6083dac", + "licenses/graphics/angle/8c7516d4b27b1e495be5e38b612298b63de48d05f49cdac94f70f3cd70f8864b.txt": "8c7516d4b27b1e495be5e38b612298b63de48d05f49cdac94f70f3cd70f8864b", + "licenses/graphics/angle/8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903.txt": "8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903", + "licenses/graphics/angle/8d07f0c9c9966be0aaec4196d7863b56ade114e9714dbc31ebba576c0446d2fc.txt": "8d07f0c9c9966be0aaec4196d7863b56ade114e9714dbc31ebba576c0446d2fc", + "licenses/graphics/angle/8dabd9a3478fabeb5ecf0ee2624ed7b41a8c346fdcb3d24a9fc4098a30ba4f54.txt": "8dabd9a3478fabeb5ecf0ee2624ed7b41a8c346fdcb3d24a9fc4098a30ba4f54", + "licenses/graphics/angle/8e95cc3fc83600845b44bd2f763d8edc48cfffe0feb3abd59d30810aef1119c7.txt": "8e95cc3fc83600845b44bd2f763d8edc48cfffe0feb3abd59d30810aef1119c7", + "licenses/graphics/angle/8ebde739ff734d4ed18082965e83dbab9673a37199d2af9cfc3fb390398b35b8.txt": "8ebde739ff734d4ed18082965e83dbab9673a37199d2af9cfc3fb390398b35b8", + "licenses/graphics/angle/8f1bd8841582bdee098eeae9eeb3862d9e7af011e94e54c14aef0568e816be19.txt": "8f1bd8841582bdee098eeae9eeb3862d9e7af011e94e54c14aef0568e816be19", + "licenses/graphics/angle/8f5442dfa8e9169045697e386bc91d19f393c939635741fa2a665ec36ca6f0ad.txt": "8f5442dfa8e9169045697e386bc91d19f393c939635741fa2a665ec36ca6f0ad", + "licenses/graphics/angle/90981a279fd882ae1966d063e002115842fe607bfe3a119c9a12b056ceed3db2.txt": "90981a279fd882ae1966d063e002115842fe607bfe3a119c9a12b056ceed3db2", + "licenses/graphics/angle/90bf2d659c43045111b65c733ab2a6d4cbcb422a098368c8c58a9ba3db4ed0c5.txt": "90bf2d659c43045111b65c733ab2a6d4cbcb422a098368c8c58a9ba3db4ed0c5", + "licenses/graphics/angle/90d7e062634054e6866d3c81e6a2b3058a840e6af733e98e80bdfe1a7dec6912.txt": "90d7e062634054e6866d3c81e6a2b3058a840e6af733e98e80bdfe1a7dec6912", + "licenses/graphics/angle/90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5.txt": "90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5", + "licenses/graphics/angle/9293c072b4854fa961b21291637532cf5ba97c6eeab48241aa60c873c711773c.txt": "9293c072b4854fa961b21291637532cf5ba97c6eeab48241aa60c873c711773c", + "licenses/graphics/angle/946c9835d8034d24404f8cfec5f4654cee5dad17e944afc3d06d742cf2882831.txt": "946c9835d8034d24404f8cfec5f4654cee5dad17e944afc3d06d742cf2882831", + "licenses/graphics/angle/95ad366d23fadf701d355bc45fb8b82ae2d700239471d35d41286ac3b08ff903.txt": "95ad366d23fadf701d355bc45fb8b82ae2d700239471d35d41286ac3b08ff903", + "licenses/graphics/angle/96f5b328adbb78eeaaec6980d73fd558cb1e4d62560ed615646bc3cf5e532430.txt": "96f5b328adbb78eeaaec6980d73fd558cb1e4d62560ed615646bc3cf5e532430", + "licenses/graphics/angle/9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138.txt": "9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138", + "licenses/graphics/angle/9755a18519666e5f0f4cae3daad3d7012bcae48a600b31237d75e9fe134e6683.txt": "9755a18519666e5f0f4cae3daad3d7012bcae48a600b31237d75e9fe134e6683", + "licenses/graphics/angle/984fb04a16a9f1e0145ffd891125dc366a01cd921f58c9b0369be400c720790d.txt": "984fb04a16a9f1e0145ffd891125dc366a01cd921f58c9b0369be400c720790d", + "licenses/graphics/angle/9a682a56cffc9524dfa9b0b1c0dca9cb81a19e96d5bd0793aaf02c08a95ee7ca.txt": "9a682a56cffc9524dfa9b0b1c0dca9cb81a19e96d5bd0793aaf02c08a95ee7ca", + "licenses/graphics/angle/9a8ad106a394e853bfe21f42f4e72d592819a22805d991b5f3275029292b658d.txt": "9a8ad106a394e853bfe21f42f4e72d592819a22805d991b5f3275029292b658d", + "licenses/graphics/angle/9c9a05118ed1b6d96781a2e52335f7d4ec3dd6e7139340a8aa95fbf7eb4f199a.txt": "9c9a05118ed1b6d96781a2e52335f7d4ec3dd6e7139340a8aa95fbf7eb4f199a", + "licenses/graphics/angle/9d185ac6703c4b0453974c0d85e9eee43e6941009296bb1f5eb0b54e2329e9f3.txt": "9d185ac6703c4b0453974c0d85e9eee43e6941009296bb1f5eb0b54e2329e9f3", + "licenses/graphics/angle/9df9ba60a11af705f2e451b53762686e615d86f76b169cf075c3237730dbd7e2.txt": "9df9ba60a11af705f2e451b53762686e615d86f76b169cf075c3237730dbd7e2", + "licenses/graphics/angle/9ebf8c4cc0b735ca13a766451f7b8097db3185975ceb2ba94b5abf439156a91f.txt": "9ebf8c4cc0b735ca13a766451f7b8097db3185975ceb2ba94b5abf439156a91f", + "licenses/graphics/angle/9ed5e982274d54d0cf94f0e9f9fd889182b6f1f50a012f0be41ce7c884347ab6.txt": "9ed5e982274d54d0cf94f0e9f9fd889182b6f1f50a012f0be41ce7c884347ab6", + "licenses/graphics/angle/9ff34fe87a89242afd776a07dbe055151121fead277acd30fee56e42b41cab93.txt": "9ff34fe87a89242afd776a07dbe055151121fead277acd30fee56e42b41cab93", + "licenses/graphics/angle/a012d664e4e01df52a65b2eeafdfb8aeb856fec0e6c372265d01b0109c3f5e2a.txt": "a012d664e4e01df52a65b2eeafdfb8aeb856fec0e6c372265d01b0109c3f5e2a", + "licenses/graphics/angle/a04665b3b2de56c66730c1f720f528175739e4104f79073614aa611da1e85539.txt": "a04665b3b2de56c66730c1f720f528175739e4104f79073614aa611da1e85539", + "licenses/graphics/angle/a078a8f80016416042c2e5f04dbb7f499f0f6deebb086511f3a3b72633a2d761.txt": "a078a8f80016416042c2e5f04dbb7f499f0f6deebb086511f3a3b72633a2d761", + "licenses/graphics/angle/a140e5d46fe734a1c78f1a3c3ef207871dd75648be71fdda8e309b23ab8b1f32.txt": "a140e5d46fe734a1c78f1a3c3ef207871dd75648be71fdda8e309b23ab8b1f32", + "licenses/graphics/angle/a21e92ec88aefc6823f0c51994783571a849e8d9b43d9ade61ec9d886776721e.txt": "a21e92ec88aefc6823f0c51994783571a849e8d9b43d9ade61ec9d886776721e", + "licenses/graphics/angle/a47ea51236098464fe0b4f559743590b533056d9e00f49ecbf80299fab47e231.txt": "a47ea51236098464fe0b4f559743590b533056d9e00f49ecbf80299fab47e231", + "licenses/graphics/angle/a59f0b0ef3635874109a4461ca44ff7a70d50696e814767bfaf721d4c9b0db0f.txt": "a59f0b0ef3635874109a4461ca44ff7a70d50696e814767bfaf721d4c9b0db0f", + "licenses/graphics/angle/a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2.txt": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/graphics/angle/a6c1acff7e7b7918ae5122700fe2da1e127dd459cc5b04271ff8f62d6a6f9e17.txt": "a6c1acff7e7b7918ae5122700fe2da1e127dd459cc5b04271ff8f62d6a6f9e17", + "licenses/graphics/angle/a7c936ff1ed8fa340172d42a98185afee078f818e907da69f3a8e336b1623b4b.txt": "a7c936ff1ed8fa340172d42a98185afee078f818e907da69f3a8e336b1623b4b", + "licenses/graphics/angle/a8ad31b1c3f40dca5a84119351b8fa8ddc868edd77fad8a8ebf6d8f2d16fa4ae.txt": "a8ad31b1c3f40dca5a84119351b8fa8ddc868edd77fad8a8ebf6d8f2d16fa4ae", + "licenses/graphics/angle/a90a4f374e17d62b1725bd687eca71ae7110ded0c2fe3e2f06c9ba7176169b5c.txt": "a90a4f374e17d62b1725bd687eca71ae7110ded0c2fe3e2f06c9ba7176169b5c", + "licenses/graphics/angle/a9d66f1d526df02e29dce73436d34e56e8632f46c275bbdffc70569e882f9f17.txt": "a9d66f1d526df02e29dce73436d34e56e8632f46c275bbdffc70569e882f9f17", + "licenses/graphics/angle/aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf.txt": "aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf", + "licenses/graphics/angle/ab15fd526bd8dd18a9e77ebc139656bf4d33e97fc7238cd11bf60e2b9b8666c6.txt": "ab15fd526bd8dd18a9e77ebc139656bf4d33e97fc7238cd11bf60e2b9b8666c6", + "licenses/graphics/angle/ab6eec6caf0fa5775e411c7a8bc6a45c4ef2956b0980b157ab74fc5cd62a928b.txt": "ab6eec6caf0fa5775e411c7a8bc6a45c4ef2956b0980b157ab74fc5cd62a928b", + "licenses/graphics/angle/ac1e6e437cd571f6b450abd33dc055cf26dfb0fe2a72952c3ef6ae3844549c12.txt": "ac1e6e437cd571f6b450abd33dc055cf26dfb0fe2a72952c3ef6ae3844549c12", + "licenses/graphics/angle/ac7e05bd11cc1cfc3f9452c1b9986a9b1d54e180fa88e44e69caf955f95dc8a6.txt": "ac7e05bd11cc1cfc3f9452c1b9986a9b1d54e180fa88e44e69caf955f95dc8a6", + "licenses/graphics/angle/ade78d04982d69972d444a8e14a94f87a2334dd3855cc80348ea8e240aa0df2d.txt": "ade78d04982d69972d444a8e14a94f87a2334dd3855cc80348ea8e240aa0df2d", + "licenses/graphics/angle/ae0f1f791e3b4faccf981c0b530199235a8b8a021c7ef0c3c85c6c676ea4d27f.txt": "ae0f1f791e3b4faccf981c0b530199235a8b8a021c7ef0c3c85c6c676ea4d27f", + "licenses/graphics/angle/afa48e5e64dc610298d80b010ae7a3450f61a79500a9f1d1697ff6dcbbfa1f72.txt": "afa48e5e64dc610298d80b010ae7a3450f61a79500a9f1d1697ff6dcbbfa1f72", + "licenses/graphics/angle/b1181a40b2a7b25cf66fd01481713bc1005df082c53ef73e851e55071b102744.txt": "b1181a40b2a7b25cf66fd01481713bc1005df082c53ef73e851e55071b102744", + "licenses/graphics/angle/b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1.txt": "b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1", + "licenses/graphics/angle/b47ca1ea743623d50c6e02faa136dea4f6574a98e200666f859dbf57fb491721.txt": "b47ca1ea743623d50c6e02faa136dea4f6574a98e200666f859dbf57fb491721", + "licenses/graphics/angle/b5efebcaca80879234098e52d1725e6d9eb8fb96a19fce625d39184b705f7b6d.txt": "b5efebcaca80879234098e52d1725e6d9eb8fb96a19fce625d39184b705f7b6d", + "licenses/graphics/angle/b6a03c1803eb58ffb1f1278d5c7d4096c4c116e66dce8a7553e8c77d163c3438.txt": "b6a03c1803eb58ffb1f1278d5c7d4096c4c116e66dce8a7553e8c77d163c3438", + "licenses/graphics/angle/b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5.txt": "b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5", + "licenses/graphics/angle/b7804b570c31c8491352bd4e0b123a9652edb72d778554986ec51f22e6c2b70b.txt": "b7804b570c31c8491352bd4e0b123a9652edb72d778554986ec51f22e6c2b70b", + "licenses/graphics/angle/b7a336abf3b04e180ec065cdd16e705d079e1cc7a14f910aa6e9187f36b9cd87.txt": "b7a336abf3b04e180ec065cdd16e705d079e1cc7a14f910aa6e9187f36b9cd87", + "licenses/graphics/angle/b7e650f3fce5c53249d1cdc608b54df156a97edd636cf9d23498d0cfe7aec63e.txt": "b7e650f3fce5c53249d1cdc608b54df156a97edd636cf9d23498d0cfe7aec63e", + "licenses/graphics/angle/b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe.txt": "b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe", + "licenses/graphics/angle/b84efe109a420fa3ca98be33f4227327af7ffa426195812c270feb1268bc2426.txt": "b84efe109a420fa3ca98be33f4227327af7ffa426195812c270feb1268bc2426", + "licenses/graphics/angle/b8c6939380a400f53e11923d50fcc4dd2fa1ba8339fd9d04cda38a0251b6c9b0.txt": "b8c6939380a400f53e11923d50fcc4dd2fa1ba8339fd9d04cda38a0251b6c9b0", + "licenses/graphics/angle/bccaa8b6c09f94e81f06696e179dbe058464bbdfbc823b6d49cada1d71e84ac3.txt": "bccaa8b6c09f94e81f06696e179dbe058464bbdfbc823b6d49cada1d71e84ac3", + "licenses/graphics/angle/bf4da21bd20bcfb5b60b7ecc67fa864a79be049e21d6178076887f178dd6c71a.txt": "bf4da21bd20bcfb5b60b7ecc67fa864a79be049e21d6178076887f178dd6c71a", + "licenses/graphics/angle/bfec18debedcb337f8af53f143ccf0b1575d0b7c30deaee137f10397eca0d353.txt": "bfec18debedcb337f8af53f143ccf0b1575d0b7c30deaee137f10397eca0d353", + "licenses/graphics/angle/c15544050f84cf503e47d60299a7c119e751f1d81ac617a8a13e706581cc05bc.txt": "c15544050f84cf503e47d60299a7c119e751f1d81ac617a8a13e706581cc05bc", + "licenses/graphics/angle/c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b.txt": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "licenses/graphics/angle/c1b900aa1f61291ccd0160a351f40d217bc0080cd057aaf320257a589fd7d220.txt": "c1b900aa1f61291ccd0160a351f40d217bc0080cd057aaf320257a589fd7d220", + "licenses/graphics/angle/c1e018d60dd011b335b5280b919bd3a75dbba81c6fbe24e2fc90cb235bdb6883.txt": "c1e018d60dd011b335b5280b919bd3a75dbba81c6fbe24e2fc90cb235bdb6883", + "licenses/graphics/angle/c203ade846c159e17bb36214eef81b55866645a3ece3cf4f10d9fcff110e444a.txt": "c203ade846c159e17bb36214eef81b55866645a3ece3cf4f10d9fcff110e444a", + "licenses/graphics/angle/c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383.txt": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "licenses/graphics/angle/c30152c94a6d75e021adbc52b3a52470366a46edb917e17deae3259251af244c.txt": "c30152c94a6d75e021adbc52b3a52470366a46edb917e17deae3259251af244c", + "licenses/graphics/angle/c3710b8fc15eee9d2de041c0302116dc30fcb370ae5cc3969e746d8f08b869fd.txt": "c3710b8fc15eee9d2de041c0302116dc30fcb370ae5cc3969e746d8f08b869fd", + "licenses/graphics/angle/c43b9a9b1387ed53d2c49263838261129a010e280e3a174a792242c3e2c98db9.txt": "c43b9a9b1387ed53d2c49263838261129a010e280e3a174a792242c3e2c98db9", + "licenses/graphics/angle/c55ce1e876843853a8a2e5c936df6dc8dd3d185f83d85e6d113143b8c24f542e.txt": "c55ce1e876843853a8a2e5c936df6dc8dd3d185f83d85e6d113143b8c24f542e", + "licenses/graphics/angle/c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08.txt": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "licenses/graphics/angle/c70d07bf7a3d935e05c62e80bc0fd30292d7182cd9ab695f7c7ddd7bcac39256.txt": "c70d07bf7a3d935e05c62e80bc0fd30292d7182cd9ab695f7c7ddd7bcac39256", + "licenses/graphics/angle/c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4.txt": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/graphics/angle/c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30.txt": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/graphics/angle/c77a4cf9da729987d0fe7ccd811e3bd27393914ddf3d23467c18cc22954513b3.txt": "c77a4cf9da729987d0fe7ccd811e3bd27393914ddf3d23467c18cc22954513b3", + "licenses/graphics/angle/c79a7fea0e3cac04cd43f20e7b648e5a0ff8fa5344e644b0ee09ca1162b62747.txt": "c79a7fea0e3cac04cd43f20e7b648e5a0ff8fa5344e644b0ee09ca1162b62747", + "licenses/graphics/angle/c8d9a0d15dd76ca3bf277b6bf6da56799e266eac60bdc321a97ebc6d76d5153c.txt": "c8d9a0d15dd76ca3bf277b6bf6da56799e266eac60bdc321a97ebc6d76d5153c", + "licenses/graphics/angle/c962ee4d1d05ddc138b202b2540219ebc57893fcf97b364852094a9a94ce1365.txt": "c962ee4d1d05ddc138b202b2540219ebc57893fcf97b364852094a9a94ce1365", + "licenses/graphics/angle/c98a2858469bd3b231c8865c5b65f80f6ffbf25e850d5d575967e3d9ee080755.txt": "c98a2858469bd3b231c8865c5b65f80f6ffbf25e850d5d575967e3d9ee080755", + "licenses/graphics/angle/c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5.txt": "c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5", + "licenses/graphics/angle/c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566.txt": "c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566", + "licenses/graphics/angle/ca382aa537f8923d6c0991fb976d184a2009eb76080313bf10dcecdc9311f0dd.txt": "ca382aa537f8923d6c0991fb976d184a2009eb76080313bf10dcecdc9311f0dd", + "licenses/graphics/angle/ca7227ddb9eed6cc809e157f67b020e78dde063240001d11856f85c49cb6e423.txt": "ca7227ddb9eed6cc809e157f67b020e78dde063240001d11856f85c49cb6e423", + "licenses/graphics/angle/cac35c02686e5d04a5a7140bfb3b36e73aed496656e891102e428886d7930318.txt": "cac35c02686e5d04a5a7140bfb3b36e73aed496656e891102e428886d7930318", + "licenses/graphics/angle/cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48.txt": "cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48", + "licenses/graphics/angle/cae8c00ca6e90a682c321ec11e7a5a345d0d317aa0b8f038e03ef03a18095b2f.txt": "cae8c00ca6e90a682c321ec11e7a5a345d0d317aa0b8f038e03ef03a18095b2f", + "licenses/graphics/angle/cb3c929a05e6cbc9de9ab06a4c57eeb60ca8c724bef6c138c87d3a577e27aa14.txt": "cb3c929a05e6cbc9de9ab06a4c57eeb60ca8c724bef6c138c87d3a577e27aa14", + "licenses/graphics/angle/cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14.txt": "cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14", + "licenses/graphics/angle/cb7ac9e8ff6f939378b777feb2615598c16380b69f604845799e462f29ab6e90.txt": "cb7ac9e8ff6f939378b777feb2615598c16380b69f604845799e462f29ab6e90", + "licenses/graphics/angle/cbc759b1f17a2ac38fe3eb9e9563b1a08ba0f900611c49faaf68b46907b6d898.txt": "cbc759b1f17a2ac38fe3eb9e9563b1a08ba0f900611c49faaf68b46907b6d898", + "licenses/graphics/angle/cdb520614db3ec62e667ece01e64e6afa21948fa51e85e748b1494597a7be907.txt": "cdb520614db3ec62e667ece01e64e6afa21948fa51e85e748b1494597a7be907", + "licenses/graphics/angle/cec0db5f6d7ed6b3a72647bd50aed02e13c3377fd44382b96dc2915534c042ad.txt": "cec0db5f6d7ed6b3a72647bd50aed02e13c3377fd44382b96dc2915534c042ad", + "licenses/graphics/angle/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/graphics/angle/d0a435e5f6a4943a2c3927c3932e7baee9c2231caa977ffeba748f3712ae437e.txt": "d0a435e5f6a4943a2c3927c3932e7baee9c2231caa977ffeba748f3712ae437e", + "licenses/graphics/angle/d136abc3388ab9b16879d6dcb9c8c4a5d70ddf821bab80b6454843abe358d34a.txt": "d136abc3388ab9b16879d6dcb9c8c4a5d70ddf821bab80b6454843abe358d34a", + "licenses/graphics/angle/d1fc1bc0d155df60b2e7705b6b2ae02a05c96f948e1cec6e2fb86360b09f346b.txt": "d1fc1bc0d155df60b2e7705b6b2ae02a05c96f948e1cec6e2fb86360b09f346b", + "licenses/graphics/angle/d201d14804d3bcd3b944147173175e4abfbd838b7c8069b6bd3452496bf13e6c.txt": "d201d14804d3bcd3b944147173175e4abfbd838b7c8069b6bd3452496bf13e6c", + "licenses/graphics/angle/d24a2b82b5d96fd64c84cdae1b1f6250d76c16f0660a593d4ac8127177054b5f.txt": "d24a2b82b5d96fd64c84cdae1b1f6250d76c16f0660a593d4ac8127177054b5f", + "licenses/graphics/angle/d2de2f566d2d0e0b509fb0ea1fa3669f49064ab1de21c57453cab3750a234e8f.txt": "d2de2f566d2d0e0b509fb0ea1fa3669f49064ab1de21c57453cab3750a234e8f", + "licenses/graphics/angle/d30047bca3b516639339a3c279bb84c3483124fb5a9dafe3c75056a85090e745.txt": "d30047bca3b516639339a3c279bb84c3483124fb5a9dafe3c75056a85090e745", + "licenses/graphics/angle/d39a21ed70fb553856f6d7e74fee4332261069502ae32ab9ac13b49d147696f7.txt": "d39a21ed70fb553856f6d7e74fee4332261069502ae32ab9ac13b49d147696f7", + "licenses/graphics/angle/d3a1ecfb2804d0b4da300870c7c2914fd4edbd6b1b5fe4f7eb7b5d7db766b19b.txt": "d3a1ecfb2804d0b4da300870c7c2914fd4edbd6b1b5fe4f7eb7b5d7db766b19b", + "licenses/graphics/angle/d5334c1ff1f71deabc8eec66ee26105d6919718b73f2eb300d641db43ee722d6.txt": "d5334c1ff1f71deabc8eec66ee26105d6919718b73f2eb300d641db43ee722d6", + "licenses/graphics/angle/d6a43f0bae029b0cea5bd0fffd87f05659dc599a763886027614ad210be1ba3d.txt": "d6a43f0bae029b0cea5bd0fffd87f05659dc599a763886027614ad210be1ba3d", + "licenses/graphics/angle/d6cb0e9e560f51085556949a84af12b79a00f10ab8b66c752537faf7cd665572.txt": "d6cb0e9e560f51085556949a84af12b79a00f10ab8b66c752537faf7cd665572", + "licenses/graphics/angle/d8b56cd45661bfc7ccf4ce5722388cab275f9dabb9e471c922cc80e36bbf9caa.txt": "d8b56cd45661bfc7ccf4ce5722388cab275f9dabb9e471c922cc80e36bbf9caa", + "licenses/graphics/angle/da28ccc6b158fc2d8cccc74e99794b1cff1d29bd7bbeb019442fcf0c04c6cad9.txt": "da28ccc6b158fc2d8cccc74e99794b1cff1d29bd7bbeb019442fcf0c04c6cad9", + "licenses/graphics/angle/db3010170b904cb7212ef6abd2336f316bf735060eeeca23f1a737f459cc73e4.txt": "db3010170b904cb7212ef6abd2336f316bf735060eeeca23f1a737f459cc73e4", + "licenses/graphics/angle/dc626520dcd53a22f727af3ee42c770e56c97a64fe3adb063799d8ab032fe551.txt": "dc626520dcd53a22f727af3ee42c770e56c97a64fe3adb063799d8ab032fe551", + "licenses/graphics/angle/ddcbb6914b62d5bebc3cb58ebe8d2738ffa9a48555469bbcbe65159979b878cf.txt": "ddcbb6914b62d5bebc3cb58ebe8d2738ffa9a48555469bbcbe65159979b878cf", + "licenses/graphics/angle/deec98192f710d6e6aa8aba33f67087199e62c2db7f9d793b0ad465248ca3d05.txt": "deec98192f710d6e6aa8aba33f67087199e62c2db7f9d793b0ad465248ca3d05", + "licenses/graphics/angle/deed7c17a4318158190a3ea239cc879a5a50271cebb98ae7025f48fbe58dca15.txt": "deed7c17a4318158190a3ea239cc879a5a50271cebb98ae7025f48fbe58dca15", + "licenses/graphics/angle/e09d954054165670b6a669e6da59673d9e85f343b9983e92a220623ff0198f8c.txt": "e09d954054165670b6a669e6da59673d9e85f343b9983e92a220623ff0198f8c", + "licenses/graphics/angle/e0cfa1006a64520633de6bfbf563f5b1bea04ef0c5b73f049681931fa297dda3.txt": "e0cfa1006a64520633de6bfbf563f5b1bea04ef0c5b73f049681931fa297dda3", + "licenses/graphics/angle/e21ff4f2af8698b4e8f44d333bf2c8b59523488357ce26513afc7404092c1884.txt": "e21ff4f2af8698b4e8f44d333bf2c8b59523488357ce26513afc7404092c1884", + "licenses/graphics/angle/e27fb2953c088c71285a4f2f54a0ac53323460ee7c2b1b838d563bd2687a38af.txt": "e27fb2953c088c71285a4f2f54a0ac53323460ee7c2b1b838d563bd2687a38af", + "licenses/graphics/angle/e2b35be49f7284a45b7baca8fc7b3ab7440e7902392b2528a457816b5bb2a15c.txt": "e2b35be49f7284a45b7baca8fc7b3ab7440e7902392b2528a457816b5bb2a15c", + "licenses/graphics/angle/e3248f259a211f4d9ed06cfd07bc64373376c92a192152e37ec3420d6036dd4e.txt": "e3248f259a211f4d9ed06cfd07bc64373376c92a192152e37ec3420d6036dd4e", + "licenses/graphics/angle/e32ff4e00d9d94930537635291da39e7e612703334bf6fde8c7f1686fe8a45a2.txt": "e32ff4e00d9d94930537635291da39e7e612703334bf6fde8c7f1686fe8a45a2", + "licenses/graphics/angle/e3aefad6cbfecc174ce6a7628e8f2fb58d1c2928d9d4f9d531d125177ab23324.txt": "e3aefad6cbfecc174ce6a7628e8f2fb58d1c2928d9d4f9d531d125177ab23324", + "licenses/graphics/angle/e3ba223bb1423f0aad8c3dfce0fe3148db48926d41e6fbc3afbbf5ff9e1c89cb.txt": "e3ba223bb1423f0aad8c3dfce0fe3148db48926d41e6fbc3afbbf5ff9e1c89cb", + "licenses/graphics/angle/e57011537d230b14e790f6666dc00816f7b371ebbd7da8a12491e51086fec278.txt": "e57011537d230b14e790f6666dc00816f7b371ebbd7da8a12491e51086fec278", + "licenses/graphics/angle/e8b80a53d0f95a3cf0f992f8cfc6b3911a7f32f47e0e4a8d4fd66582eeae9484.txt": "e8b80a53d0f95a3cf0f992f8cfc6b3911a7f32f47e0e4a8d4fd66582eeae9484", + "licenses/graphics/angle/e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7.txt": "e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7", + "licenses/graphics/angle/e99d88d232bf57d70f0fb87f6b496d44b6653f99f8a63d250a54c61ea4bcde40.txt": "e99d88d232bf57d70f0fb87f6b496d44b6653f99f8a63d250a54c61ea4bcde40", + "licenses/graphics/angle/ea084a2373ebc1f0902c09266e7bf25a05ab3814c1805bb017ffa7308f90c061.txt": "ea084a2373ebc1f0902c09266e7bf25a05ab3814c1805bb017ffa7308f90c061", + "licenses/graphics/angle/ea43b1de38a6f90c488800d66dec1ed671e68cda530266bc96951fb5b6307613.txt": "ea43b1de38a6f90c488800d66dec1ed671e68cda530266bc96951fb5b6307613", + "licenses/graphics/angle/eaf40297c75da471f7cda1f3458e8d91b4b2ec866e609527a13acfa93b638652.txt": "eaf40297c75da471f7cda1f3458e8d91b4b2ec866e609527a13acfa93b638652", + "licenses/graphics/angle/eb07d497d26e6d68fbc76e793f5e5c9cfa197df2a580e47383569c287a55edf9.txt": "eb07d497d26e6d68fbc76e793f5e5c9cfa197df2a580e47383569c287a55edf9", + "licenses/graphics/angle/eb31a0c5a4fb09b8a4e32055d25c1e5f9c358a2752fef3cd720213d1ccfee241.txt": "eb31a0c5a4fb09b8a4e32055d25c1e5f9c358a2752fef3cd720213d1ccfee241", + "licenses/graphics/angle/eb7e9ab9690124c5c9f42bdc81383d886a3dede26345b6ed15bbad7caf81f7ea.txt": "eb7e9ab9690124c5c9f42bdc81383d886a3dede26345b6ed15bbad7caf81f7ea", + "licenses/graphics/angle/eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0.txt": "eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0", + "licenses/graphics/angle/ebcd9bbf783a73d05c53ba4d586b8d5813dcdf3bbec50265860ccc885e606f47.txt": "ebcd9bbf783a73d05c53ba4d586b8d5813dcdf3bbec50265860ccc885e606f47", + "licenses/graphics/angle/ecc269ef87fd38a1d98e30bfac9ba964a9dbd9315c3770fed98d4d7cb5882055.txt": "ecc269ef87fd38a1d98e30bfac9ba964a9dbd9315c3770fed98d4d7cb5882055", + "licenses/graphics/angle/edc930ca714966b56089c5a3e9a790366f9bf37e1749fb17e6dcf40b8783251f.txt": "edc930ca714966b56089c5a3e9a790366f9bf37e1749fb17e6dcf40b8783251f", + "licenses/graphics/angle/eeb50cca0bf0537aeeef00874e1e22f0de50cb035f5db37e36036dc9b8218e8d.txt": "eeb50cca0bf0537aeeef00874e1e22f0de50cb035f5db37e36036dc9b8218e8d", + "licenses/graphics/angle/ef5b39dfcafe08323262e3f51a3a9de649978a55ed8ef8eef3c451f2c1e78a53.txt": "ef5b39dfcafe08323262e3f51a3a9de649978a55ed8ef8eef3c451f2c1e78a53", + "licenses/graphics/angle/ef8e5604a137b1eb920336bad1a4948ab8dc71f2e1a4cb765178964d3598c434.txt": "ef8e5604a137b1eb920336bad1a4948ab8dc71f2e1a4cb765178964d3598c434", + "licenses/graphics/angle/f0df289ba9d03d857ad1c2f5918861376b1510b71588ffc60eff5c7a7bfedb09.txt": "f0df289ba9d03d857ad1c2f5918861376b1510b71588ffc60eff5c7a7bfedb09", + "licenses/graphics/angle/f1a2b233e8a9a71c40f4aa885be08a0842ac85bb8588703c1dd7e6e6502e3124.txt": "f1a2b233e8a9a71c40f4aa885be08a0842ac85bb8588703c1dd7e6e6502e3124", + "licenses/graphics/angle/f234d44d4afa9dad03246705dcedb1a70a7562bf595fdbfafa93aa73c8839d57.txt": "f234d44d4afa9dad03246705dcedb1a70a7562bf595fdbfafa93aa73c8839d57", + "licenses/graphics/angle/f23bae6ada76095610a77137fb92aec7342723900211c5826d54b4c57907ca56.txt": "f23bae6ada76095610a77137fb92aec7342723900211c5826d54b4c57907ca56", + "licenses/graphics/angle/f2da73c752c6b87624755edacf927cbd915fa76555d383e74494bad4a5155ab3.txt": "f2da73c752c6b87624755edacf927cbd915fa76555d383e74494bad4a5155ab3", + "licenses/graphics/angle/f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2.txt": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/graphics/angle/f3834b4a6b6e7c112207c84a11e87d4255bee0310b90338b5aaccd849fab1afb.txt": "f3834b4a6b6e7c112207c84a11e87d4255bee0310b90338b5aaccd849fab1afb", + "licenses/graphics/angle/f388fd38cad13112c1dc0f669bbe80e7f84541edbafb72f3030d2ca7642c3c9d.txt": "f388fd38cad13112c1dc0f669bbe80e7f84541edbafb72f3030d2ca7642c3c9d", + "licenses/graphics/angle/f4360ca8f779e6a673cd2882f73419bc2c5f74184fd9db91d2e86a368cc04e0b.txt": "f4360ca8f779e6a673cd2882f73419bc2c5f74184fd9db91d2e86a368cc04e0b", + "licenses/graphics/angle/f5110972dedad2b4e9d314518daf3b7d72d6e02e499acd802181de6f74571dcc.txt": "f5110972dedad2b4e9d314518daf3b7d72d6e02e499acd802181de6f74571dcc", + "licenses/graphics/angle/f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7.txt": "f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7", + "licenses/graphics/angle/f7230d5a427449430ec09e331f751ae8a7e26cafdc0af89c02e5312e4320de9b.txt": "f7230d5a427449430ec09e331f751ae8a7e26cafdc0af89c02e5312e4320de9b", + "licenses/graphics/angle/f77133324f35589f9f170473456321fe76aa35b750293cb8a475e26afa8f2bac.txt": "f77133324f35589f9f170473456321fe76aa35b750293cb8a475e26afa8f2bac", + "licenses/graphics/angle/f7715d38a3fa1b4ac97c5729740752505a39cb92ee83ab5b102aeb5eaa7cdea4.txt": "f7715d38a3fa1b4ac97c5729740752505a39cb92ee83ab5b102aeb5eaa7cdea4", + "licenses/graphics/angle/f7bdb3426d045cd50efd4953026e3eb5a83d0199f458a075602611b9344da5b9.txt": "f7bdb3426d045cd50efd4953026e3eb5a83d0199f458a075602611b9344da5b9", + "licenses/graphics/angle/f7db81051789b729fea528a63ec4c938fdcb93d9d61d97dc8cc2e9df6d47f2a1.txt": "f7db81051789b729fea528a63ec4c938fdcb93d9d61d97dc8cc2e9df6d47f2a1", + "licenses/graphics/angle/f7ef3add54eda59b0a8b882154c8d1e98d1192f3d0287d1a37b1c65a95ec2ff3.txt": "f7ef3add54eda59b0a8b882154c8d1e98d1192f3d0287d1a37b1c65a95ec2ff3", + "licenses/graphics/angle/f980a306a01e5881cc8004115f7e6dde44e7f5296477237d37d169a86ce7c094.txt": "f980a306a01e5881cc8004115f7e6dde44e7f5296477237d37d169a86ce7c094", + "licenses/graphics/angle/f9db6a9bcfcc0644975526b4f9a21af61473ac2767e3c4764ff14c48fbff4000.txt": "f9db6a9bcfcc0644975526b4f9a21af61473ac2767e3c4764ff14c48fbff4000", + "licenses/graphics/angle/fa84f04c495f2533ba036606acc8644b50077752f17bc2e943235459dda1c12c.txt": "fa84f04c495f2533ba036606acc8644b50077752f17bc2e943235459dda1c12c", + "licenses/graphics/angle/fb77f0a9c53e473abe5103c8632ef9f0f2874d4fb3f17cb2d8c661aab9cee9d7.txt": "fb77f0a9c53e473abe5103c8632ef9f0f2874d4fb3f17cb2d8c661aab9cee9d7", + "licenses/graphics/angle/fbeaca472f4f70e276dd1106ca5097435967a22ad6c1d8200aef7ad9f70aaf3f.txt": "fbeaca472f4f70e276dd1106ca5097435967a22ad6c1d8200aef7ad9f70aaf3f", + "licenses/graphics/angle/fc0c17466a53b104d5b0d907b97bbf5f7ab7031581be924001ab15e42f9d893a.txt": "fc0c17466a53b104d5b0d907b97bbf5f7ab7031581be924001ab15e42f9d893a", + "licenses/graphics/angle/fdd3b4e30f42d35402ee9c33a6b72ad202f9658374b61b97becde4603551b95d.txt": "fdd3b4e30f42d35402ee9c33a6b72ad202f9658374b61b97becde4603551b95d", + "licenses/graphics/angle/ff11d445fb41a1087c7630e120ab15f1a2cb67c1b707173cb494141805fca35e.txt": "ff11d445fb41a1087c7630e120ab15f1a2cb67c1b707173cb494141805fca35e", + "licenses/graphics/angle/ff512aac9ef231d504be5afaf4429005024e4b2aaf257be39524f37b8402aaf2.txt": "ff512aac9ef231d504be5afaf4429005024e4b2aaf257be39524f37b8402aaf2", + "licenses/graphics/angle/ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2.txt": "ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2", + "licenses/graphics/angle/fffd497be5f4ae0a10b8258e191125fb58b90250ecbf3c79398d79604dd00b7d.txt": "fffd497be5f4ae0a10b8258e191125fb58b90250ecbf3c79398d79604dd00b7d", + "licenses/graphics/dawn/02de69b64fc36d9e938f418e52723e42f0b2b226d58a9cb3c8dcbdf7059f5074.txt": "02de69b64fc36d9e938f418e52723e42f0b2b226d58a9cb3c8dcbdf7059f5074", + "licenses/graphics/dawn/0424e57d4303164dc59a8509c20dae0518b853692e5c2b0e98b11816fdbc97c7.txt": "0424e57d4303164dc59a8509c20dae0518b853692e5c2b0e98b11816fdbc97c7", + "licenses/graphics/dawn/0493f897193af1796d5054659f45ec7d4c5af648fa67a99f01d30e55cc805abc.txt": "0493f897193af1796d5054659f45ec7d4c5af648fa67a99f01d30e55cc805abc", + "licenses/graphics/dawn/0b7c936ff1270fb5089750e326f732ce2f08b18e804dc8847aa44561d0a7a277.txt": "0b7c936ff1270fb5089750e326f732ce2f08b18e804dc8847aa44561d0a7a277", + "licenses/graphics/dawn/0bbe88228fd63d20ec097f64e58d5a0a465123ae139140a18d406c60b48824b5.txt": "0bbe88228fd63d20ec097f64e58d5a0a465123ae139140a18d406c60b48824b5", + "licenses/graphics/dawn/149704059b5d0bf551637e50042dd4de9c2cae921021f6636298911e3a5f9462.txt": "149704059b5d0bf551637e50042dd4de9c2cae921021f6636298911e3a5f9462", + "licenses/graphics/dawn/17420d366df90c474bd70ad474694956cdb7fc64be70387a49a45458c4152d22.txt": "17420d366df90c474bd70ad474694956cdb7fc64be70387a49a45458c4152d22", + "licenses/graphics/dawn/17e70c676e1521ff3e4686f04a2053d93a7e28a33be8de7ec37ab0ff72feb677.txt": "17e70c676e1521ff3e4686f04a2053d93a7e28a33be8de7ec37ab0ff72feb677", + "licenses/graphics/dawn/23353f4505b1c8ce4f8f72fc3b11dc74b4a8a7bf95921d93ff77f227c171a710.txt": "23353f4505b1c8ce4f8f72fc3b11dc74b4a8a7bf95921d93ff77f227c171a710", + "licenses/graphics/dawn/27a49e35d1da96eba18fba54bc882667ff0ff8c0254f16f2b6e165d605ba7df8.txt": "27a49e35d1da96eba18fba54bc882667ff0ff8c0254f16f2b6e165d605ba7df8", + "licenses/graphics/dawn/2f79bf3699b0870251255b381670237f73f21a04a38c094f791eba39c5fd1df7.txt": "2f79bf3699b0870251255b381670237f73f21a04a38c094f791eba39c5fd1df7", + "licenses/graphics/dawn/368cca1106be99d39ecd32a38d8305585d802a475effb66380b91ffc9bcf709b.txt": "368cca1106be99d39ecd32a38d8305585d802a475effb66380b91ffc9bcf709b", + "licenses/graphics/dawn/3a528aae8731663f7b2b02ee709b1ba1fd4f9bbd8935941b2a93981c5ab78bc4.txt": "3a528aae8731663f7b2b02ee709b1ba1fd4f9bbd8935941b2a93981c5ab78bc4", + "licenses/graphics/dawn/3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b.txt": "3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b", + "licenses/graphics/dawn/43c0a37e6a0fa7ff3c843b3ec5a4fac84b712558ddac103fbd4c1649662a9ece.txt": "43c0a37e6a0fa7ff3c843b3ec5a4fac84b712558ddac103fbd4c1649662a9ece", + "licenses/graphics/dawn/4782253b8777b2c679544b31b18a52616d3c2ba0515dcf695e262bd318b356f0.txt": "4782253b8777b2c679544b31b18a52616d3c2ba0515dcf695e262bd318b356f0", + "licenses/graphics/dawn/609ae74144cd07a61653f733a0e11abf241a10c7dff4acd07dacda9fbffae22e.txt": "609ae74144cd07a61653f733a0e11abf241a10c7dff4acd07dacda9fbffae22e", + "licenses/graphics/dawn/69760673abf91cfd0280ae73739a29c078f493804d9016a122b3b189b48ad6e6.txt": "69760673abf91cfd0280ae73739a29c078f493804d9016a122b3b189b48ad6e6", + "licenses/graphics/dawn/6e5e117324afd944dcf67f36cf329843bc1a92229a8cd9bb573d7a83130fea7d.txt": "6e5e117324afd944dcf67f36cf329843bc1a92229a8cd9bb573d7a83130fea7d", + "licenses/graphics/dawn/6f20fa7672b00e2e975c291df737cf227addf3ad32e36fef3fe0f416e4664d3d.txt": "6f20fa7672b00e2e975c291df737cf227addf3ad32e36fef3fe0f416e4664d3d", + "licenses/graphics/dawn/7c77a44a8acd9b41fdc209864a8016b3d430b5d0e09309818d5b7444336df744.txt": "7c77a44a8acd9b41fdc209864a8016b3d430b5d0e09309818d5b7444336df744", + "licenses/graphics/dawn/7e1efc85a78732a13d7ddfc8b52912da7c8f8d3c6d334624b20e3f3a96297de0.txt": "7e1efc85a78732a13d7ddfc8b52912da7c8f8d3c6d334624b20e3f3a96297de0", + "licenses/graphics/dawn/7ff3a8e0e49a0141989e4dea4fa92e18f908fe462a19d4b6cb2f1225c857bfa1.txt": "7ff3a8e0e49a0141989e4dea4fa92e18f908fe462a19d4b6cb2f1225c857bfa1", + "licenses/graphics/dawn/903df5512f7d02609fed0c780a9b704f5a3eeb6e4d84ebe42a29845c81899a3c.txt": "903df5512f7d02609fed0c780a9b704f5a3eeb6e4d84ebe42a29845c81899a3c", + "licenses/graphics/dawn/95ad366d23fadf701d355bc45fb8b82ae2d700239471d35d41286ac3b08ff903.txt": "95ad366d23fadf701d355bc45fb8b82ae2d700239471d35d41286ac3b08ff903", + "licenses/graphics/dawn/9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138.txt": "9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138", + "licenses/graphics/dawn/b47ca1ea743623d50c6e02faa136dea4f6574a98e200666f859dbf57fb491721.txt": "b47ca1ea743623d50c6e02faa136dea4f6574a98e200666f859dbf57fb491721", + "licenses/graphics/dawn/b921912e9e433291f6010631a0dd41cec76c4a877966ecc22ba86151b2e66718.txt": "b921912e9e433291f6010631a0dd41cec76c4a877966ecc22ba86151b2e66718", + "licenses/graphics/dawn/c79a7fea0e3cac04cd43f20e7b648e5a0ff8fa5344e644b0ee09ca1162b62747.txt": "c79a7fea0e3cac04cd43f20e7b648e5a0ff8fa5344e644b0ee09ca1162b62747", + "licenses/graphics/dawn/caf3f489e3959df3605fec3c1f921fe72456c5d3640d998a5e635e8a9505cec5.txt": "caf3f489e3959df3605fec3c1f921fe72456c5d3640d998a5e635e8a9505cec5", + "licenses/graphics/dawn/cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/graphics/dawn/d0d8b09800a45cd982e9568fc7669d9c1a4c330e275a821bbe24d54366d16fe9.txt": "d0d8b09800a45cd982e9568fc7669d9c1a4c330e275a821bbe24d54366d16fe9", + "licenses/graphics/dawn/d20809e7f3c8116615249bdefdc826c29b0b8332e7ac7ac249fd3949b716e8c5.txt": "d20809e7f3c8116615249bdefdc826c29b0b8332e7ac7ac249fd3949b716e8c5", + "licenses/graphics/dawn/ea43b1de38a6f90c488800d66dec1ed671e68cda530266bc96951fb5b6307613.txt": "ea43b1de38a6f90c488800d66dec1ed671e68cda530266bc96951fb5b6307613", + "licenses/graphics/dawn/eb425408dc2905e3506310bfdc33fdf00c1dfc2f2f01de95fb01809b29765be2.txt": "eb425408dc2905e3506310bfdc33fdf00c1dfc2f2f01de95fb01809b29765be2", + "runtimes/osx-arm64/native/icudtl.dat": "9f48c7f9c7c94d516a14870707e910ab94d75ae640ff6842c4af53276cd26ebe", + "runtimes/osx-arm64/native/libEGL.dylib": "7028670d2e3c2c2f8a6032668144035e2caca54afc71559a789b9759a5670830", + "runtimes/osx-arm64/native/libGLESv2.dylib": "edcce24792bc722a2ce5fc3080c13a317740ea5e1954f74efd97e04c95dfdf17", + "runtimes/osx-arm64/native/libwebgpu_dawn.dylib": "ba985ff167a82defc17db7ee2c1bd41ef9551ec9a78070e2622eccc4212023b6", + "runtimes/osx-arm64/native/libwebscene_native_engine.dylib": "5402950a67b8a0bd5471ad96eb99b061d242b934e1b47d0ed2601b4c3a64537f", + "runtimes/osx-arm64/native/webscene-graphics-runtime.json": "4d27d462cff465d86c6c358c714b96a56f4e31603d0cff86993e627cf208cb2d", + "runtimes/osx-arm64/native/webscene-native-runtime.json": "df06cbd26bc1a0454d395db3e3ea0127a83116ade4f5d263a3b1c9b3161fc27a", + "runtimes/osx-arm64/native/webscene_bootstrap_snapshot.bin": "09e8d710777536a1180ed5f100f84d4bcea76383d849f94e473755989254b584", + "runtimes/osx-arm64/native/webscene_bootstrap_snapshot.meta": "4c603264f27966dc52a734f62f706d88f890c1c653057af279654a9be0264021", + "webscene-logo.jpg": "05151fc3155f6675a5588e0bcda81f462477fdfa5235ff8cf701faae8499c732", + "[Content_Types].xml": "64fcdd04c69634de0d9e8f8daf31db9f6c5517c7605f1437b418a5e7fcba9559", + "package/services/metadata/core-properties/7c524cc9073e47f1b83cd23f4d355b8b.psmdcp": "fde52fa0c193bc277a36cd7030a4050ad4d5d69da0daeab51ec68ebf7a3399da" + } +} diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-angle/README.md b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/README.md new file mode 100644 index 000000000..43ce5a781 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/README.md @@ -0,0 +1,16 @@ +# Linux ANGLE hosted build and incomplete artifact + +[Job 101733463401](https://github.com/wieslawsoltes/WebScene/actions/runs/34119103227/job/101733463401) successfully built the pinned Vulkan ANGLE SDK, verified the installed SDK on Linux, and linked its diagnostic probe at revision `0d815e98422244d2242965f219acf4d1de93f41c`. No hardware execution is claimed. + +Downloading `graphics-build-only-angle-linux-x64` and running the repository SDK verifier failed inventory validation: the upload omitted nine hidden `.clang-format` files. All delivered inventoried files match their recorded hashes, but the downloaded package is incomplete and is not an accepted SDK. The exact missing paths are recorded in `download-integrity.json`; original log, manifest, source graph and available GN arguments are retained compressed here. + +Commit `055836e` enables hidden-file inclusion for the narrowly scoped SDK artifact upload. A new downloaded artifact must pass the complete verifier before this packaging failure is considered resolved. Do not repair this recorded failed artifact by copying local files into it. + +Reproduce the complete build on Linux with: + +```sh +python3 eng/graphics/build.py angle --rid linux-x64 --jobs 2 +python3 eng/graphics/verify-sdk.py artifacts/graphics-sdk/linux-x64/angle --component angle --rid linux-x64 +``` + +Full runner prerequisites and probe link commands are in `.github/workflows/graphics-sdk-build.yml`. GPU pixels, native runtime integration and relocation remain separate issue #23 gates. diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-angle/args.gn.gz b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/args.gn.gz new file mode 100644 index 000000000..822992953 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/args.gn.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-angle/build.log.gz b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/build.log.gz new file mode 100644 index 000000000..59155844d Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-angle/download-integrity.json b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/download-integrity.json new file mode 100644 index 000000000..c27b284e1 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/download-integrity.json @@ -0,0 +1,16 @@ +{ + "inventoryFiles": 1095, + "missing": [ + "include/CL/.clang-format", + "include/EGL/.clang-format", + "include/GLES/.clang-format", + "include/GLES2/.clang-format", + "include/GLES3/.clang-format", + "include/GLX/.clang-format", + "include/KHR/.clang-format", + "include/WGL/.clang-format", + "include/platform/autogen/.clang-format" + ], + "hashMismatches": [], + "qualification": "failed: incomplete downloaded artifact" +} diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-angle/source-graph.json.gz b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/source-graph.json.gz new file mode 100644 index 000000000..a9027c5e3 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/source-graph.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-angle/webscene-graphics-package.json.gz b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/webscene-graphics-package.json.gz new file mode 100644 index 000000000..f8d55c4ad Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-linux-angle/webscene-graphics-package.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/CMakeCache.txt.gz b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/CMakeCache.txt.gz new file mode 100644 index 000000000..95f7e2521 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/CMakeCache.txt.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/README.md b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/README.md new file mode 100644 index 000000000..952a53067 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/README.md @@ -0,0 +1,20 @@ +# Hosted Linux Dawn build evidence + +GitHub Actions [job 101724950691](https://github.com/wieslawsoltes/WebScene/actions/runs/34116313032/job/101724950691) succeeded at revision 1ef6394dbd6f2f6a06ad65f06d4bdfb421a168ee on ubuntu-22.04. The job built the pinned Vulkan Dawn SDK, audited its exported C API, verified its installed inventory, and configured and linked the native probe. It did **not** execute the probe or qualify GPU hardware, native runtime integration, or relocation. + +The compressed full build log, SDK manifest (including every installed file hash and tool versions), CMake cache, source graph and export audit are retained here. Full SDK binaries are in Actions artifact `graphics-build-only-dawn-linux-x64` (artifact ID 10017202937; 14-day retention), and downloaded locally under `artifacts/graphics-hosted-linux-dawn-34116313032`. Artifact retention is not permanent binary archival. + +The downloaded artifact passed the current verifier with: + +```sh +python3 eng/graphics/verify-sdk.py artifacts/graphics-hosted-linux-dawn-34116313032/graphics-sdk/linux-x64/dawn --component dawn --rid linux-x64 +``` + +Reproduce compilation on Linux x64 from this checkout: + +```sh +python3 eng/graphics/build.py dawn --rid linux-x64 --jobs 2 +python3 eng/graphics/verify-sdk.py artifacts/graphics-sdk/linux-x64/dawn --component dawn --rid linux-x64 +``` + +See `.github/workflows/graphics-sdk-build.yml` for system packages and probe link commands. Hardware pixel verification remains required by issue #23. diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/build.log.gz b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/build.log.gz new file mode 100644 index 000000000..204cc3600 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/exports.json.gz b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/exports.json.gz new file mode 100644 index 000000000..525827933 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/exports.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/source-graph.json.gz b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/source-graph.json.gz new file mode 100644 index 000000000..afd9cf29e Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/source-graph.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/webscene-graphics-package.json.gz b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/webscene-graphics-package.json.gz new file mode 100644 index 000000000..5ee687be4 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-linux-dawn/webscene-graphics-package.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/CMakeCache.txt.gz b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/CMakeCache.txt.gz new file mode 100644 index 000000000..6be088c53 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/CMakeCache.txt.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/README.md b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/README.md new file mode 100644 index 000000000..e01d92c2c --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/README.md @@ -0,0 +1,22 @@ +# Hosted Windows Dawn build evidence + +[Job 101733462964](https://github.com/wieslawsoltes/WebScene/actions/runs/34119103227/job/101733462964) passed at revision `0d815e98422244d2242965f219acf4d1de93f41c` on windows-2022. It compiled pinned Dawn with D3D12, installed and verified the SDK, audited DLL exports, and configured and linked the diagnostic probe. The CMake cache confirms `ABSL_MSVC_STATIC_RUNTIME=ON` and `CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded`, resolving the previous CRT linkage failure. + +This is build evidence only: no GPU probe execution, verified pixels, V8 integration, relocation, or Windows package qualification is claimed. Issue #23 remains incomplete. + +The full build log, package inventory with file hashes and tool versions, source graph, CMake cache and C API export audit are retained compressed here. The full SDK is available in Actions artifact `graphics-build-only-dawn-win-x64` for 14 days and locally under `artifacts/graphics-hosted-windows-dawn-34119103227`. This is not permanent binary archival. + +The downloaded SDK passed the current verifier for all 81 inventoried files: + +```sh +python3 eng/graphics/verify-sdk.py artifacts/graphics-hosted-windows-dawn-34119103227/graphics-sdk/win-x64/dawn --component dawn --rid win-x64 +``` + +Reproduce from an x64 Visual C++ developer shell with Python, CMake and Ninja installed: + +```sh +python eng/graphics/build.py dawn --rid win-x64 --jobs 2 +python eng/graphics/verify-sdk.py artifacts/graphics-sdk/win-x64/dawn --component dawn --rid win-x64 +``` + +See `.github/workflows/graphics-sdk-build.yml` for the exact compiler initialization and probe configure/link commands. Dedicated hardware execution remains required. diff --git a/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/build.log.gz b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/build.log.gz new file mode 100644 index 000000000..e5dd4fac2 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/exports.json.gz b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/exports.json.gz new file mode 100644 index 000000000..98e2ba7ce Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/exports.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/source-graph.json.gz b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/source-graph.json.gz new file mode 100644 index 000000000..9b603491b Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/source-graph.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/webscene-graphics-package.json.gz b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/webscene-graphics-package.json.gz new file mode 100644 index 000000000..0ff2e8946 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-hosted-windows-dawn/webscene-graphics-package.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/README.md b/docs/graphics/evidence/2026-09-07-isolated-dawn/README.md new file mode 100644 index 000000000..aaf99a94e --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-isolated-dawn/README.md @@ -0,0 +1,18 @@ +# G01: integrated Dawn isolation, macOS ARM64 + +The shared Dawn C API boundary resolves the V8 integration hang observed with static co-linkage. This evidence covers the implemented SDK builder and normal CMake consumer configuration, replacing the earlier disposable Ninja experiment. No application rendering or CPU pixel presentation path is added. + +Verified on Apple M4 / Metal: + +- Dawn, ANGLE ES 2 and ANGLE ES 3 diagnostic probes all verified their pixels on hardware. +- Ten SDK integrity/mismatch/relocation checks passed. +- Five macOS relocation checks passed, including actual adjacent Dawn/ANGLE load paths and their absence from graphics-disabled native loading. +- The graphics-enabled V8 native runtime and three parser suites passed (11.71 seconds total). The upstream-aligned no-graphics control passed separately; its log remains in the sibling V8 integration evidence directory. +- Binary export inspection accepted only WebGPU C functions. The shared library exposes no Abseil, Tint or Dawn C++ symbols. +- Nine Python evidence/export tests passed, including rejection of leaked dependency exports and unknown symbol output. + +`index.json` identifies the implementation commit and artifact hashes. Compressed JSON retains the full SDK manifests and hardware/loader results. Compressed logs retain builder, configure, link and native test output. These builds reused pinned dependency source/build caches; they do not replace the outstanding clean-build gates on other platforms. + +Reproduce from the repository root with the commands in `eng/graphics/README.md`: build both SDK components, configure/build the probes, run `run-probes.py`, `check-sdk-integrity.py`, and `check-macos-relocation.py`. Both components must be rebuilt for the updated lock. For native V8 verification, use a matching patched V8 15.3.10 SDK, configure the native engine with graphics and Inspector enabled, pointer compression/shared cage enabled, PartitionAlloc disabled, Release/dense linking and the bootstrap snapshot. Copy its `icudtl.dat` beside the binary before CTest. The stale inspector-header/archive combination is rejected during configure. + +Windows/Linux build and GPU qualification, complete reference archival, and runtime package integration remain outstanding. This evidence does not close #23, establish browser API conformance, or claim Kestrel runs inside WebScene. diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/angle-isolated-final-build.log.gz b/docs/graphics/evidence/2026-09-07-isolated-dawn/angle-isolated-final-build.log.gz new file mode 100644 index 000000000..a8c0946a3 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-isolated-dawn/angle-isolated-final-build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/dawn-exports.json b/docs/graphics/evidence/2026-09-07-isolated-dawn/dawn-exports.json new file mode 100644 index 000000000..045121705 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-isolated-dawn/dawn-exports.json @@ -0,0 +1,287 @@ +{ + "status": "passed", + "command": [ + "nm", + "-g", + "-U", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/lib/libwebgpu_dawn.dylib" + ], + "exports": [ + "wgpuAdapterAddRef", + "wgpuAdapterCreateDevice", + "wgpuAdapterGetFeatures", + "wgpuAdapterGetFormatCapabilities", + "wgpuAdapterGetInfo", + "wgpuAdapterGetInstance", + "wgpuAdapterGetLimits", + "wgpuAdapterHasFeature", + "wgpuAdapterInfoFreeMembers", + "wgpuAdapterPropertiesMemoryHeapsFreeMembers", + "wgpuAdapterPropertiesSubgroupMatrixConfigsFreeMembers", + "wgpuAdapterRelease", + "wgpuAdapterRequestDevice", + "wgpuBindGroupAddRef", + "wgpuBindGroupLayoutAddRef", + "wgpuBindGroupLayoutRelease", + "wgpuBindGroupLayoutSetLabel", + "wgpuBindGroupRelease", + "wgpuBindGroupSetLabel", + "wgpuBufferAddRef", + "wgpuBufferCreateTexelView", + "wgpuBufferDestroy", + "wgpuBufferGetConstMappedRange", + "wgpuBufferGetMapState", + "wgpuBufferGetMappedRange", + "wgpuBufferGetSize", + "wgpuBufferGetUsage", + "wgpuBufferMapAsync", + "wgpuBufferReadMappedRange", + "wgpuBufferRelease", + "wgpuBufferSetLabel", + "wgpuBufferUnmap", + "wgpuBufferWriteMappedRange", + "wgpuCommandBufferAddRef", + "wgpuCommandBufferRelease", + "wgpuCommandBufferSetLabel", + "wgpuCommandEncoderAddRef", + "wgpuCommandEncoderBeginComputePass", + "wgpuCommandEncoderBeginRenderPass", + "wgpuCommandEncoderClearBuffer", + "wgpuCommandEncoderCopyBufferToBuffer", + "wgpuCommandEncoderCopyBufferToTexture", + "wgpuCommandEncoderCopyTextureToBuffer", + "wgpuCommandEncoderCopyTextureToTexture", + "wgpuCommandEncoderFinish", + "wgpuCommandEncoderInjectValidationError", + "wgpuCommandEncoderInsertDebugMarker", + "wgpuCommandEncoderPopDebugGroup", + "wgpuCommandEncoderPushDebugGroup", + "wgpuCommandEncoderRelease", + "wgpuCommandEncoderResolveQuerySet", + "wgpuCommandEncoderSetLabel", + "wgpuCommandEncoderWriteBuffer", + "wgpuCommandEncoderWriteTimestamp", + "wgpuComputePassEncoderAddRef", + "wgpuComputePassEncoderDispatchWorkgroups", + "wgpuComputePassEncoderDispatchWorkgroupsIndirect", + "wgpuComputePassEncoderEnd", + "wgpuComputePassEncoderInsertDebugMarker", + "wgpuComputePassEncoderPopDebugGroup", + "wgpuComputePassEncoderPushDebugGroup", + "wgpuComputePassEncoderRelease", + "wgpuComputePassEncoderSetBindGroup", + "wgpuComputePassEncoderSetImmediates", + "wgpuComputePassEncoderSetLabel", + "wgpuComputePassEncoderSetPipeline", + "wgpuComputePassEncoderSetResourceTable", + "wgpuComputePassEncoderWriteTimestamp", + "wgpuComputePipelineAddRef", + "wgpuComputePipelineGetBindGroupLayout", + "wgpuComputePipelineRelease", + "wgpuComputePipelineSetLabel", + "wgpuCreateInstance", + "wgpuDawnDrmFormatCapabilitiesFreeMembers", + "wgpuDeviceAddRef", + "wgpuDeviceCreateBindGroup", + "wgpuDeviceCreateBindGroupLayout", + "wgpuDeviceCreateBuffer", + "wgpuDeviceCreateCommandEncoder", + "wgpuDeviceCreateComputePipeline", + "wgpuDeviceCreateComputePipelineAsync", + "wgpuDeviceCreateErrorBuffer", + "wgpuDeviceCreateErrorComputePipeline", + "wgpuDeviceCreateErrorExternalTexture", + "wgpuDeviceCreateErrorRenderPipeline", + "wgpuDeviceCreateErrorShaderModule", + "wgpuDeviceCreateErrorTexture", + "wgpuDeviceCreateExternalTexture", + "wgpuDeviceCreatePipelineLayout", + "wgpuDeviceCreateQuerySet", + "wgpuDeviceCreateRenderBundleEncoder", + "wgpuDeviceCreateRenderPipeline", + "wgpuDeviceCreateRenderPipelineAsync", + "wgpuDeviceCreateResourceTable", + "wgpuDeviceCreateSampler", + "wgpuDeviceCreateShaderModule", + "wgpuDeviceCreateTexture", + "wgpuDeviceDestroy", + "wgpuDeviceForceLoss", + "wgpuDeviceGetAHardwareBufferProperties", + "wgpuDeviceGetAdapter", + "wgpuDeviceGetAdapterInfo", + "wgpuDeviceGetFeatures", + "wgpuDeviceGetLimits", + "wgpuDeviceGetLostFuture", + "wgpuDeviceGetQueue", + "wgpuDeviceHasFeature", + "wgpuDeviceImportSharedBufferMemory", + "wgpuDeviceImportSharedFence", + "wgpuDeviceImportSharedTextureMemory", + "wgpuDeviceInjectError", + "wgpuDevicePopErrorScope", + "wgpuDevicePushErrorScope", + "wgpuDeviceRelease", + "wgpuDeviceSetLabel", + "wgpuDeviceSetLoggingCallback", + "wgpuDeviceTick", + "wgpuDeviceValidateTextureDescriptor", + "wgpuExternalTextureAddRef", + "wgpuExternalTextureDestroy", + "wgpuExternalTextureExpire", + "wgpuExternalTextureRefresh", + "wgpuExternalTextureRelease", + "wgpuExternalTextureSetLabel", + "wgpuGetInstanceFeatures", + "wgpuGetInstanceLimits", + "wgpuGetProcAddress", + "wgpuHasInstanceFeature", + "wgpuInstanceAddRef", + "wgpuInstanceCreateSurface", + "wgpuInstanceGetWGSLLanguageFeatures", + "wgpuInstanceHasWGSLLanguageFeature", + "wgpuInstanceProcessEvents", + "wgpuInstanceRelease", + "wgpuInstanceRequestAdapter", + "wgpuInstanceWaitAny", + "wgpuPipelineLayoutAddRef", + "wgpuPipelineLayoutRelease", + "wgpuPipelineLayoutSetLabel", + "wgpuQuerySetAddRef", + "wgpuQuerySetDestroy", + "wgpuQuerySetGetCount", + "wgpuQuerySetGetType", + "wgpuQuerySetRelease", + "wgpuQuerySetSetLabel", + "wgpuQueueAddRef", + "wgpuQueueCopyExternalTextureForBrowser", + "wgpuQueueCopyTextureForBrowser", + "wgpuQueueOnSubmittedWorkDone", + "wgpuQueueRelease", + "wgpuQueueSetLabel", + "wgpuQueueSubmit", + "wgpuQueueWriteBuffer", + "wgpuQueueWriteTexture", + "wgpuRenderBundleAddRef", + "wgpuRenderBundleEncoderAddRef", + "wgpuRenderBundleEncoderDraw", + "wgpuRenderBundleEncoderDrawIndexed", + "wgpuRenderBundleEncoderDrawIndexedIndirect", + "wgpuRenderBundleEncoderDrawIndirect", + "wgpuRenderBundleEncoderFinish", + "wgpuRenderBundleEncoderInsertDebugMarker", + "wgpuRenderBundleEncoderPopDebugGroup", + "wgpuRenderBundleEncoderPushDebugGroup", + "wgpuRenderBundleEncoderRelease", + "wgpuRenderBundleEncoderSetBindGroup", + "wgpuRenderBundleEncoderSetImmediates", + "wgpuRenderBundleEncoderSetIndexBuffer", + "wgpuRenderBundleEncoderSetLabel", + "wgpuRenderBundleEncoderSetPipeline", + "wgpuRenderBundleEncoderSetVertexBuffer", + "wgpuRenderBundleRelease", + "wgpuRenderBundleSetLabel", + "wgpuRenderPassEncoderAddRef", + "wgpuRenderPassEncoderBeginOcclusionQuery", + "wgpuRenderPassEncoderDraw", + "wgpuRenderPassEncoderDrawIndexed", + "wgpuRenderPassEncoderDrawIndexedIndirect", + "wgpuRenderPassEncoderDrawIndirect", + "wgpuRenderPassEncoderEnd", + "wgpuRenderPassEncoderEndOcclusionQuery", + "wgpuRenderPassEncoderExecuteBundles", + "wgpuRenderPassEncoderInsertDebugMarker", + "wgpuRenderPassEncoderMultiDrawIndexedIndirect", + "wgpuRenderPassEncoderMultiDrawIndirect", + "wgpuRenderPassEncoderPixelLocalStorageBarrier", + "wgpuRenderPassEncoderPopDebugGroup", + "wgpuRenderPassEncoderPushDebugGroup", + "wgpuRenderPassEncoderRelease", + "wgpuRenderPassEncoderSetBindGroup", + "wgpuRenderPassEncoderSetBlendConstant", + "wgpuRenderPassEncoderSetImmediates", + "wgpuRenderPassEncoderSetIndexBuffer", + "wgpuRenderPassEncoderSetLabel", + "wgpuRenderPassEncoderSetPipeline", + "wgpuRenderPassEncoderSetResourceTable", + "wgpuRenderPassEncoderSetScissorRect", + "wgpuRenderPassEncoderSetStencilReference", + "wgpuRenderPassEncoderSetVertexBuffer", + "wgpuRenderPassEncoderSetViewport", + "wgpuRenderPassEncoderWriteTimestamp", + "wgpuRenderPipelineAddRef", + "wgpuRenderPipelineGetBindGroupLayout", + "wgpuRenderPipelineRelease", + "wgpuRenderPipelineSetLabel", + "wgpuResourceTableAddRef", + "wgpuResourceTableDestroy", + "wgpuResourceTableGetSize", + "wgpuResourceTableInsert", + "wgpuResourceTableRelease", + "wgpuResourceTableRemove", + "wgpuResourceTableSetLabel", + "wgpuResourceTableUpdate", + "wgpuSamplerAddRef", + "wgpuSamplerRelease", + "wgpuSamplerSetLabel", + "wgpuShaderModuleAddRef", + "wgpuShaderModuleGetCompilationInfo", + "wgpuShaderModuleRelease", + "wgpuShaderModuleSetLabel", + "wgpuSharedBufferMemoryAddRef", + "wgpuSharedBufferMemoryBeginAccess", + "wgpuSharedBufferMemoryCreateBuffer", + "wgpuSharedBufferMemoryEndAccess", + "wgpuSharedBufferMemoryEndAccessStateFreeMembers", + "wgpuSharedBufferMemoryGetProperties", + "wgpuSharedBufferMemoryIsDeviceLost", + "wgpuSharedBufferMemoryRelease", + "wgpuSharedBufferMemorySetLabel", + "wgpuSharedFenceAddRef", + "wgpuSharedFenceExportInfo", + "wgpuSharedFenceRelease", + "wgpuSharedFenceSetLabel", + "wgpuSharedTextureMemoryAddRef", + "wgpuSharedTextureMemoryBeginAccess", + "wgpuSharedTextureMemoryCreateTexture", + "wgpuSharedTextureMemoryEndAccess", + "wgpuSharedTextureMemoryEndAccessStateFreeMembers", + "wgpuSharedTextureMemoryGetProperties", + "wgpuSharedTextureMemoryIsDeviceLost", + "wgpuSharedTextureMemoryRelease", + "wgpuSharedTextureMemorySetLabel", + "wgpuSupportedFeaturesFreeMembers", + "wgpuSupportedInstanceFeaturesFreeMembers", + "wgpuSupportedWGSLLanguageFeaturesFreeMembers", + "wgpuSurfaceAddRef", + "wgpuSurfaceCapabilitiesFreeMembers", + "wgpuSurfaceConfigure", + "wgpuSurfaceGetCapabilities", + "wgpuSurfaceGetCurrentTexture", + "wgpuSurfacePresent", + "wgpuSurfaceRelease", + "wgpuSurfaceSetLabel", + "wgpuSurfaceUnconfigure", + "wgpuTexelBufferViewAddRef", + "wgpuTexelBufferViewRelease", + "wgpuTexelBufferViewSetLabel", + "wgpuTextureAddRef", + "wgpuTextureCreateErrorView", + "wgpuTextureCreateView", + "wgpuTextureDestroy", + "wgpuTextureGetDepthOrArrayLayers", + "wgpuTextureGetDimension", + "wgpuTextureGetFormat", + "wgpuTextureGetHeight", + "wgpuTextureGetMipLevelCount", + "wgpuTextureGetSampleCount", + "wgpuTextureGetTextureBindingViewDimension", + "wgpuTextureGetUsage", + "wgpuTextureGetWidth", + "wgpuTextureRelease", + "wgpuTextureSetLabel", + "wgpuTextureSetOwnershipForMemoryDump", + "wgpuTextureViewAddRef", + "wgpuTextureViewRelease", + "wgpuTextureViewSetLabel" + ] +} diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/dawn-isolated-sdk-verified-build.log.gz b/docs/graphics/evidence/2026-09-07-isolated-dawn/dawn-isolated-sdk-verified-build.log.gz new file mode 100644 index 000000000..568f02da8 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-isolated-dawn/dawn-isolated-sdk-verified-build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/index.json b/docs/graphics/evidence/2026-09-07-isolated-dawn/index.json new file mode 100644 index 000000000..2ffbb735e --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-isolated-dawn/index.json @@ -0,0 +1,16 @@ +{ + "implementationCommit": "793fa5840d8377c325abfc177c3e8b03cec8e5b1", + "status": "partial G01; macOS isolated dependency verification passed", + "files": { + "isolated-probes-configure.log.gz": "73f070cbefeeb088acde47fd787858eb7fcd674133f44f6a3c39444a382c5888", + "isolated-probes-build.log.gz": "4189c683ed1b962c56a780a6dbeec5314a68d981f5fff67a4cd098c5abc36d43", + "dawn-exports.json": "446eb6bc52e710f44e800f9fc14801b848a77e04c9d1a1880cb13530ea913ac8", + "sdk-integrity.json.gz": "460c9817c2bdbb65d5d6fc48cb8a82de4d1038bbff9abc245e358566ee911bdf", + "angle-isolated-final-build.log.gz": "8b88d2c1cba6b8d40153736d218034649e233d4c282abb6d78ca36d25de4cbd9", + "isolated-native-tests.log.gz": "4ec463543da6ca53f7321e6d4d14e7aff67ba3eb8931c6e6b37645ff0b5779d8", + "relocation.json.gz": "52303be7193473f27a9d3f42ee7c75854ceff9ce67a543a7d57b4324fea7e25e", + "dawn-isolated-sdk-verified-build.log.gz": "b1c9d34a3beb46601572a0e84657fbb21a6d6ed0651f8bd308f0bd585ec06a58", + "isolated-native-build.log.gz": "1784671b3c6858c2eee1c64445f09c52322009649186991ea7451af859fe7b11", + "native-probes.json.gz": "b43cda5c93eacdff363f2ef842206a2694b7dfe15ba689619ac3d5034164b43a" + } +} diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-native-build.log.gz b/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-native-build.log.gz new file mode 100644 index 000000000..9f3456666 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-native-build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-native-tests.log.gz b/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-native-tests.log.gz new file mode 100644 index 000000000..80e9a5305 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-native-tests.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-probes-build.log.gz b/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-probes-build.log.gz new file mode 100644 index 000000000..bcbf2a408 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-probes-build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-probes-configure.log.gz b/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-probes-configure.log.gz new file mode 100644 index 000000000..5adcd76b7 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-isolated-dawn/isolated-probes-configure.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/native-probes.json.gz b/docs/graphics/evidence/2026-09-07-isolated-dawn/native-probes.json.gz new file mode 100644 index 000000000..917d33382 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-isolated-dawn/native-probes.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/relocation.json.gz b/docs/graphics/evidence/2026-09-07-isolated-dawn/relocation.json.gz new file mode 100644 index 000000000..15e5ccf99 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-isolated-dawn/relocation.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-isolated-dawn/sdk-integrity.json.gz b/docs/graphics/evidence/2026-09-07-isolated-dawn/sdk-integrity.json.gz new file mode 100644 index 000000000..1f1a65288 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-isolated-dawn/sdk-integrity.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/README.md b/docs/graphics/evidence/2026-09-07-osx-arm64/README.md new file mode 100644 index 000000000..68ea99ca1 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/README.md @@ -0,0 +1,40 @@ +# Partial G01 evidence — Apple M4, 2026-09-07 + +Code revision: `215bb60` (full SHA in `index.json`). These results qualify the recorded native prerequisite checks on this Mac only. **Epic #22 and issue #23 remain incomplete.** No browser API implementation, retained GPU presentation, Kestrel run in WebScene, or standards conformance pass is claimed. + +| Check | Result | +|---|---| +| Dawn/Metal texture clear, queue submission, padded copy and mapping | Passed; 68 pixels checked | +| ANGLE/Metal ES 2 WebGL-compatible context | Passed; 68 pixels checked | +| ANGLE/Metal ES 3 WebGL-compatible context | Passed; 68 pixels checked | +| SDK relocation and mismatch rejection | 10 checks passed | +| macOS probe/native relocation, actual dyld paths | 5 checks passed | +| Graphics-enabled V8-free native parser tests | 3 passed | +| Graphics-disabled V8-free native parser tests | 3 passed | +| Official suite acquisition | All three locked checkouts acquired; tests not run | +| Windows/Linux hardware | Not run; runner access unconfirmed | + +`native-probes.json` contains exact native package revisions, file hashes, transitive source graph fingerprints, CMake/GN settings, tool information, host/GPU/driver identity, executable hashes and probe output. The package manifests describe the SDK inputs; the framework version fields are declared repository versions, not evidence of framework composition. `index.json` hashes the accompanying raw evidence. Logs retain original local paths to preserve provenance. + +Reproduction starts with [the graphics prerequisite guide](../../../../eng/graphics/README.md). The local build paths used here are `artifacts/graphics-build/probes-osx-arm64`, `artifacts/graphics-build/native-enabled`, `artifacts/graphics-build/native-disabled`, and `artifacts/graphics-sdk/osx-arm64`. The native builds use Release, V8 OFF, default html5ever/cssparser/Servo parsers, and graphics ON/OFF respectively. + +Hardware/ABI checks: + +```sh +python3 eng/graphics/run-probes.py --rid osx-arm64 --sdk artifacts/graphics-sdk/osx-arm64 --probes artifacts/graphics-build/probes-osx-arm64 --output artifacts/graphics-evidence/osx-arm64/native-probes.json +python3 eng/graphics/check-sdk-integrity.py --sdk artifacts/graphics-sdk/osx-arm64/dawn --rid osx-arm64 --output artifacts/graphics-evidence/osx-arm64/sdk-integrity.json +python3 eng/graphics/check-macos-relocation.py --sdk artifacts/graphics-sdk/osx-arm64 --probes artifacts/graphics-build/probes-osx-arm64 --native-enabled artifacts/graphics-build/native-enabled --native-disabled artifacts/graphics-build/native-disabled --output artifacts/graphics-evidence/osx-arm64/relocation.json +ctest --test-dir artifacts/graphics-build/native-enabled --output-on-failure +ctest --test-dir artifacts/graphics-build/native-disabled --output-on-failure +python3 -m unittest discover -s eng/graphics/tests -v +python3 tests/GraphicsCompatibility/prepare-kestrel.py +``` + +The retained-render and retained-apply JSON files are initial CPU-side non-GPU measurements using unchanged benchmark/renderer source at the base revision. Commands: + +```sh +dotnet run --project benchmarks/WebScene.NativeEngine.Benchmarks -c Release -- probe native-retained-render --layers 2048 --visible 32 --iterations 40 --samples 11 +dotnet run --project benchmarks/WebScene.NativeEngine.Benchmarks -c Release --no-build -- probe native-retained-apply --layers 4096 --batch 256 --iterations 100 --samples 11 +``` + +Sparse rendering measured 12.93 µs median / 14.53 µs p95 with zero allocated bytes per render; reference pixels matched. These are CPU retained-scene microbenchmarks at their recorded 320×240 viewport, **not** GPU submission or actual presentation timings, and not the required 1920×1080 Kestrel/Chrome baseline. No performance regression threshold is established from a single before-only sample. Browser reference captures, full native/V8 workload baselines and repeatability remain outstanding. diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/angle-reproduction.txt b/docs/graphics/evidence/2026-09-07-osx-arm64/angle-reproduction.txt new file mode 100644 index 000000000..fd78f0945 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/angle-reproduction.txt @@ -0,0 +1,27 @@ ++ git -C /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/depot_tools config core.autocrlf false ++ git -C /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle config core.autocrlf false ++ /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/depot_tools/gclient sync --shallow --no-history --revision angle@082d85ba19efba24d3c25108dc1f0cad9cf149f9 +________ running 'python3 third_party/depot_tools/update_depot_tools_toggle.py --disable' in '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle' +________ running 'python3 build/mac_toolchain.py' in '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle' +Skipping Mac toolchain installation for mac +________ running 'python3 tools/rust/update_rust.py' in '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle' +________ running 'python3 build/util/lastchange.py -o build/util/LASTCHANGE' in '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle' +________ running 'python3 third_party/depot_tools/download_from_google_storage.py --no_resume --bucket chromium-browser-clang -s tools/clang/dsymutil/bin/dsymutil.arm64.sha1 -o tools/clang/dsymutil/bin/dsymutil' in '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle' +________ running 'python3 third_party/depot_tools/download_from_google_storage.py --no_resume --platform=darwin* --bucket angle-flex-bison -d tools/flex-bison/mac/' in '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle' +________ running 'python3 build/config/siso/configure_siso.py --rbe_instance projects/rbe-chrome-untrusted/instances/default_instance' in '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle' ++ /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle/buildtools/mac/gn gen /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle/out/WebScene-osx-arm64 --fail-on-unused-args +Generating compile_commands took 32ms +Done. Made 1325 targets from 224 files in 702ms ++ ninja -C /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle/out/WebScene-osx-arm64 -j 6 libEGL libGLESv2 +ninja: Entering directory `/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle/out/WebScene-osx-arm64' +ninja: no work to do. ++ install_name_tool -id @rpath/libEGL.dylib /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/angle/lib/libEGL.dylib ++ install_name_tool -change ./libEGL.dylib @loader_path/libEGL.dylib /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/angle/lib/libEGL.dylib ++ install_name_tool -change ./libGLESv2.dylib @loader_path/libGLESv2.dylib /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/angle/lib/libEGL.dylib ++ codesign --force --sign - /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/angle/lib/libEGL.dylib +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/angle/lib/libEGL.dylib: replacing existing signature ++ install_name_tool -id @rpath/libGLESv2.dylib /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/angle/lib/libGLESv2.dylib ++ install_name_tool -change ./libEGL.dylib @loader_path/libEGL.dylib /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/angle/lib/libGLESv2.dylib ++ install_name_tool -change ./libGLESv2.dylib @loader_path/libGLESv2.dylib /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/angle/lib/libGLESv2.dylib ++ codesign --force --sign - /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/angle/lib/libGLESv2.dylib +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/angle/lib/libGLESv2.dylib: replacing existing signature diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/dawn-build.txt b/docs/graphics/evidence/2026-09-07-osx-arm64/dawn-build.txt new file mode 100644 index 000000000..1d3d1c602 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/dawn-build.txt @@ -0,0 +1,932 @@ +[1/932] Building CXX object src/tint/CMakeFiles/tint_utils_macros.dir/utils/macros/macros.cc.o +[2/932] Building CXX object src/utils/CMakeFiles/dawn_shared_utils.dir/placeholder.cc.o +[3/932] Building CXX object src/tint/CMakeFiles/tint_utils_ice.dir/utils/ice/debugger.cc.o +[4/932] Building CXX object src/tint/CMakeFiles/tint_utils_memory.dir/utils/memory/memory.cc.o +[5/932] Building CXX object src/tint/CMakeFiles/tint_utils_math.dir/utils/math/math.cc.o +[6/932] Building CXX object src/tint/CMakeFiles/tint_utils_rtti.dir/utils/rtti/castable.cc.o +[7/932] Building CXX object src/utils/CMakeFiles/dawn_shared_utils.dir/assert.cc.o +[8/932] Building CXX object src/tint/CMakeFiles/tint_utils_ice.dir/utils/ice/ice.cc.o +[9/932] Building CXX object src/utils/CMakeFiles/dawn_shared_utils.dir/log.cc.o +[10/932] Building CXX object src/tint/CMakeFiles/tint_utils_rtti.dir/utils/rtti/switch.cc.o +[11/932] Building CXX object src/tint/CMakeFiles/tint_utils_containers.dir/utils/containers/containers.cc.o +[12/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_spinlock_wait.dir/internal/spinlock_wait.cc.o +[13/932] Building CXX object src/tint/CMakeFiles/tint_utils_system.dir/utils/system/env_other.cc.o +[14/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_log_severity.dir/log_severity.cc.o +[15/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_raw_logging_internal.dir/internal/raw_logging.cc.o +[16/932] Building CXX object src/tint/CMakeFiles/tint_utils_system.dir/utils/system/terminal_posix.cc.o +[17/932] Building CXX object src/tint/CMakeFiles/tint_utils_system.dir/utils/system/executable_file_mac.cc.o +[18/932] Building CXX object src/tint/CMakeFiles/tint_utils.dir/utils/result.cc.o +[19/932] Linking CXX static library src/utils/libdawn_shared_utils.a +[20/932] Linking CXX static library third_party/abseil/absl/base/libabsl_spinlock_wait.a +[21/932] Linking CXX static library src/tint/libtint_utils_macros.a +[22/932] Linking CXX static library src/tint/libtint_utils_ice.a +[23/932] Linking CXX static library src/tint/libtint_utils_math.a +[24/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_base.dir/casts.cc.o +[25/932] Linking CXX static library src/tint/libtint_utils_memory.a +[26/932] Linking CXX static library src/tint/libtint_utils_rtti.a +[27/932] Linking CXX static library third_party/abseil/absl/base/libabsl_log_severity.a +[28/932] Linking CXX static library src/tint/libtint_utils_containers.a +[29/932] Linking CXX static library third_party/abseil/absl/base/libabsl_raw_logging_internal.a +[30/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_base.dir/internal/cycleclock.cc.o +[31/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_base.dir/internal/unscaledcycleclock.cc.o +[32/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_base.dir/internal/spinlock.cc.o +[33/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_utf8_for_code_point.dir/internal/utf8_for_code_point.cc.o +[34/932] Linking CXX static library src/tint/libtint_utils_system.a +[35/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_base.dir/internal/sysinfo.cc.o +[36/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_base.dir/internal/thread_identity.cc.o +[37/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_throw_delegate.dir/throw_delegate.cc.o +[38/932] Building CXX object src/tint/CMakeFiles/tint_utils_text.dir/utils/text/color_mode.cc.o +[39/932] Building CXX object src/tint/CMakeFiles/tint_utils_text.dir/utils/text/base64.cc.o +[40/932] Building CXX object src/tint/CMakeFiles/tint_utils_text.dir/utils/text/string.cc.o +[41/932] Building CXX object src/tint/CMakeFiles/tint_utils_text.dir/utils/text/string_stream.cc.o +[42/932] Building CXX object src/tint/CMakeFiles/tint_utils_text.dir/utils/text/styled_text.cc.o +[43/932] Linking CXX static library src/tint/libtint_utils.a +[44/932] Linking CXX static library third_party/abseil/absl/base/libabsl_base.a +[45/932] Building CXX object src/tint/CMakeFiles/tint_utils_text.dir/utils/text/styled_text_printer.cc.o +[46/932] Building CXX object src/tint/CMakeFiles/tint_utils_text.dir/utils/text/styled_text_theme.cc.o +[47/932] Linking CXX static library third_party/abseil/absl/base/libabsl_throw_delegate.a +[48/932] Linking CXX static library third_party/abseil/absl/debugging/libabsl_utf8_for_code_point.a +[49/932] Building CXX object src/tint/CMakeFiles/tint_utils_text.dir/utils/text/styled_text_printer_ansi.cc.o +[50/932] Building CXX object src/tint/CMakeFiles/tint_utils_text.dir/utils/text/styled_text_printer_posix.cc.o +[51/932] Building CXX object src/tint/CMakeFiles/tint_utils_text.dir/utils/text/unicode.cc.o +[52/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings_internal.dir/internal/utf8.cc.o +[53/932] Building CXX object third_party/abseil/absl/types/CMakeFiles/absl_source_location.dir/source_location.cc.o +[54/932] Linking CXX static library src/tint/libtint_utils_text.a +[55/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_decode_rust_punycode.dir/internal/decode_rust_punycode.cc.o +[56/932] Linking CXX static library third_party/abseil/absl/debugging/libabsl_decode_rust_punycode.a +[57/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings_internal.dir/internal/ostringstream.cc.o +[58/932] Building CXX object third_party/abseil/absl/numeric/CMakeFiles/absl_int128.dir/int128.cc.o +[59/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_base_cpu_detect.dir/internal/cpu_detect.cc.o +[60/932] Linking CXX static library third_party/abseil/absl/numeric/libabsl_int128.a +[61/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_demangle_rust.dir/internal/demangle_rust.cc.o +[62/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings_internal.dir/internal/escaping.cc.o +[63/932] Building CXX object src/tint/CMakeFiles/tint_utils_reflection.dir/utils/reflection/reflection.cc.o +[64/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/ascii.cc.o +[65/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/internal/charconv_parse.cc.o +[66/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/internal/memutil.cc.o +[67/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/internal/damerau_levenshtein_distance.cc.o +[68/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/charconv.cc.o +[69/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/internal/charconv_bigint.cc.o +[70/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/escaping.cc.o +[71/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/internal/stringify_sink.cc.o +[72/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/match.cc.o +[73/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/str_replace.cc.o +[74/932] Linking CXX static library third_party/abseil/absl/strings/libabsl_strings_internal.a +[75/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/str_cat.cc.o +[76/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/numbers.cc.o +[77/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/str_split.cc.o +[78/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_strings.dir/substitute.cc.o +[79/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time_zone.dir/internal/cctz/src/time_zone_fixed.cc.o +[80/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_civil_time.dir/internal/cctz/src/civil_time_detail.cc.o +[81/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time_zone.dir/internal/cctz/src/time_zone_if.cc.o +[82/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time_zone.dir/internal/cctz/src/time_zone_format.cc.o +[83/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time_zone.dir/internal/cctz/src/time_zone_impl.cc.o +[84/932] Linking CXX static library third_party/abseil/absl/types/libabsl_source_location.a +[85/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time_zone.dir/internal/cctz/src/time_zone_posix.cc.o +[86/932] Linking CXX static library src/tint/libtint_utils_reflection.a +[87/932] Linking CXX static library third_party/abseil/absl/base/libabsl_base_cpu_detect.a +[88/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time_zone.dir/internal/cctz/src/time_zone_libc.cc.o +[89/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time_zone.dir/internal/cctz/src/time_zone_lookup.cc.o +[90/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time_zone.dir/internal/cctz/src/zone_info_source.cc.o +[91/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_debugging_internal.dir/internal/address_is_readable.cc.o +[92/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_debugging_internal.dir/internal/elf_mem_image.cc.o +[93/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_debugging_internal.dir/internal/vdso_support.cc.o +[94/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time_zone.dir/internal/cctz/src/time_zone_info.cc.o +[95/932] Building CXX object src/tint/CMakeFiles/tint_api_common.dir/api/common/vertex_pulling_config.cc.o +[96/932] Linking CXX static library third_party/abseil/absl/debugging/libabsl_demangle_rust.a +[97/932] Linking CXX static library third_party/abseil/absl/strings/libabsl_strings.a +[98/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_malloc_internal.dir/internal/low_level_alloc.cc.o +[99/932] Building CXX object third_party/abseil/absl/crc/CMakeFiles/absl_crc_internal.dir/internal/crc.cc.o +[100/932] Building CXX object third_party/abseil/absl/crc/CMakeFiles/absl_crc_internal.dir/internal/crc_x86_arm_combined.cc.o +[101/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_demangle_internal.dir/internal/demangle.cc.o +[102/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_str_format_internal.dir/internal/str_format/bind.cc.o +[103/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_str_format_internal.dir/internal/str_format/arg.cc.o +[104/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_str_format_internal.dir/internal/str_format/extension.cc.o +[105/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_str_format_internal.dir/internal/str_format/output.cc.o +[106/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_str_format_internal.dir/internal/str_format/parser.cc.o +[107/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time.dir/clock.cc.o +[108/932] Linking CXX static library third_party/abseil/absl/time/libabsl_civil_time.a +[109/932] Linking CXX static library third_party/abseil/absl/time/libabsl_time_zone.a +[110/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_str_format_internal.dir/internal/str_format/float_conversion.cc.o +[111/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time.dir/civil_time.cc.o +[112/932] Linking CXX static library src/tint/libtint_api_common.a +[113/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time.dir/duration.cc.o +[114/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time.dir/format.cc.o +[115/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_time.dir/time.cc.o +[116/932] Building CXX object src/tint/CMakeFiles/tint_lang_core.dir/lang/core/binary_op.cc.o +[117/932] Building CXX object src/tint/CMakeFiles/tint_lang_core.dir/lang/core/number.cc.o +[118/932] Building CXX object src/tint/CMakeFiles/tint_lang_core.dir/lang/core/unary_op.cc.o +[119/932] Building CXX object src/tint/CMakeFiles/tint_lang_core.dir/lang/core/enums.cc.o +[120/932] Building CXX object src/tint/CMakeFiles/tint_utils_diagnostic.dir/utils/diagnostic/diagnostic.cc.o +[121/932] Linking CXX static library third_party/abseil/absl/base/libabsl_malloc_internal.a +[122/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_tracing_internal.dir/internal/tracing.cc.o +[123/932] Linking CXX static library third_party/abseil/absl/crc/libabsl_crc_internal.a +[124/932] Building CXX object src/tint/CMakeFiles/tint_utils_symbol.dir/utils/symbol/generation_id.cc.o +[125/932] Building CXX object src/tint/CMakeFiles/tint_utils_diagnostic.dir/utils/diagnostic/source.cc.o +[126/932] Building CXX object src/tint/CMakeFiles/tint_utils_diagnostic.dir/utils/diagnostic/formatter.cc.o +[127/932] Building CXX object src/tint/CMakeFiles/tint_utils_symbol.dir/utils/symbol/symbol.cc.o +[128/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_stacktrace.dir/stacktrace.cc.o +[129/932] Building CXX object src/tint/CMakeFiles/tint_utils_symbol.dir/utils/symbol/symbol_table.cc.o +[130/932] Linking CXX static library third_party/abseil/absl/debugging/libabsl_debugging_internal.a +[131/932] Linking CXX static library third_party/abseil/absl/debugging/libabsl_demangle_internal.a +[132/932] Building CXX object third_party/abseil/absl/crc/CMakeFiles/absl_crc32c.dir/crc32c.cc.o +[133/932] Linking CXX static library third_party/abseil/absl/strings/libabsl_str_format_internal.a +[134/932] Building CXX object third_party/abseil/absl/crc/CMakeFiles/absl_crc32c.dir/internal/crc_memcpy_fallback.cc.o +[135/932] Building CXX object third_party/abseil/absl/crc/CMakeFiles/absl_crc32c.dir/internal/crc_non_temporal_memcpy.cc.o +[136/932] Linking CXX static library third_party/abseil/absl/time/libabsl_time.a +[137/932] Building CXX object third_party/abseil/absl/crc/CMakeFiles/absl_crc32c.dir/internal/crc_memcpy_x86_arm_combined.cc.o +[138/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_symbolize.dir/symbolize.cc.o +[139/932] Building CXX object third_party/abseil/absl/hash/CMakeFiles/absl_city.dir/internal/city.cc.o +[140/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_kernel_timeout_internal.dir/internal/kernel_timeout.cc.o +[141/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_graphcycles_internal.dir/internal/graphcycles.cc.o +[142/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/abstract_numeric.cc.o +[143/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/abstract_float.cc.o +[144/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/abstract_int.cc.o +[145/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/array.cc.o +[146/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/array_count.cc.o +[147/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/atomic.cc.o +[148/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/binding_array.cc.o +[149/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/buffer.cc.o +[150/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/bool.cc.o +[151/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/builtin_structs.cc.o +[152/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/depth_multisampled_texture.cc.o +[153/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/depth_texture.cc.o +[154/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/external_texture.cc.o +[155/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/f32.cc.o +[156/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/f16.cc.o +[157/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/function.cc.o +[158/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/i32.cc.o +[159/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/i8.cc.o +[160/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/invalid.cc.o +[161/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/input_attachment.cc.o +[162/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/matrix.cc.o +[163/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/manager.cc.o +[164/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/node.cc.o +[165/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/numeric_scalar.cc.o +[166/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/memory_view.cc.o +[167/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/multisampled_texture.cc.o +[168/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/pointer.cc.o +[169/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/reference.cc.o +[170/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/resource_table.cc.o +[171/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/sampled_texture.cc.o +[172/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/sampler.cc.o +[173/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/resource_type.cc.o +[174/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/sampler_kind.cc.o +[175/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/scalar.cc.o +[176/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/storage_texture.cc.o +[177/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/string.cc.o +[178/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/subgroup_matrix.cc.o +[179/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/struct.cc.o +[180/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/swizzle_view.cc.o +[181/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/texel_buffer.cc.o +[182/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/texture_dimension.cc.o +[183/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/texture.cc.o +[184/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/type.cc.o +[185/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/u16.cc.o +[186/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/unique_node.cc.o +[187/932] Linking CXX static library src/tint/libtint_lang_core.a +[188/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/u32.cc.o +[189/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/u64.cc.o +[190/932] Linking CXX static library src/tint/libtint_utils_diagnostic.a +[191/932] Linking CXX static library src/tint/libtint_utils_symbol.a +[192/932] Linking CXX static library third_party/abseil/absl/base/libabsl_tracing_internal.a +[193/932] Linking CXX static library third_party/abseil/absl/crc/libabsl_crc32c.a +[194/932] Linking CXX static library third_party/abseil/absl/debugging/libabsl_stacktrace.a +[195/932] Linking CXX static library third_party/abseil/absl/debugging/libabsl_symbolize.a +[196/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/u8.cc.o +[197/932] Linking CXX static library third_party/abseil/absl/hash/libabsl_city.a +[198/932] Linking CXX static library third_party/abseil/absl/synchronization/libabsl_graphcycles_internal.a +[199/932] Linking CXX static library third_party/abseil/absl/synchronization/libabsl_kernel_timeout_internal.a +[200/932] Building CXX object third_party/abseil/absl/profiling/CMakeFiles/absl_exponential_biased.dir/internal/exponential_biased.cc.o +[201/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/vector.cc.o +[202/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_type.dir/lang/core/type/void.cc.o +[203/932] Building CXX object third_party/abseil/absl/hash/CMakeFiles/absl_hash.dir/internal/hash.cc.o +[204/932] Building CXX object third_party/abseil/absl/crc/CMakeFiles/absl_crc_cord_state.dir/internal/crc_cord_state.cc.o +[205/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/barrier.cc.o +[206/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/internal/sem_waiter.cc.o +[207/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/blocking_counter.cc.o +[208/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/internal/create_thread_identity.cc.o +[209/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/internal/win32_waiter.cc.o +[210/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/internal/futex_waiter.cc.o +[211/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/internal/per_thread_sem.cc.o +[212/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/internal/pthread_waiter.cc.o +[213/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/internal/stdcpp_waiter.cc.o +[214/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/internal/waiter_base.cc.o +[215/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/notification.cc.o +[216/932] Building CXX object third_party/abseil/absl/synchronization/CMakeFiles/absl_synchronization.dir/mutex.cc.o +[217/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_constant.dir/lang/core/constant/composite.cc.o +[218/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_constant.dir/lang/core/constant/invalid.cc.o +[219/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_constant.dir/lang/core/constant/node.cc.o +[220/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_constant.dir/lang/core/constant/manager.cc.o +[221/932] Linking CXX static library src/tint/libtint_lang_core_type.a +[222/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_constant.dir/lang/core/constant/scalar.cc.o +[223/932] Linking CXX static library third_party/abseil/absl/crc/libabsl_crc_cord_state.a +[224/932] Linking CXX static library third_party/abseil/absl/hash/libabsl_hash.a +[225/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_constant.dir/lang/core/constant/splat.cc.o +[226/932] Linking CXX static library third_party/abseil/absl/profiling/libabsl_exponential_biased.a +[227/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_constant.dir/lang/core/constant/value.cc.o +[228/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_constant.dir/lang/core/constant/string.cc.o +[229/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cord_internal.dir/internal/cord_rep_btree_navigator.cc.o +[230/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cord_internal.dir/internal/cord_internal.cc.o +[231/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cord_internal.dir/internal/cord_rep_btree_reader.cc.o +[232/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cord_internal.dir/internal/cord_rep_crc.cc.o +[233/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cord_internal.dir/internal/cord_rep_btree.cc.o +[234/932] Linking CXX static library third_party/abseil/absl/synchronization/libabsl_synchronization.a +[235/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cord_internal.dir/internal/cord_rep_consume.cc.o +[236/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cordz_functions.dir/internal/cordz_functions.cc.o +[237/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cordz_handle.dir/internal/cordz_handle.cc.o +[238/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_intrinsic.dir/lang/core/intrinsic/ctor_conv.cc.o +[239/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl.dir/lang/wgsl/enums.cc.o +[240/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl.dir/lang/wgsl/feature_status.cc.o +[241/932] Linking CXX static library third_party/abseil/absl/strings/libabsl_cord_internal.a +[242/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl.dir/lang/wgsl/reserved_words.cc.o +[243/932] Linking CXX static library third_party/abseil/absl/strings/libabsl_cordz_functions.a +[244/932] Linking CXX static library third_party/abseil/absl/strings/libabsl_cordz_handle.a +[245/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/array_count.cc.o +[246/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_intrinsic.dir/lang/core/intrinsic/data.cc.o +[247/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_intrinsic.dir/lang/core/intrinsic/table.cc.o +[248/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cordz_info.dir/internal/cordz_info.cc.o +[249/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/access.cc.o +[250/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/binary.cc.o +[251/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/block.cc.o +[252/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/block_param.cc.o +[253/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/break_if.cc.o +[254/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/builder.cc.o +[255/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/builtin_call.cc.o +[256/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/call.cc.o +[257/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/constant.cc.o +[258/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_constant.dir/lang/core/constant/eval.cc.o +[259/932] Linking CXX static library src/tint/libtint_lang_core_constant.a +[260/932] Linking CXX static library src/tint/libtint_lang_core_intrinsic.a +[261/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/clone_context.cc.o +[262/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/constexpr_if.cc.o +[263/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/control_instruction.cc.o +[264/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/construct.cc.o +[265/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/continue.cc.o +[266/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/convert.cc.o +[267/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/core_binary.cc.o +[268/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/core_builtin_call.cc.o +[269/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/core_unary.cc.o +[270/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/exit.cc.o +[271/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/discard.cc.o +[272/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/exit_if.cc.o +[273/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/evaluator.cc.o +[274/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/exit_loop.cc.o +[275/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/function.cc.o +[276/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/exit_switch.cc.o +[277/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/instruction.cc.o +[278/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/function_param.cc.o +[279/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/if.cc.o +[280/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/instruction_result.cc.o +[281/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/let.cc.o +[282/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/disassembler.cc.o +[283/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/load.cc.o +[284/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/member_builtin_call.cc.o +[285/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/load_vector_element.cc.o +[286/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/loop.cc.o +[287/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/multi_in_block.cc.o +[288/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/operand_instruction.cc.o +[289/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/module.cc.o +[290/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/next_iteration.cc.o +[291/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/override.cc.o +[292/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/phony.cc.o +[293/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/reflection.cc.o +[294/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/return.cc.o +[295/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/store.cc.o +[296/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/store_vector_element.cc.o +[297/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/swizzle.cc.o +[298/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/switch.cc.o +[299/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/terminate_invocation.cc.o +[300/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/terminator.cc.o +[301/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/unary.cc.o +[302/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/unused.cc.o +[303/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/unreachable.cc.o +[304/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/value.cc.o +[305/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/user_call.cc.o +[306/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/accessor_expression.cc.o +[307/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir.dir/lang/core/ir/var.cc.o +[308/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/attribute.cc.o +[309/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/alias.cc.o +[310/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/assignment_statement.cc.o +[311/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/blend_src_attribute.cc.o +[312/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/binary_expression.cc.o +[313/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/binding_attribute.cc.o +[314/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/block_statement.cc.o +[315/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/bool_literal_expression.cc.o +[316/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/break_if_statement.cc.o +[317/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/break_statement.cc.o +[318/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/builtin_attribute.cc.o +[319/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/builder.cc.o +[320/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/call_statement.cc.o +[321/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/call_expression.cc.o +[322/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/case_selector.cc.o +[323/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/color_attribute.cc.o +[324/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/case_statement.cc.o +[325/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/compound_assignment_statement.cc.o +[326/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/continue_statement.cc.o +[327/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/const_assert.cc.o +[328/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/const.cc.o +[329/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/diagnostic_control.cc.o +[330/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/diagnostic_attribute.cc.o +[331/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/diagnostic_directive.cc.o +[332/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/expression.cc.o +[333/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/diagnostic_rule_name.cc.o +[334/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/enable.cc.o +[335/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/discard_statement.cc.o +[336/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/extension.cc.o +[337/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/float_literal_expression.cc.o +[338/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/for_loop_statement.cc.o +[339/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/group_attribute.cc.o +[340/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/id_attribute.cc.o +[341/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/function.cc.o +[342/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/identifier.cc.o +[343/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/identifier_expression.cc.o +[344/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/if_statement.cc.o +[345/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/increment_decrement_statement.cc.o +[346/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/input_attachment_index_attribute.cc.o +[347/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/index_accessor_expression.cc.o +[348/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/int_literal_expression.cc.o +[349/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/literal_expression.cc.o +[350/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/interpolate_attribute.cc.o +[351/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/invariant_attribute.cc.o +[352/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/let.cc.o +[353/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/location_attribute.cc.o +[354/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/loop_statement.cc.o +[355/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/node.cc.o +[356/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/member_accessor_expression.cc.o +[357/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/must_use_attribute.cc.o +[358/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/module.cc.o +[359/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/override.cc.o +[360/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/parameter.cc.o +[361/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/pipeline_stage.cc.o +[362/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/phony_expression.cc.o +[363/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/requires.cc.o +[364/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/return_statement.cc.o +[365/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/stage_attribute.cc.o +[366/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/statement.cc.o +[367/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/struct.cc.o +[368/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/struct_member.cc.o +[369/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/struct_member_align_attribute.cc.o +[370/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/subgroup_size_attribute.cc.o +[371/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/struct_member_size_attribute.cc.o +[372/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/switch_statement.cc.o +[373/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/type_decl.cc.o +[374/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/templated_identifier.cc.o +[375/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/variable.cc.o +[376/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/unary_op_expression.cc.o +[377/932] Linking CXX static library src/tint/libtint_lang_wgsl.a +[378/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/var.cc.o +[379/932] Linking CXX static library third_party/abseil/absl/strings/libabsl_cordz_info.a +[380/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/variable_decl_statement.cc.o +[381/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/while_statement.cc.o +[382/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ast.dir/lang/wgsl/ast/workgroup_attribute.cc.o +[383/932] Building CXX object third_party/abseil/absl/container/CMakeFiles/absl_hashtablez_sampler.dir/internal/hashtablez_sampler.cc.o +[384/932] Building CXX object third_party/abseil/absl/container/CMakeFiles/absl_hashtablez_sampler.dir/internal/hashtablez_sampler_force_weak_definition.cc.o +[385/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cord.dir/cord_analysis.cc.o +[386/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_analysis.dir/lang/core/ir/analysis/for_loop_analysis.cc.o +[387/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_analysis.dir/lang/core/ir/analysis/loop_analysis.cc.o +[388/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cord.dir/cord.cc.o +[389/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_analysis.dir/lang/core/ir/analysis/subgroup_matrix.cc.o +[390/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_analysis.dir/lang/core/ir/analysis/integer_range_analysis.cc.o +[391/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_validator.dir/lang/core/ir/validator/validate.cc.o +[392/932] Linking CXX static library src/tint/libtint_lang_core_ir.a +[393/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_type.dir/lang/msl/type/bias.cc.o +[394/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_validator.dir/lang/core/ir/validator/validator_function.cc.o +[395/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_validator.dir/lang/core/ir/validator/validator_io.cc.o +[396/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_validator.dir/lang/core/ir/validator/validator_types.cc.o +[397/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_validator.dir/lang/core/ir/validator/validator.cc.o +[398/932] Linking CXX static library src/tint/libtint_lang_wgsl_ast.a +[399/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_type.dir/lang/msl/type/cooperative_tensor.cc.o +[400/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_type.dir/lang/msl/type/gradient.cc.o +[401/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_validator.dir/lang/core/ir/validator/validator_instructions.cc.o +[402/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_type.dir/lang/msl/type/level.cc.o +[403/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl.dir/lang/msl/builtin_fn.cc.o +[404/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/accessor_expression.cc.o +[405/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/array_count.cc.o +[406/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/behavior.cc.o +[407/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/array.cc.o +[408/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/block_statement.cc.o +[409/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/break_if_statement.cc.o +[410/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/builtin_enum_expression.cc.o +[411/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/call.cc.o +[412/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/builtin_fn.cc.o +[413/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/call_target.cc.o +[414/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/expression.cc.o +[415/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/for_loop_statement.cc.o +[416/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/function_expression.cc.o +[417/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/if_statement.cc.o +[418/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/index_accessor_expression.cc.o +[419/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/function.cc.o +[420/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/load.cc.o +[421/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/info.cc.o +[422/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/loop_statement.cc.o +[423/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/materialize.cc.o +[424/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/member_accessor_expression.cc.o +[425/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/module.cc.o +[426/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/node.cc.o +[427/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/struct.cc.o +[428/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/statement.cc.o +[429/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/switch_statement.cc.o +[430/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/type_expression.cc.o +[431/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/value_conversion.cc.o +[432/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/value_constructor.cc.o +[433/932] Linking CXX static library third_party/abseil/absl/container/libabsl_hashtablez_sampler.a +[434/932] Linking CXX static library third_party/abseil/absl/strings/libabsl_cord.a +[435/932] Linking CXX static library src/tint/libtint_lang_core_ir_analysis.a +[436/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/value_expression.cc.o +[437/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/while_statement.cc.o +[438/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_sem.dir/lang/wgsl/sem/variable.cc.o +[439/932] Building CXX object third_party/abseil/absl/container/CMakeFiles/absl_raw_hash_set.dir/internal/raw_hash_set.cc.o +[440/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/binding_remapper.cc.o +[441/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/bgra8unorm_polyfill.cc.o +[442/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/block_decorated_structs.cc.o +[443/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/binary_polyfill.cc.o +[444/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/array_length_from.cc.o +[445/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/change_immediate_to_uniform.cc.o +[446/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/builtin_scalarize.cc.o +[447/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/collapse_subgroup_min_max.cc.o +[448/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/combine_access_instructions.cc.o +[449/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/conversion_polyfill.cc.o +[450/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/dead_code_elimination.cc.o +[451/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/builtin_polyfill.cc.o +[452/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/demote_to_helper.cc.o +[453/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/lower_swizzle_view.cc.o +[454/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/prepare_immediate_data.cc.o +[455/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/decompose_access.cc.o +[456/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/direct_variable_access.cc.o +[457/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/preserve_padding.cc.o +[458/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/multiplanar_external_texture.cc.o +[459/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/prevent_infinite_loops.cc.o +[460/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/remove_continue_in_switch.cc.o +[461/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/remove_terminator_args.cc.o +[462/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/remove_uniform_vector_component_loads.cc.o +[463/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/propagate_buffer_sizes.cc.o +[464/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/rename_conflicts.cc.o +[465/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/resource_table_helper.cc.o +[466/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/resource_table.cc.o +[467/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/signed_integer_polyfill.cc.o +[468/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/shader_io.cc.o +[469/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/robustness.cc.o +[470/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/single_entry_point.cc.o +[471/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/value_to_let.cc.o +[472/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/std140.cc.o +[473/932] Linking CXX static library src/tint/libtint_lang_core_ir_validator.a +[474/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/vectorize_scalar_matrix_constructors.cc.o +[475/932] Linking CXX static library src/tint/libtint_lang_msl_type.a +[476/932] Linking CXX static library src/tint/libtint_lang_msl.a +[477/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/substitute_overrides.cc.o +[478/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_intrinsic.dir/lang/wgsl/intrinsic/ctor_conv.cc.o +[479/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/vertex_pulling.cc.o +[480/932] Linking CXX static library src/tint/libtint_lang_wgsl_sem.a +[481/932] Building CXX object src/tint/CMakeFiles/tint_lang_core_ir_transform.dir/lang/core/ir/transform/zero_init_workgroup_memory.cc.o +[482/932] Building CXX object src/tint/CMakeFiles/tint_utils_strconv.dir/utils/strconv/float_to_string.cc.o +[483/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_program.dir/lang/wgsl/program/program.cc.o +[484/932] Linking CXX static library third_party/abseil/absl/container/libabsl_raw_hash_set.a +[485/932] Linking CXX static library src/tint/libtint_lang_core_ir_transform.a +[486/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_intrinsic.dir/lang/msl/intrinsic/data.cc.o +[487/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_program.dir/lang/wgsl/program/program_builder.cc.o +[488/932] Linking CXX static library src/tint/libtint_lang_msl_intrinsic.a +[489/932] Building CXX object src/tint/CMakeFiles/tint_utils_strconv.dir/utils/strconv/parse_num.cc.o +[490/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_ir.dir/lang/msl/ir/component.cc.o +[491/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_intrinsic.dir/lang/wgsl/intrinsic/data.cc.o +[492/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_ir.dir/lang/msl/ir/memory_order.cc.o +[493/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_ir.dir/lang/msl/ir/builtin_call.cc.o +[494/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_ir.dir/lang/msl/ir/member_builtin_call.cc.o +[495/932] Linking CXX static library src/tint/libtint_lang_wgsl_intrinsic.a +[496/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_common.dir/lang/msl/writer/common/output.cc.o +[497/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_common.dir/lang/msl/writer/common/option_helpers.cc.o +[498/932] Linking CXX static library src/tint/libtint_lang_wgsl_program.a +[499/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_common.dir/lang/msl/writer/common/printer_support.cc.o +[500/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_common.dir/lang/msl/writer/common/options.cc.o +[501/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_resolver.dir/lang/wgsl/resolver/incomplete_type.cc.o +[502/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ir.dir/lang/wgsl/ir/unary.cc.o +[503/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ir.dir/lang/wgsl/ir/builtin_call.cc.o +[504/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_ir.dir/lang/wgsl/ir/atomic_vec2u_to_from_u64.cc.o +[505/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_resolver.dir/lang/wgsl/resolver/resolve.cc.o +[506/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_resolver.dir/lang/wgsl/resolver/unresolved_identifier.cc.o +[507/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_writer_common.dir/lang/wgsl/writer/common/common.cc.o +[508/932] Linking CXX static library src/tint/libtint_utils_strconv.a +[509/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_resolver.dir/lang/wgsl/resolver/dependency_graph.cc.o +[510/932] Dawn: Generating files for Dawn version based utilities. +[511/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_resolver.dir/lang/wgsl/resolver/sem_helper.cc.o +[512/932] Dawn: Generating files for Dawn headers. +[513/932] Linking CXX static library src/tint/libtint_lang_msl_ir.a +[514/932] Linking CXX static library src/tint/libtint_lang_msl_writer_common.a +[515/932] Dawn: Generating files for Dawn C++ headers. +[516/932] Building CXX object src/tint/CMakeFiles/tint_utils_text_generator.dir/utils/text_generator/text_generator.cc.o +[517/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_resolver.dir/lang/wgsl/resolver/uniformity.cc.o +[518/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/binary_polyfill.cc.o +[519/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_printer.dir/lang/msl/writer/printer/printer.cc.o +[520/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/argument_buffers.cc.o +[521/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_resolver.dir/lang/wgsl/resolver/validator.cc.o +[522/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/convert_print_to_log.cc.o +[523/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/cooperative_tensors.cc.o +[524/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/decompose_buffer.cc.o +[525/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/fix_u32_div_mod.cc.o +[526/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/builtin_polyfill.cc.o +[527/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/fix_type_layout.cc.o +[528/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/module_constant.cc.o +[529/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/polyfill_bool_vector_dynamic_stores.cc.o +[530/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/module_scope_vars.cc.o +[531/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/raise.cc.o +[532/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_resolver.dir/lang/wgsl/resolver/resolver.cc.o +[533/932] Linking CXX static library src/tint/libtint_lang_wgsl_ir.a +[534/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/resource_table_helper.cc.o +[535/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/shader_io.cc.o +[536/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/simd_ballot.cc.o +[537/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/switch_return.cc.o +[538/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer_raise.dir/lang/msl/writer/raise/validate_subgroup_matrix.cc.o +[539/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_reader_parser.dir/lang/wgsl/reader/parser/lexer.cc.o +[540/932] Linking CXX static library src/tint/libtint_lang_wgsl_resolver.a +[541/932] Linking CXX static library src/tint/libtint_lang_wgsl_writer_common.a +[542/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_reader_lower.dir/lang/wgsl/reader/lower/lower.cc.o +[543/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_reader_parser.dir/lang/wgsl/reader/parser/token.cc.o +[544/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_writer_ast_printer.dir/lang/wgsl/writer/ast_printer/ast_printer.cc.o +[545/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_writer_raise.dir/lang/wgsl/writer/raise/ptr_to_ref.cc.o +[546/932] Linking CXX static library src/tint/libtint_utils_text_generator.a +[547/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_writer_raise.dir/lang/wgsl/writer/raise/raise.cc.o +[548/932] Dawn: Generating files for Dawn GPU info utilities. +[549/932] Building CXX object third_party/abseil/absl/flags/CMakeFiles/absl_flags_commandlineflag_internal.dir/internal/commandlineflag.cc.o +[550/932] Linking CXX static library src/tint/libtint_lang_msl_writer_printer.a +[551/932] Linking CXX static library src/tint/libtint_lang_msl_writer_raise.a +[552/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_inspector.dir/lang/wgsl/inspector/entry_point.cc.o +[553/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_reader_parser.dir/lang/wgsl/reader/parser/parser.cc.o +[554/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_writer_raise.dir/lang/wgsl/writer/raise/value_to_let.cc.o +[555/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_reader_program_to_ir.dir/lang/wgsl/reader/program_to_ir/program_to_ir.cc.o +[556/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_inspector.dir/lang/wgsl/inspector/scalar.cc.o +[557/932] Linking CXX static library src/tint/libtint_lang_wgsl_reader_lower.a +[558/932] Linking CXX static library src/tint/libtint_lang_wgsl_reader_parser.a +[559/932] Linking CXX static library src/tint/libtint_lang_wgsl_reader_program_to_ir.a +[560/932] Linking CXX static library src/tint/libtint_lang_wgsl_writer_ast_printer.a +[561/932] Linking CXX static library src/tint/libtint_lang_wgsl_writer_raise.a +[562/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_inspector.dir/lang/wgsl/inspector/resource_binding.cc.o +[563/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_writer_ir_to_program.dir/lang/wgsl/writer/ir_to_program/ir_to_program.cc.o +[564/932] Linking CXX static library src/tint/libtint_lang_wgsl_writer_ir_to_program.a +[565/932] Building CXX object src/tint/CMakeFiles/tint_lang_msl_writer.dir/lang/msl/writer/writer.cc.o +[566/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_writer.dir/lang/wgsl/writer/output.cc.o +[567/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/AlignedAlloc.cpp.o +[568/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/__/__/__/gen/src/dawn/common/GPUInfo_autogen.cpp.o +[569/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/Defer.cpp.o +[570/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/FutureUtils.cpp.o +[571/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_reader.dir/lang/wgsl/reader/reader.cc.o +[572/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_writer.dir/lang/wgsl/writer/writer.cc.o +[573/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/DynamicLib.cpp.o +[574/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/RefCounted.cpp.o +[575/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/Math.cpp.o +[576/932] Building CXX object src/tint/CMakeFiles/tint_lang_wgsl_inspector.dir/lang/wgsl/inspector/inspector.cc.o +[577/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/Result.cpp.o +[578/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/GPUInfo.cpp.o +[579/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/MemoryBlockAllocator.cpp.o +[580/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/Sha3.cpp.o +[581/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/StringViewUtils.cpp.o +[582/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/SystemHandle.cpp.o +[583/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/SlabAllocator.cpp.o +[584/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/SystemUtils.cpp.o +[585/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/WeakRefSupport.cpp.o +[586/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/SystemUtils_mac.mm.o +[587/932] Linking CXX static library third_party/abseil/absl/flags/libabsl_flags_commandlineflag_internal.a +[588/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/ExternalTextureParams.cpp.o +[589/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/ThreadLocal.cpp.o +[590/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/WGPUDeviceCallbackInfos.cpp.o +[591/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_internal_platform.dir/internal/randen_round_keys.cc.o +[592/932] Building CXX object third_party/abseil/absl/flags/CMakeFiles/absl_flags_commandlineflag.dir/commandlineflag.cc.o +[593/932] Building CXX object src/dawn/common/CMakeFiles/dawn_common.dir/IOSurfaceUtils.cpp.o +[594/932] Linking CXX static library src/tint/libtint_lang_msl_writer.a +[595/932] Building CXX object third_party/abseil/absl/flags/CMakeFiles/absl_flags_program_name.dir/internal/program_name.cc.o +[596/932] Linking CXX static library src/tint/libtint_lang_wgsl_inspector.a +[597/932] Linking CXX static library src/tint/libtint_lang_wgsl_reader.a +[598/932] Linking CXX static library src/tint/libtint_lang_wgsl_writer.a +[599/932] Linking CXX static library src/dawn/common/libdawn_common.a +[600/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_system_utils.dir/SystemUtils.cpp.o +[601/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_internal_fnmatch.dir/internal/fnmatch.cc.o +[602/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_system_utils.dir/EmptyDebugLogger.cpp.o +[603/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_internal_proto.dir/internal/proto.cc.o +[604/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_system_utils.dir/ObjCUtils.mm.o +[605/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_system_utils.dir/OSXTimer.cpp.o +[606/932] Linking CXX static library third_party/abseil/absl/flags/libabsl_flags_program_name.a +[607/932] Building CXX object src/dawn/platform/CMakeFiles/dawn_platform.dir/metrics/HistogramMacros.cpp.o +[608/932] Building CXX object src/dawn/platform/CMakeFiles/dawn_platform.dir/DawnPlatform.cpp.o +[609/932] Linking CXX static library third_party/abseil/absl/flags/libabsl_flags_commandlineflag.a +[610/932] Building CXX object src/dawn/platform/CMakeFiles/dawn_platform.dir/WorkerThread.cpp.o +[611/932] Building CXX object src/tint/CMakeFiles/tint_api.dir/api/tint.cc.o +[612/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_internal_proto.a +[613/932] Building CXX object third_party/abseil/absl/flags/CMakeFiles/absl_flags_config.dir/usage_config.cc.o +[614/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_internal_fnmatch.a +[615/932] Linking CXX static library third_party/abseil/absl/random/libabsl_random_internal_platform.a +[616/932] Building CXX object third_party/abseil/absl/flags/CMakeFiles/absl_flags_private_handle_accessor.dir/internal/private_handle_accessor.cc.o +[617/932] Linking CXX static library src/tint/libtint_api.a +[618/932] Linking CXX static library src/dawn/platform/libdawn_platform.a +[619/932] Linking CXX static library third_party/abseil/absl/flags/libabsl_flags_config.a +[620/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_system_utils.dir/CommandLineParser.cpp.o +[621/932] Linking CXX static library third_party/abseil/absl/flags/libabsl_flags_private_handle_accessor.a +[622/932] Linking CXX static library src/dawn/utils/libdawn_system_utils.a +[623/932] Building CXX object third_party/abseil/absl/flags/CMakeFiles/absl_flags_marshalling.dir/marshalling.cc.o +[624/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_internal_randen_hwaes_impl.dir/internal/randen_hwaes.cc.o +[625/932] Linking CXX static library third_party/abseil/absl/flags/libabsl_flags_marshalling.a +[626/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_entry.dir/log_entry.cc.o +[627/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_entry.a +[628/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_vlog_config_internal.dir/internal/vlog_config.cc.o +[629/932] Linking CXX static library third_party/abseil/absl/log/libabsl_vlog_config_internal.a +[630/932] Building CXX object third_party/abseil/absl/flags/CMakeFiles/absl_flags_internal.dir/internal/flag.cc.o +[631/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_internal_globals.dir/internal/globals.cc.o +[632/932] Linking CXX static library third_party/abseil/absl/random/libabsl_random_internal_randen_hwaes_impl.a +[633/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_sink.dir/log_sink.cc.o +[634/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_globals.dir/globals.cc.o +[635/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_internal_randen_slow.dir/internal/randen_slow.cc.o +[636/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_leak_check.dir/leak_check.cc.o +[637/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_internal_randen_hwaes.dir/internal/randen_detect.cc.o +[638/932] Linking CXX static library third_party/abseil/absl/flags/libabsl_flags_internal.a +[639/932] Dawn: Generating files for Dawn native utilities. +[640/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_internal_globals.a +[641/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_strerror.dir/internal/strerror.cc.o +[642/932] Building CXX object third_party/abseil/absl/flags/CMakeFiles/absl_flags_reflection.dir/reflection.cc.o +[643/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_examine_stack.dir/internal/examine_stack.cc.o +[644/932] Linking CXX static library third_party/abseil/absl/flags/libabsl_flags_reflection.a +[645/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_internal_nullguard.dir/internal/nullguard.cc.o +[646/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_globals.a +[647/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_sink.a +[648/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_internal_format.dir/internal/log_format.cc.o +[649/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_internal_randen.dir/internal/randen.cc.o +[650/932] Linking CXX static library third_party/abseil/absl/random/libabsl_random_internal_randen_slow.a +[651/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_internal_log_sink_set.dir/internal/log_sink_set.cc.o +[652/932] Linking CXX static library third_party/abseil/absl/random/libabsl_random_internal_randen_hwaes.a +[653/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_internal_structured_proto.dir/internal/structured_proto.cc.o +[654/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_seed_gen_exception.dir/seed_gen_exception.cc.o +[655/932] Dawn: Generating files for Dawn native utilities. +[656/932] Dawn: Generating files for Dawn C++ wrapper. +[657/932] Building CXX object third_party/abseil/absl/flags/CMakeFiles/absl_flags_usage_internal.dir/internal/usage.cc.o +[658/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_internal_seed_material.dir/internal/seed_material.cc.o +[659/932] Dawn: Generating files for Dawn wire. +[660/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/__/__/__/gen/src/dawn/native/wgpu_structs_defaults_autogen.cpp.o +[661/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/__/__/__/gen/src/dawn/native/wgpu_structs_autogen.cpp.o +[662/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/__/__/__/gen/src/dawn/native/ValidationUtils_autogen.cpp.o +[663/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/__/__/__/gen/src/dawn/native/ObjectType_autogen.cpp.o +[664/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/__/__/__/gen/src/dawn/native/webgpu_absl_format_autogen.cpp.o +[665/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/__/__/__/gen/src/dawn/native/webgpu_StreamImpl_autogen.cpp.o +[666/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/__/__/__/gen/src/dawn/native/ChainUtils_autogen.cpp.o +[667/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/MultiDrawEncoder.mm.o +[668/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/__/__/__/gen/src/dawn/native/ProcTable.cpp.o +[669/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/AsyncTask.cpp.o +[670/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Adapter.cpp.o +[671/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/AttachmentState.cpp.o +[672/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ApplyClearColorValueWithDrawHelper.cpp.o +[673/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BackendConnection.cpp.o +[674/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BindGroupLayout.cpp.o +[675/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BindGroup.cpp.o +[676/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BindingInfo.cpp.o +[677/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BindGroupLayoutInternal.cpp.o +[678/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BlitBufferToDepthStencil.cpp.o +[679/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BlitBufferToTexture.cpp.o +[680/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BlitColorToColorWithDraw.cpp.o +[681/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BuddyAllocator.cpp.o +[682/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BlitDepthToDepth.cpp.o +[683/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Blob.cpp.o +[684/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BuddyMemoryAllocator.cpp.o +[685/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BlitTextureToBuffer.cpp.o +[686/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/BlobCache.cpp.o +[687/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CacheKey.cpp.o +[688/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CallbackTaskManager.cpp.o +[689/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CommandAllocator.cpp.o +[690/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Buffer.cpp.o +[691/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CachedObject.cpp.o +[692/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CacheRequest.cpp.o +[693/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CommandBuffer.cpp.o +[694/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Commands.cpp.o +[695/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CommandBufferStateTracker.cpp.o +[696/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CompilationMessages.cpp.o +[697/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CommandValidation.cpp.o +[698/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CommandEncoder.cpp.o +[699/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/dawn_platform.cpp.o +[700/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ComputePipeline.cpp.o +[701/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ComputePassEncoder.cpp.o +[702/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CreatePipelineAsyncEvent.cpp.o +[703/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/CopyTextureForBrowserHelper.cpp.o +[704/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Error.cpp.o +[705/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ErrorData.cpp.o +[706/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ErrorInjector.cpp.o +[707/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/DeviceGuard.cpp.o +[708/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/DynamicUploader.cpp.o +[709/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/EncodingContext.cpp.o +[710/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ErrorScope.cpp.o +[711/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Device.cpp.o +[712/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Features.cpp.o +[713/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ExecutionQueue.cpp.o +[714/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/EventManager.cpp.o +[715/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ImmediatesLayout.cpp.o +[716/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ExternalTexture.cpp.o +[717/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Format.cpp.o +[718/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/IndirectDrawMetadata.cpp.o +[719/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ObjectContentHasher.cpp.o +[720/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/InternalPipelineStore.cpp.o +[721/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/IndirectDrawValidationEncoder.cpp.o +[722/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Limits.cpp.o +[723/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Instance.cpp.o +[724/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ObjectBase.cpp.o +[725/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/PerStage.cpp.o +[726/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/PassResourceUsage.cpp.o +[727/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/PassResourceUsageTracker.cpp.o +[728/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/PipelineCache.cpp.o +[729/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/PhysicalDevice.cpp.o +[730/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/PooledResourceMemoryAllocator.cpp.o +[731/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Pipeline.cpp.o +[732/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/PipelineLayout.cpp.o +[733/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ProgrammableEncoder.cpp.o +[734/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/QuerySet.cpp.o +[735/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/QueryHelper.cpp.o +[736/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/RenderBundle.cpp.o +[737/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Queue.cpp.o +[738/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/RenderBundleEncoder.cpp.o +[739/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ResourceMemoryAllocation.cpp.o +[740/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/RenderPassWorkaroundsHelper.cpp.o +[741/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/RenderEncoderBase.cpp.o +[742/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/RenderPassEncoder.cpp.o +[743/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/RingBufferAllocator.cpp.o +[744/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/RenderPipeline.cpp.o +[745/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ResourceTableDefaultResources.cpp.o +[746/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ResourceTable.cpp.o +[747/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ScratchBuffer.cpp.o +[748/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Sampler.cpp.o +[749/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ShaderModuleParseRequest.cpp.o +[750/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/SharedBufferMemory.cpp.o +[751/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/SharedFence.cpp.o +[752/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/SharedTextureMemory.cpp.o +[753/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/SharedResourceMemory.cpp.o +[754/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/stream/BlobSource.cpp.o +[755/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Subresource.cpp.o +[756/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/stream/ByteVectorSink.cpp.o +[757/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ShaderModule.cpp.o +[758/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/SystemEvent.cpp.o +[759/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/SwapChain.cpp.o +[760/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Surface.cpp.o +[761/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/TexelBufferView.cpp.o +[762/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Toggles.cpp.o +[763/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Texture.cpp.o +[764/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/ValidationUtils.cpp.o +[765/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/TintUtils.cpp.o +[766/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/WaitListEvent.cpp.o +[767/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/utils/WGPUHelpers.cpp.o +[768/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/webgpu_absl_format.cpp.o +[769/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/BackendMTL.mm.o +[770/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/BindGroupMTL.mm.o +[771/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/BindGroupLayoutMTL.mm.o +[772/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/BufferMTL.mm.o +[773/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/CommandRecordingContext.mm.o +[774/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/CommandBufferMTL.mm.o +[775/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/ComputePipelineMTL.mm.o +[776/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/PipelineLayoutMTL.mm.o +[777/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/PhysicalDeviceMTL.mm.o +[778/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/DeviceMTL.mm.o +[779/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/QuerySetMTL.mm.o +[780/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/QueueMTL.mm.o +[781/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/RenderPipelineMTL.mm.o +[782/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/SamplerMTL.mm.o +[783/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/SharedFenceMTL.mm.o +[784/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/Surface_metal.mm.o +[785/932] Linking CXX static library third_party/abseil/absl/base/libabsl_strerror.a +[786/932] Linking CXX static library third_party/abseil/absl/debugging/libabsl_examine_stack.a +[787/932] Building CXX object third_party/abseil/absl/debugging/CMakeFiles/absl_failure_signal_handler.dir/failure_signal_handler.cc.o +[788/932] Linking CXX static library third_party/abseil/absl/debugging/libabsl_leak_check.a +[789/932] Linking CXX static library third_party/abseil/absl/flags/libabsl_flags_usage_internal.a +[790/932] Building CXX object third_party/abseil/absl/flags/CMakeFiles/absl_flags_usage.dir/usage.cc.o +[791/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_internal_conditions.dir/internal/conditions.cc.o +[792/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_internal_format.a +[793/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/ShaderModuleMTL.mm.o +[794/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_internal_log_sink_set.a +[795/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_internal_nullguard.a +[796/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_internal_structured_proto.a +[797/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/SharedTextureMemoryMTL.mm.o +[798/932] Linking CXX static library third_party/abseil/absl/random/libabsl_random_seed_gen_exception.a +[799/932] Linking CXX static library third_party/abseil/absl/random/libabsl_random_internal_seed_material.a +[800/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/SwapChainMTL.mm.o +[801/932] Linking CXX static library third_party/abseil/absl/random/libabsl_random_internal_randen.a +[802/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/TextureMTL.mm.o +[803/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_internal_message.dir/internal/log_message.cc.o +[804/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_internal_entropy_pool.dir/internal/entropy_pool.cc.o +[805/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native_objects.dir/metal/UtilsMetal.mm.o +[806/932] Building CXX object src/dawn/CMakeFiles/dawn_proc.dir/__/__/gen/src/dawn/dawn_proc.cpp.o +[807/932] Building CXX object third_party/abseil/absl/profiling/CMakeFiles/absl_profile_builder.dir/internal/profile_builder.cc.o +[808/932] Building CXX object third_party/abseil/absl/status/CMakeFiles/absl_status.dir/internal/status_internal.cc.o +[809/932] Building CXX object third_party/abseil/absl/status/CMakeFiles/absl_status.dir/status_payload_printer.cc.o +[810/932] Building CXX object third_party/abseil/absl/status/CMakeFiles/absl_status.dir/status.cc.o +[811/932] Building CXX object src/dawn/CMakeFiles/dawn_proc.dir/__/__/gen/src/dawn/dawn_thread_dispatch_proc.cpp.o +[812/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_wgpu_utils.dir/__/__/__/gen/src/dawn/utils/ComboLimits.cpp.o +[813/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_wgpu_utils.dir/ScopedIgnoreValidationErrors.cpp.o +[814/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_wgpu_utils.dir/TextureUtils.cpp.o +[815/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_wgpu_utils.dir/ComboRenderBundleEncoderDescriptor.cpp.o +[816/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_wgpu_utils.dir/ComboRenderPipelineDescriptor.cpp.o +[817/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/__/__/__/gen/src/dawn/wire/wgpu_structs_autogen.cpp.o +[818/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_wgpu_utils.dir/WGPUHelpers.cpp.o +[819/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/__/__/__/gen/src/dawn/wire/client/wgpu_structs_autogen.cpp.o +[820/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/__/__/__/gen/src/dawn/wire/client/ClientHandlers_autogen.cpp.o +[821/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/ChunkedCommandSerializer.cpp.o +[822/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/ChunkedCommandHandler.cpp.o +[823/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/__/__/__/gen/src/dawn/wire/server/ServerDoers_autogen.cpp.o +[824/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/__/__/__/gen/src/dawn/wire/server/ServerHandlers_autogen.cpp.o +[825/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/InlineSharedMemoryManager.cpp.o +[826/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/Adapter.cpp.o +[827/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/Client.cpp.o +[828/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/Buffer.cpp.o +[829/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/ClientDoers.cpp.o +[830/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/ClientInlineMemoryTransferService.cpp.o +[831/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/Device.cpp.o +[832/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/LimitsAndFeatures.cpp.o +[833/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/EventManager.cpp.o +[834/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/ObjectBase.cpp.o +[835/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/Instance.cpp.o +[836/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/ObjectStore.cpp.o +[837/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/QuerySet.cpp.o +[838/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/Queue.cpp.o +[839/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/ResourceTable.cpp.o +[840/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/ObjectHandle.cpp.o +[841/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/ShaderModule.cpp.o +[842/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/Surface.cpp.o +[843/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/client/Texture.cpp.o +[844/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/server/Server.cpp.o +[845/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/server/ServerAdapter.cpp.o +[846/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/server/ServerBuffer.cpp.o +[847/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/server/ServerDevice.cpp.o +[848/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/server/ServerInlineMemoryTransferService.cpp.o +[849/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/SupportedFeatures.cpp.o +[850/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/__/__/__/gen/src/dawn/wire/WireCmd_autogen.cpp.o +[851/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/Wire.cpp.o +[852/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/server/ServerInstance.cpp.o +[853/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/server/ServerQueue.cpp.o +[854/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/server/ServerShaderModule.cpp.o +[855/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/server/ServerSurface.cpp.o +[856/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/WireDeserializeAllocator.cpp.o +[857/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_poison.dir/internal/poison.cc.o +[858/932] Linking CXX static library third_party/abseil/absl/debugging/libabsl_failure_signal_handler.a +[859/932] Dawn: Generating files for Dawn native WebGPU procs. +[860/932] Linking CXX static library third_party/abseil/absl/flags/libabsl_flags_usage.a +[861/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_internal_conditions.a +[862/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_internal_message.a +[863/932] Building CXX object third_party/abseil/absl/base/CMakeFiles/absl_scoped_set_env.dir/internal/scoped_set_env.cc.o +[864/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_internal_check_op.dir/internal/check_op.cc.o +[865/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_die_if_null.dir/die_if_null.cc.o +[866/932] Building CXX object third_party/abseil/absl/profiling/CMakeFiles/absl_periodic_sampler.dir/internal/periodic_sampler.cc.o +[867/932] Linking CXX static library third_party/abseil/absl/profiling/libabsl_profile_builder.a +[868/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_flags.dir/flags.cc.o +[869/932] Building CXX object third_party/abseil/absl/log/CMakeFiles/absl_log_initialize.dir/initialize.cc.o +[870/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/WireClient.cpp.o +[871/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_distributions.dir/discrete_distribution.cc.o +[872/932] Building CXX object third_party/abseil/absl/flags/CMakeFiles/absl_flags_parse.dir/parse.cc.o +[873/932] Building CXX object src/dawn/wire/CMakeFiles/dawn_wire.dir/WireServer.cpp.o +[874/932] Linking CXX static library third_party/abseil/absl/random/libabsl_random_internal_entropy_pool.a +[875/932] Linking CXX static library third_party/abseil/absl/status/libabsl_status.a +[876/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_distributions.dir/gaussian_distribution.cc.o +[877/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_seed_sequences.dir/seed_sequences.cc.o +[878/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_internal_distribution_test_util.dir/internal/chi_square.cc.o +[879/932] Building CXX object third_party/abseil/absl/profiling/CMakeFiles/absl_hashtable_profiler.dir/hashtable.cc.o +[880/932] Building CXX object third_party/abseil/absl/random/CMakeFiles/absl_random_internal_distribution_test_util.dir/internal/distribution_test_util.cc.o +[881/932] Building CXX object third_party/abseil/absl/status/CMakeFiles/absl_statusor.dir/statusor.cc.o +[882/932] Building CXX object src/utils/CMakeFiles/dawn_crash_handler.dir/crash_handler.cc.o +[883/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_cordz_sample_token.dir/internal/cordz_sample_token.cc.o +[884/932] Linking CXX static library src/dawn/libdawn_proc.a +[885/932] Building CXX object third_party/abseil/absl/time/CMakeFiles/absl_clock_interface.dir/clock_interface.cc.o +[886/932] Building CXX object third_party/abseil/absl/strings/CMakeFiles/absl_generic_printer_internal.dir/internal/generic_printer.cc.o +[887/932] Linking CXX static library src/dawn/utils/libdawn_wgpu_utils.a +[888/932] Building CXX object third_party/abseil/absl/status/CMakeFiles/absl_status_builder.dir/status_builder.cc.o +[889/932] Dawn: Generating files for WebGPU headers. +[890/932] Linking CXX static library src/dawn/wire/libdawn_wire.a +[891/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_test_utils.dir/BinarySemaphore.cpp.o +[892/932] Building CXX object src/utils/chromium_test_compat/CMakeFiles/dawn_shared_utils_chromium_test_compat.dir/chromium_test_compat.cc.o +[893/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_test_utils.dir/TerribleCommandBuffer.cpp.o +[894/932] Building CXX object src/dawn/native/CMakeFiles/webgpu_dawn_objects.dir/__/__/__/gen/src/dawn/native/webgpu_dawn_native_proc.cpp.o +[895/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_test_utils.dir/WireHelper.cpp.o +[896/932] Building CXX object src/dawn/utils/CMakeFiles/dawn_test_utils.dir/TestUtils.cpp.o +[897/932] Building CXX object src/dawn/replay/CMakeFiles/replay.dir/BlitBufferToDepthTexture.cpp.o +[898/932] Building CXX object src/dawn/replay/CMakeFiles/replay.dir/Capture.cpp.o +[899/932] Building CXX object src/dawn/replay/CMakeFiles/replay.dir/Deserialization.cpp.o +[900/932] Building CXX object src/dawn/replay/CMakeFiles/replay.dir/Error.cpp.o +[901/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native.dir/DawnNative.cpp.o +[902/932] Building CXX object src/dawn/replay/CMakeFiles/replay.dir/ReadHead.cpp.o +[903/932] Building CXX object src/dawn/native/CMakeFiles/webgpu_dawn_objects.dir/DawnNative.cpp.o +[904/932] Building CXX object src/dawn/native/CMakeFiles/dawn_native.dir/metal/MetalBackend.mm.o +[905/932] Linking CXX static library third_party/abseil/absl/base/libabsl_scoped_set_env.a +[906/932] Linking CXX static library third_party/abseil/absl/base/libabsl_poison.a +[907/932] Linking CXX static library third_party/abseil/absl/flags/libabsl_flags_parse.a +[908/932] Building CXX object src/dawn/native/CMakeFiles/webgpu_dawn_objects.dir/metal/MetalBackend.mm.o +[909/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_internal_check_op.a +[910/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_flags.a +[911/932] Linking CXX static library third_party/abseil/absl/log/libabsl_die_if_null.a +[912/932] Linking CXX static library third_party/abseil/absl/log/libabsl_log_initialize.a +[913/932] Linking CXX static library third_party/abseil/absl/profiling/libabsl_hashtable_profiler.a +[914/932] Linking CXX static library third_party/abseil/absl/profiling/libabsl_periodic_sampler.a +[915/932] Linking CXX static library third_party/abseil/absl/random/libabsl_random_seed_sequences.a +[916/932] Linking CXX static library third_party/abseil/absl/random/libabsl_random_distributions.a +[917/932] Linking CXX static library third_party/abseil/absl/random/libabsl_random_internal_distribution_test_util.a +[918/932] Linking CXX static library third_party/abseil/absl/status/libabsl_status_builder.a +[919/932] Linking CXX static library third_party/abseil/absl/status/libabsl_statusor.a +[920/932] Linking CXX static library third_party/abseil/absl/strings/libabsl_cordz_sample_token.a +[921/932] Linking CXX static library third_party/abseil/absl/strings/libabsl_generic_printer_internal.a +[922/932] Linking CXX static library third_party/abseil/absl/time/libabsl_clock_interface.a +[923/932] Generating ../../gen/include/webgpu_upstream/webgpu/webgpu_enum_class_bitmasks.h +[924/932] Linking CXX static library src/utils/libdawn_crash_handler.a +[925/932] Linking CXX static library src/utils/chromium_test_compat/libdawn_shared_utils_chromium_test_compat.a +[926/932] Linking CXX static library src/dawn/utils/libdawn_test_utils.a +[927/932] Linking CXX static library src/dawn/native/libdawn_native.a +[928/932] Building CXX object src/dawn/replay/CMakeFiles/replay.dir/SurfaceDiscovery.cpp.o +[929/932] Linking CXX static library src/dawn/native/libwebgpu_dawn.a +[930/932] Building CXX object src/dawn/replay/CMakeFiles/replay.dir/CaptureWalker.cpp.o +[931/932] Building CXX object src/dawn/replay/CMakeFiles/replay.dir/Replay.cpp.o +[932/932] Linking CXX static library src/dawn/replay/libreplay.a diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/dawn-configure.txt b/docs/graphics/evidence/2026-09-07-osx-arm64/dawn-configure.txt new file mode 100644 index 000000000..b0d19ff75 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/dawn-configure.txt @@ -0,0 +1,285 @@ +-- The C compiler identification is AppleClang 21.0.0.21000101 +-- The CXX compiler identification is AppleClang 21.0.0.21000101 +-- Detecting C compiler ABI info +-- Detecting C compiler ABI info - done +-- Check for working C compiler: /usr/bin/cc - skipped +-- Detecting C compile features +-- Detecting C compile features - done +-- Detecting CXX compiler ABI info +-- Detecting CXX compiler ABI info - done +-- Check for working CXX compiler: /usr/bin/c++ - skipped +-- Detecting CXX compile features +-- Detecting CXX compile features - done +-- Build type...........: Release +-- C++ compiler.........: /usr/bin/c++ +-- CMake generator......: Ninja +-- CMake version........: 4.0.3 +-- +-- Dawn building using Emscripten toolchain: 0 +-- Dawn build D3D11 backend: OFF +-- Dawn build D3D12 backend: OFF +-- Dawn build Metal backend: ON +-- Dawn build Vulkan backend: OFF +-- Dawn build OpenGL backend: OFF +-- Dawn build OpenGL ES backend: OFF +-- Dawn build Null backend: OFF +-- +-- Tint build SPIR-V reader: OFF +-- Tint build WGSL reader: ON +-- Tint build GLSL writer: OFF +-- Tint build GLSL validator: OFF +-- Tint build HLSL writer: OFF +-- Tint build MSL writer: ON +-- Tint build SPIR-V writer: OFF +-- Tint build WGSL writer: ON +-- Tint build NULL writer: OFF +-- +-- Dawn build with ASan: OFF +-- Dawn build with TSan: OFF +-- Dawn build with MSan: OFF +-- Dawn build with UBSan: OFF +-- Dawn build with RTTI: OFF +-- DAWN Werror: OFF +-- Dawn enable install: ON +-- Dawn allow system component fallback: OFF +-- Dawn enable SPIR-V validation: OFF +-- Dawn build with asserts in all configurations: OFF +-- Dawn build Wayland support: OFF +-- Dawn build X11 support: OFF +-- Dawn build GLFW support: OFF +-- Dawn build Windows UI support: OFF +-- Dawn build and use DXC: OFF +-- Dawn enable DXC asserts in non-debug builds: ON +-- Dawn target MacOS: ON +-- Dawn build samples: OFF +-- Dawn build Node bindings: OFF +-- Dawn build Swiftshader: OFF +-- Dawn build benchmarks: OFF +-- Dawn build protobuf: OFF +-- Dawn build monolithic library: STATIC +-- Dawn build PIC: ON +-- Dawn emit coverage: OFF +-- Dawn fetch dependencies: ON +-- LLVM Source dir: +-- +-- Tint build command line executable tools: OFF +-- Tint install: OFF +-- Tint build IR binary: OFF +-- Tint build fuzzers: OFF +-- Tint build fuzzing vulkan drivers: OFF +-- Tint build benchmarks: OFF +-- Tint build tests: OFF +-- Tint enable IR dumping: ON +-- Tint enable IR validation assertions: ON +-- Tint enable break in debugger: OFF +-- Tint build checking [chromium-style]: OFF +-- Tint randomize hashes: OFF +-- Tint build Mesa: OFF +-- +-- Dawn third_party dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party +-- Dawn GLFW dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/glfw3/src +-- Dawn Jinja2 dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/jinja2 +-- Dawn MarkupSafe dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/markupsafe +-- Dawn Khronos dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/khronos +-- Dawn Swiftshader dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/swiftshader +-- Dawn Protobuf dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/protobuf +-- Dawn LPM dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/libprotobuf-mutator/src +-- Dawn Emdawnwebgpu dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/emdawnwebgpu +-- Dawn Spir-Tools dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/spirv-tools/src +-- Dawn Spirv-Headers dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/spirv-headers/src +-- Dawn Glslang dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/glslang/src +-- Dawn Vulkan Headers dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/vulkan-headers/src +-- Dawn Vulkan Utility Libraries dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/vulkan-utility-libraries/src +-- +-- Node Addon API dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/node-addon-api +-- Node API Headers dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/node-api-headers +-- Webgpu IDL path: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/gpuweb/webgpu.idl +-- Go exe: go +-- +-- Performing Test dawn_have_compiler_flag-C--Wconditional-uninitialized +-- Performing Test dawn_have_compiler_flag-C--Wconditional-uninitialized - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wconditional-uninitialized +-- Performing Test dawn_have_compiler_flag-CXX--Wconditional-uninitialized - Success +-- Performing Test dawn_have_compiler_flag-C--Wcstring-format-directive +-- Performing Test dawn_have_compiler_flag-C--Wcstring-format-directive - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wcstring-format-directive +-- Performing Test dawn_have_compiler_flag-CXX--Wcstring-format-directive - Success +-- Performing Test dawn_have_compiler_flag-C--Wctad-maybe-unsupported +-- Performing Test dawn_have_compiler_flag-C--Wctad-maybe-unsupported - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wctad-maybe-unsupported +-- Performing Test dawn_have_compiler_flag-CXX--Wctad-maybe-unsupported - Success +-- Performing Test dawn_have_compiler_flag-C--Wc++11-narrowing +-- Performing Test dawn_have_compiler_flag-C--Wc++11-narrowing - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wc++11-narrowing +-- Performing Test dawn_have_compiler_flag-CXX--Wc++11-narrowing - Success +-- Performing Test dawn_have_compiler_flag-C--Wdeprecated-copy +-- Performing Test dawn_have_compiler_flag-C--Wdeprecated-copy - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wdeprecated-copy +-- Performing Test dawn_have_compiler_flag-CXX--Wdeprecated-copy - Success +-- Performing Test dawn_have_compiler_flag-C--Wdeprecated-copy-dtor +-- Performing Test dawn_have_compiler_flag-C--Wdeprecated-copy-dtor - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wdeprecated-copy-dtor +-- Performing Test dawn_have_compiler_flag-CXX--Wdeprecated-copy-dtor - Success +-- Performing Test dawn_have_compiler_flag-C--Wduplicate-enum +-- Performing Test dawn_have_compiler_flag-C--Wduplicate-enum - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wduplicate-enum +-- Performing Test dawn_have_compiler_flag-CXX--Wduplicate-enum - Success +-- Performing Test dawn_have_compiler_flag-C--Wextra-semi +-- Performing Test dawn_have_compiler_flag-C--Wextra-semi - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wextra-semi +-- Performing Test dawn_have_compiler_flag-CXX--Wextra-semi - Success +-- Performing Test dawn_have_compiler_flag-C--Wextra-semi-stmt +-- Performing Test dawn_have_compiler_flag-C--Wextra-semi-stmt - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wextra-semi-stmt +-- Performing Test dawn_have_compiler_flag-CXX--Wextra-semi-stmt - Success +-- Performing Test dawn_have_compiler_flag-C--Wimplicit-fallthrough +-- Performing Test dawn_have_compiler_flag-C--Wimplicit-fallthrough - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wimplicit-fallthrough +-- Performing Test dawn_have_compiler_flag-CXX--Wimplicit-fallthrough - Success +-- Performing Test dawn_have_compiler_flag-C--Winconsistent-missing-destructor-override +-- Performing Test dawn_have_compiler_flag-C--Winconsistent-missing-destructor-override - Success +-- Performing Test dawn_have_compiler_flag-CXX--Winconsistent-missing-destructor-override +-- Performing Test dawn_have_compiler_flag-CXX--Winconsistent-missing-destructor-override - Success +-- Performing Test dawn_have_compiler_flag-C--Winvalid-offsetof +-- Performing Test dawn_have_compiler_flag-C--Winvalid-offsetof - Success +-- Performing Test dawn_have_compiler_flag-CXX--Winvalid-offsetof +-- Performing Test dawn_have_compiler_flag-CXX--Winvalid-offsetof - Success +-- Performing Test dawn_have_compiler_flag-C--Wmissing-field-initializers +-- Performing Test dawn_have_compiler_flag-C--Wmissing-field-initializers - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wmissing-field-initializers +-- Performing Test dawn_have_compiler_flag-CXX--Wmissing-field-initializers - Success +-- Performing Test dawn_have_compiler_flag-C--Wnon-c-typedef-for-linkage +-- Performing Test dawn_have_compiler_flag-C--Wnon-c-typedef-for-linkage - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wnon-c-typedef-for-linkage +-- Performing Test dawn_have_compiler_flag-CXX--Wnon-c-typedef-for-linkage - Success +-- Performing Test dawn_have_compiler_flag-C--Wpessimizing-move +-- Performing Test dawn_have_compiler_flag-C--Wpessimizing-move - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wpessimizing-move +-- Performing Test dawn_have_compiler_flag-CXX--Wpessimizing-move - Success +-- Performing Test dawn_have_compiler_flag-C--Wrange-loop-analysis +-- Performing Test dawn_have_compiler_flag-C--Wrange-loop-analysis - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wrange-loop-analysis +-- Performing Test dawn_have_compiler_flag-CXX--Wrange-loop-analysis - Success +-- Performing Test dawn_have_compiler_flag-C--Wredundant-move +-- Performing Test dawn_have_compiler_flag-C--Wredundant-move - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wredundant-move +-- Performing Test dawn_have_compiler_flag-CXX--Wredundant-move - Success +-- Performing Test dawn_have_compiler_flag-C--Wshadow-field +-- Performing Test dawn_have_compiler_flag-C--Wshadow-field - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wshadow-field +-- Performing Test dawn_have_compiler_flag-CXX--Wshadow-field - Success +-- Performing Test dawn_have_compiler_flag-C--Wstrict-prototypes +-- Performing Test dawn_have_compiler_flag-C--Wstrict-prototypes - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wstrict-prototypes +-- Performing Test dawn_have_compiler_flag-CXX--Wstrict-prototypes - Success +-- Performing Test dawn_have_compiler_flag-C--Wsuggest-destructor-override +-- Performing Test dawn_have_compiler_flag-C--Wsuggest-destructor-override - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wsuggest-destructor-override +-- Performing Test dawn_have_compiler_flag-CXX--Wsuggest-destructor-override - Success +-- Performing Test dawn_have_compiler_flag-C--Wsuggest-override +-- Performing Test dawn_have_compiler_flag-C--Wsuggest-override - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wsuggest-override +-- Performing Test dawn_have_compiler_flag-CXX--Wsuggest-override - Success +-- Performing Test dawn_have_compiler_flag-C--Wtautological-unsigned-zero-compare +-- Performing Test dawn_have_compiler_flag-C--Wtautological-unsigned-zero-compare - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wtautological-unsigned-zero-compare +-- Performing Test dawn_have_compiler_flag-CXX--Wtautological-unsigned-zero-compare - Success +-- Performing Test dawn_have_compiler_flag-C--Wunreachable-code-aggressive +-- Performing Test dawn_have_compiler_flag-C--Wunreachable-code-aggressive - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wunreachable-code-aggressive +-- Performing Test dawn_have_compiler_flag-CXX--Wunreachable-code-aggressive - Success +-- Performing Test dawn_have_compiler_flag-C--Wunused-but-set-variable +-- Performing Test dawn_have_compiler_flag-C--Wunused-but-set-variable - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wunused-but-set-variable +-- Performing Test dawn_have_compiler_flag-CXX--Wunused-but-set-variable - Success +-- Performing Test dawn_have_compiler_flag-C--Wunused-macros +-- Performing Test dawn_have_compiler_flag-C--Wunused-macros - Success +-- Performing Test dawn_have_compiler_flag-CXX--Wunused-macros +-- Performing Test dawn_have_compiler_flag-CXX--Wunused-macros - Success +-- Found Python3: /opt/homebrew/Frameworks/Python.framework/Versions/3.14/bin/python3.14 (found version "3.14.4") found components: Interpreter +-- Dawn: using python at /opt/homebrew/Frameworks/Python.framework/Versions/3.14/bin/python3.14 +-- Running fetch_dawn_dependencies: +-- -- Listing dependencies from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn +-- -- Fetching dependency 'third_party/abseil-cpp' +-- -- Shallow cloning 'https://chromium.googlesource.com/chromium/src/third_party/abseil-cpp' at '435e7d977fb36fb47854a4c552c0706dad0bd7cf' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/abseil-cpp' +-- -- Checking out tag '435e7d977fb36fb47854a4c552c0706dad0bd7cf' +-- -- Fetching dependency 'third_party/directx-shader-compiler/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/microsoft/DirectXShaderCompiler' at '1b949a448e4c9e821f010c488defebfbb1a50b27' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/directx-shader-compiler/src' +-- -- Checking out tag '1b949a448e4c9e821f010c488defebfbb1a50b27' +-- -- Fetching dependency 'third_party/directx-headers/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/microsoft/DirectX-Headers' at '980971e835876dc0cde415e8f9bc646e64667bf7' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/directx-headers/src' +-- -- Checking out tag '980971e835876dc0cde415e8f9bc646e64667bf7' +-- -- Fetching dependency 'third_party/glfw3/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/glfw/glfw' at '92dcf4ce74f2e2554a98fea09be7c705c17daa5a' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/glfw3/src' +-- -- Checking out tag '92dcf4ce74f2e2554a98fea09be7c705c17daa5a' +-- -- Fetching dependency 'third_party/jinja2' +-- -- Shallow cloning 'https://chromium.googlesource.com/chromium/src/third_party/jinja2' at 'c3027d884967773057bf74b957e3fea87e5df4d7' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/jinja2' +-- -- Checking out tag 'c3027d884967773057bf74b957e3fea87e5df4d7' +-- -- Fetching dependency 'third_party/EGL-Registry/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/KhronosGroup/EGL-Registry' at '5961a7fe64cf8a126890ced6f13d69e0a1e1b83e' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/EGL-Registry/src' +-- -- Checking out tag '5961a7fe64cf8a126890ced6f13d69e0a1e1b83e' +-- -- Fetching dependency 'third_party/OpenGL-Registry/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/KhronosGroup/OpenGL-Registry' at '1cdd228e34966dd6b95bd203e9f84faba0f371a1' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/OpenGL-Registry/src' +-- -- Checking out tag '1cdd228e34966dd6b95bd203e9f84faba0f371a1' +-- -- Fetching dependency 'third_party/libprotobuf-mutator/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/google/libprotobuf-mutator.git' at 'c1c950eae0440c3808f2b8bd7c57d0c6a42c1a90' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/libprotobuf-mutator/src' +-- -- Checking out tag 'c1c950eae0440c3808f2b8bd7c57d0c6a42c1a90' +-- -- Fetching dependency 'third_party/protobuf' +-- -- Shallow cloning 'https://chromium.googlesource.com/chromium/src/third_party/protobuf' at '5f8c379d1fc89fe8eee16ae560dd5e514a4608da' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/protobuf' +-- -- Checking out tag '5f8c379d1fc89fe8eee16ae560dd5e514a4608da' +-- -- Listing dependencies from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/protobuf +-- -- WARNING: DEPS file '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/protobuf/DEPS' does not define a 'deps' variable +-- -- Fetching dependency 'third_party/markupsafe' +-- -- Shallow cloning 'https://chromium.googlesource.com/chromium/src/third_party/markupsafe' at '4256084ae14175d38a3ff7d739dca83ae49ccec6' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/markupsafe' +-- -- Checking out tag '4256084ae14175d38a3ff7d739dca83ae49ccec6' +-- -- Fetching dependency 'third_party/glslang/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/KhronosGroup/glslang' at 'efa016659ffc4f2ae566b6b1db71a70655ac33a1' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/glslang/src' +-- -- Checking out tag 'efa016659ffc4f2ae566b6b1db71a70655ac33a1' +-- -- Listing dependencies from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/glslang/src +-- -- Fetching dependency 'third_party/google_benchmark/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/google/benchmark.git' at '8abf1e701fbd88c8170f48fe0558247e2e5f8e7d' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/google_benchmark/src' +-- -- Checking out tag '8abf1e701fbd88c8170f48fe0558247e2e5f8e7d' +-- -- Fetching dependency 'third_party/googletest/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/google/googletest' at '4fe3307fb2d9f86d19777c7eb0e4809e9694dde7' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/googletest/src' +-- -- Checking out tag '4fe3307fb2d9f86d19777c7eb0e4809e9694dde7' +-- -- Fetching dependency 'third_party/spirv-headers/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/KhronosGroup/SPIRV-Headers' at '496543121ce6419f23d6fa5d7194ba66c36212d2' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/spirv-headers/src' +-- -- Checking out tag '496543121ce6419f23d6fa5d7194ba66c36212d2' +-- -- Fetching dependency 'third_party/spirv-tools/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/KhronosGroup/SPIRV-Tools' at '907d104d2b7197b0207b7889671b149e1d1bc8ab' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/spirv-tools/src' +-- -- Checking out tag '907d104d2b7197b0207b7889671b149e1d1bc8ab' +-- -- Listing dependencies from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/spirv-tools/src +-- -- Fetching dependency 'third_party/vulkan-headers/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/KhronosGroup/Vulkan-Headers' at 'ee2ec5fd83dafce291024683b50dc89219333076' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/vulkan-headers/src' +-- -- Checking out tag 'ee2ec5fd83dafce291024683b50dc89219333076' +-- -- Fetching dependency 'third_party/vulkan-loader/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/KhronosGroup/Vulkan-Loader' at 'b8b96a2862bff1eed468e602d43f706beae89cf1' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/vulkan-loader/src' +-- -- Checking out tag 'b8b96a2862bff1eed468e602d43f706beae89cf1' +-- -- Fetching dependency 'third_party/vulkan-utility-libraries/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/KhronosGroup/Vulkan-Utility-Libraries' at '2176ec8c5f5d2272161277ab96fe5b8f7633113e' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/vulkan-utility-libraries/src' +-- -- Checking out tag '2176ec8c5f5d2272161277ab96fe5b8f7633113e' +-- -- Fetching dependency 'third_party/webgpu-headers/src' +-- -- Shallow cloning 'https://chromium.googlesource.com/external/github.com/webgpu-native/webgpu-headers' at 'b5ff182caa90e53293f47939716281342c0812ba' into '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/webgpu-headers/src' +-- -- Checking out tag 'b5ff182caa90e53293f47939716281342c0812ba' +-- Dawn: using Abseil at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/abseil-cpp +-- Performing Test CMAKE_HAVE_LIBC_PTHREAD +-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success +-- Found Threads: TRUE +-- Dawn: using SPIRV-Headers at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/spirv-headers/src +-- Dawn: using jinja2 at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/jinja2 +-- Dawn: using markupsafe at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/markupsafe +-- Dawn: Configuring DawnGenerator for Dawn WebGPU Emscripten headers. +-- Dawn: Configuring DawnGenerator for Dawn WebGPU Emscripten JS files. +-- Dawn: Configuring DawnGenerator for Dawn version based utilities. +-- Dawn: Configuring DawnGenerator for Dawn headers. +-- Dawn: Configuring DawnGenerator for Dawn C++ headers. +-- Dawn: Configuring DawnGenerator for Dawn GPU info utilities. +-- Dawn: Configuring DawnGenerator for Dawn native utilities. +-- Dawn: Configuring DawnGenerator for Dawn wire. +-- Dawn: Configuring DawnGenerator for Dawn native utilities. +-- Dawn: Configuring DawnGenerator for Dawn native WebGPU procs. +-- Dawn: Configuring DawnGenerator for Dawn C++ wrapper. +-- Dawn: Configuring DawnGenerator for WebGPU headers. +-- Configuring done (30.5s) +-- Generating done (0.3s) +-- Build files have been written to: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/dawn-osx-arm64 diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/dawn-reproduction.txt b/docs/graphics/evidence/2026-09-07-osx-arm64/dawn-reproduction.txt new file mode 100644 index 000000000..8149020fe --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/dawn-reproduction.txt @@ -0,0 +1,161 @@ ++ git -C /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn config core.autocrlf false ++ cmake -S /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn -B /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/dawn-osx-arm64 -G Ninja -DCMAKE_INSTALL_PREFIX=/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DDAWN_FETCH_DEPENDENCIES=ON -DDAWN_ENABLE_INSTALL=ON -DDAWN_BUILD_MONOLITHIC_LIBRARY=STATIC -DDAWN_BUILD_SAMPLES=OFF -DDAWN_BUILD_TESTS=OFF -DDAWN_BUILD_PROTOBUF=OFF -DDAWN_BUILD_BENCHMARKS=OFF -DTINT_BUILD_TESTS=OFF -DTINT_BUILD_CMD_TOOLS=OFF -DTINT_BUILD_GLSL_VALIDATOR=OFF -DDAWN_USE_GLFW=OFF -DDAWN_ENABLE_D3D11=OFF -DDAWN_ENABLE_D3D12=OFF -DDAWN_ENABLE_METAL=ON -DDAWN_ENABLE_VULKAN=OFF -DDAWN_ENABLE_DESKTOP_GL=OFF -DDAWN_ENABLE_OPENGLES=OFF -DDAWN_ENABLE_NULL=OFF -DDAWN_ENABLE_SWIFTSHADER=OFF -DCMAKE_OSX_ARCHITECTURES=arm64 +-- Build type...........: Release +-- C++ compiler.........: /usr/bin/c++ +-- CMake generator......: Ninja +-- CMake version........: 4.0.3 +-- +-- Dawn building using Emscripten toolchain: 0 +-- Dawn build D3D11 backend: OFF +-- Dawn build D3D12 backend: OFF +-- Dawn build Metal backend: ON +-- Dawn build Vulkan backend: OFF +-- Dawn build OpenGL backend: OFF +-- Dawn build OpenGL ES backend: OFF +-- Dawn build Null backend: OFF +-- +-- Tint build SPIR-V reader: OFF +-- Tint build WGSL reader: ON +-- Tint build GLSL writer: OFF +-- Tint build GLSL validator: OFF +-- Tint build HLSL writer: OFF +-- Tint build MSL writer: ON +-- Tint build SPIR-V writer: OFF +-- Tint build WGSL writer: ON +-- Tint build NULL writer: OFF +-- +-- Dawn build with ASan: OFF +-- Dawn build with TSan: OFF +-- Dawn build with MSan: OFF +-- Dawn build with UBSan: OFF +-- Dawn build with RTTI: OFF +-- DAWN Werror: OFF +-- Dawn enable install: ON +-- Dawn allow system component fallback: OFF +-- Dawn enable SPIR-V validation: OFF +-- Dawn build with asserts in all configurations: OFF +-- Dawn build Wayland support: OFF +-- Dawn build X11 support: OFF +-- Dawn build GLFW support: OFF +-- Dawn build Windows UI support: OFF +-- Dawn build and use DXC: OFF +-- Dawn enable DXC asserts in non-debug builds: ON +-- Dawn target MacOS: ON +-- Dawn build samples: OFF +-- Dawn build Node bindings: OFF +-- Dawn build Swiftshader: OFF +-- Dawn build benchmarks: OFF +-- Dawn build protobuf: OFF +-- Dawn build monolithic library: STATIC +-- Dawn build PIC: ON +-- Dawn emit coverage: OFF +-- Dawn fetch dependencies: ON +-- LLVM Source dir: +-- +-- Tint build command line executable tools: OFF +-- Tint install: OFF +-- Tint build IR binary: OFF +-- Tint build fuzzers: OFF +-- Tint build fuzzing vulkan drivers: OFF +-- Tint build benchmarks: OFF +-- Tint build tests: OFF +-- Tint enable IR dumping: ON +-- Tint enable IR validation assertions: ON +-- Tint enable break in debugger: OFF +-- Tint build checking [chromium-style]: OFF +-- Tint randomize hashes: OFF +-- Tint build Mesa: OFF +-- +-- Dawn third_party dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party +-- Dawn GLFW dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/glfw3/src +-- Dawn Jinja2 dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/jinja2 +-- Dawn MarkupSafe dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/markupsafe +-- Dawn Khronos dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/khronos +-- Dawn Swiftshader dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/swiftshader +-- Dawn Protobuf dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/protobuf +-- Dawn LPM dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/libprotobuf-mutator/src +-- Dawn Emdawnwebgpu dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/emdawnwebgpu +-- Dawn Spir-Tools dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/spirv-tools/src +-- Dawn Spirv-Headers dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/spirv-headers/src +-- Dawn Glslang dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/glslang/src +-- Dawn Vulkan Headers dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/vulkan-headers/src +-- Dawn Vulkan Utility Libraries dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/vulkan-utility-libraries/src +-- +-- Node Addon API dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/node-addon-api +-- Node API Headers dir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/node-api-headers +-- Webgpu IDL path: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/gpuweb/webgpu.idl +-- Go exe: go +-- +-- Dawn: using python at /opt/homebrew/Frameworks/Python.framework/Versions/3.14/bin/python3.14 +-- Running fetch_dawn_dependencies: +-- -- Listing dependencies from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn +-- -- Fetching dependency 'third_party/abseil-cpp' +-- -- Fetching dependency 'third_party/directx-shader-compiler/src' +-- -- Fetching dependency 'third_party/directx-headers/src' +-- -- Fetching dependency 'third_party/glfw3/src' +-- -- Fetching dependency 'third_party/jinja2' +-- -- Fetching dependency 'third_party/EGL-Registry/src' +-- -- Fetching dependency 'third_party/OpenGL-Registry/src' +-- -- Fetching dependency 'third_party/libprotobuf-mutator/src' +-- -- Fetching dependency 'third_party/protobuf' +-- -- Listing dependencies from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/protobuf +-- -- WARNING: DEPS file '/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/protobuf/DEPS' does not define a 'deps' variable +-- -- Fetching dependency 'third_party/markupsafe' +-- -- Fetching dependency 'third_party/glslang/src' +-- -- Listing dependencies from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/glslang/src +-- -- Fetching dependency 'third_party/google_benchmark/src' +-- -- Fetching dependency 'third_party/googletest/src' +-- -- Fetching dependency 'third_party/spirv-headers/src' +-- -- Fetching dependency 'third_party/spirv-tools/src' +-- -- Listing dependencies from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/spirv-tools/src +-- -- Fetching dependency 'third_party/vulkan-headers/src' +-- -- Fetching dependency 'third_party/vulkan-loader/src' +-- -- Fetching dependency 'third_party/vulkan-utility-libraries/src' +-- -- Fetching dependency 'third_party/webgpu-headers/src' +-- Dawn: using Abseil at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/abseil-cpp +-- Dawn: using SPIRV-Headers at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/spirv-headers/src +-- Dawn: using jinja2 at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/jinja2 +-- Dawn: using markupsafe at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/dawn/third_party/markupsafe +-- Dawn: Configuring DawnGenerator for Dawn WebGPU Emscripten headers. +-- Dawn: Configuring DawnGenerator for Dawn WebGPU Emscripten JS files. +-- Dawn: Configuring DawnGenerator for Dawn version based utilities. +-- Dawn: Configuring DawnGenerator for Dawn headers. +-- Dawn: Configuring DawnGenerator for Dawn C++ headers. +-- Dawn: Configuring DawnGenerator for Dawn GPU info utilities. +-- Dawn: Configuring DawnGenerator for Dawn native utilities. +-- Dawn: Configuring DawnGenerator for Dawn wire. +-- Dawn: Configuring DawnGenerator for Dawn native utilities. +-- Dawn: Configuring DawnGenerator for Dawn native WebGPU procs. +-- Dawn: Configuring DawnGenerator for Dawn C++ wrapper. +-- Dawn: Configuring DawnGenerator for WebGPU headers. +-- Configuring done (2.5s) +-- Generating done (0.4s) +-- Build files have been written to: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/dawn-osx-arm64 ++ cmake --build /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/dawn-osx-arm64 --parallel 6 +ninja: no work to do. ++ cmake --install /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/dawn-osx-arm64 +-- Install configuration: "Release" +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/lib/libwebgpu_dawn.a +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/dawn/native/DawnNative.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/dawn/native/dawn_native_export.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/dawn/native/MetalBackend.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/webgpu/webgpu_cpp.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/webgpu/webgpu_cpp_print.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/webgpu/webgpu_enum_class_bitmasks.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/dawn/webgpu_cpp.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/dawn/wire/client/webgpu_cpp.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/dawn/webgpu_cpp_print.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/dawn/wire/client/webgpu_cpp_print.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/webgpu/webgpu_cpp_chained_struct.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/webgpu/webgpu.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/dawn/webgpu.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/dawn/wire/client/webgpu.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/dawn/dawn_proc_table.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/webgpu_upstream/webgpu/webgpu_cpp.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/webgpu_upstream/webgpu/webgpu_cpp_chained_struct.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/webgpu_upstream/webgpu/webgpu_cpp_print.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include/webgpu_upstream/webgpu/webgpu_enum_class_bitmasks.h +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/lib/cmake/Dawn/DawnTargets.cmake +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/lib/cmake/Dawn/DawnTargets-release.cmake +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/lib/cmake/Dawn/DawnConfig.cmake +-- Installing: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/lib/cmake/Dawn/DawnConfigVersion.cmake diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/dotnet-info.txt b/docs/graphics/evidence/2026-09-07-osx-arm64/dotnet-info.txt new file mode 100644 index 000000000..5f3db92e8 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/dotnet-info.txt @@ -0,0 +1,79 @@ +.NET SDK: + Version: 10.0.301 + Commit: 96856fd726 + Workload version: 10.0.301.1 + MSBuild version: 18.6.4+96856fd72 + +Runtime Environment: + OS Name: Mac OS X + OS Version: 26.6 + OS Platform: Darwin + RID: osx-arm64 + Base Path: /Volumes/SSD/dotnet/sdk/10.0.301/ + +.NET workloads installed: + [macos] + Installation Source: SDK 10.0.300 + Manifest Version: 26.5.10284/10.0.100 + Manifest Path: /Volumes/SSD/dotnet/sdk-manifests/10.0.100/microsoft.net.sdk.macos/26.5.10284/WorkloadManifest.json + Install Type: FileBased + + [ios] + Installation Source: SDK 10.0.300 + Manifest Version: 26.5.10284/10.0.100 + Manifest Path: /Volumes/SSD/dotnet/sdk-manifests/10.0.100/microsoft.net.sdk.ios/26.5.10284/WorkloadManifest.json + Install Type: FileBased + + [android] + Installation Source: SDK 10.0.300 + Manifest Version: 36.1.69/10.0.100 + Manifest Path: /Volumes/SSD/dotnet/sdk-manifests/10.0.100/microsoft.net.sdk.android/36.1.69/WorkloadManifest.json + Install Type: FileBased + +Configured to use workload sets when installing new manifests. + +Host: + Version: 10.0.9 + Architecture: arm64 + Commit: 901ca94124 + +.NET SDKs installed: + 9.0.305 [/Volumes/SSD/dotnet/sdk] + 10.0.301 [/Volumes/SSD/dotnet/sdk] + +.NET runtimes installed: + Microsoft.AspNetCore.App 8.0.10 [/Volumes/SSD/dotnet/shared/Microsoft.AspNetCore.App] + Microsoft.AspNetCore.App 8.0.19 [/Volumes/SSD/dotnet/shared/Microsoft.AspNetCore.App] + Microsoft.AspNetCore.App 9.0.0 [/Volumes/SSD/dotnet/shared/Microsoft.AspNetCore.App] + Microsoft.AspNetCore.App 9.0.3 [/Volumes/SSD/dotnet/shared/Microsoft.AspNetCore.App] + Microsoft.AspNetCore.App 9.0.9 [/Volumes/SSD/dotnet/shared/Microsoft.AspNetCore.App] + Microsoft.AspNetCore.App 10.0.0-rc.2.25502.107 [/Volumes/SSD/dotnet/shared/Microsoft.AspNetCore.App] + Microsoft.AspNetCore.App 10.0.0 [/Volumes/SSD/dotnet/shared/Microsoft.AspNetCore.App] + Microsoft.AspNetCore.App 10.0.2 [/Volumes/SSD/dotnet/shared/Microsoft.AspNetCore.App] + Microsoft.AspNetCore.App 10.0.7 [/Volumes/SSD/dotnet/shared/Microsoft.AspNetCore.App] + Microsoft.AspNetCore.App 10.0.9 [/Volumes/SSD/dotnet/shared/Microsoft.AspNetCore.App] + Microsoft.NETCore.App 8.0.10 [/Volumes/SSD/dotnet/shared/Microsoft.NETCore.App] + Microsoft.NETCore.App 8.0.19 [/Volumes/SSD/dotnet/shared/Microsoft.NETCore.App] + Microsoft.NETCore.App 9.0.0 [/Volumes/SSD/dotnet/shared/Microsoft.NETCore.App] + Microsoft.NETCore.App 9.0.3 [/Volumes/SSD/dotnet/shared/Microsoft.NETCore.App] + Microsoft.NETCore.App 9.0.9 [/Volumes/SSD/dotnet/shared/Microsoft.NETCore.App] + Microsoft.NETCore.App 10.0.0-rc.2.25502.107 [/Volumes/SSD/dotnet/shared/Microsoft.NETCore.App] + Microsoft.NETCore.App 10.0.0 [/Volumes/SSD/dotnet/shared/Microsoft.NETCore.App] + Microsoft.NETCore.App 10.0.2 [/Volumes/SSD/dotnet/shared/Microsoft.NETCore.App] + Microsoft.NETCore.App 10.0.7 [/Volumes/SSD/dotnet/shared/Microsoft.NETCore.App] + Microsoft.NETCore.App 10.0.9 [/Volumes/SSD/dotnet/shared/Microsoft.NETCore.App] + +Other architectures found: + None + +Environment variables: + Not set + +global.json file: + /Volumes/SSD/repos/worktrees/aa5a/HtmlML/global.json + +Learn more: + https://aka.ms/dotnet/info + +Download .NET: + https://aka.ms/dotnet/download diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/index.json b/docs/graphics/evidence/2026-09-07-osx-arm64/index.json new file mode 100644 index 000000000..ef5478660 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/index.json @@ -0,0 +1,38 @@ +{ + "schemaVersion": 1, + "codeCommit": "215bb60c7f58e456e7011ac6e791d65612cddcbe", + "qualificationScope": "Partial G01 macOS prerequisites only", + "epicStatus": "incomplete", + "issue23Status": "incomplete", + "hardware": { + "osx-arm64": "native prerequisite probes passed on Apple M4", + "win-x64": "not-run; runner access unconfirmed", + "linux-x64": "not-run; runner access unconfirmed" + }, + "missing": [ + "Windows/Linux native builds and GPU evidence", + "V8-enabled runtime/package integration", + "independent GL qualification where needed", + "hardware Chrome reference scenes and interaction traces", + "complete non-GPU baseline", + "WebScene unchanged Kestrel execution", + "browser bindings and all later epic sub-issues" + ], + "files": { + "angle-reproduction.txt": "aa35a146648dadb6ca41d1765e462d1b1e3aaa7235e265f2915d0fa8b6d047b7", + "dawn-build.txt": "9442aa005b18f1abd29c1e8f70aba25c5ac518943228612304f2cd35cfdf2132", + "dawn-configure.txt": "3ea596d926c88b5eb50551d941d938d8ea0e92d0e32143facb26cd1a27ae3100", + "dawn-reproduction.txt": "4519ea34ae29bc1351fa915f1247ad3ddfd2b57bbe0333e97c646d4a2ea1cb0e", + "dotnet-info.txt": "606903843e17a39c9e8c70f86c2f5ca7c3d5ff74e4fdeeb80f2b4a5f0552f819", + "native-disabled-tests.txt": "de752415c348b364186326671420ae2669245050ff4dab83c83b6a73eaad37d4", + "native-enabled-tests.txt": "bd628ff1d0c6c48226195ce757ad88553ea388d30711fdd6384907f78d77f2dc", + "native-probes.json": "301dd76e9d6ceb5a6b6e7ce3ccbbe3b685337c0e3101d5f87e83e0ac07083438", + "non-gpu-retained-apply.json": "3e698917284ff4b35218f467006f60149f3eae9fc685d6d1f7c505928be8408a", + "non-gpu-retained-render.json": "c383894d77adbb4b1f795b74f39152e235b0495486bf45652251ef777421ed5d", + "relocation.json": "9ae8db4b3864ef611cd3d7a2f3a8dcb88ab330446dcd1a309898f7afcc645ed6", + "sdk-integrity.json": "712f96d9dc0a21e2e4f6d451158af4163e4f03bdf70592e309fd29ab37884cc8", + "webgl-cts-acquisition.json": "316ede989a534444d94786eae8468de63663d2663e99bac9384c9e9de7b334d6", + "webgpu-cts-acquisition.json": "e136a561ae6075f1dda3081097f9910e02ec5ddf44f77ab54e9387a6c815e638", + "wpt-acquisition.json": "1b1460a43d6d716f17ef83e45ef42a8ba7db48bebc8cb0bcb3e5ff8c93c6ea61" + } +} diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/native-disabled-tests.txt b/docs/graphics/evidence/2026-09-07-osx-arm64/native-disabled-tests.txt new file mode 100644 index 000000000..64f743212 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/native-disabled-tests.txt @@ -0,0 +1,11 @@ +Test project /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-disabled + Start 1: webscene_html_parser_tests +1/3 Test #1: webscene_html_parser_tests ....... Passed 0.28 sec + Start 2: webscene_css_parser_tests +2/3 Test #2: webscene_css_parser_tests ........ Passed 0.25 sec + Start 3: webscene_selector_parser_tests +3/3 Test #3: webscene_selector_parser_tests ... Passed 0.25 sec + +100% tests passed, 0 tests failed out of 3 + +Total Test time (real) = 0.78 sec diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/native-enabled-tests.txt b/docs/graphics/evidence/2026-09-07-osx-arm64/native-enabled-tests.txt new file mode 100644 index 000000000..aad3b112c --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/native-enabled-tests.txt @@ -0,0 +1,11 @@ +Test project /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-enabled + Start 1: webscene_html_parser_tests +1/3 Test #1: webscene_html_parser_tests ....... Passed 0.01 sec + Start 2: webscene_css_parser_tests +2/3 Test #2: webscene_css_parser_tests ........ Passed 0.01 sec + Start 3: webscene_selector_parser_tests +3/3 Test #3: webscene_selector_parser_tests ... Passed 0.01 sec + +100% tests passed, 0 tests failed out of 3 + +Total Test time (real) = 0.02 sec diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/native-probes.json b/docs/graphics/evidence/2026-09-07-osx-arm64/native-probes.json new file mode 100644 index 000000000..edf9f5c92 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/native-probes.json @@ -0,0 +1,1343 @@ +{ + "schemaVersion": 1, + "scope": "G01 native resource/clear/readback only; not browser API, presentation or epic qualification", + "status": "passed", + "capturedAtUtc": "2026-09-07T09:56:06.948638+00:00", + "rid": "osx-arm64", + "host": { + "os": "macOS-26.6.2-arm64-arm-64bit-Mach-O", + "machine": "arm64" + }, + "declaredFrameworkVersions": { + "Avalonia": "11.3.4", + "SkiaSharp": "2.88.9", + "Uno.WinUI": "6.5.153", + "Uno.SkiaSharp": "3.119.2" + }, + "repository": { + "exitCode": 0, + "stdout": "215bb60c7f58e456e7011ac6e791d65612cddcbe", + "stderr": "" + }, + "worktree": { + "exitCode": 0, + "stdout": "", + "stderr": "" + }, + "inputs": { + "eng/graphics/verify-sdk.py": "6d9d368358577a6d0cb4dd58350af03e25d59f816ab376df08f380f3719de613", + "eng/graphics/build.py": "1badce875cc834f1924a219aa457971a5f6f61b7a2803ef7fc03a9ceec44a4c5", + "eng/graphics/run-probes.py": "f7edb80ae12a88e118d150482eeb20a3d59749246d119c0013c109ac58c1c772", + "eng/graphics/sync-suites.py": "f77b0f4ea7f72a5a34a3bdeb25e82aa7cda69f2d3d8bf4892e3517fd535833a8", + "eng/graphics/dependencies.lock.json": "be8c4effbcbd8ac6afb95d9042412cf970db7cec4beafa3e375ed42fa16360f1", + "eng/graphics/check-macos-relocation.py": "4fc069346d850df9a2c1401dcba2dec6ac6f21c2b9583de41dd628b286b96e58", + "eng/graphics/README.md": "7f446c26b9c4832eb0e48724388b9f678bc4d0072ec84bb837bb6f75ad0966e2", + "eng/graphics/coverage.json": "7f27b021f3ac9eadaad6a427dc2a7c5bf63a345283f9303b79180e817884b84f", + "eng/graphics/GraphicsDependencies.cmake": "4c21770fb0754b690dc2e76dc3b81e463b22a2ce9633d3ed077c17ab95777970", + "eng/graphics/check-sdk-integrity.py": "7e00ec2c076e9744035ef1e785157228d35e603520ddf251078fe3149a34409c", + "eng/graphics/probes/CMakeLists.txt": "f9a256ca11ed8cac54c50107c599fa90f440338944dcfbca7551285ba58e47c5", + "eng/graphics/probes/angle_probe.cpp": "d5796d0bf2a1d18f00083682be75c1c83e95c4c83c30459f350268d269f80775", + "eng/graphics/probes/dawn_probe.cpp": "80240445bc34c0e52ad3ff84f3e01ee0f8d57611a0e6e99fcd5be09e71f8beb5", + "eng/graphics/tests/test_evidence.py": "bc3e169a78914336331c125e04d26cb981b9cf4e2c55ce57fff70015f4c240a6" + }, + "packages": { + "dawn": { + "schemaVersion": 1, + "component": "dawn", + "rid": "osx-arm64", + "revision": "2ca8cbfe0f8275aa0f739e7b6b4345a16e2f0378", + "lockSha256": "be8c4effbcbd8ac6afb95d9042412cf970db7cec4beafa3e375ed42fa16360f1", + "settings": { + "CMAKE_BUILD_TYPE": "Release", + "BUILD_SHARED_LIBS": "OFF", + "DAWN_FETCH_DEPENDENCIES": "ON", + "DAWN_ENABLE_INSTALL": "ON", + "DAWN_BUILD_MONOLITHIC_LIBRARY": "STATIC", + "DAWN_BUILD_SAMPLES": "OFF", + "DAWN_BUILD_TESTS": "OFF", + "DAWN_BUILD_PROTOBUF": "OFF", + "DAWN_BUILD_BENCHMARKS": "OFF", + "TINT_BUILD_TESTS": "OFF", + "TINT_BUILD_CMD_TOOLS": "OFF", + "TINT_BUILD_GLSL_VALIDATOR": "OFF", + "DAWN_USE_GLFW": "OFF", + "DAWN_ENABLE_D3D11": "OFF", + "DAWN_ENABLE_D3D12": "OFF", + "DAWN_ENABLE_METAL": "ON", + "DAWN_ENABLE_VULKAN": "OFF", + "DAWN_ENABLE_DESKTOP_GL": "OFF", + "DAWN_ENABLE_OPENGLES": "OFF", + "DAWN_ENABLE_NULL": "OFF", + "DAWN_ENABLE_SWIFTSHADER": "OFF", + "CMAKE_OSX_ARCHITECTURES": "arm64" + }, + "tools": { + "cmake": "cmake version 4.0.3\n\nCMake suite maintained and supported by Kitware (kitware.com/cmake).", + "ninja": "1.13.1", + "python": "3.14.4 (main, Apr 7 2026, 13:13:20) [Clang 21.0.0 (clang-2100.0.123.102)]", + "host": "macOS-26.6.2-arm64-arm-64bit-Mach-O" + }, + "files": { + "build-info/CMakeCCompiler.cmake": "076dd5b9d0f54cd1dcfef28e0f226717fe2e893a6ec6dbf814ebab00ce4b10f0", + "build-info/CMakeCXXCompiler.cmake": "d54da49b97dd012c406a90fb9986447552e65f68cd8dd444a329a2789af87142", + "build-info/CMakeCache.txt": "6a9afabf578e851c6fd0c2f5cac670d66e136d7b784a3d5e7395e88c51f3c3d9", + "build-info/CMakeSystem.cmake": "a4b33b7b37999a9f693561dc97d4b941fb135975b61ff71451409c490feb41ff", + "build-info/DEPS": "65e94306981c004dd8ee2453031b78e2b6aed6b385fa069d0453a604dbba49e4", + "build-info/source-graph.json": "217eef74464d3cfe6de5d7aa2299583f67b9a52a16aa40084293cb0438c06104", + "include/dawn/dawn_proc_table.h": "00e7057898ce2e9f58e7452e3efbef18bd601173ffb9f965b6bbb41f61ac39c1", + "include/dawn/native/DawnNative.h": "4f685839e24c8fd7dec9d31d27bc38ec2d8a6c4d8b25ef3eb311bb773bd2ace1", + "include/dawn/native/MetalBackend.h": "623722360ac7ea6f3b49e00d6762ee6457a055e9b52dc5ae9324156047796b71", + "include/dawn/native/dawn_native_export.h": "c722ebaa9c0c9fda4d477bb8861cf60fdcbe225aa10ade9c523ee9456c5eb639", + "include/dawn/webgpu.h": "f93fc6b4e1a8ef2248bec4463efc488086c149d7cac9a26815ef91ca8056fdbf", + "include/dawn/webgpu_cpp.h": "2fe894d1cc51f8b15d68929bf2c624dfaf82813f56091fa87b1a6a94149a45dc", + "include/dawn/webgpu_cpp_print.h": "a764de8b9986a95a1cb0321fa8d6bf7fbfe5cea881a8653524148391704f51b1", + "include/dawn/wire/client/webgpu.h": "6df679a0b078056cc2aee5c1c653de56b3aeae2e817f472a01a64ba94427f987", + "include/dawn/wire/client/webgpu_cpp.h": "bf3ba6cf1e2fb36ad88cc56d02243a8b3ffbd1a583016743d8cc0f80fe872cc3", + "include/dawn/wire/client/webgpu_cpp_print.h": "895f8b9a4dead2066f3e63d2723d8b8af24c061b36612bb942b17e45f12b5afc", + "include/webgpu/webgpu.h": "5316d9fd241e604b3260fa8d4398a458a3dc88ee69c540ac959260b444bdfae3", + "include/webgpu/webgpu_cpp.h": "1166cea03743213cbd0a7fe04b01c8bcae8aa117d1370c124572c65b24f74aac", + "include/webgpu/webgpu_cpp_chained_struct.h": "7329fc197c9047ce1c08c3294401836f7f7b24bdf07fbaf0a266253ffa1ac3b8", + "include/webgpu/webgpu_cpp_print.h": "123e1c0567abe626d2730490e3ea00da9adf207ab4cc005c50a8a6a8bef7bd56", + "include/webgpu/webgpu_enum_class_bitmasks.h": "fd436dc17d050e156ac073b6148d90d388e2585de924ae0274b6aed3067c2a96", + "include/webgpu_upstream/webgpu/webgpu_cpp.h": "44370912b8a7fdf928bb305ad12fe969984ae76c02b1064122a0ac6996e8c439", + "include/webgpu_upstream/webgpu/webgpu_cpp_chained_struct.h": "582e62d371f29391d78a7356c634c6b76e1d58b6257a6f5be51701d15797fce1", + "include/webgpu_upstream/webgpu/webgpu_cpp_print.h": "1e6596a0a3d148e2bfa3c3deb9425520f783d21a8f4c34dc3ed16abc3d079bc6", + "include/webgpu_upstream/webgpu/webgpu_enum_class_bitmasks.h": "fd436dc17d050e156ac073b6148d90d388e2585de924ae0274b6aed3067c2a96", + "lib/cmake/Dawn/DawnConfig.cmake": "e4f4312e7039186388c8f7821b408835af7602d42820be0469a03e1e9f235bb5", + "lib/cmake/Dawn/DawnConfigVersion.cmake": "1b3ccd74b486260d855cc8e66eaa73fefb9539becb89e565a2e4c7b96e48a0f1", + "lib/cmake/Dawn/DawnTargets-release.cmake": "91bbd38c244065c3f1ea38dc9a14907794bf4fd28a3883e763242bd34a5ee20b", + "lib/cmake/Dawn/DawnTargets.cmake": "dfbf83810f6ca8e2ed98300110c0ac9649b96e42820076a8f16797ea84cb389e", + "lib/libwebgpu_dawn.a": "2865ca6b22acc82947ccbe64474c7e92effd9c48f18532715b41bd1877f1624e", + "licenses/LICENSE": "0493f897193af1796d5054659f45ec7d4c5af648fa67a99f01d30e55cc805abc", + "licenses/src/emdawnwebgpu/pkg/webgpu_cpp/LICENSE": "7e1efc85a78732a13d7ddfc8b52912da7c8f8d3c6d334624b20e3f3a96297de0", + "licenses/third_party/EGL-Registry/LICENSE": "4782253b8777b2c679544b31b18a52616d3c2ba0515dcf695e262bd318b356f0", + "licenses/third_party/EGL-Registry/src/sdk/docs/man/copyright.xml": "3a528aae8731663f7b2b02ee709b1ba1fd4f9bbd8935941b2a93981c5ab78bc4", + "licenses/third_party/EGL-Registry/src/sdk/docs/man/xhtml/copyright.inc.xsl": "609ae74144cd07a61653f733a0e11abf241a10c7dff4acd07dacda9fbffae22e", + "licenses/third_party/OpenGL-Registry/LICENSE": "d20809e7f3c8116615249bdefdc826c29b0b8332e7ac7ac249fd3949b716e8c5", + "licenses/third_party/abseil-cpp/LICENSE": "c79a7fea0e3cac04cd43f20e7b648e5a0ff8fa5344e644b0ee09ca1162b62747", + "licenses/third_party/agility-sdk/LICENSE": "caf3f489e3959df3605fec3c1f921fe72456c5d3640d998a5e635e8a9505cec5", + "licenses/third_party/benchmark_shaders/unity_boat_attack/LICENSE.md": "7ff3a8e0e49a0141989e4dea4fa92e18f908fe462a19d4b6cb2f1225c857bfa1", + "licenses/third_party/directx-headers/LICENSE": "7c77a44a8acd9b41fdc209864a8016b3d430b5d0e09309818d5b7444336df744", + "licenses/third_party/directx-headers/src/LICENSE": "903df5512f7d02609fed0c780a9b704f5a3eeb6e4d84ebe42a29845c81899a3c", + "licenses/third_party/directx-shader-compiler/LICENSE": "27a49e35d1da96eba18fba54bc882667ff0ff8c0254f16f2b6e165d605ba7df8", + "licenses/third_party/directx-shader-compiler/src/LICENSE.TXT": "27a49e35d1da96eba18fba54bc882667ff0ff8c0254f16f2b6e165d605ba7df8", + "licenses/third_party/directx-shader-compiler/src/lib/DxilCompression/LICENSE.TXT": "6f20fa7672b00e2e975c291df737cf227addf3ad32e36fef3fe0f416e4664d3d", + "licenses/third_party/directx-shader-compiler/src/lib/Support/COPYRIGHT.regex": "0424e57d4303164dc59a8509c20dae0518b853692e5c2b0e98b11816fdbc97c7", + "licenses/third_party/directx-shader-compiler/src/test/YAMLParser/LICENSE.txt": "d0d8b09800a45cd982e9568fc7669d9c1a4c330e275a821bbe24d54366d16fe9", + "licenses/third_party/directx-shader-compiler/src/tools/clang/lib/Headers/hlsl/LICENSE.txt": "eb425408dc2905e3506310bfdc33fdf00c1dfc2f2f01de95fb01809b29765be2", + "licenses/third_party/directx-shader-compiler/src/utils/unittest/googlemock/LICENSE.txt": "9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138", + "licenses/third_party/directx-shader-compiler/src/utils/unittest/googletest/LICENSE.TXT": "9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138", + "licenses/third_party/emdawnwebgpu/LICENSE": "2f79bf3699b0870251255b381670237f73f21a04a38c094f791eba39c5fd1df7", + "licenses/third_party/emdawnwebgpu/pkg/webgpu/src/LICENSE": "2f79bf3699b0870251255b381670237f73f21a04a38c094f791eba39c5fd1df7", + "licenses/third_party/glfw3/LICENSE": "149704059b5d0bf551637e50042dd4de9c2cae921021f6636298911e3a5f9462", + "licenses/third_party/glfw3/src/LICENSE.md": "149704059b5d0bf551637e50042dd4de9c2cae921021f6636298911e3a5f9462", + "licenses/third_party/glslang/LICENSE": "23353f4505b1c8ce4f8f72fc3b11dc74b4a8a7bf95921d93ff77f227c171a710", + "licenses/third_party/glslang/src/LICENSE.txt": "17e70c676e1521ff3e4686f04a2053d93a7e28a33be8de7ec37ab0ff72feb677", + "licenses/third_party/glslang/src/license-checker.cfg": "0b7c936ff1270fb5089750e326f732ce2f08b18e804dc8847aa44561d0a7a277", + "licenses/third_party/google_benchmark/src/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/googletest/src/LICENSE": "9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138", + "licenses/third_party/jinja2/LICENSE.rst": "3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b", + "licenses/third_party/libprotobuf-mutator/src/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/markupsafe/LICENSE": "0bbe88228fd63d20ec097f64e58d5a0a465123ae139140a18d406c60b48824b5", + "licenses/third_party/protobuf/LICENSE": "6e5e117324afd944dcf67f36cf329843bc1a92229a8cd9bb573d7a83130fea7d", + "licenses/third_party/protobuf/src/google/protobuf/compiler/notices.h": "b47ca1ea743623d50c6e02faa136dea4f6574a98e200666f859dbf57fb491721", + "licenses/third_party/protobuf/third_party/utf8_range/LICENSE": "02de69b64fc36d9e938f418e52723e42f0b2b226d58a9cb3c8dcbdf7059f5074", + "licenses/third_party/renderdoc/LICENSE.md": "b921912e9e433291f6010631a0dd41cec76c4a877966ecc22ba86151b2e66718", + "licenses/third_party/spirv-headers/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/spirv-headers/src/LICENSE": "ea43b1de38a6f90c488800d66dec1ed671e68cda530266bc96951fb5b6307613", + "licenses/third_party/spirv-tools/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/spirv-tools/src/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/spirv-tools/src/utils/vscode/src/lsp/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/vulkan-headers/LICENSE.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/vulkan-headers/src/LICENSE.md": "95ad366d23fadf701d355bc45fb8b82ae2d700239471d35d41286ac3b08ff903", + "licenses/third_party/vulkan-loader/src/LICENSE.txt": "43c0a37e6a0fa7ff3c843b3ec5a4fac84b712558ddac103fbd4c1649662a9ece", + "licenses/third_party/vulkan-utility-libraries/src/LICENSE.md": "69760673abf91cfd0280ae73739a29c078f493804d9016a122b3b189b48ad6e6", + "licenses/third_party/webgpu-headers/LICENSE": "17420d366df90c474bd70ad474694956cdb7fc64be70387a49a45458c4152d22", + "licenses/third_party/webgpu-headers/src/LICENSE": "17420d366df90c474bd70ad474694956cdb7fc64be70387a49a45458c4152d22", + "licenses/tools/nocompile/LICENSE": "368cca1106be99d39ecd32a38d8305585d802a475effb66380b91ffc9bcf709b" + }, + "buildScriptSha256": "1badce875cc834f1924a219aa457971a5f6f61b7a2803ef7fc03a9ceec44a4c5", + "qualification": "built; requires hardware probe and platform acceptance evidence" + }, + "angle": { + "schemaVersion": 1, + "component": "angle", + "rid": "osx-arm64", + "revision": "082d85ba19efba24d3c25108dc1f0cad9cf149f9", + "lockSha256": "be8c4effbcbd8ac6afb95d9042412cf970db7cec4beafa3e375ed42fa16360f1", + "settings": { + "is_debug": false, + "is_component_build": false, + "symbol_level": 1, + "angle_build_tests": false, + "angle_enable_d3d11": false, + "angle_enable_metal": true, + "angle_enable_vulkan": false, + "angle_enable_gl": false, + "angle_enable_wgpu": false, + "angle_enable_null": false, + "angle_enable_swiftshader": false, + "use_remoteexec": false, + "target_cpu": "arm64" + }, + "tools": { + "gn": "2552 (4f6a76b64b82)", + "ninja": "1.13.1", + "clang": "clang version 24.0.0git (https://chromium.googlesource.com/a/external/github.com/llvm/llvm-project 640ab6c44d94fbec679b3314e50468bb0c5a8f05)\nTarget: arm64-apple-darwin25.6.0\nThread model: posix\nInstalledDir: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/angle-workspace/angle/third_party/llvm-build/Release+Asserts/bin", + "clangSha256": "e79437df88619c43a94825a54f743c7d5830970acb1aee182fe9f3dd52c3aa48", + "depotTools": "69a652ea05e450f84620f56957a801923186fda5", + "host": "macOS-26.6.2-arm64-arm-64bit-Mach-O" + }, + "files": { + "build-info/DEPS": "08b207a0201d412d998733467b12916e2cd364a40e317400c68676cfd184d4f5", + "build-info/args.gn": "c579b6cba04f8bbcbb4dc1419c9523a9d23fe5a09d42ae1350eba8d98d448274", + "build-info/resolved-args.gn": "4fb99b973bf8ea24f33dbf3189c4b2faf4d9ecb5b3b0c2df8d678d33ee6d5b16", + "build-info/source-graph.json": "1d7d735cd311314e4683fb8920324a341949a523855ba374001956322d445c8b", + "include/CL/.clang-format": "f8405bf75d1ab2ed2e69b525a538b409b801812a7c71225da7bb46434f7dcc18", + "include/CL/README.md": "4bb252e68432d973e9c27403ceeb87fa0dd8803626c34bd68c71edd9ed5c2748", + "include/CL/cl.h": "3c51949c38bf9bca6dbe919a1f60727bad1ef4a7a321c6d530f7502ddc1fd706", + "include/CL/cl_d3d10.h": "9dd1c0706da71db79465285d723b43f12eef04680ea02efa529be8b2e38f5dbe", + "include/CL/cl_d3d11.h": "f18e5fed81f0133362f82f67bb5c4d8dd3364df1140343be4d1ec82e60f57a4c", + "include/CL/cl_dx9_media_sharing.h": "b1c2071aabd613738b5b60612413acfec618b14902015f3f55d25da9a2cd3a9e", + "include/CL/cl_dx9_media_sharing_intel.h": "0ce4432973392815885d4b6fe968135682502d702ec3dbde6db78e173adfa8da", + "include/CL/cl_egl.h": "a16fcaec96792b6768d82e351c07ebbf7c9e12be415ee735eb2af5ce379cfcb7", + "include/CL/cl_ext.h": "909055c3448f9a6f07dc0b9cf8bd3582fdf6bd24c934abadd7ccba5ba5ac53be", + "include/CL/cl_ext_intel.h": "20fc359c30ad2339af7c8442da97ebd8e6ea4fb8f57c47b96b0f284d4c281953", + "include/CL/cl_function_types.h": "a90d127737e412ef3edb2ffa90f6c237ddb58899b3b14a1ec412ea0eb88c8009", + "include/CL/cl_gl.h": "621a3a1fb33604a9026584fc423110b9ecaa094ac2c8cf3d2c3dfe795debbbff", + "include/CL/cl_gl_ext.h": "7fd0d4ad6072a90b05749fe4d2131b424b71d19ae809ae3d7d4862b0d0aee16a", + "include/CL/cl_half.h": "c4d64cb15e710e60058d6072f8fbb1ed988eda60d16f6fc1ddb7df8c9e903109", + "include/CL/cl_icd.h": "a43e3a5c9921a4f9a59001884e75d8b79a0afef50644f80543ec63f0ca71cfce", + "include/CL/cl_layer.h": "2eff3e48b4f478e054d077074c72719d2b3e8006e6b8057a0e55f6078e00555e", + "include/CL/cl_platform.h": "be9647d121bfea20cfac9fe2be2e3ac7851a0dd77a141363dcc72f126a1a358c", + "include/CL/cl_va_api_media_sharing_intel.h": "fe9457119b44cdd0fca90e3b21fd2c8b81de792a81ce19faa17490767fd4ed07", + "include/CL/cl_version.h": "1b161f73c0f0b07121314055f75df6c19d6a93e3a0d24202c6f3e2dd1615b2a2", + "include/CL/opencl.h": "9a76fa63630e8fd5b30b5e6c692dbe7ab22edff708dfe6a06d04a3671a7716e1", + "include/EGL/.clang-format": "f8405bf75d1ab2ed2e69b525a538b409b801812a7c71225da7bb46434f7dcc18", + "include/EGL/egl.h": "b9669499729432653659b99ec9e92f3defed9207c480ec7926024f424e693f9a", + "include/EGL/eglext.h": "92fe2e491f9374afb88e9c8d96be7288f33f27db97d06610ba5671ea732f8aae", + "include/EGL/eglext_angle.h": "4cb01b43610b99054279555e047a69e8f33f585856ee502a771a631f327ec5a7", + "include/EGL/eglplatform.h": "b748729767798d85ecf8e1923552879328a76d572327b641ce737549b391cc9c", + "include/GLES/.clang-format": "f8405bf75d1ab2ed2e69b525a538b409b801812a7c71225da7bb46434f7dcc18", + "include/GLES/README.md": "d58d1684a9b2352233fad1d1e4eb8cb81f7d8e091546152b79422a453d9bf1de", + "include/GLES/egl.h": "1ba84c782436b5e103cd1ee8060840846b96ec4727d00944cee59d1c44db0dda", + "include/GLES/gl.h": "85eab30a741d4582bd2f97507cef71a96c6f5ca0d08b3f958a9cc1eada95b1ee", + "include/GLES/glext.h": "f06c15fe13af244f68029b853602ba20387b4f120555342632dd76df32a46631", + "include/GLES/glplatform.h": "857bf0685ecb0dddf692157ea9ef98c95e9f58b5ed0c41b26fd3fe51ed1a3624", + "include/GLES2/.clang-format": "f8405bf75d1ab2ed2e69b525a538b409b801812a7c71225da7bb46434f7dcc18", + "include/GLES2/gl2.h": "822ced52b4cb67bb36446c82b3ca049dc1df5b3f7a011c7d77085a05efb9194e", + "include/GLES2/gl2ext.h": "53c564985d35b7ca4b744a0a8b81f120aa0c114c319b3b98d3223bf599bc815b", + "include/GLES2/gl2ext_angle.h": "b99b291afc03452195b8b84658c203b83de367cc7fcb7be537177847c268a1b8", + "include/GLES2/gl2platform.h": "f5da0747540a50be5f44aad264aae45bdf157a192c40f17487dd9a2f99c71b6c", + "include/GLES3/.clang-format": "f8405bf75d1ab2ed2e69b525a538b409b801812a7c71225da7bb46434f7dcc18", + "include/GLES3/gl3.h": "b56feb7c5a1de8ed0be161fa5205900f24d15d16fe72005ef68d236c3c39982d", + "include/GLES3/gl31.h": "1c48ee870ab94b0e7a02fe69ccbe0f83123215c9a9577f5c2a52f070664cddcc", + "include/GLES3/gl32.h": "5524d3a5965084585da4111d98751c7e534e80ec679c9d4d1a1323be2a0ea0f5", + "include/GLES3/gl3platform.h": "a9e060dae5a2b11c5a889b679692b7089a10a7e03ebfbb6cf28217f6e322fb08", + "include/GLSLANG/ShaderLang.h": "4057e38faaaf85d599f2c962289f3aa11c64e6b991bd5091beddddb9c74c57f1", + "include/GLSLANG/ShaderVars.h": "a29d371a0bfac307b7e49711beb9bc2a12e9434bcfb2fe620f2f4e1700a8f3c2", + "include/GLX/.clang-format": "f8405bf75d1ab2ed2e69b525a538b409b801812a7c71225da7bb46434f7dcc18", + "include/GLX/glxext.h": "1649a24997d9bd448f52e3d59c67fc3985b04433251efb584b62ae2d4294ef36", + "include/KHR/.clang-format": "f8405bf75d1ab2ed2e69b525a538b409b801812a7c71225da7bb46434f7dcc18", + "include/KHR/khrplatform.h": "e206a6931f98ffe1c5c7ece69c4f94bbe1c9279243f40cbe7782848a0d3fa2de", + "include/WGL/.clang-format": "f8405bf75d1ab2ed2e69b525a538b409b801812a7c71225da7bb46434f7dcc18", + "include/WGL/wgl.h": "e12266d28013a73c240193cd571d381c885744e65cf2229fb04d3f7a3d3d5f6e", + "include/angle_cl.h": "969515cd01035304c2480bc7fe44e195cbcc2c69b6ca58753461165128e66652", + "include/angle_gl.h": "6777b1403747e1ccc38dadd07a6e777db21a8a3526a060ef9e77b3b0e9a0e081", + "include/angle_windowsstore.h": "1870aee46b27e941e0ec9e07667dbf5df558acaf0b839ca0fa2767c65aba8740", + "include/export.h": "98abf7576893b250aabd8e7e31d38ebb0b4c27def977688b010cd4a3e68ec03e", + "include/platform/Feature.h": "d11abc73a9fa0c8b7e211c1194b85e625740fa6278ca4c09230e7358b5e7764f", + "include/platform/PlatformMethods.h": "eb9ba97210079e63c17d9f345385bc5d2b00d96423f6a251dc6c66db7f1da888", + "include/platform/autogen/.clang-format": "62729c82e63c5c265ba8ed72f382291e82b123b30027d5563fdd6003a260c31a", + "include/platform/autogen/FeaturesD3D_autogen.h": "e40a63941add051b03eac5df795cbdf4ac08439a0c426a3ad1776c716d6ea7a9", + "include/platform/autogen/FeaturesGL_autogen.h": "83cf3c6b3d52f0f22797d8a46f8907529888ce72995f188fbb317867ad15cfb6", + "include/platform/autogen/FeaturesMtl_autogen.h": "921fc0e9435511e0fb3ae60a19ea1439a60a5fb0a615a7c37d78ac0624d00108", + "include/platform/autogen/FeaturesVk_autogen.h": "2c39cc0bbaf805233b0b9d82d031f4a8e33618df8e200a0677a5b6a82820d726", + "include/platform/autogen/FeaturesWgpu_autogen.h": "35dfc26dc7e64f218bf19e22d085fe056ce36c31bd1059742125a11bf142d0f0", + "include/platform/autogen/FrontendFeatures_autogen.h": "6de497d21bd7edb2ff228f0e08dbfd09d630d670efd55664c01ce9bec10ecfee", + "include/platform/d3d_features.json": "a772a2863c108fd1c0effc728cd63534c8627d5388a945300e21644a6727ae94", + "include/platform/frontend_features.json": "9d535c18b58cfa8a0923e1c62599cedd71118195d65f1ddcfbdc09560eb1703a", + "include/platform/gen_features.py": "3cf9edde1b6e756cde3e2c42ed86876f595c0f29d1c20276746f6c38d46079bc", + "include/platform/gl_features.json": "cd713b346aa08ce50260b3d54777b57c8e1dc7110dc0d80cc56accdc09af0ff6", + "include/platform/mtl_features.json": "f90100ce74362383769600596c319a7744f578509f7504632dd26a611fd060cc", + "include/platform/vk_features.json": "d287a0a40021702b6d3cbaa51a5e5cc3191a3492dda20a164a940a1d614627f1", + "include/platform/wgpu_features.json": "0e569b175362c6d4f17b2b299bef6b2729e9039743630b83a8902f08b5c6dbab", + "include/vulkan/vulkan_fuchsia_ext.h": "30e38e54b38d1bb981a4b2980e853043987b7dfa0bc7d0a9bb1981f3490b3722", + "lib/libEGL.dylib": "7028670d2e3c2c2f8a6032668144035e2caca54afc71559a789b9759a5670830", + "lib/libGLESv2.dylib": "edcce24792bc722a2ce5fc3080c13a317740ea5e1954f74efd97e04c95dfdf17", + "licenses/LICENSE": "bf4da21bd20bcfb5b60b7ecc67fa864a79be049e21d6178076887f178dd6c71a", + "licenses/build/android/incremental_install/third_party/AndroidHiddenApiBypass/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/buildtools/LICENSE": "ff11d445fb41a1087c7630e120ab15f1a2cb67c1b707173cb494141805fca35e", + "licenses/buildtools/reclient/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/buildtools/reclient/NOTICE": "a90a4f374e17d62b1725bd687eca71ae7110ded0c2fe3e2f06c9ba7176169b5c", + "licenses/buildtools/third_party/mold/LICENSE": "c98a2858469bd3b231c8865c5b65f80f6ffbf25e850d5d575967e3d9ee080755", + "licenses/src/common/third_party/xxhash/LICENSE": "6ffedbc0f7878612d2b23589f1ff2ab15633e1df7963a5d9fc750ec5500c7e7a", + "licenses/src/libANGLE/renderer/vulkan/shaders/src/third_party/etc_decoder/LICENSE": "118be5792e5126839694ca2209c62c71d2d7cd49e7bbb43bbdee9b016fb06094", + "licenses/src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/LICENSE": "09a7c3fbc0b4ae6a9ccc4ffdcbfa511c14b8647a24f24783838862cf6c226d4e", + "licenses/src/tests/test_utils/third_party/LICENSE": "0e64c1e9cd62f47682caeb545d2943fb4c38a9b2e5d9fd7e3b456973bb430d1b", + "licenses/src/third_party/ceval/LICENSE": "0dd71b8a6d6db0db4dc38a983749e1b2f4bb57aba30141a168042042c4a7b6f8", + "licenses/src/third_party/libXNVCtrl/LICENSE": "31346421254a3e6e12687cf17f19f6357ee73a617fa7b3d3ccefdcbabe49bdd3", + "licenses/src/third_party/volk/LICENSE.md": "336f505f8d5aa73ea40b4d798dde86953e9c1f6525757f1d7f18120fea09bb1d", + "licenses/third_party/EGL-Registry/src/sdk/docs/man/copyright.xml": "3a528aae8731663f7b2b02ee709b1ba1fd4f9bbd8935941b2a93981c5ab78bc4", + "licenses/third_party/EGL-Registry/src/sdk/docs/man/xhtml/copyright.inc.xsl": "609ae74144cd07a61653f733a0e11abf241a10c7dff4acd07dacda9fbffae22e", + "licenses/third_party/OpenCL-Docs/src/LICENSE": "01db48fbe12f95dfd63b92dc7c29afcf8783f96f8ca0061a9fb7e869f5a08512", + "licenses/third_party/OpenCL-Docs/src/config/copyright-ccby.txt": "62fbcd9ebefaa5dd4245b241d36655e83391bb196c873c6023e1296fbc447ab8", + "licenses/third_party/OpenCL-Docs/src/copyrights-ccby.txt": "f7ef3add54eda59b0a8b882154c8d1e98d1192f3d0287d1a37b1c65a95ec2ff3", + "licenses/third_party/OpenCL-Docs/src/copyrights.txt": "18a3e8b3d7d0adf096d0b28183f6c808749877829cc53b8ac9ed79e2bd01ac15", + "licenses/third_party/Python-Markdown/LICENSE.md": "6f1193cb634718e65c3a537d6e25ebd614820ec0ef693cfc12248112638d64da", + "licenses/third_party/SwiftShader/LICENSE.txt": "3ddf9be5c28fe27dad143a5dc76eea25222ad1dd68934a047064e56ed2fa40c5", + "licenses/third_party/SwiftShader/third_party/SPIRV-Headers/LICENSE": "ea43b1de38a6f90c488800d66dec1ed671e68cda530266bc96951fb5b6307613", + "licenses/third_party/SwiftShader/third_party/SPIRV-Tools/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/SwiftShader/third_party/SPIRV-Tools/utils/vscode/src/lsp/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/SwiftShader/third_party/astc-encoder/LICENSE.txt": "494accc32e50eb523a0e384d0ae6d4b702db867a89d6971216760e92b240ee12", + "licenses/third_party/SwiftShader/third_party/llvm-10.0/llvm/include/llvm/Support/LICENSE.TXT": "a012d664e4e01df52a65b2eeafdfb8aeb856fec0e6c372265d01b0109c3f5e2a", + "licenses/third_party/SwiftShader/third_party/llvm-10.0/llvm/lib/Support/COPYRIGHT.regex": "0424e57d4303164dc59a8509c20dae0518b853692e5c2b0e98b11816fdbc97c7", + "licenses/third_party/SwiftShader/third_party/llvm-16.0/llvm/include/llvm/Support/LICENSE.TXT": "54cbc326a78b9400065bfc5830a57fdcdaf808286d4ac35d8a9e324aa77b7241", + "licenses/third_party/SwiftShader/third_party/llvm-16.0/llvm/lib/Support/BLAKE3/LICENSE": "6a94bedb8b707ed97f6e310d0d015ab14e0683ffa0a612b02958581b9cc9fc0e", + "licenses/third_party/SwiftShader/third_party/llvm-16.0/llvm/lib/Support/COPYRIGHT.regex": "0424e57d4303164dc59a8509c20dae0518b853692e5c2b0e98b11816fdbc97c7", + "licenses/third_party/SwiftShader/third_party/llvm-subzero/LICENSE.TXT": "9c9a05118ed1b6d96781a2e52335f7d4ec3dd6e7139340a8aa95fbf7eb4f199a", + "licenses/third_party/SwiftShader/third_party/marl/LICENSE": "58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd", + "licenses/third_party/SwiftShader/third_party/marl/license-checker.cfg": "398974c0415d06f88df08dc434cf58a0d0038afaf95d100799f42bae973ea945", + "licenses/third_party/SwiftShader/third_party/subzero/LICENSE.TXT": "c55ce1e876843853a8a2e5c936df6dc8dd3d185f83d85e6d113143b8c24f542e", + "licenses/third_party/VK-GL-CTS/src/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/VK-GL-CTS/src/NOTICE": "ca382aa537f8923d6c0991fb976d184a2009eb76080313bf10dcecdc9311f0dd", + "licenses/third_party/VK-GL-CTS/src/external/graphicsfuzz/data/gles3/graphicsfuzz/LICENSE": "8e95cc3fc83600845b44bd2f763d8edc48cfffe0feb3abd59d30810aef1119c7", + "licenses/third_party/VK-GL-CTS/src/external/vulkancts/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/abseil-cpp/LICENSE": "c79a7fea0e3cac04cd43f20e7b648e5a0ff8fa5344e644b0ee09ca1162b62747", + "licenses/third_party/android_system_sdk/LICENSE": "8f1bd8841582bdee098eeae9eeb3862d9e7af011e94e54c14aef0568e816be19", + "licenses/third_party/astc-encoder/src/LICENSE.txt": "494accc32e50eb523a0e384d0ae6d4b702db867a89d6971216760e92b240ee12", + "licenses/third_party/astc-encoder/src/Test/Images/HDRIHaven/LICENSE.txt": "f7230d5a427449430ec09e331f751ae8a7e26cafdc0af89c02e5312e4320de9b", + "licenses/third_party/astc-encoder/src/Test/Images/Khronos/LICENSE.txt": "edc930ca714966b56089c5a3e9a790366f9bf37e1749fb17e6dcf40b8783251f", + "licenses/third_party/catapult/LICENSE": "f0df289ba9d03d857ad1c2f5918861376b1510b71588ffc60eff5c7a7bfedb09", + "licenses/third_party/catapult/common/py_vulcanize/third_party/rcssmin/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/catapult/common/py_vulcanize/third_party/rcssmin/bench/LICENSE.cssmin": "65d4ed698fb5cbcd1d44c78bc6a02c5bf1da00df5395d2d6ac43bdafe6bc20dc", + "licenses/third_party/catapult/common/py_vulcanize/third_party/rjsmin/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/catapult/experimental/trace_on_tap/LICENSE": "3b38d48befd0af70b892e13d10c9e34679416c24a9277f962629951c64d71f4c", + "licenses/third_party/catapult/experimental/trace_on_tap/third_party/pako/LICENSE": "3bc404ffa7888053253eedcc5a667619aa08dc1be0bea400e5ff28c51602180f", + "licenses/third_party/catapult/systrace/profile_chrome/third_party/COPYING": "8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643", + "licenses/third_party/catapult/systrace/systrace/LICENSE": "ef5b39dfcafe08323262e3f51a3a9de649978a55ed8ef8eef3c451f2c1e78a53", + "licenses/third_party/catapult/telemetry/third_party/altgraph/LICENSE": "348dfecdd95ac4de096f7495674c9e90c778f8795d3faf5cd880b0a25bcbdd15", + "licenses/third_party/catapult/telemetry/third_party/altgraph/doc/license.rst": "e21ff4f2af8698b4e8f44d333bf2c8b59523488357ce26513afc7404092c1884", + "licenses/third_party/catapult/telemetry/third_party/chromite/LICENSE": "212c5a071f61512786b5e5840b3d70c85e017f3f82939ad4d4a870fc48b33477", + "licenses/third_party/catapult/telemetry/third_party/flot/LICENSE.txt": "e09d954054165670b6a669e6da59673d9e85f343b9983e92a220623ff0198f8c", + "licenses/third_party/catapult/telemetry/third_party/modulegraph/LICENSE": "c70d07bf7a3d935e05c62e80bc0fd30292d7182cd9ab695f7c7ddd7bcac39256", + "licenses/third_party/catapult/telemetry/third_party/mox3/COPYING.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/catapult/telemetry/third_party/png/LICENSE": "8ebde739ff734d4ed18082965e83dbab9673a37199d2af9cfc3fb390398b35b8", + "licenses/third_party/catapult/telemetry/third_party/pyfakefs/pyfakefs/COPYING": "09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b", + "licenses/third_party/catapult/telemetry/third_party/websocket-client/LICENSE": "f3834b4a6b6e7c112207c84a11e87d4255bee0310b90338b5aaccd849fab1afb", + "licenses/third_party/catapult/third_party/apiclient/LICENSE": "43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1", + "licenses/third_party/catapult/third_party/beautifulsoup4/COPYING.txt": "424336c2b3446b3c179f07217271bb914dc881a65c2bf7021da98c77e776d2c9", + "licenses/third_party/catapult/third_party/beautifulsoup4-4.9.3/COPYING.txt": "a47ea51236098464fe0b4f559743590b533056d9e00f49ecbf80299fab47e231", + "licenses/third_party/catapult/third_party/beautifulsoup4-4.9.3/LICENSE": "ca7227ddb9eed6cc809e157f67b020e78dde063240001d11856f85c49cb6e423", + "licenses/third_party/catapult/third_party/cachetools/LICENSE": "7dd496262c0ba3787f7eebf02663c50c305ff575a81f69208e1645738da3cffc", + "licenses/third_party/catapult/third_party/chai/LICENSE": "17afb4516438c26ee15213c5a082206340d976a68472b8eab2499d7bce4debec", + "licenses/third_party/catapult/third_party/chardet/LICENSE": "6095e9ffa777dd22839f7801aa845b31c9ed07f3d6bf8a26dc5d2dec8ccc0ef3", + "licenses/third_party/catapult/third_party/click/LICENSE": "9a8ad106a394e853bfe21f42f4e72d592819a22805d991b5f3275029292b658d", + "licenses/third_party/catapult/third_party/cloudstorage/COPYING": "50e6751797c50dedd75ef1b8a0d9e42f5f8472e9fbce91f34718e9f97b0c780a", + "licenses/third_party/catapult/third_party/coverage/LICENSE.txt": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/catapult/third_party/coverage/NOTICE.txt": "55f703486573f73b00920a8d46fc551debc4d1fa35ff4c18784363b09b3bf780", + "licenses/third_party/catapult/third_party/d3/LICENSE": "7a3cb0e5055874e67db9aa2d5fe26de23204fa994ffbad198901ffe9c812a717", + "licenses/third_party/catapult/third_party/d3/v5/LICENSE": "7a3cb0e5055874e67db9aa2d5fe26de23204fa994ffbad198901ffe9c812a717", + "licenses/third_party/catapult/third_party/depot_tools/depot_tools/third_party/schema/LICENSE-MIT": "f4360ca8f779e6a673cd2882f73419bc2c5f74184fd9db91d2e86a368cc04e0b", + "licenses/third_party/catapult/third_party/flask/LICENSE": "489a8e1108509ed98a37bb983e11e0f7e1d31f0bd8f99a79c8448e7ff37d07ea", + "licenses/third_party/catapult/third_party/flot/LICENSE.txt": "52cb566b16d84314b92b91361ed072eaaf166e8d3dfa3d0fd3577613925f205c", + "licenses/third_party/catapult/third_party/google-auth/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/catapult/third_party/graphy/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/catapult/third_party/gsutil/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/catapult/third_party/gsutil/gslib/vendored/boto/LICENSE": "e3248f259a211f4d9ed06cfd07bc64373376c92a192152e37ec3420d6036dd4e", + "licenses/third_party/catapult/third_party/gsutil/gslib/vendored/oauth2client/LICENSE": "d6a43f0bae029b0cea5bd0fffd87f05659dc599a763886027614ad210be1ba3d", + "licenses/third_party/catapult/third_party/gsutil/third_party/apitools/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/catapult/third_party/gsutil/third_party/argcomplete/LICENSE.rst": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/catapult/third_party/gsutil/third_party/argcomplete/NOTICE": "2c889c721ec8ae6d7664680afaefbb4c7620976f434b57a506ecd92f0649b6a0", + "licenses/third_party/catapult/third_party/gsutil/third_party/cachetools/LICENSE": "23c4eff7a1c027a977a0b79c4497e17582c334c5f17ef6ac8ca0b52d1e7d8417", + "licenses/third_party/catapult/third_party/gsutil/third_party/certifi/LICENSE": "e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7", + "licenses/third_party/catapult/third_party/gsutil/third_party/chardet/LICENSE": "dc626520dcd53a22f727af3ee42c770e56c97a64fe3adb063799d8ab032fe551", + "licenses/third_party/catapult/third_party/gsutil/third_party/charset_normalizer/LICENSE": "6d0d41bfe170ac6c7dc248c9a63e254d0fb45a60d50a8257d0af92c6e249b887", + "licenses/third_party/catapult/third_party/gsutil/third_party/charset_normalizer/data/NOTICE.md": "0cb3efcfd8f7a02a337e98dc3de4b0b57424d7208a332b26e7deb8cb94c13922", + "licenses/third_party/catapult/third_party/gsutil/third_party/crcmod/LICENSE": "89480768826f408daea1f3caff0509c2cc9606e10f6bb0ccfd12a3d604842c35", + "licenses/third_party/catapult/third_party/gsutil/third_party/crcmod_osx/LICENSE": "89480768826f408daea1f3caff0509c2cc9606e10f6bb0ccfd12a3d604842c35", + "licenses/third_party/catapult/third_party/gsutil/third_party/fasteners/LICENSE": "d2de2f566d2d0e0b509fb0ea1fa3669f49064ab1de21c57453cab3750a234e8f", + "licenses/third_party/catapult/third_party/gsutil/third_party/gcs-oauth2-boto-plugin/LICENSE": "8c6db340475136df3c1201d458fa5755698eace76e510471ecc9d857d6083dac", + "licenses/third_party/catapult/third_party/gsutil/third_party/google-auth-library-python/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/catapult/third_party/gsutil/third_party/google-auth-library-python-httplib2/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/catapult/third_party/gsutil/third_party/google-reauth-python/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/catapult/third_party/gsutil/third_party/httplib2/LICENSE": "589eec38f72df2be203711d3b8cbece9b908c5e7ff00bc3cab7f63bae9e366b4", + "licenses/third_party/catapult/third_party/gsutil/third_party/idna/LICENSE.md": "b7a336abf3b04e180ec065cdd16e705d079e1cc7a14f910aa6e9187f36b9cd87", + "licenses/third_party/catapult/third_party/gsutil/third_party/monotonic/LICENSE": "cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14", + "licenses/third_party/catapult/third_party/gsutil/third_party/pyasn1/LICENSE.rst": "2aad5fc00f705c4a1addb83eed10a6a75d286a3779f0cf8519d87e62bc4735fd", + "licenses/third_party/catapult/third_party/gsutil/third_party/pyasn1-modules/LICENSE.txt": "70bb0e4c89f4e41a11950365d98a13e2e6ad6ee4aed80cd1ecffc93d98d44e8c", + "licenses/third_party/catapult/third_party/gsutil/third_party/pyparsing/LICENSE": "10d5120a16805804ffda8b688c220bfb4e8f39741b57320604d455a309e01972", + "licenses/third_party/catapult/third_party/gsutil/third_party/pyu2f/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/catapult/third_party/gsutil/third_party/requests/LICENSE": "09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b", + "licenses/third_party/catapult/third_party/gsutil/third_party/requests/NOTICE": "f5110972dedad2b4e9d314518daf3b7d72d6e02e499acd802181de6f74571dcc", + "licenses/third_party/catapult/third_party/gsutil/third_party/requests/ext/LICENSE": "3172d399cbd8f10609e73fec73d0e0b33eecd3c572a68b0722229d8c7059f725", + "licenses/third_party/catapult/third_party/gsutil/third_party/retry-decorator/LICENSE.txt": "c3710b8fc15eee9d2de041c0302116dc30fcb370ae5cc3969e746d8f08b869fd", + "licenses/third_party/catapult/third_party/gsutil/third_party/rsa/LICENSE": "073f28b7d389c8fe74f607e17c27f81eaa5ace69edc43a884f23f41b41c5c726", + "licenses/third_party/catapult/third_party/gsutil/third_party/six/LICENSE": "4375ba20e2b9c6c4e7cad2940a628fd90e95cc3d50ee92aae755715d8ba1fbd0", + "licenses/third_party/catapult/third_party/gsutil/third_party/urllib3/LICENSE.txt": "130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686", + "licenses/third_party/catapult/third_party/html5lib-1.1/LICENSE": "16a39991619e92f18680932da2a9199fdf7d95df3ecaedc52ea06218aabafd6f", + "licenses/third_party/catapult/third_party/html5lib-1.1/html5lib/tests/testdata/LICENSE": "ff512aac9ef231d504be5afaf4429005024e4b2aaf257be39524f37b8402aaf2", + "licenses/third_party/catapult/third_party/idb/LICENSE": "873a2f333fda393ec3464f4579209b019d98e97c3bf498b10e85f630162fd708", + "licenses/third_party/catapult/third_party/idna/LICENSE.rst": "0d4bc7abd48dcfb14e24254ee404066737ff0167144e222914a2113b8794683e", + "licenses/third_party/catapult/third_party/ijson/LICENSE.txt": "3cdb5f5be14a92dec127561fc90d8f7127aa59d81522938ffc91952615ea13eb", + "licenses/third_party/catapult/third_party/itsdangerous/LICENSE": "a6c1acff7e7b7918ae5122700fe2da1e127dd459cc5b04271ff8f62d6a6f9e17", + "licenses/third_party/catapult/third_party/jinja2/LICENSE": "3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b", + "licenses/third_party/catapult/third_party/jquery/LICENSE.txt": "a078a8f80016416042c2e5f04dbb7f499f0f6deebb086511f3a3b72633a2d761", + "licenses/third_party/catapult/third_party/jszip/LICENSE.markdown": "b7804b570c31c8491352bd4e0b123a9652edb72d778554986ec51f22e6c2b70b", + "licenses/third_party/catapult/third_party/markupsafe/LICENSE": "489a8e1108509ed98a37bb983e11e0f7e1d31f0bd8f99a79c8448e7ff37d07ea", + "licenses/third_party/catapult/third_party/mocha/LICENSE": "1f194a987fa1dc60e4bcf5e04e0fc03fff8f2ee587c52136adb2cebb397250b8", + "licenses/third_party/catapult/third_party/mox3/COPYING.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/catapult/third_party/oauth2client/LICENSE": "e3aefad6cbfecc174ce6a7628e8f2fb58d1c2928d9d4f9d531d125177ab23324", + "licenses/third_party/catapult/third_party/polymer/LICENSE.polymer": "24699c6858472311aa9acc6c2b7112ff9de6e7792569158ba9e439deb0529ef6", + "licenses/third_party/catapult/third_party/polymer/components/google-apis/LICENSE": "4149f7427385d27e5915e68129cff9706f424353783a958919f99e80cb6fcc63", + "licenses/third_party/catapult/third_party/polymer/components/google-signin/LICENSE": "328cb74a9f2c5b67be2f63da900f09363060feac3c01ee42e3f441c2cab1eec3", + "licenses/third_party/catapult/third_party/polymer/components/polymer/LICENSE.txt": "984fb04a16a9f1e0145ffd891125dc366a01cd921f58c9b0369be400c720790d", + "licenses/third_party/catapult/third_party/polymer/components/promise-polyfill/LICENSE": "453a712c58161b74efa998578aaf10fd7ad8204120730de38ca04d7f47f3ea46", + "licenses/third_party/catapult/third_party/polymer/components/shadycss/LICENSE.md": "10ae82b5a349c1ac15015d2c50e5adaf6413538f69be961cf0140cfc152b97e3", + "licenses/third_party/catapult/third_party/polymer/components/web-animations-js/COPYING": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/catapult/third_party/polymer-svg-template/LICENSE": "737070ec67c0feed5e767af9c774d159c4132604812ac29736ee5e7a917c998d", + "licenses/third_party/catapult/third_party/pyasn1_modules/LICENSE.txt": "22c5cc6922ab5d69fba32d8c5ee4cdd14981508cb53afc0ebd85593847fd95a5", + "licenses/third_party/catapult/third_party/pyfakefs/COPYING": "09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b", + "licenses/third_party/catapult/third_party/pyparsing/LICENSE": "10d5120a16805804ffda8b688c220bfb4e8f39741b57320604d455a309e01972", + "licenses/third_party/catapult/third_party/redux/LICENSE.md": "f2da73c752c6b87624755edacf927cbd915fa76555d383e74494bad4a5155ab3", + "licenses/third_party/catapult/third_party/requests/LICENSE": "c15544050f84cf503e47d60299a7c119e751f1d81ac617a8a13e706581cc05bc", + "licenses/third_party/catapult/third_party/six/LICENSE": "8bb850c565aa389fdc16f3a46965ad23d82adff60f2393fc2762b63185e8e6c9", + "licenses/third_party/catapult/third_party/snap-it/LICENSE": "b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1", + "licenses/third_party/catapult/third_party/tsproxy/LICENSE": "b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1", + "licenses/third_party/catapult/third_party/typ/LICENSE": "6dc0e068dcf3a5bc8e054205b85b7720e1d49265bbc64bf515d2cf79197df69a", + "licenses/third_party/catapult/third_party/uritemplate/LICENSE": "2d1f6074aca5e089e1cd580c0a5a925fc508ad542aec8bfe804c0031cf717667", + "licenses/third_party/catapult/third_party/vinn/LICENSE": "842d692fdbb8b4dd8e22461d5091e29c1c8725dd7618fcd5d59436c1c10f8804", + "licenses/third_party/catapult/third_party/vinn/third_party/parse5/LICENSE": "d0a435e5f6a4943a2c3927c3932e7baee9c2231caa977ffeba748f3712ae437e", + "licenses/third_party/catapult/third_party/vinn/third_party/v8/LICENSE": "f9db6a9bcfcc0644975526b4f9a21af61473ac2767e3c4764ff14c48fbff4000", + "licenses/third_party/catapult/third_party/vinn/third_party/v8/LICENSE.strongtalk": "6a585a9f466654abc8fc0829d56b1bc987e3a073d31faa03bba37d33640a23cd", + "licenses/third_party/catapult/third_party/vinn/third_party/v8/LICENSE.v8": "4af93c12062c58058378de2397dc1c92bbff9ddfb1d583a01c84127557ce97ca", + "licenses/third_party/catapult/third_party/vinn/third_party/v8/LICENSE.valgrind": "cae8c00ca6e90a682c321ec11e7a5a345d0d317aa0b8f038e03ef03a18095b2f", + "licenses/third_party/catapult/third_party/webapp2/LICENSE": "5359da685feee46d7e22acc5b8fcc496c5ca176fc46986eb640aa21aaedfaf1d", + "licenses/third_party/catapult/third_party/webencodings-0.5.1/LICENSE": "f23bae6ada76095610a77137fb92aec7342723900211c5826d54b4c57907ca56", + "licenses/third_party/catapult/third_party/werkzeug/LICENSE": "3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b", + "licenses/third_party/catapult/tracing/LICENSE": "f77133324f35589f9f170473456321fe76aa35b750293cb8a475e26afa8f2bac", + "licenses/third_party/catapult/tracing/third_party/chai/LICENSE": "17afb4516438c26ee15213c5a082206340d976a68472b8eab2499d7bce4debec", + "licenses/third_party/catapult/tracing/third_party/d3/LICENSE": "1920d2326ebbad34dcbd9681b4fe4926f113aa5e7dc9a92fceb456d859ee142e", + "licenses/third_party/catapult/tracing/third_party/gl-matrix/LICENSE.md": "e8b80a53d0f95a3cf0f992f8cfc6b3911a7f32f47e0e4a8d4fd66582eeae9484", + "licenses/third_party/catapult/tracing/third_party/jpeg-js/LICENSE": "24604018b3d42b92eb3a0ee55a9e8d3bde92f95a0809f9ef22c06ce32f627940", + "licenses/third_party/catapult/tracing/third_party/jszip/LICENSE.markdown": "602ef1d5d3db1b23ada0b61d4230ef336012de7bc3b773d565f2b27a2757f51d", + "licenses/third_party/catapult/tracing/third_party/mannwhitneyu/LICENSE": "6aa99913137a7f9b212e53e8768871fe178e4ee01d8da0b267dbcbee314c527a", + "licenses/third_party/catapult/tracing/third_party/mocha/LICENSE": "1f194a987fa1dc60e4bcf5e04e0fc03fff8f2ee587c52136adb2cebb397250b8", + "licenses/third_party/catapult/tracing/third_party/pako/LICENSE": "a04665b3b2de56c66730c1f720f528175739e4104f79073614aa611da1e85539", + "licenses/third_party/cherry/LICENSE": "04c35849b20d927d99f1f498cfc3b4e0050cd726875be6ad5392a9f75f93bb03", + "licenses/third_party/cherry/third_party/angular/LICENSE": "fc0c17466a53b104d5b0d907b97bbf5f7ab7031581be924001ab15e42f9d893a", + "licenses/third_party/cherry/third_party/angular/docs/components/google-code-prettify-1.0.1/COPYING": "deec98192f710d6e6aa8aba33f67087199e62c2db7f9d793b0ad465248ca3d05", + "licenses/third_party/cherry/third_party/angular-spinner/LICENSE": "d5334c1ff1f71deabc8eec66ee26105d6919718b73f2eb300d641db43ee722d6", + "licenses/third_party/cherry/third_party/angular-tree-control/LICENSE": "2e61cef458cfa3b764eadb2d0b6cbfe557ba70f2b39433d85fa41361450c50c4", + "licenses/third_party/cherry/third_party/bootstrap/LICENSE": "9293c072b4854fa961b21291637532cf5ba97c6eeab48241aa60c873c711773c", + "licenses/third_party/cherry/third_party/go-sqlite3/LICENSE": "afa48e5e64dc610298d80b010ae7a3450f61a79500a9f1d1697ff6dcbbfa1f72", + "licenses/third_party/cherry/third_party/jquery/LICENSE": "f980a306a01e5881cc8004115f7e6dde44e7f5296477237d37d169a86ce7c094", + "licenses/third_party/cherry/third_party/sax/LICENSE": "21425a6ffc6c2a9dc2a091fcab8f815afdcef6f0fdf2748c1043904bf38bdae1", + "licenses/third_party/cherry/third_party/sax/LICENSE-W3C.html": "066b84cfd245e2ba8c6940aba7d63465c027550906301d8104be07cbb8398c46", + "licenses/third_party/cherry/third_party/spin/LICENSE": "90981a279fd882ae1966d063e002115842fe607bfe3a119c9a12b056ceed3db2", + "licenses/third_party/cherry/third_party/ui-bootstrap/LICENSE": "d3a1ecfb2804d0b4da300870c7c2914fd4edbd6b1b5fe4f7eb7b5d7db766b19b", + "licenses/third_party/cherry/third_party/ui-router/LICENSE": "824db5eb5d83d8415d09f3b1ef0753ec7cfb2453b34eb767f4a200252fc299fb", + "licenses/third_party/cherry/third_party/underscore/LICENSE": "c1b900aa1f61291ccd0160a351f40d217bc0080cd057aaf320257a589fd7d220", + "licenses/third_party/cherry/third_party/websocket/LICENSE": "2be1b548b0387ca8948e1bb9434e709126904d15f622cc2d0d8e7f186e4d122d", + "licenses/third_party/colorama/LICENSE": "15137d6c822e3ab097093a33c3a39a9df699f373f6438867ad534ff60762a947", + "licenses/third_party/cpython3/host/lib/python3.11/LICENSE.txt": "3b2f81fe21d181c499c59a256c8e1968455d6689d269aa85373bfb6af41da3bf", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE": "cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE.APACHE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE.BSD": "b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/cachecontrol/LICENSE.txt": "86eeee87be2a43f3ff1f56496f451f69243926f025fedbb033666c304c4c161b", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/certifi/LICENSE": "e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/distlib/LICENSE.txt": "808e10c8a6ab8deb149ff9b3fb19f447a808094606d712a9ca57fead3552599d", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/distro/LICENSE": "cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/idna/LICENSE.md": "1a9a4f0e3d479a27240ddd59a9137a66ab4a0f9dfdc8ca6188cc0bfd85187f04", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/msgpack/COPYING": "492dedba85da5872f78e6091bcd1fea474d660d35acb4dee964b8aab3f007427", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE": "cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE.APACHE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE.BSD": "b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/pkg_resources/LICENSE": "86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/platformdirs/LICENSE": "29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/pygments/LICENSE": "a9d66f1d526df02e29dce73436d34e56e8632f46c275bbdffc70569e882f9f17", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/LICENSE": "1b22b049b5267d6dfc23a67bf4a84d8ec04b9fdfb1a51d360e42b4342c8b4154", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/requests/LICENSE": "09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/resolvelib/LICENSE": "f388fd38cad13112c1dc0f669bbe80e7f84541edbafb72f3030d2ca7642c3c9d", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/rich/LICENSE": "deed7c17a4318158190a3ea239cc879a5a50271cebb98ae7025f48fbe58dca15", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/tomli/LICENSE": "b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/tomli_w/LICENSE": "b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/truststore/LICENSE": "33be7b7e8fa4fd19b1760e1a8ed8a668bdab852c91b692dd41424bcb725a9fca", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip/_vendor/urllib3/LICENSE.txt": "130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/LICENSE.txt": "634300a669d49aeae65b12c6c48c924c51a4cdf3d1ff086dc3456dc8bcaa2104", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt": "86eeee87be2a43f3ff1f56496f451f69243926f025fedbb033666c304c4c161b", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE": "e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt": "808e10c8a6ab8deb149ff9b3fb19f447a808094606d712a9ca57fead3552599d", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE": "cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md": "1a9a4f0e3d479a27240ddd59a9137a66ab4a0f9dfdc8ca6188cc0bfd85187f04", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING": "492dedba85da5872f78e6091bcd1fea474d660d35acb4dee964b8aab3f007427", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE": "cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD": "b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE": "86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE": "29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE": "a9d66f1d526df02e29dce73436d34e56e8632f46c275bbdffc70569e882f9f17", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE": "1b22b049b5267d6dfc23a67bf4a84d8ec04b9fdfb1a51d360e42b4342c8b4154", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE": "09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE": "f388fd38cad13112c1dc0f669bbe80e7f84541edbafb72f3030d2ca7642c3c9d", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE": "deed7c17a4318158190a3ea239cc879a5a50271cebb98ae7025f48fbe58dca15", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE": "b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE": "b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE": "33be7b7e8fa4fd19b1760e1a8ed8a668bdab852c91b692dd41424bcb725a9fca", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt": "130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE": "ade78d04982d69972d444a8e14a94f87a2334dd3855cc80348ea8e240aa0df2d", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE": "86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/licenses/LICENSE": "458502e12d97bbf64438606a20044aa85eb05fb0a8a807bb35dbec253fd1fc04", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/LICENSE": "86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/licenses/LICENSE": "9755a18519666e5f0f4cae3daad3d7012bcae48a600b31237d75e9fe134e6683", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/licenses/LICENSE": "5a57cb4db85e2a2dd88c290628908add57e3451449e0a9a71fdfb38776fd759d", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/licenses/LICENSE": "09f1c8c9e941af3e584d59641ea9b87d83c0cb0fd007eb5ef391a7e2643c1a46", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE": "cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.APACHE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.BSD": "b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/licenses/LICENSE": "29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/licenses/LICENSE": "b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/licenses/LICENSE.txt": "30c23618679108f3e8ea1d2a658c7ca417bdfc891c98ef1a89fa4ff0c9828654", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/licenses/LICENSE": "5a57cb4db85e2a2dd88c290628908add57e3451449e0a9a71fdfb38776fd759d", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/config/NOTICE": "2dddf08818297a3b89d43d95ff659d8da85741108c9136dfa3a4d856c0623bd8", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/NOTICE": "09c9bcea95ca086f8bc5bed174e40bc835b297d40fb5f86bbbb570fe0a5581a7", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/setuptools-83.0.0.dist-info/licenses/LICENSE": "86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741", + "licenses/third_party/cpython3/host/lib/python3.11/site-packages/wheel-0.47.0.dist-info/licenses/LICENSE.txt": "30c23618679108f3e8ea1d2a658c7ca417bdfc891c98ef1a89fa4ff0c9828654", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/LICENSE.txt": "3b2f81fe21d181c499c59a256c8e1968455d6689d269aa85373bfb6af41da3bf", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE": "cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE.APACHE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/packaging-26.3.dist-info/licenses/LICENSE.BSD": "b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/cachecontrol/LICENSE.txt": "86eeee87be2a43f3ff1f56496f451f69243926f025fedbb033666c304c4c161b", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/certifi/LICENSE": "e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/distlib/LICENSE.txt": "808e10c8a6ab8deb149ff9b3fb19f447a808094606d712a9ca57fead3552599d", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/distro/LICENSE": "cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/idna/LICENSE.md": "1a9a4f0e3d479a27240ddd59a9137a66ab4a0f9dfdc8ca6188cc0bfd85187f04", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/msgpack/COPYING": "492dedba85da5872f78e6091bcd1fea474d660d35acb4dee964b8aab3f007427", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE": "cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE.APACHE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/packaging/LICENSE.BSD": "b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/pkg_resources/LICENSE": "86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/platformdirs/LICENSE": "29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/pygments/LICENSE": "a9d66f1d526df02e29dce73436d34e56e8632f46c275bbdffc70569e882f9f17", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/LICENSE": "1b22b049b5267d6dfc23a67bf4a84d8ec04b9fdfb1a51d360e42b4342c8b4154", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/requests/LICENSE": "09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/resolvelib/LICENSE": "f388fd38cad13112c1dc0f669bbe80e7f84541edbafb72f3030d2ca7642c3c9d", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/rich/LICENSE": "deed7c17a4318158190a3ea239cc879a5a50271cebb98ae7025f48fbe58dca15", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/tomli/LICENSE": "b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/tomli_w/LICENSE": "b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/truststore/LICENSE": "33be7b7e8fa4fd19b1760e1a8ed8a668bdab852c91b692dd41424bcb725a9fca", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip/_vendor/urllib3/LICENSE.txt": "130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/LICENSE.txt": "634300a669d49aeae65b12c6c48c924c51a4cdf3d1ff086dc3456dc8bcaa2104", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt": "86eeee87be2a43f3ff1f56496f451f69243926f025fedbb033666c304c4c161b", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/certifi/LICENSE": "e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt": "808e10c8a6ab8deb149ff9b3fb19f447a808094606d712a9ca57fead3552599d", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/distro/LICENSE": "cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md": "1a9a4f0e3d479a27240ddd59a9137a66ab4a0f9dfdc8ca6188cc0bfd85187f04", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/msgpack/COPYING": "492dedba85da5872f78e6091bcd1fea474d660d35acb4dee964b8aab3f007427", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE": "cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD": "b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE": "86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE": "29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pygments/LICENSE": "a9d66f1d526df02e29dce73436d34e56e8632f46c275bbdffc70569e882f9f17", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE": "1b22b049b5267d6dfc23a67bf4a84d8ec04b9fdfb1a51d360e42b4342c8b4154", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/requests/LICENSE": "09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE": "f388fd38cad13112c1dc0f669bbe80e7f84541edbafb72f3030d2ca7642c3c9d", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/rich/LICENSE": "deed7c17a4318158190a3ea239cc879a5a50271cebb98ae7025f48fbe58dca15", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli/LICENSE": "b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE": "b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/truststore/LICENSE": "33be7b7e8fa4fd19b1760e1a8ed8a668bdab852c91b692dd41424bcb725a9fca", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/pip-26.2.1.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt": "130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE": "ade78d04982d69972d444a8e14a94f87a2334dd3855cc80348ea8e240aa0df2d", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE": "86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/importlib_metadata-8.7.1.dist-info/licenses/LICENSE": "458502e12d97bbf64438606a20044aa85eb05fb0a8a807bb35dbec253fd1fc04", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/jaraco.text-4.0.0.dist-info/LICENSE": "86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/jaraco_context-6.1.0.dist-info/licenses/LICENSE": "9755a18519666e5f0f4cae3daad3d7012bcae48a600b31237d75e9fe134e6683", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/jaraco_functools-4.4.0.dist-info/licenses/LICENSE": "5a57cb4db85e2a2dd88c290628908add57e3451449e0a9a71fdfb38776fd759d", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/more_itertools-10.8.0.dist-info/licenses/LICENSE": "09f1c8c9e941af3e584d59641ea9b87d83c0cb0fd007eb5ef391a7e2643c1a46", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE": "cad1ef5bd340d73e074ba614d26f7deaca5c7940c3d8c34852e65c4909686c48", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.APACHE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/packaging-26.0.dist-info/licenses/LICENSE.BSD": "b70e7e9b742f1cc6f948b34c16aa39ffece94196364bc88ff0d2180f0028fac5", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/platformdirs-4.4.0.dist-info/licenses/LICENSE": "29e0fd62e929850e86eb28c3fdccf0cefdf4fa94879011cffb3d0d4bed6d4db6", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/tomli-2.4.0.dist-info/licenses/LICENSE": "b80816b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/wheel-0.46.3.dist-info/licenses/LICENSE.txt": "30c23618679108f3e8ea1d2a658c7ca417bdfc891c98ef1a89fa4ff0c9828654", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/_vendor/zipp-3.23.0.dist-info/licenses/LICENSE": "5a57cb4db85e2a2dd88c290628908add57e3451449e0a9a71fdfb38776fd759d", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/config/NOTICE": "2dddf08818297a3b89d43d95ff659d8da85741108c9136dfa3a4d856c0623bd8", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools/config/_validate_pyproject/NOTICE": "09c9bcea95ca086f8bc5bed174e40bc835b297d40fb5f86bbbb570fe0a5581a7", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/setuptools-83.0.0.dist-info/licenses/LICENSE": "86da0f01aeae46348a3c3d465195dc1ceccde79f79e87769a64b8da04b2a4741", + "licenses/third_party/cpython3/linux-amd64/lib/python3.11/site-packages/wheel-0.47.0.dist-info/licenses/LICENSE.txt": "30c23618679108f3e8ea1d2a658c7ca417bdfc891c98ef1a89fa4ff0c9828654", + "licenses/third_party/depot_tools/LICENSE": "20b1e32e55821d109bcd17e1e150cfe242b54590d3e8083648d1276f88d33d16", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/gslib/vendored/boto/LICENSE": "e3248f259a211f4d9ed06cfd07bc64373376c92a192152e37ec3420d6036dd4e", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/gslib/vendored/oauth2client/LICENSE": "d6a43f0bae029b0cea5bd0fffd87f05659dc599a763886027614ad210be1ba3d", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/apitools/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/argcomplete/LICENSE.rst": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/argcomplete/NOTICE": "2c889c721ec8ae6d7664680afaefbb4c7620976f434b57a506ecd92f0649b6a0", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/cachetools/LICENSE": "2f4d2ff05f05c5da3879f40292b7600332d775dc7ed320d43dd42f3cd7d92c9b", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/certifi/LICENSE": "e93716da6b9c0d5a4a1df60fe695b370f0695603d21f6f83f053e42cfc10caf7", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/chardet/LICENSE": "dc626520dcd53a22f727af3ee42c770e56c97a64fe3adb063799d8ab032fe551", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/charset_normalizer/LICENSE": "eb31a0c5a4fb09b8a4e32055d25c1e5f9c358a2752fef3cd720213d1ccfee241", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/charset_normalizer/data/NOTICE.md": "0cb3efcfd8f7a02a337e98dc3de4b0b57424d7208a332b26e7deb8cb94c13922", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/crcmod/LICENSE": "89480768826f408daea1f3caff0509c2cc9606e10f6bb0ccfd12a3d604842c35", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/crcmod_osx/LICENSE": "89480768826f408daea1f3caff0509c2cc9606e10f6bb0ccfd12a3d604842c35", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/fasteners/LICENSE": "d2de2f566d2d0e0b509fb0ea1fa3669f49064ab1de21c57453cab3750a234e8f", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/funcsigs/LICENSE": "559229b4b693d80fe087d517f7c79d4857c965add18031512d0981efc28755f0", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/gcs-oauth2-boto-plugin/LICENSE": "8c6db340475136df3c1201d458fa5755698eace76e510471ecc9d857d6083dac", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/google-auth-library-python/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/google-auth-library-python-httplib2/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/google-reauth-python/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/httplib2/LICENSE": "589eec38f72df2be203711d3b8cbece9b908c5e7ff00bc3cab7f63bae9e366b4", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/idna/LICENSE.md": "a59f0b0ef3635874109a4461ca44ff7a70d50696e814767bfaf721d4c9b0db0f", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/mock/LICENSE.txt": "5831ee149d3850b28df8ff02fb7bd07cecda81e85cc8435c20827d3922202d34", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/monotonic/LICENSE": "cb5e8e7e5f4a3988e1063c142c60dc2df75605f4c46515e776e3aca6df976e14", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/pyasn1/LICENSE.rst": "2aad5fc00f705c4a1addb83eed10a6a75d286a3779f0cf8519d87e62bc4735fd", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/pyasn1/docs/source/license.rst": "2fd7257410c4d7d9c8d8d85cb7f9f4ef9eee34126a96a993245c71577997c345", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/pyasn1-modules/LICENSE.txt": "70bb0e4c89f4e41a11950365d98a13e2e6ad6ee4aed80cd1ecffc93d98d44e8c", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/pyparsing/LICENSE": "10d5120a16805804ffda8b688c220bfb4e8f39741b57320604d455a309e01972", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/pyu2f/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/requests/LICENSE": "09e8a9bcec8067104652c168685ab0931e7868f9c8284b66f5ae6edae5f1130b", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/requests/NOTICE": "f5110972dedad2b4e9d314518daf3b7d72d6e02e499acd802181de6f74571dcc", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/requests/docs/_themes/LICENSE": "6afc9d58f919ab52f4806a895a37aefe4d263f8e52278e6a1e87c5d7ec82299c", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/requests/ext/LICENSE": "3172d399cbd8f10609e73fec73d0e0b33eecd3c572a68b0722229d8c7059f725", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/retry-decorator/LICENSE.txt": "c3710b8fc15eee9d2de041c0302116dc30fcb370ae5cc3969e746d8f08b869fd", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/rsa/LICENSE": "073f28b7d389c8fe74f607e17c27f81eaa5ace69edc43a884f23f41b41c5c726", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/six/LICENSE": "4375ba20e2b9c6c4e7cad2940a628fd90e95cc3d50ee92aae755715d8ba1fbd0", + "licenses/third_party/depot_tools/external_bin/gsutil/gsutil_5.35/gsutil/third_party/urllib3/LICENSE.txt": "130e3a64d5fdd5d096a752694634a7d9df284469de86e5732100268041e3d686", + "licenses/third_party/depot_tools/metadata/LICENSE_OWNERS": "c203ade846c159e17bb36214eef81b55866645a3ece3cf4f10d9fcff110e444a", + "licenses/third_party/depot_tools/metadata/fields/custom/license.py": "8dabd9a3478fabeb5ecf0ee2624ed7b41a8c346fdcb3d24a9fc4098a30ba4f54", + "licenses/third_party/depot_tools/metadata/fields/custom/license_allowlist.py": "ef8e5604a137b1eb920336bad1a4948ab8dc71f2e1a4cb765178964d3598c434", + "licenses/third_party/depot_tools/metadata/fields/custom/license_file.py": "4ab59682a096a2c31ab9ae42f2666dc6d5e72bac0071942a2e28d6d57d6732b6", + "licenses/third_party/depot_tools/metadata/tests/data/LICENSE": "8bc55eba9da5911fd65d6b9dddfd56d94e30ff7fc2a9a30a5782bfc47cdf9c35", + "licenses/third_party/depot_tools/metadata/tests/data/src/LICENSE.txt": "19ad13f8d801c13b5dde35625d2a57a0c3e1e4cb4d073dd2aff72be3925940e6", + "licenses/third_party/depot_tools/third_party/colorama/LICENSE.txt": "cac35c02686e5d04a5a7140bfb3b36e73aed496656e891102e428886d7930318", + "licenses/third_party/depot_tools/third_party/repo/COPYING": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/depot_tools/third_party/schema/LICENSE-MIT": "f4360ca8f779e6a673cd2882f73419bc2c5f74184fd9db91d2e86a368cc04e0b", + "licenses/third_party/flatbuffers/LICENSE": "7ec9661a8afafab1eee3523d6f1a193eff76314a5ab10b4ce96aefd87621b0c3", + "licenses/third_party/glmark2/src/COPYING": "8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903", + "licenses/third_party/glmark2/src/COPYING.SGI": "16fbc228292bd774b263b212ae422c524cbf3b2078bcf21b22f8bdd4373be617", + "licenses/third_party/glmark2/src/src/libjpeg-turbo/LICENSE.md": "fffd497be5f4ae0a10b8258e191125fb58b90250ecbf3c79398d79604dd00b7d", + "licenses/third_party/glmark2/src/src/libmatrix/COPYING": "79d3f64f22269a86ce0e25a62f6a391f1e07e2735909bd8de710b3e4c51bf196", + "licenses/third_party/glmark2/src/src/libpng/LICENSE": "cb7ac9e8ff6f939378b777feb2615598c16380b69f604845799e462f29ab6e90", + "licenses/third_party/glslang/LICENSE": "23353f4505b1c8ce4f8f72fc3b11dc74b4a8a7bf95921d93ff77f227c171a710", + "licenses/third_party/glslang/src/LICENSE.txt": "17e70c676e1521ff3e4686f04a2053d93a7e28a33be8de7ec37ab0ff72feb677", + "licenses/third_party/glslang/src/license-checker.cfg": "0b7c936ff1270fb5089750e326f732ce2f08b18e804dc8847aa44561d0a7a277", + "licenses/third_party/googletest/src/LICENSE": "9702de7e4117a8e2b20dafab11ffda58c198aede066406496bef670d40a22138", + "licenses/third_party/jinja2/LICENSE.rst": "3b49dcee4105eb37bac10faf1be260408fe85d252b8e9df2e0979fc1e094437b", + "licenses/third_party/jsoncpp/LICENSE": "76c45ece83a26117f86f4e349e7df118708e061e87225328fb478ce1e8b3eb86", + "licenses/third_party/jsoncpp/source/LICENSE": "cec0db5f6d7ed6b3a72647bd50aed02e13c3377fd44382b96dc2915534c042ad", + "licenses/third_party/jsoncpp/source/devtools/licenseupdater.py": "81d0fc4498e695444090a0ba9a74398f8738cd1ae18c788734be93fec2dc3515", + "licenses/third_party/libc++/src/LICENSE.TXT": "539dd7aed86e8a4f12cbdd0e6c50c189c7d74847e4fecc64ce2c6ee3a01da38b", + "licenses/third_party/libc++abi/src/LICENSE.TXT": "e2b35be49f7284a45b7baca8fc7b3ab7440e7902392b2528a457816b5bb2a15c", + "licenses/third_party/libjpeg_turbo/LICENSE.md": "96f5b328adbb78eeaaec6980d73fd558cb1e4d62560ed615646bc3cf5e532430", + "licenses/third_party/libjpeg_turbo/LICENSE.md.chromium": "152a0f78d9c3a3afc09470cba1e66eabb915515cb121252d6c85c4ed6352a073", + "licenses/third_party/libpng/src/LICENSE": "7317e078e2d3b5d7ba5a6159e650945153262b44b76f6700f8e9edb261c5143e", + "licenses/third_party/libpng/src/ci/LICENSE_MIT.txt": "508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1", + "licenses/third_party/libpng/src/contrib/gregbook/COPYING": "d6cb0e9e560f51085556949a84af12b79a00f10ab8b66c752537faf7cd665572", + "licenses/third_party/libpng/src/contrib/gregbook/LICENSE": "b6a03c1803eb58ffb1f1278d5c7d4096c4c116e66dce8a7553e8c77d163c3438", + "licenses/third_party/libpng/src/contrib/pngexif/LICENSE_MIT.txt": "508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1", + "licenses/third_party/libpng/src/contrib/pngminus/LICENSE.txt": "eeb50cca0bf0537aeeef00874e1e22f0de50cb035f5db37e36036dc9b8218e8d", + "licenses/third_party/libunwind/src/LICENSE.TXT": "b5efebcaca80879234098e52d1725e6d9eb8fb96a19fce625d39184b705f7b6d", + "licenses/third_party/llvm-libc/src/LICENSE.TXT": "ebcd9bbf783a73d05c53ba4d586b8d5813dcdf3bbec50265860ccc885e606f47", + "licenses/third_party/lunarg-vulkantools/src/LICENSE.txt": "400635d6ddaa1efc61cc38c6a737b8bbb975f4424f4727cd3983f53110eafe67", + "licenses/third_party/markupsafe/LICENSE": "0bbe88228fd63d20ec097f64e58d5a0a465123ae139140a18d406c60b48824b5", + "licenses/third_party/nasm/LICENSE": "7436a7c46b6e4d969b41e1ce387885ae4ced25710662189ff2983665253729ac", + "licenses/third_party/nasm/zlib/LICENSE": "845efc77857d485d91fb3e0b884aaa929368c717ae8186b66fe1ed2495753243", + "licenses/third_party/ninja/COPYING": "eb7e9ab9690124c5c9f42bdc81383d886a3dede26345b6ed15bbad7caf81f7ea", + "licenses/third_party/perfetto/LICENSE": "9a682a56cffc9524dfa9b0b1c0dca9cb81a19e96d5bd0793aaf02c08a95ee7ca", + "licenses/third_party/perfetto/python/LICENSE": "80f13607677e9932bf08e5f0bc025f8d77bde813d62bf3d5465c709025710d3d", + "licenses/third_party/perfetto/ui/src/plugins/dev.perfetto.TraceInfoPage/tabs/notices.ts": "72e7fdaba087f43ae01cd304f4e654c78b9265906727efd75429b4c0d6bcf08c", + "licenses/third_party/proguard/LICENSE": "294f58267c6f473c4ce7270bf5c8d34b2003cb43804552459654c36553431276", + "licenses/third_party/protobuf/LICENSE": "6e5e117324afd944dcf67f36cf329843bc1a92229a8cd9bb573d7a83130fea7d", + "licenses/third_party/protobuf/src/google/protobuf/compiler/notices.h": "b47ca1ea743623d50c6e02faa136dea4f6574a98e200666f859dbf57fb491721", + "licenses/third_party/protobuf/third_party/utf8_range/LICENSE": "02de69b64fc36d9e938f418e52723e42f0b2b226d58a9cb3c8dcbdf7059f5074", + "licenses/third_party/r8/LICENSE": "68834f116f8ff545f05d14753357b620748156d60ee36b26beab4cb3f317efe4", + "licenses/third_party/rapidjson/src/bin/jsonschema/LICENSE": "837402bd25fad9b704265801ca3f92566a98157c1f9a7acd6f446299ba1c305a", + "licenses/third_party/rapidjson/src/contrib/natvis/LICENSE": "394faaedb93c1da8ecbd61322518834908fee64381117e01a611bf9fac20baa6", + "licenses/third_party/rapidjson/src/license.txt": "a140e5d46fe734a1c78f1a3c3ef207871dd75648be71fdda8e309b23ab8b1f32", + "licenses/third_party/re2/LICENSE": "6040cda75d90b1738292a631d89934c411ef7ffd543c4d6a1b7edfc8edf29449", + "licenses/third_party/re2/src/LICENSE": "6040cda75d90b1738292a631d89934c411ef7ffd543c4d6a1b7edfc8edf29449", + "licenses/third_party/re2/src/python/LICENSE": "6040cda75d90b1738292a631d89934c411ef7ffd543c4d6a1b7edfc8edf29449", + "licenses/third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/addr2line-v0_25/LICENSE-MIT": "e99d88d232bf57d70f0fb87f6b496d44b6653f99f8a63d250a54c61ea4bcde40", + "licenses/third_party/rust/chromium_crates_io/vendor/adler2-v2/LICENSE-0BSD": "861399f8c21c042b110517e76dc6b63a2b334276c8cf17412fc3c8908ca8dc17", + "licenses/third_party/rust/chromium_crates_io/vendor/adler2-v2/LICENSE-APACHE": "8ada45cd9f843acf64e4722ae262c622a2b3b3007c7310ef36ac1061a30f6adb", + "licenses/third_party/rust/chromium_crates_io/vendor/adler2-v2/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/ahash-v0_8/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/ahash-v0_8/LICENSE-MIT": "0444c6991eead6822f7b9102e654448d51624431119546492e8b231db42c48bb", + "licenses/third_party/rust/chromium_crates_io/vendor/aho-corasick-v1/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f", + "licenses/third_party/rust/chromium_crates_io/vendor/aho-corasick-v1/LICENSE-MIT": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f", + "licenses/third_party/rust/chromium_crates_io/vendor/android_system_properties-v0_1/LICENSE-APACHE": "216486f29671a4262efe32af6d84a75bef398127f8c5f369b5c8305983887a06", + "licenses/third_party/rust/chromium_crates_io/vendor/android_system_properties-v0_1/LICENSE-MIT": "80f275e90d799911ed3830a7f242a2ef5a4ade2092fe0aa07bfb2d2cf2f2b95e", + "licenses/third_party/rust/chromium_crates_io/vendor/anstyle-v1/LICENSE-APACHE": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "licenses/third_party/rust/chromium_crates_io/vendor/anstyle-v1/LICENSE-MIT": "6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6", + "licenses/third_party/rust/chromium_crates_io/vendor/antlr4rust-v0_5/LICENSE.txt": "3e1f197b6b221b918470078665608303aeebb27c1f54cd8241873b35f3affa62", + "licenses/third_party/rust/chromium_crates_io/vendor/anyhow-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/anyhow-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/arbitrary-int-v1/LICENSE.txt": "6982f0cd109b04512cbb5f0e0f0ef82154f33a57d2127afe058ecc72039ab88c", + "licenses/third_party/rust/chromium_crates_io/vendor/arbitrary-int-v2/LICENSE.txt": "6982f0cd109b04512cbb5f0e0f0ef82154f33a57d2127afe058ecc72039ab88c", + "licenses/third_party/rust/chromium_crates_io/vendor/array-init-v2/LICENSE-APACHE": "c8d9a0d15dd76ca3bf277b6bf6da56799e266eac60bdc321a97ebc6d76d5153c", + "licenses/third_party/rust/chromium_crates_io/vendor/array-init-v2/LICENSE-MIT": "e27fb2953c088c71285a4f2f54a0ac53323460ee7c2b1b838d563bd2687a38af", + "licenses/third_party/rust/chromium_crates_io/vendor/autocfg-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/autocfg-v1/LICENSE-MIT": "27995d58ad5c1145c1a8cd86244ce844886958a35eb2b78c6b772748669999ac", + "licenses/third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/backtrace-v0_3/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/third_party/rust/chromium_crates_io/vendor/base64-v0_22/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/base64-v0_22/LICENSE-MIT": "0dd882e53de11566d50f8e8e2d5a651bcf3fabee4987d70f306233cf39094ba7", + "licenses/third_party/rust/chromium_crates_io/vendor/bincode-v2/LICENSE.md": "90d7e062634054e6866d3c81e6a2b3058a840e6af733e98e80bdfe1a7dec6912", + "licenses/third_party/rust/chromium_crates_io/vendor/bincode_derive-v2/LICENSE.md": "90d7e062634054e6866d3c81e6a2b3058a840e6af733e98e80bdfe1a7dec6912", + "licenses/third_party/rust/chromium_crates_io/vendor/bit-set-v0_8/LICENSE-APACHE": "8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90", + "licenses/third_party/rust/chromium_crates_io/vendor/bit-set-v0_8/LICENSE-MIT": "f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7", + "licenses/third_party/rust/chromium_crates_io/vendor/bit-vec-v0_8/LICENSE-APACHE": "8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90", + "licenses/third_party/rust/chromium_crates_io/vendor/bit-vec-v0_8/LICENSE-MIT": "f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7", + "licenses/third_party/rust/chromium_crates_io/vendor/bitbybit-v1/LICENSE": "0a73de6c78c0743aef49c275563c9486fd3e55d61611044cecc5620f4dfe772d", + "licenses/third_party/rust/chromium_crates_io/vendor/bitflags-v2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/bitflags-v2/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/bytemuck-v1/LICENSE-APACHE": "e3ba223bb1423f0aad8c3dfce0fe3148db48926d41e6fbc3afbbf5ff9e1c89cb", + "licenses/third_party/rust/chromium_crates_io/vendor/bytemuck-v1/LICENSE-MIT": "9df9ba60a11af705f2e451b53762686e615d86f76b169cf075c3237730dbd7e2", + "licenses/third_party/rust/chromium_crates_io/vendor/bytemuck-v1/LICENSE-ZLIB": "84b34dd7608f7fb9b17bd588a6bf392bf7de504e2716f024a77d89f1b145a151", + "licenses/third_party/rust/chromium_crates_io/vendor/bytemuck_derive-v1/LICENSE-APACHE": "e3ba223bb1423f0aad8c3dfce0fe3148db48926d41e6fbc3afbbf5ff9e1c89cb", + "licenses/third_party/rust/chromium_crates_io/vendor/bytemuck_derive-v1/LICENSE-MIT": "9df9ba60a11af705f2e451b53762686e615d86f76b169cf075c3237730dbd7e2", + "licenses/third_party/rust/chromium_crates_io/vendor/bytemuck_derive-v1/LICENSE-ZLIB": "84b34dd7608f7fb9b17bd588a6bf392bf7de504e2716f024a77d89f1b145a151", + "licenses/third_party/rust/chromium_crates_io/vendor/byteorder-lite-v0_1/LICENSE-MIT": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f", + "licenses/third_party/rust/chromium_crates_io/vendor/byteorder-v1/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f", + "licenses/third_party/rust/chromium_crates_io/vendor/byteorder-v1/LICENSE-MIT": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f", + "licenses/third_party/rust/chromium_crates_io/vendor/bytes-v1/LICENSE": "45f522cacecb1023856e46df79ca625dfc550c94910078bd8aec6e02880b3d42", + "licenses/third_party/rust/chromium_crates_io/vendor/calendrical_calculations-v0_2/LICENSE": "192ea857d1bff2b87c174de36cbae5c173234726c6b8eceab9790a535d7dbc95", + "licenses/third_party/rust/chromium_crates_io/vendor/cfg-if-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/cfg-if-v1/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/third_party/rust/chromium_crates_io/vendor/cfg_aliases-v0_2/LICENSE": "31b94860253d8ec7b4529f51901044d3b459d6292d996504a36b1bae3a36a812", + "licenses/third_party/rust/chromium_crates_io/vendor/cfg_aliases-v0_2/NOTICES.md": "1e2b7ade3fb228130408b9990cae6a7618eb314c75aa0b164bfe485d9d9756ee", + "licenses/third_party/rust/chromium_crates_io/vendor/chrono-v0_4/LICENSE.txt": "946c9835d8034d24404f8cfec5f4654cee5dad17e944afc3d06d742cf2882831", + "licenses/third_party/rust/chromium_crates_io/vendor/clap-v4/LICENSE-APACHE": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "licenses/third_party/rust/chromium_crates_io/vendor/clap-v4/LICENSE-MIT": "6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6", + "licenses/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/LICENSE-APACHE": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "licenses/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/LICENSE-MIT": "6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6", + "licenses/third_party/rust/chromium_crates_io/vendor/clap_lex-v1/LICENSE-APACHE": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "licenses/third_party/rust/chromium_crates_io/vendor/clap_lex-v1/LICENSE-MIT": "6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6", + "licenses/third_party/rust/chromium_crates_io/vendor/cobs-v0_3/LICENSE-APACHE": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "licenses/third_party/rust/chromium_crates_io/vendor/cobs-v0_3/LICENSE-MIT": "e0cfa1006a64520633de6bfbf563f5b1bea04ef0c5b73f049681931fa297dda3", + "licenses/third_party/rust/chromium_crates_io/vendor/codespan-reporting-v0_13/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/rust/chromium_crates_io/vendor/const_format-v0_2/LICENSE-ZLIB.md": "c1e018d60dd011b335b5280b919bd3a75dbba81c6fbe24e2fc90cb235bdb6883", + "licenses/third_party/rust/chromium_crates_io/vendor/const_format_proc_macros-v0_2/LICENSE-ZLIB.md": "c1e018d60dd011b335b5280b919bd3a75dbba81c6fbe24e2fc90cb235bdb6883", + "licenses/third_party/rust/chromium_crates_io/vendor/const_panic-v0_2/LICENSE-ZLIB.md": "573e362dc50a6d9eb444cea38ef61587e16a0645cb8098ba13a2c42fdde72acd", + "licenses/third_party/rust/chromium_crates_io/vendor/core-foundation-sys-v0_8/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/core-foundation-sys-v0_8/LICENSE-MIT": "62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3", + "licenses/third_party/rust/chromium_crates_io/vendor/core_maths-v0_1/LICENSE": "9ebf8c4cc0b735ca13a766451f7b8097db3185975ceb2ba94b5abf439156a91f", + "licenses/third_party/rust/chromium_crates_io/vendor/crc32fast-v1/LICENSE-APACHE": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "licenses/third_party/rust/chromium_crates_io/vendor/crc32fast-v1/LICENSE-MIT": "61d383b05b87d78f94d2937e2580cce47226d17823c0430fbcad09596537efcf", + "licenses/third_party/rust/chromium_crates_io/vendor/ctor-proc-macro-v0_0_7/LICENSE-APACHE": "a8ad31b1c3f40dca5a84119351b8fa8ddc868edd77fad8a8ebf6d8f2d16fa4ae", + "licenses/third_party/rust/chromium_crates_io/vendor/ctor-proc-macro-v0_0_7/LICENSE-MIT": "bccaa8b6c09f94e81f06696e179dbe058464bbdfbc823b6d49cada1d71e84ac3", + "licenses/third_party/rust/chromium_crates_io/vendor/ctor-v0_6/LICENSE-APACHE": "a8ad31b1c3f40dca5a84119351b8fa8ddc868edd77fad8a8ebf6d8f2d16fa4ae", + "licenses/third_party/rust/chromium_crates_io/vendor/ctor-v0_6/LICENSE-MIT": "bccaa8b6c09f94e81f06696e179dbe058464bbdfbc823b6d49cada1d71e84ac3", + "licenses/third_party/rust/chromium_crates_io/vendor/cxx-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/cxx-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/cxxbridge-cmd-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/cxxbridge-cmd-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/cxxbridge-flags-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/cxxbridge-flags-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/cxxbridge-macro-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/cxxbridge-macro-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/derivre-v0_3/LICENSE": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "licenses/third_party/rust/chromium_crates_io/vendor/diplomat-runtime-v0_15/LICENSE-APACHE": "639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666", + "licenses/third_party/rust/chromium_crates_io/vendor/diplomat-runtime-v0_15/LICENSE-MIT": "3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4", + "licenses/third_party/rust/chromium_crates_io/vendor/diplomat-v0_15/LICENSE-APACHE": "639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666", + "licenses/third_party/rust/chromium_crates_io/vendor/diplomat-v0_15/LICENSE-MIT": "3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4", + "licenses/third_party/rust/chromium_crates_io/vendor/diplomat-v0_16/LICENSE-APACHE": "639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666", + "licenses/third_party/rust/chromium_crates_io/vendor/diplomat-v0_16/LICENSE-MIT": "3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4", + "licenses/third_party/rust/chromium_crates_io/vendor/diplomat_core-v0_15/LICENSE-APACHE": "639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666", + "licenses/third_party/rust/chromium_crates_io/vendor/diplomat_core-v0_15/LICENSE-MIT": "3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4", + "licenses/third_party/rust/chromium_crates_io/vendor/diplomat_core-v0_16/LICENSE-APACHE": "639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666", + "licenses/third_party/rust/chromium_crates_io/vendor/diplomat_core-v0_16/LICENSE-MIT": "3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4", + "licenses/third_party/rust/chromium_crates_io/vendor/displaydoc-v0_2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/displaydoc-v0_2/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/dtor-proc-macro-v0_0_6/LICENSE-APACHE": "a8ad31b1c3f40dca5a84119351b8fa8ddc868edd77fad8a8ebf6d8f2d16fa4ae", + "licenses/third_party/rust/chromium_crates_io/vendor/dtor-proc-macro-v0_0_6/LICENSE-MIT": "bccaa8b6c09f94e81f06696e179dbe058464bbdfbc823b6d49cada1d71e84ac3", + "licenses/third_party/rust/chromium_crates_io/vendor/dtor-v0_1/LICENSE-APACHE": "a8ad31b1c3f40dca5a84119351b8fa8ddc868edd77fad8a8ebf6d8f2d16fa4ae", + "licenses/third_party/rust/chromium_crates_io/vendor/dtor-v0_1/LICENSE-MIT": "bccaa8b6c09f94e81f06696e179dbe058464bbdfbc823b6d49cada1d71e84ac3", + "licenses/third_party/rust/chromium_crates_io/vendor/either-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/either-v1/LICENSE-MIT": "7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545", + "licenses/third_party/rust/chromium_crates_io/vendor/equivalent-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/equivalent-v1/LICENSE-MIT": "7365cc8878a1d7ce155a58c4ca09c3d7a6be413efa5334a80ea842912b669349", + "licenses/third_party/rust/chromium_crates_io/vendor/erased-serde-v0_4/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/erased-serde-v0_4/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/extended-v0_1/LICENSE.txt": "25a0874d15e7c834a47c3adc80901edb2219759254992023ef1010c3065413d5", + "licenses/third_party/rust/chromium_crates_io/vendor/fastbloom-v0_14/LICENSE-APACHE": "7cde763ba32b3ec2a84eddd8beb0dcb895fd6436aeb18491ab9572a7eb8de996", + "licenses/third_party/rust/chromium_crates_io/vendor/fastbloom-v0_14/LICENSE-MIT": "7c86aec715e38bf01c316a69de917c1245bf9945a5dc0329eecc774cdb4f26c2", + "licenses/third_party/rust/chromium_crates_io/vendor/fdeflate-v0_3/LICENSE-APACHE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/rust/chromium_crates_io/vendor/fdeflate-v0_3/LICENSE-MIT": "c77a4cf9da729987d0fe7ccd811e3bd27393914ddf3d23467c18cc22954513b3", + "licenses/third_party/rust/chromium_crates_io/vendor/fend-core-v1/LICENSE.md": "d39a21ed70fb553856f6d7e74fee4332261069502ae32ab9ac13b49d147696f7", + "licenses/third_party/rust/chromium_crates_io/vendor/fixed_decimal-v0_7/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/flate2-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/flate2-v1/LICENSE-MIT": "025436edff4cfcdde17a5811fdea78892d8482efd1abdec5a17872d07a4f2112", + "licenses/third_party/rust/chromium_crates_io/vendor/foldhash-v0_2/LICENSE": "b1181a40b2a7b25cf66fd01481713bc1005df082c53ef73e851e55071b102744", + "licenses/third_party/rust/chromium_crates_io/vendor/font-types-v0_12/LICENSE-APACHE": "eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0", + "licenses/third_party/rust/chromium_crates_io/vendor/font-types-v0_12/LICENSE-MIT": "7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427", + "licenses/third_party/rust/chromium_crates_io/vendor/fs2-v0_4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/fs2-v0_4/LICENSE-MIT": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0", + "licenses/third_party/rust/chromium_crates_io/vendor/getrandom-v0_3/LICENSE-APACHE": "aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf", + "licenses/third_party/rust/chromium_crates_io/vendor/getrandom-v0_3/LICENSE-MIT": "29e9fe5074bd27e0e5d5d110394fbbcd841baee2651a3c4b4560a632702cede4", + "licenses/third_party/rust/chromium_crates_io/vendor/getrandom-v0_4/LICENSE-APACHE": "aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf", + "licenses/third_party/rust/chromium_crates_io/vendor/getrandom-v0_4/LICENSE-MIT": "523a42c25d245dde9c015f882cec7f4555aad883382a6cf19b4b7d9b2cd5419b", + "licenses/third_party/rust/chromium_crates_io/vendor/gimli-v0_32/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/gimli-v0_32/LICENSE-MIT": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0", + "licenses/third_party/rust/chromium_crates_io/vendor/harfrust-v0_13/LICENSE": "3a7c3f0b887abb7c638faf022d29257418458614c6724b120ceeffb279f9c7d2", + "licenses/third_party/rust/chromium_crates_io/vendor/hashbrown-v0_16/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/hashbrown-v0_16/LICENSE-MIT": "ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2", + "licenses/third_party/rust/chromium_crates_io/vendor/hashbrown-v0_17/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/hashbrown-v0_17/LICENSE-MIT": "ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2", + "licenses/third_party/rust/chromium_crates_io/vendor/heck-v0_5/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/heck-v0_5/LICENSE-MIT": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0", + "licenses/third_party/rust/chromium_crates_io/vendor/hex-v0_4/LICENSE-APACHE": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "licenses/third_party/rust/chromium_crates_io/vendor/hex-v0_4/LICENSE-MIT": "f7bdb3426d045cd50efd4953026e3eb5a83d0199f458a075602611b9344da5b9", + "licenses/third_party/rust/chromium_crates_io/vendor/hmac-sha256-v1/LICENSE": "6f4e2de03c87fde1f0d4481b5a6358f9d2ba1f4bf8ed331d8f2d2fc4579b4747", + "licenses/third_party/rust/chromium_crates_io/vendor/hostname-v0_4/LICENSE": "2e4213e573312c8c75e6e7e2c55a45283427e759432b56014afaf3c7d950a568", + "licenses/third_party/rust/chromium_crates_io/vendor/iana-time-zone-v0_1/LICENSE-APACHE": "696759d65dfe558ff7d9f031c76db19ec5c0767470fb67c4e8d990820d1e99c9", + "licenses/third_party/rust/chromium_crates_io/vendor/iana-time-zone-v0_1/LICENSE-MIT": "da28ccc6b158fc2d8cccc74e99794b1cff1d29bd7bbeb019442fcf0c04c6cad9", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_calendar-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_calendar_data-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_capi-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_casemap-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_casemap_data-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_collections-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_decimal-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_decimal_data-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_experimental-v0_5/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_experimental_data-v0_5/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_list-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_list_data-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_locale-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_locale_core-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_locale_data-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_normalizer-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_normalizer_data-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_pattern-v0_4/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_plurals-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_plurals_data-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_properties-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_properties_data-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_provider-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/icu_provider_adapters-v2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/image-v0_25/LICENSE-APACHE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/rust/chromium_crates_io/vendor/image-v0_25/LICENSE-MIT": "c77a4cf9da729987d0fe7ccd811e3bd27393914ddf3d23467c18cc22954513b3", + "licenses/third_party/rust/chromium_crates_io/vendor/incremental-font-transfer-v0_7/LICENSE-APACHE": "eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0", + "licenses/third_party/rust/chromium_crates_io/vendor/incremental-font-transfer-v0_7/LICENSE-MIT": "7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427", + "licenses/third_party/rust/chromium_crates_io/vendor/indexmap-v2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/indexmap-v2/LICENSE-MIT": "ecc269ef87fd38a1d98e30bfac9ba964a9dbd9315c3770fed98d4d7cb5882055", + "licenses/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/itertools-v0_14/LICENSE-MIT": "7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545", + "licenses/third_party/rust/chromium_crates_io/vendor/itoa-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/itoa-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/ixdtf-v0_6/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/jpeg-encoder-v0_7/LICENSE-APACHE": "85a884980abd6032fc6b5439ba918ab16db9ac9051b5cf18db9c27c587df07c0", + "licenses/third_party/rust/chromium_crates_io/vendor/jpeg-encoder-v0_7/LICENSE-IJG": "a21e92ec88aefc6823f0c51994783571a849e8d9b43d9ade61ec9d886776721e", + "licenses/third_party/rust/chromium_crates_io/vendor/jpeg-encoder-v0_7/LICENSE-MIT": "4d022727ea392ebba2fe5e9a5f78c610a0eae506b491f3db29c78b6d430d6503", + "licenses/third_party/rust/chromium_crates_io/vendor/jxl-v0_6/LICENSE": "8405932022a556380c2d8c272eff154a923feb197233f348ce5f7334fb0a5ede", + "licenses/third_party/rust/chromium_crates_io/vendor/jxl_macros-v0_6/LICENSE": "8405932022a556380c2d8c272eff154a923feb197233f348ce5f7334fb0a5ede", + "licenses/third_party/rust/chromium_crates_io/vendor/jxl_simd-v0_6/LICENSE": "8405932022a556380c2d8c272eff154a923feb197233f348ce5f7334fb0a5ede", + "licenses/third_party/rust/chromium_crates_io/vendor/jxl_transforms-v0_6/LICENSE": "8405932022a556380c2d8c272eff154a923feb197233f348ce5f7334fb0a5ede", + "licenses/third_party/rust/chromium_crates_io/vendor/konst-v0_2/LICENSE-ZLIB.md": "573e362dc50a6d9eb444cea38ef61587e16a0645cb8098ba13a2c42fdde72acd", + "licenses/third_party/rust/chromium_crates_io/vendor/konst_macro_rules-v0_2/LICENSE-ZLIB.md": "573e362dc50a6d9eb444cea38ef61587e16a0645cb8098ba13a2c42fdde72acd", + "licenses/third_party/rust/chromium_crates_io/vendor/lazy_static-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/lazy_static-v1/LICENSE-MIT": "0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720", + "licenses/third_party/rust/chromium_crates_io/vendor/libafl-v0_15/LICENSE-APACHE": "43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1", + "licenses/third_party/rust/chromium_crates_io/vendor/libafl-v0_15/LICENSE-MIT": "30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652", + "licenses/third_party/rust/chromium_crates_io/vendor/libafl_bolts-v0_15/LICENSE-APACHE": "43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1", + "licenses/third_party/rust/chromium_crates_io/vendor/libafl_bolts-v0_15/LICENSE-MIT": "30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652", + "licenses/third_party/rust/chromium_crates_io/vendor/libafl_derive-v0_15/LICENSE-APACHE": "43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1", + "licenses/third_party/rust/chromium_crates_io/vendor/libafl_derive-v0_15/LICENSE-MIT": "30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652", + "licenses/third_party/rust/chromium_crates_io/vendor/libc-v0_2/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/libc-v0_2/LICENSE-MIT": "123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e", + "licenses/third_party/rust/chromium_crates_io/vendor/libm-v0_2/LICENSE.txt": "3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7", + "licenses/third_party/rust/chromium_crates_io/vendor/litemap-v0_8/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/llguidance-v1/LICENSE": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "licenses/third_party/rust/chromium_crates_io/vendor/lock_api-v0_4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/lock_api-v0_4/LICENSE-MIT": "c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5", + "licenses/third_party/rust/chromium_crates_io/vendor/log-v0_4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/log-v0_4/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/mach2-v0_5/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/mach2-v0_5/LICENSE-BSD": "044983df14c97f2f9570766aaf977b3cdfc4a06cf1f36b776331c5ff89b4fb89", + "licenses/third_party/rust/chromium_crates_io/vendor/mach2-v0_5/LICENSE-MIT": "3f9f0f7e5a5911a8042e32c83ff5d061ce1ffd02e8a207ec2135a44ad73b4191", + "licenses/third_party/rust/chromium_crates_io/vendor/memchr-v2/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f", + "licenses/third_party/rust/chromium_crates_io/vendor/memchr-v2/LICENSE-MIT": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f", + "licenses/third_party/rust/chromium_crates_io/vendor/meminterval-v0_4/LICENSE-APACHE": "43070e2d4e532684de521b885f385d0841030efa2b1a20bafb76133a5e1379c1", + "licenses/third_party/rust/chromium_crates_io/vendor/meminterval-v0_4/LICENSE-MIT": "30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652", + "licenses/third_party/rust/chromium_crates_io/vendor/memo-map-v0_3/LICENSE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/memoffset-v0_9/LICENSE": "3234ac55816264ee7b6c7ee27efd61cf0a1fe775806870e3d9b4c41ea73c5cb1", + "licenses/third_party/rust/chromium_crates_io/vendor/minijinja-v2/LICENSE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/minijinja-v2/src/vendor/self_cell/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_8/LICENSE": "4108245a1f2df9d4e94df8abed5b4ba0759bb2f9b40a6b939f1be141077ae50b", + "licenses/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_8/LICENSE-APACHE.md": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_8/LICENSE-MIT.md": "799e9ca9d179295ef372f25d3769cdda7d25bb2668add6a6a1e22d1e4c678b8d", + "licenses/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_8/LICENSE-ZLIB.md": "0a54e647fe54104658b5e563c04c6f9edf251710e47bce692e0bd990a4ddaa39", + "licenses/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_9/LICENSE": "4108245a1f2df9d4e94df8abed5b4ba0759bb2f9b40a6b939f1be141077ae50b", + "licenses/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_9/LICENSE-APACHE.md": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_9/LICENSE-MIT.md": "799e9ca9d179295ef372f25d3769cdda7d25bb2668add6a6a1e22d1e4c678b8d", + "licenses/third_party/rust/chromium_crates_io/vendor/miniz_oxide-v0_9/LICENSE-ZLIB.md": "0a54e647fe54104658b5e563c04c6f9edf251710e47bce692e0bd990a4ddaa39", + "licenses/third_party/rust/chromium_crates_io/vendor/moxcms-v0_8/LICENSE-APACHE.md": "90bf2d659c43045111b65c733ab2a6d4cbcb422a098368c8c58a9ba3db4ed0c5", + "licenses/third_party/rust/chromium_crates_io/vendor/moxcms-v0_8/LICENSE.md": "2aa92cada6431e75615e3fe6cb1a9082c98f777d48ae1c087c0da0e37f7b8bff", + "licenses/third_party/rust/chromium_crates_io/vendor/murmur3-v0_4/LICENSE-APACHE": "c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08", + "licenses/third_party/rust/chromium_crates_io/vendor/murmur3-v0_4/LICENSE-MIT": "d24a2b82b5d96fd64c84cdae1b1f6250d76c16f0660a593d4ac8127177054b5f", + "licenses/third_party/rust/chromium_crates_io/vendor/nix-v0_30/LICENSE": "66e3ee1fa7f909ad3c612d556f2a0cdabcd809ad6e66f3b0605015ac64841b70", + "licenses/third_party/rust/chromium_crates_io/vendor/num-bigint-v0_4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/num-bigint-v0_4/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/num-complex-v0_4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/num-complex-v0_4/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/num-derive-v0_4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/num-derive-v0_4/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/num-integer-v0_1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/num-integer-v0_1/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/num-rational-v0_4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/num-rational-v0_4/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/num-traits-v0_2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/num-traits-v0_2/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/num_enum-v0_7/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/num_enum-v0_7/LICENSE-BSD": "0be96d891d00e0ae0df75d7f3289b12871c000a1f5ac744f3b570768d4bb277c", + "licenses/third_party/rust/chromium_crates_io/vendor/num_enum-v0_7/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/num_enum_derive-v0_7/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/num_enum_derive-v0_7/LICENSE-BSD": "0be96d891d00e0ae0df75d7f3289b12871c000a1f5ac744f3b570768d4bb277c", + "licenses/third_party/rust/chromium_crates_io/vendor/num_enum_derive-v0_7/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/object-v0_37/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/object-v0_37/LICENSE-MIT": "0b74dfa0bcee5c420c6b7f67b4b2658f9ab8388c97b8e733975f2cecbdd668a6", + "licenses/third_party/rust/chromium_crates_io/vendor/once_cell-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/once_cell-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/ordered-float-v5/LICENSE-MIT": "f7715d38a3fa1b4ac97c5729740752505a39cb92ee83ab5b102aeb5eaa7cdea4", + "licenses/third_party/rust/chromium_crates_io/vendor/parking_lot-v0_12/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/parking_lot-v0_12/LICENSE-MIT": "c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5", + "licenses/third_party/rust/chromium_crates_io/vendor/parking_lot_core-v0_9/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/parking_lot_core-v0_9/LICENSE-MIT": "c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5", + "licenses/third_party/rust/chromium_crates_io/vendor/png-v0_18/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/png-v0_18/LICENSE-MIT": "eaf40297c75da471f7cda1f3458e8d91b4b2ec866e609527a13acfa93b638652", + "licenses/third_party/rust/chromium_crates_io/vendor/postcard-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/postcard-v1/LICENSE-MIT": "177540cad091a40e8071db310bc3b6115c4e329a92a234609b60c154b008a888", + "licenses/third_party/rust/chromium_crates_io/vendor/potential_utf-v0_1/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/primal-check-v0_3/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/primal-check-v0_3/LICENSE-MIT": "6d3a9431e65e69c73a8923e6517b889d17549b23db406b9ec027710d16af701f", + "licenses/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/prost-derive-v0_14/LICENSE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/prost-v0_14/LICENSE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/pxfm-v0_1/LICENSE-APACHE.md": "90bf2d659c43045111b65c733ab2a6d4cbcb422a098368c8c58a9ba3db4ed0c5", + "licenses/third_party/rust/chromium_crates_io/vendor/pxfm-v0_1/LICENSE.md": "2aa92cada6431e75615e3fe6cb1a9082c98f777d48ae1c087c0da0e37f7b8bff", + "licenses/third_party/rust/chromium_crates_io/vendor/qr_code-v2/LICENSE-APACHE.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/rust/chromium_crates_io/vendor/qr_code-v2/LICENSE-MIT.txt": "7f865e72ab3644ea5887aa1f352aa435b36d139c35a964f47091e7dc02722e9a", + "licenses/third_party/rust/chromium_crates_io/vendor/quote-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/quote-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/rand_core-v0_9/COPYRIGHT": "90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5", + "licenses/third_party/rust/chromium_crates_io/vendor/rand_core-v0_9/LICENSE-APACHE": "6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51", + "licenses/third_party/rust/chromium_crates_io/vendor/rand_core-v0_9/LICENSE-MIT": "209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b", + "licenses/third_party/rust/chromium_crates_io/vendor/read-fonts-v0_43/LICENSE-APACHE": "eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0", + "licenses/third_party/rust/chromium_crates_io/vendor/read-fonts-v0_43/LICENSE-MIT": "7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427", + "licenses/third_party/rust/chromium_crates_io/vendor/ref-cast-impl-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/ref-cast-impl-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/ref-cast-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/ref-cast-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/regex-automata-v0_4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/regex-automata-v0_4/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/regex-lite-v0_1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/regex-lite-v0_1/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/regex-syntax-v0_8/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/regex-syntax-v0_8/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/regex-syntax-v0_8/src/unicode_tables/LICENSE-UNICODE": "74db5baf44a41b1000312c673544b3374e4198af5605c7f9080a402cec42cfa3", + "licenses/third_party/rust/chromium_crates_io/vendor/regex-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/regex-v1/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust/chromium_crates_io/vendor/resb-v0_1/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/rustc-demangle-capi-v0_1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/rustc-demangle-capi-v0_1/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/third_party/rust/chromium_crates_io/vendor/rustc-demangle-v0_1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/rustc-demangle-v0_1/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/third_party/rust/chromium_crates_io/vendor/rustfft-v6/LICENSE-APACHE": "2e54cd84a645bea25943c75dd8ae67cb291e66a47a11578333c9b4b3b6b86c85", + "licenses/third_party/rust/chromium_crates_io/vendor/rustfft-v6/LICENSE-MIT": "8f5442dfa8e9169045697e386bc91d19f393c939635741fa2a665ec36ca6f0ad", + "licenses/third_party/rust/chromium_crates_io/vendor/rustversion-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/rustversion-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/ryu-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/ryu-v1/LICENSE-BOOST": "c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566", + "licenses/third_party/rust/chromium_crates_io/vendor/safe_arch-v0_7/LICENSE-APACHE.md": "e3ba223bb1423f0aad8c3dfce0fe3148db48926d41e6fbc3afbbf5ff9e1c89cb", + "licenses/third_party/rust/chromium_crates_io/vendor/safe_arch-v0_7/LICENSE-MIT.md": "e57011537d230b14e790f6666dc00816f7b371ebbd7da8a12491e51086fec278", + "licenses/third_party/rust/chromium_crates_io/vendor/safe_arch-v0_7/LICENSE-ZLIB.md": "c43b9a9b1387ed53d2c49263838261129a010e280e3a174a792242c3e2c98db9", + "licenses/third_party/rust/chromium_crates_io/vendor/scopeguard-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/scopeguard-v1/LICENSE-MIT": "fb77f0a9c53e473abe5103c8632ef9f0f2874d4fb3f17cb2d8c661aab9cee9d7", + "licenses/third_party/rust/chromium_crates_io/vendor/serde-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/serde-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/serde_core-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/serde_core-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/serde_derive-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/serde_derive-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/serde_json-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/serde_json-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/serde_json_lenient-v0_2/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/serde_json_lenient-v0_2/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/serial_test-v3/LICENSE": "ac7e05bd11cc1cfc3f9452c1b9986a9b1d54e180fa88e44e69caf955f95dc8a6", + "licenses/third_party/rust/chromium_crates_io/vendor/serial_test_derive-v3/LICENSE": "ac7e05bd11cc1cfc3f9452c1b9986a9b1d54e180fa88e44e69caf955f95dc8a6", + "licenses/third_party/rust/chromium_crates_io/vendor/sfv-v0_15/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/sfv-v0_15/LICENSE-MIT": "5318787a14e32720b1652f08a24408aaea67fdb5154ceda0f46a6069a0c5e5e3", + "licenses/third_party/rust/chromium_crates_io/vendor/shared-brotli-patch-decoder-v0_1/LICENSE-APACHE": "eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0", + "licenses/third_party/rust/chromium_crates_io/vendor/shared-brotli-patch-decoder-v0_1/LICENSE-MIT": "7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427", + "licenses/third_party/rust/chromium_crates_io/vendor/simd-adler32-v0_3/LICENSE.md": "42a35170233e83e18856792e748de4c1ce4a63b2afce9a370c89ef3fe23f9f2d", + "licenses/third_party/rust/chromium_crates_io/vendor/siphasher-v1/COPYING": "c962ee4d1d05ddc138b202b2540219ebc57893fcf97b364852094a9a94ce1365", + "licenses/third_party/rust/chromium_crates_io/vendor/siphasher-v1/LICENSE-APACHE": "58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd", + "licenses/third_party/rust/chromium_crates_io/vendor/skera-v0_6/LICENSE-APACHE": "eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0", + "licenses/third_party/rust/chromium_crates_io/vendor/skera-v0_6/LICENSE-MIT": "7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427", + "licenses/third_party/rust/chromium_crates_io/vendor/skrifa-v0_46/LICENSE-APACHE": "eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0", + "licenses/third_party/rust/chromium_crates_io/vendor/skrifa-v0_46/LICENSE-MIT": "7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427", + "licenses/third_party/rust/chromium_crates_io/vendor/small_ctor-v0_1/LICENSE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/smallvec-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/smallvec-v1/LICENSE-MIT": "0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9", + "licenses/third_party/rust/chromium_crates_io/vendor/stable_deref_trait-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/stable_deref_trait-v1/LICENSE-MIT": "5e05b024f653a5ce199e77cbbbd42fb5553562ec714b819421ed0c3e552a75d7", + "licenses/third_party/rust/chromium_crates_io/vendor/static_assertions-v1/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/rust/chromium_crates_io/vendor/static_assertions-v1/LICENSE-MIT": "ea084a2373ebc1f0902c09266e7bf25a05ab3814c1805bb017ffa7308f90c061", + "licenses/third_party/rust/chromium_crates_io/vendor/strck-v1/LICENSE": "7e24648e3d082d4f8026cdedb21d084d91ef13223bff6b76cb8bf19a744b7d28", + "licenses/third_party/rust/chromium_crates_io/vendor/strength_reduce-v0_2/LICENSE-APACHE": "2e54cd84a645bea25943c75dd8ae67cb291e66a47a11578333c9b4b3b6b86c85", + "licenses/third_party/rust/chromium_crates_io/vendor/strength_reduce-v0_2/LICENSE-MIT": "8f5442dfa8e9169045697e386bc91d19f393c939635741fa2a665ec36ca6f0ad", + "licenses/third_party/rust/chromium_crates_io/vendor/strsim-v0_11/LICENSE": "1e697ce8d21401fbf1bddd9b5c3fd4c4c79ae1e3bdf51f81761c85e11d5a89cd", + "licenses/third_party/rust/chromium_crates_io/vendor/strum-v0_28/LICENSE": "8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42", + "licenses/third_party/rust/chromium_crates_io/vendor/strum_macros-v0_28/LICENSE": "8bce3b45e49ecd1461f223b46de133d8f62cd39f745cfdaf81bee554b908bd42", + "licenses/third_party/rust/chromium_crates_io/vendor/subtle-v2/LICENSE": "d1fc1bc0d155df60b2e7705b6b2ae02a05c96f948e1cec6e2fb86360b09f346b", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-bundle-flac-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-bundle-mp3-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-codec-pcm-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-codec-vorbis-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-common-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-core-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-format-isomp4-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-format-mkv-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-format-ogg-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-format-riff-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-metadata-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/symphonia-v0_6/LICENSE": "c76f740d1521b9bed9ca7a04ad526c310493c62621b1341d623b431736533b30", + "licenses/third_party/rust/chromium_crates_io/vendor/syn-v2/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/syn-v2/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/syn-v3/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/syn-v3/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/synstructure-v0_13/LICENSE": "219920e865eee70b7dcfc948a86b099e7f4fe2de01bcca2ca9a20c0a033f2b59", + "licenses/third_party/rust/chromium_crates_io/vendor/temporal_capi-v0_2/LICENSE-Apache": "4e6bdc19db64d455dbddc0ee2f53ecba556f0226d4558954b5f02673c7358e59", + "licenses/third_party/rust/chromium_crates_io/vendor/temporal_capi-v0_2/LICENSE-MIT": "073d3574ac6389e263360572b331e045dda7ac5cdba239ce5bdb02f299ef47bb", + "licenses/third_party/rust/chromium_crates_io/vendor/temporal_rs-v0_2/LICENSE-Apache": "4e6bdc19db64d455dbddc0ee2f53ecba556f0226d4558954b5f02673c7358e59", + "licenses/third_party/rust/chromium_crates_io/vendor/temporal_rs-v0_2/LICENSE-MIT": "073d3574ac6389e263360572b331e045dda7ac5cdba239ce5bdb02f299ef47bb", + "licenses/third_party/rust/chromium_crates_io/vendor/termcolor-v1/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f", + "licenses/third_party/rust/chromium_crates_io/vendor/termcolor-v1/LICENSE-MIT": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f", + "licenses/third_party/rust/chromium_crates_io/vendor/thiserror-impl-v2/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/thiserror-impl-v2/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/thiserror-v2/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/thiserror-v2/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/timezone_provider-v0_2/LICENSE-Apache": "4e6bdc19db64d455dbddc0ee2f53ecba556f0226d4558954b5f02673c7358e59", + "licenses/third_party/rust/chromium_crates_io/vendor/timezone_provider-v0_2/LICENSE-MIT": "073d3574ac6389e263360572b331e045dda7ac5cdba239ce5bdb02f299ef47bb", + "licenses/third_party/rust/chromium_crates_io/vendor/tinystr-v0_8/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/toktrie-v1/LICENSE": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "licenses/third_party/rust/chromium_crates_io/vendor/transpose-v0_2/LICENSE-APACHE": "8797ef61538ec5ee9222ebef7ca4e0f3ec5761b145ca9943d358c450efb644dd", + "licenses/third_party/rust/chromium_crates_io/vendor/transpose-v0_2/LICENSE-MIT": "5080149357fd0be590bdc10cf92165412bb4d61ce496284d56f2d12874ae3121", + "licenses/third_party/rust/chromium_crates_io/vendor/tuple_list-v0_1/LICENSE": "fdd3b4e30f42d35402ee9c33a6b72ad202f9658374b61b97becde4603551b95d", + "licenses/third_party/rust/chromium_crates_io/vendor/typed-arena-v2/LICENSE": "9ed5e982274d54d0cf94f0e9f9fd889182b6f1f50a012f0be41ce7c884347ab6", + "licenses/third_party/rust/chromium_crates_io/vendor/typed-builder-macro-v0_22/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/typed-builder-macro-v0_22/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/typed-builder-v0_22/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/typed-builder-v0_22/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/typed-path-v0_12/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/typed-path-v0_12/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/typeid-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/typeid-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/typewit-v1/LICENSE-ZLIB.md": "6db6d36c8aae8c2f6ebec32965bab9b4769128caed09a55b8696afce4301838a", + "licenses/third_party/rust/chromium_crates_io/vendor/uds-v0_4/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/rust/chromium_crates_io/vendor/uds-v0_4/LICENSE-MIT": "f234d44d4afa9dad03246705dcedb1a70a7562bf595fdbfafa93aa73c8839d57", + "licenses/third_party/rust/chromium_crates_io/vendor/unicode-ident-v1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust/chromium_crates_io/vendor/unicode-ident-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/unicode-ident-v1/LICENSE-UNICODE": "f7db81051789b729fea528a63ec4c938fdcb93d9d61d97dc8cc2e9df6d47f2a1", + "licenses/third_party/rust/chromium_crates_io/vendor/unicode-width-v0_2/COPYRIGHT": "23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d", + "licenses/third_party/rust/chromium_crates_io/vendor/unicode-width-v0_2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/unicode-width-v0_2/LICENSE-MIT": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0", + "licenses/third_party/rust/chromium_crates_io/vendor/unicode-xid-v0_2/COPYRIGHT": "23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d", + "licenses/third_party/rust/chromium_crates_io/vendor/unicode-xid-v0_2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/unicode-xid-v0_2/LICENSE-MIT": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0", + "licenses/third_party/rust/chromium_crates_io/vendor/unty-v0_0_4/LICENSE-APACHE": "fa84f04c495f2533ba036606acc8644b50077752f17bc2e943235459dda1c12c", + "licenses/third_party/rust/chromium_crates_io/vendor/unty-v0_0_4/LICENSE-MIT": "ac1e6e437cd571f6b450abd33dc055cf26dfb0fe2a72952c3ef6ae3844549c12", + "licenses/third_party/rust/chromium_crates_io/vendor/utf16_iter-v1/COPYRIGHT": "b84efe109a420fa3ca98be33f4227327af7ffa426195812c270feb1268bc2426", + "licenses/third_party/rust/chromium_crates_io/vendor/utf16_iter-v1/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/rust/chromium_crates_io/vendor/utf16_iter-v1/LICENSE-MIT": "3fa4ca83dcc9237839b1bdeb2e6d16bdfb5ec0c5ce42b24694d8bbf0dcbef72c", + "licenses/third_party/rust/chromium_crates_io/vendor/utf8_iter-v1/COPYRIGHT": "c30152c94a6d75e021adbc52b3a52470366a46edb917e17deae3259251af244c", + "licenses/third_party/rust/chromium_crates_io/vendor/utf8_iter-v1/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/rust/chromium_crates_io/vendor/utf8_iter-v1/LICENSE-MIT": "3fa4ca83dcc9237839b1bdeb2e6d16bdfb5ec0c5ce42b24694d8bbf0dcbef72c", + "licenses/third_party/rust/chromium_crates_io/vendor/uuid-v1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/uuid-v1/LICENSE-MIT": "436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d", + "licenses/third_party/rust/chromium_crates_io/vendor/version_check-v0_9/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/version_check-v0_9/LICENSE-MIT": "b7e650f3fce5c53249d1cdc608b54df156a97edd636cf9d23498d0cfe7aec63e", + "licenses/third_party/rust/chromium_crates_io/vendor/virtue-v0_0_18/LICENSE.md": "ddcbb6914b62d5bebc3cb58ebe8d2738ffa9a48555469bbcbe65159979b878cf", + "licenses/third_party/rust/chromium_crates_io/vendor/wait-timeout-v0_2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust/chromium_crates_io/vendor/wait-timeout-v0_2/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/third_party/rust/chromium_crates_io/vendor/wide-v0_7/LICENSE-ZLIB.md": "c43b9a9b1387ed53d2c49263838261129a010e280e3a174a792242c3e2c98db9", + "licenses/third_party/rust/chromium_crates_io/vendor/winapi-util-v0_1/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f", + "licenses/third_party/rust/chromium_crates_io/vendor/winapi-util-v0_1/LICENSE-MIT": "cb3c929a05e6cbc9de9ab06a4c57eeb60ca8c724bef6c138c87d3a577e27aa14", + "licenses/third_party/rust/chromium_crates_io/vendor/windows-link-v0_2/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "licenses/third_party/rust/chromium_crates_io/vendor/windows-link-v0_2/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "licenses/third_party/rust/chromium_crates_io/vendor/windows-sys-v0_52/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "licenses/third_party/rust/chromium_crates_io/vendor/windows-sys-v0_52/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "licenses/third_party/rust/chromium_crates_io/vendor/windows-targets-v0_52/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "licenses/third_party/rust/chromium_crates_io/vendor/windows-targets-v0_52/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "licenses/third_party/rust/chromium_crates_io/vendor/windows_aarch64_msvc-v0_52/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "licenses/third_party/rust/chromium_crates_io/vendor/windows_aarch64_msvc-v0_52/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "licenses/third_party/rust/chromium_crates_io/vendor/windows_i686_msvc-v0_52/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "licenses/third_party/rust/chromium_crates_io/vendor/windows_i686_msvc-v0_52/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "licenses/third_party/rust/chromium_crates_io/vendor/windows_x86_64_msvc-v0_52/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b", + "licenses/third_party/rust/chromium_crates_io/vendor/windows_x86_64_msvc-v0_52/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383", + "licenses/third_party/rust/chromium_crates_io/vendor/write-fonts-v0_52/LICENSE-APACHE": "eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0", + "licenses/third_party/rust/chromium_crates_io/vendor/write-fonts-v0_52/LICENSE-MIT": "7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427", + "licenses/third_party/rust/chromium_crates_io/vendor/write16-v1/COPYRIGHT": "3210be7332b5bdf48eb24a945258b9f38616a2cceb0dfc06e3c3c7e9740475a0", + "licenses/third_party/rust/chromium_crates_io/vendor/write16-v1/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/rust/chromium_crates_io/vendor/write16-v1/LICENSE-MIT": "3fa4ca83dcc9237839b1bdeb2e6d16bdfb5ec0c5ce42b24694d8bbf0dcbef72c", + "licenses/third_party/rust/chromium_crates_io/vendor/writeable-v0_6/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/xml-v1/LICENSE": "0dc18d924dc0a5f41172a393012843a5eaaef338e795b3645da9cc3b6068220b", + "licenses/third_party/rust/chromium_crates_io/vendor/xxhash-rust-v0_8/LICENSE": "c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566", + "licenses/third_party/rust/chromium_crates_io/vendor/yoke-derive-v0_8/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/yoke-v0_8/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/zerocopy-v0_8/LICENSE-APACHE": "9d185ac6703c4b0453974c0d85e9eee43e6941009296bb1f5eb0b54e2329e9f3", + "licenses/third_party/rust/chromium_crates_io/vendor/zerocopy-v0_8/LICENSE-BSD": "83c1763356e822adde0a2cae748d938a73fdc263849ccff6b27776dff213bd32", + "licenses/third_party/rust/chromium_crates_io/vendor/zerocopy-v0_8/LICENSE-MIT": "1a2f5c12ddc934d58956aa5dbdd3255fe55fd957633ab7d0d39e4f0daa73f7df", + "licenses/third_party/rust/chromium_crates_io/vendor/zerofrom-derive-v0_1/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/zerofrom-v0_1/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/zeroize-v1/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/rust/chromium_crates_io/vendor/zeroize-v1/LICENSE-MIT": "8c7516d4b27b1e495be5e38b612298b63de48d05f49cdac94f70f3cd70f8864b", + "licenses/third_party/rust/chromium_crates_io/vendor/zeroize_derive-v1/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/rust/chromium_crates_io/vendor/zeroize_derive-v1/LICENSE-MIT": "b8c6939380a400f53e11923d50fcc4dd2fa1ba8339fd9d04cda38a0251b6c9b0", + "licenses/third_party/rust/chromium_crates_io/vendor/zerotrie-v0_2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/zerovec-derive-v0_11/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/zerovec-v0_11/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/zip-v8/LICENSE": "58545fed1565e42d687aecec6897d35c6d37ccb71479a137c0deb2203e125c79", + "licenses/third_party/rust/chromium_crates_io/vendor/zmij-v1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust/chromium_crates_io/vendor/zoneinfo64-v0_3/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2", + "licenses/third_party/rust/chromium_crates_io/vendor/zune-core-v0_5/LICENSE-APACHE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/rust/chromium_crates_io/vendor/zune-core-v0_5/LICENSE-MIT": "d30047bca3b516639339a3c279bb84c3483124fb5a9dafe3c75056a85090e745", + "licenses/third_party/rust/chromium_crates_io/vendor/zune-core-v0_5/LICENSE-ZLIB": "d201d14804d3bcd3b944147173175e4abfbd838b7c8069b6bd3452496bf13e6c", + "licenses/third_party/rust/chromium_crates_io/vendor/zune-jpeg-v0_5/LICENSE-APACHE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/rust/chromium_crates_io/vendor/zune-jpeg-v0_5/LICENSE-MIT": "d30047bca3b516639339a3c279bb84c3483124fb5a9dafe3c75056a85090e745", + "licenses/third_party/rust/chromium_crates_io/vendor/zune-jpeg-v0_5/LICENSE-ZLIB": "d201d14804d3bcd3b944147173175e4abfbd838b7c8069b6bd3452496bf13e6c", + "licenses/third_party/rust-toolchain/lib/rustlib/rustc-src/rust/compiler/rustc_codegen_cranelift/LICENSE-APACHE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4", + "licenses/third_party/rust-toolchain/lib/rustlib/rustc-src/rust/compiler/rustc_codegen_cranelift/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust-toolchain/lib/rustlib/rustc-src/rust/compiler/rustc_codegen_gcc/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust-toolchain/lib/rustlib/rustc-src/rust/compiler/rustc_codegen_gcc/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/backtrace/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/backtrace/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/compiler-builtins/LICENSE.txt": "ab6eec6caf0fa5775e411c7a8bc6a45c4ef2956b0980b157ab74fc5cd62a928b", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/compiler-builtins/libm/LICENSE.txt": "3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/portable-simd/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/portable-simd/LICENSE-MIT": "eb07d497d26e6d68fbc76e793f5e5c9cfa197df2a580e47383569c287a55edf9", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/portable-simd/crates/core_simd/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/portable-simd/crates/core_simd/LICENSE-MIT": "eb07d497d26e6d68fbc76e793f5e5c9cfa197df2a580e47383569c287a55edf9", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/LICENSE-MIT": "29662666b44dff84977b46e05642cdef910bc3a93a17b5fd86e632bafa59cf21", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/core_arch/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/core_arch/LICENSE-MIT": "29662666b44dff84977b46e05642cdef910bc3a93a17b5fd86e632bafa59cf21", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/intrinsic-test/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/stdarch/crates/intrinsic-test/LICENSE-MIT": "8bc20184c0ddf3006df05e89fdf7193b33dbe4c751ae59d6bb1835f71bbe70da", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/addr2line-0.27.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/addr2line-0.27.1/LICENSE-MIT": "e99d88d232bf57d70f0fb87f6b496d44b6653f99f8a63d250a54c61ea4bcde40", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/adler2-2.0.1/LICENSE-0BSD": "861399f8c21c042b110517e76dc6b63a2b334276c8cf17412fc3c8908ca8dc17", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/adler2-2.0.1/LICENSE-APACHE": "8ada45cd9f843acf64e4722ae262c622a2b3b3007c7310ef36ac1061a30f6adb", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/adler2-2.0.1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/cc-1.4.3/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/cc-1.4.3/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/cfg-if-1.0.4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/cfg-if-1.0.4/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/dlmalloc-0.2.14/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/dlmalloc-0.2.14/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/find-msvc-tools-0.1.11/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/find-msvc-tools-0.1.11/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/foldhash-0.2.0/LICENSE": "b1181a40b2a7b25cf66fd01481713bc1005df082c53ef73e851e55071b102744", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/getopts-0.2.24/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/getopts-0.2.24/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/gimli-0.34.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/gimli-0.34.0/LICENSE-MIT": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.17.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hashbrown-0.17.1/LICENSE-MIT": "ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hermit-abi-0.5.3/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/hermit-abi-0.5.3/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/libc-0.2.189/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/libc-0.2.189/LICENSE-MIT": "123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/memchr-2.8.3/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/memchr-2.8.3/LICENSE-MIT": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/miniz_oxide-0.9.1/LICENSE": "4108245a1f2df9d4e94df8abed5b4ba0759bb2f9b40a6b939f1be141077ae50b", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/miniz_oxide-0.9.1/LICENSE-APACHE.md": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/miniz_oxide-0.9.1/LICENSE-MIT.md": "799e9ca9d179295ef372f25d3769cdda7d25bb2668add6a6a1e22d1e4c678b8d", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/miniz_oxide-0.9.1/LICENSE-ZLIB.md": "0a54e647fe54104658b5e563c04c6f9edf251710e47bce692e0bd990a4ddaa39", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/moto-rt-0.17.4/LICENSE-APACHE": "69fef7b0f322a65554156141f2bc6256ed0bb78cba7e49f0d8829d7ec4ee62cd", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/moto-rt-0.17.4/LICENSE-MIT": "a7c936ff1ed8fa340172d42a98185afee078f818e907da69f3a8e336b1623b4b", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/object-0.39.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/object-0.39.1/LICENSE-MIT": "0b74dfa0bcee5c420c6b7f67b4b2658f9ab8388c97b8e733975f2cecbdd668a6", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand-0.9.5/COPYRIGHT": "90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand-0.9.5/LICENSE-APACHE": "35242e7a83f69875e6edeff02291e688c97caafe2f8902e4e19b49d3e78b4cab", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand-0.9.5/LICENSE-MIT": "209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_core-0.9.5/COPYRIGHT": "90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_core-0.9.5/LICENSE-APACHE": "6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_core-0.9.5/LICENSE-MIT": "209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_xorshift-0.4.0/COPYRIGHT": "90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_xorshift-0.4.0/LICENSE-APACHE": "35242e7a83f69875e6edeff02291e688c97caafe2f8902e4e19b49d3e78b4cab", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rand_xorshift-0.4.0/LICENSE-MIT": "209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rustc-demangle-0.1.28/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rustc-demangle-0.1.28/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rustc-literal-escaper-0.0.8/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/rustc-literal-escaper-0.0.8/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/shlex-2.0.1/LICENSE-APACHE": "553fffcd9b1cb158bc3e9edc35da85ca5c3b3d7d2e61c883ebcfa8a65814b583", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/shlex-2.0.1/LICENSE-MIT": "4455bf75a91154108304cb283e0fea9948c14f13e20d60887cf2552449dea3b1", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/unwinding-0.2.10/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/unwinding-0.2.10/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip2-1.0.4+wasi-0.2.12/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip2-1.0.4+wasi-0.2.12/LICENSE-Apache-2.0_WITH_LLVM-exception": "268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip2-1.0.4+wasi-0.2.12/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip3-0.7.1+wasi-0.3.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip3-0.7.1+wasi-0.3.0/LICENSE-Apache-2.0_WITH_LLVM-exception": "268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wasip3-0.7.1+wasi-0.3.0/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wit-bindgen-0.57.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wit-bindgen-0.57.1/LICENSE-Apache-2.0_WITH_LLVM-exception": "268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/wit-bindgen-0.57.1/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust-toolchain/lib/rustlib/src/rust/src/llvm-project/libunwind/LICENSE.TXT": "b5efebcaca80879234098e52d1725e6d9eb8fb96a19fce625d39184b705f7b6d", + "licenses/third_party/rust-toolchain/lib/third_party/crubit/LICENSE": "d136abc3388ab9b16879d6dcb9c8c4a5d70ddf821bab80b6454843abe358d34a", + "licenses/third_party/rust-toolchain/share/doc/cargo/LICENSE-APACHE": "8ada45cd9f843acf64e4722ae262c622a2b3b3007c7310ef36ac1061a30f6adb", + "licenses/third_party/rust-toolchain/share/doc/cargo/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust-toolchain/share/doc/cargo/LICENSE-THIRD-PARTY": "cbc759b1f17a2ac38fe3eb9e9563b1a08ba0f900611c49faaf68b46907b6d898", + "licenses/third_party/rust-toolchain/share/doc/clippy/LICENSE-APACHE": "d8b56cd45661bfc7ccf4ce5722388cab275f9dabb9e471c922cc80e36bbf9caa", + "licenses/third_party/rust-toolchain/share/doc/clippy/LICENSE-MIT": "8d07f0c9c9966be0aaec4196d7863b56ade114e9714dbc31ebba576c0446d2fc", + "licenses/third_party/rust-toolchain/share/doc/rust-analyzer/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a", + "licenses/third_party/rust-toolchain/share/doc/rust-analyzer/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3", + "licenses/third_party/rust-toolchain/share/doc/rustc/COPYRIGHT-library.html": "07ca08d0838ccbbfb79bf292741c7c4c45311f90784431d09af9a1e0b90a9469", + "licenses/third_party/rust-toolchain/share/doc/rustc/COPYRIGHT.html": "9ff34fe87a89242afd776a07dbe055151121fead277acd30fee56e42b41cab93", + "licenses/third_party/rust-toolchain/share/doc/rustfmt/LICENSE-APACHE": "092c8e82c47fb859d7385253c1b04052dbd65a8bcf0bbd8b6f7dba58a9e8753d", + "licenses/third_party/rust-toolchain/share/doc/rustfmt/LICENSE-MIT": "ae0f1f791e3b4faccf981c0b530199235a8b8a021c7ef0c3c85c6c676ea4d27f", + "licenses/third_party/spirv-cross/src/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/spirv-cross/src/LICENSES/LicenseRef-KhronosFreeUse.txt": "fbeaca472f4f70e276dd1106ca5097435967a22ad6c1d8200aef7ad9f70aaf3f", + "licenses/third_party/spirv-headers/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/spirv-headers/src/LICENSE": "ea43b1de38a6f90c488800d66dec1ed671e68cda530266bc96951fb5b6307613", + "licenses/third_party/spirv-tools/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/spirv-tools/src/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/spirv-tools/src/utils/vscode/src/lsp/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/turbine/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/vulkan-deps/LICENSE": "845022e0c1db1abb41a6ba4cd3c4b674ec290f3359d9d3c78ae558d4c0ed9308", + "licenses/third_party/vulkan-deps/glslang/LICENSE": "23353f4505b1c8ce4f8f72fc3b11dc74b4a8a7bf95921d93ff77f227c171a710", + "licenses/third_party/vulkan-deps/spirv-headers/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/vulkan-deps/spirv-tools/LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/vulkan-deps/vulkan-headers/LICENSE.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/vulkan-headers/LICENSE.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/vulkan-headers/src/LICENSE.md": "95ad366d23fadf701d355bc45fb8b82ae2d700239471d35d41286ac3b08ff903", + "licenses/third_party/vulkan-loader/src/LICENSE.txt": "43c0a37e6a0fa7ff3c843b3ec5a4fac84b712558ddac103fbd4c1649662a9ece", + "licenses/third_party/vulkan-tools/src/LICENSE.txt": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30", + "licenses/third_party/vulkan-utility-libraries/src/LICENSE.md": "69760673abf91cfd0280ae73739a29c078f493804d9016a122b3b189b48ad6e6", + "licenses/third_party/vulkan-validation-layers/src/LICENSE.txt": "db3010170b904cb7212ef6abd2336f316bf735060eeeca23f1a737f459cc73e4", + "licenses/third_party/vulkan_memory_allocator/LICENSE.txt": "cdb520614db3ec62e667ece01e64e6afa21948fa51e85e748b1494597a7be907", + "licenses/third_party/wayland/LICENSE": "778a9c936b9fa24f3842b6071e3cc5c794d3f7cc6d6fddbf356b6f2202afb6a0", + "licenses/third_party/wayland-protocols/LICENSE": "f1a2b233e8a9a71c40f4aa885be08a0842ac85bb8588703c1dd7e6e6502e3124", + "licenses/third_party/zlib/LICENSE": "e32ff4e00d9d94930537635291da39e7e612703334bf6fde8c7f1686fe8a45a2", + "licenses/tools/flex-bison/third_party/m4sugar/LICENSE": "ab15fd526bd8dd18a9e77ebc139656bf4d33e97fc7238cd11bf60e2b9b8666c6", + "licenses/tools/flex-bison/third_party/skeletons/LICENSE": "8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903", + "licenses/tools/valgrind/asan/third_party/LICENSE.TXT": "1a8f1058753f1ba890de984e48f0242a3a5c29a6a8f2ed9fd813f36985387e8d", + "licenses/util/android/third_party/LICENSE": "58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd", + "licenses/util/windows/third_party/StackWalker/LICENSE": "bfec18debedcb337f8af53f143ccf0b1575d0b7c30deaee137f10397eca0d353" + }, + "buildScriptSha256": "1badce875cc834f1924a219aa457971a5f6f61b7a2803ef7fc03a9ceec44a4c5", + "qualification": "built; requires hardware probe and platform acceptance evidence" + } + }, + "probes": [ + { + "command": [ + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/probes-osx-arm64/webscene_dawn_probe", + "metal" + ], + "component": "dawn", + "esMajor": null, + "binarySha256": "3ab5c3d7dd0f8ea1834cb29a78c6807f4b1b93f4633dbfe6b892ee7debcb8886", + "exitCode": 0, + "stdout": "{\"schemaVersion\":1,\"probe\":\"dawn\",\"status\":\"passed\",\"hardwareAccelerated\":true,\"backend\":\"metal\",\"adapter\":\"Apple M4\",\"vendor\":\"apple\",\"driver\":\"Metal driver on macOS Version 26.6.2 (Build 25G83)\",\"vendorId\":4203,\"deviceId\":0,\"verifiedPixels\":68,\"expectedRGBA\":[51,102,153,255],\"tolerance\":1,\"diagnosticReadback\":true}\n", + "stderr": "", + "result": { + "schemaVersion": 1, + "probe": "dawn", + "status": "passed", + "hardwareAccelerated": true, + "backend": "metal", + "adapter": "Apple M4", + "vendor": "apple", + "driver": "Metal driver on macOS Version 26.6.2 (Build 25G83)", + "vendorId": 4203, + "deviceId": 0, + "verifiedPixels": 68, + "expectedRGBA": [ + 51, + 102, + 153, + 255 + ], + "tolerance": 1, + "diagnosticReadback": true + } + }, + { + "command": [ + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/probes-osx-arm64/webscene_angle_probe", + "metal", + "2" + ], + "component": "angle", + "esMajor": 2, + "binarySha256": "d6f561d1c22ab103714237aeaed477e8135a7f28ebfadd7cdd099d730bd63d19", + "exitCode": 0, + "stdout": "{\"schemaVersion\":1,\"probe\":\"angle\",\"status\":\"passed\",\"hardwareAccelerated\":true,\"backend\":\"metal\",\"esMajor\":2,\"adapter\":\"ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)\",\"vendor\":\"Google Inc. (Apple)\",\"driver\":\"OpenGL ES 2.0 (ANGLE 2.1.1 git hash: 082d85ba19ef)\",\"hardwareEvidence\":\"Explicit ANGLE hardware device on native Metal/D3D11 backend\",\"verifiedPixels\":68,\"webglCompatibleContext\":true,\"robustResourceInitialization\":true,\"diagnosticReadback\":true,\"expectedRGBA\":[51,102,153,255],\"tolerance\":1}\n", + "stderr": "", + "result": { + "schemaVersion": 1, + "probe": "angle", + "status": "passed", + "hardwareAccelerated": true, + "backend": "metal", + "esMajor": 2, + "adapter": "ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)", + "vendor": "Google Inc. (Apple)", + "driver": "OpenGL ES 2.0 (ANGLE 2.1.1 git hash: 082d85ba19ef)", + "hardwareEvidence": "Explicit ANGLE hardware device on native Metal/D3D11 backend", + "verifiedPixels": 68, + "webglCompatibleContext": true, + "robustResourceInitialization": true, + "diagnosticReadback": true, + "expectedRGBA": [ + 51, + 102, + 153, + 255 + ], + "tolerance": 1 + } + }, + { + "command": [ + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/probes-osx-arm64/webscene_angle_probe", + "metal", + "3" + ], + "component": "angle", + "esMajor": 3, + "binarySha256": "d6f561d1c22ab103714237aeaed477e8135a7f28ebfadd7cdd099d730bd63d19", + "exitCode": 0, + "stdout": "{\"schemaVersion\":1,\"probe\":\"angle\",\"status\":\"passed\",\"hardwareAccelerated\":true,\"backend\":\"metal\",\"esMajor\":3,\"adapter\":\"ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)\",\"vendor\":\"Google Inc. (Apple)\",\"driver\":\"OpenGL ES 3.0 (ANGLE 2.1.1 git hash: 082d85ba19ef)\",\"hardwareEvidence\":\"Explicit ANGLE hardware device on native Metal/D3D11 backend\",\"verifiedPixels\":68,\"webglCompatibleContext\":true,\"robustResourceInitialization\":true,\"diagnosticReadback\":true,\"expectedRGBA\":[51,102,153,255],\"tolerance\":1}\n", + "stderr": "", + "result": { + "schemaVersion": 1, + "probe": "angle", + "status": "passed", + "hardwareAccelerated": true, + "backend": "metal", + "esMajor": 3, + "adapter": "ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)", + "vendor": "Google Inc. (Apple)", + "driver": "OpenGL ES 3.0 (ANGLE 2.1.1 git hash: 082d85ba19ef)", + "hardwareEvidence": "Explicit ANGLE hardware device on native Metal/D3D11 backend", + "verifiedPixels": 68, + "webglCompatibleContext": true, + "robustResourceInitialization": true, + "diagnosticReadback": true, + "expectedRGBA": [ + 51, + 102, + 153, + 255 + ], + "tolerance": 1 + } + } + ] +} diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/non-gpu-retained-apply.json b/docs/graphics/evidence/2026-09-07-osx-arm64/non-gpu-retained-apply.json new file mode 100644 index 000000000..2734ecbd9 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/non-gpu-retained-apply.json @@ -0,0 +1,31 @@ +{ + "schema": "webscene-native-retained-apply-v1", + "capturedUtc": "2026-09-07T09:43:25.269883+00:00", + "options": { + "layerCount": 4096, + "batchSize": 256, + "iterations": 100, + "samples": 11 + }, + "correctness": { + "retainedOrderConsistent": true + }, + "sparseReplacement": { + "MedianNanosecondsPerOperation": 4213, + "P95NanosecondsPerOperation": 29498, + "MeanNanosecondsPerOperation": 6717.545454545455, + "AllocatedBytesPerOperation": 3616 + }, + "batchReplacement": { + "MedianNanosecondsPerOperation": 593143, + "P95NanosecondsPerOperation": 1411059, + "MeanNanosecondsPerOperation": 868994, + "AllocatedBytesPerOperation": 888848 + }, + "zOrderChange": { + "MedianNanosecondsPerOperation": 5717, + "P95NanosecondsPerOperation": 16610, + "MeanNanosecondsPerOperation": 7002.545454545455, + "AllocatedBytesPerOperation": 3360 + } +} diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/non-gpu-retained-render.json b/docs/graphics/evidence/2026-09-07-osx-arm64/non-gpu-retained-render.json new file mode 100644 index 000000000..3273c0c10 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/non-gpu-retained-render.json @@ -0,0 +1,37 @@ +{ + "schema": "webscene-native-retained-render-v1", + "capturedUtc": "2026-09-07T09:42:51.223761+00:00", + "options": { + "layerCount": 2048, + "visibleLayerCount": 32, + "iterations": 40, + "samples": 11, + "viewportWidth": 320, + "viewportHeight": 240 + }, + "correctness": { + "surfaceClearedBeforeEveryRender": true, + "sparseFrameSha256": "4455EE96B6129573493626BE4B4FD1A46B1A9817323D4E8C10844FC62022ADE1", + "sparseReplacementFrameSha256": "4455EE96B6129573493626BE4B4FD1A46B1A9817323D4E8C10844FC62022ADE1", + "visibleOnlyReferenceSha256": "4455EE96B6129573493626BE4B4FD1A46B1A9817323D4E8C10844FC62022ADE1", + "framesMatch": true + }, + "sparse": { + "MedianNanosecondsPerRender": 12930, + "P95NanosecondsPerRender": 14527.5, + "MeanNanosecondsPerRender": 13138.181818181818, + "AllocatedBytesPerRender": 0 + }, + "dense": { + "MedianNanosecondsPerRender": 399582.5, + "P95NanosecondsPerRender": 406235, + "MeanNanosecondsPerRender": 394638.86363636365, + "AllocatedBytesPerRender": 0 + }, + "sparseWithReplacement": { + "MedianNanosecondsPerRender": 41145, + "P95NanosecondsPerRender": 44545, + "MeanNanosecondsPerRender": 41713.181818181816, + "AllocatedBytesPerRender": 3616 + } +} diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/relocation.json b/docs/graphics/evidence/2026-09-07-osx-arm64/relocation.json new file mode 100644 index 000000000..f8b391d55 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/relocation.json @@ -0,0 +1,60 @@ +{ + "scope": "macOS relocation and native load only", + "status": "passed", + "checks": [ + { + "command": [ + "/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/webscene_dawn_probe", + "metal" + ], + "passed": true, + "exitCode": 0, + "stdout": "{\"schemaVersion\":1,\"probe\":\"dawn\",\"status\":\"passed\",\"hardwareAccelerated\":true,\"backend\":\"metal\",\"adapter\":\"Apple M4\",\"vendor\":\"apple\",\"driver\":\"Metal driver on macOS Version 26.6.2 (Build 25G83)\",\"vendorId\":4203,\"deviceId\":0,\"verifiedPixels\":68,\"expectedRGBA\":[51,102,153,255],\"tolerance\":1,\"diagnosticReadback\":true}\n", + "loaderTrace": "dyld[33985]: <11A85BC9-A6ED-3718-B2D7-BAADC3E1F51C> /private/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/webscene_dawn_probe\ndyld[33985]: <9B672762-7B1F-30BC-96DE-F176B372D66D> /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation\ndyld[33985]: <03BD9E32-CF0A-37B0-898A-3CE8DE06D842> /usr/lib/libobjc.A.dylib\ndyld[33985]: <7D56DA94-31EB-35F0-B886-4010C075E035> /System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal\ndyld[33985]: <91DACE39-FA28-3191-818D-1FCC6A0E615A> /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation\ndyld[33985]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/liboah.dylib\ndyld[33985]: <820D290D-51A0-3064-A1F2-4F0AAF7E6BF4> /usr/lib/libfakelink.dylib\ndyld[33985]: <53A3E31E-06A8-325E-B5A8-316B88AA3C92> /usr/lib/libicucore.A.dylib\ndyld[33985]: <4FED5EE2-5D3E-35B1-A170-9859C4B683BB> /usr/lib/libSystem.B.dylib\ndyld[33985]: <4109E8DD-0A81-310C-B1B3-23B87186D0D8> /System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking\ndyld[33985]: <83794FB3-DE9B-3D23-AB5E-2C1D5D30F134> /usr/lib/swift/libswiftCore.dylib\ndyld[33985]: /usr/lib/libc++abi.dylib\ndyld[33985]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/libRosetta.dylib\ndyld[33985]: /usr/lib/libc++.1.dylib\ndyld[33985]: <4FD234EA-2C18-3C25-8BD0-B1F4805C6675> /usr/lib/swift/libswiftObjectiveC.dylib\ndyld[33985]: <9E3C7597-446F-3C50-9930-2425D9252C0C> /usr/lib/libswiftPrespecialized.dylib\ndyld[33985]: <1479C415-3678-3968-AC77-06373490860E> /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration\ndyld[33985]: <13EDE3A5-A7D9-3FB8-B0C2-2FB7F7272B34> /usr/lib/libz.1.dylib\ndyld[33985]: <54AD73AF-852E-3CD6-8B7D-E73BE79857D3> /System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout\ndyld[33985]: <1A2A9A41-5269-3B0C-BCEE-B446966CE366> /usr/lib/libcmark-gfm.dylib\ndyld[33985]: /usr/lib/libcompression.dylib\ndyld[33985]: <4A3B95C5-AA2E-338C-9398-56895AF82D97> /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork\ndyld[33985]: <332C4B80-5B3C-34E7-AD1F-F6131E607F95> /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration\ndyld[33985]: <0048DB96-1737-3FC5-AF0C-AF784FA24A03> /usr/lib/libarchive.2.dylib\ndyld[33985]: <6CD959AA-4825-306A-864A-BD69EC5F2DC0> /usr/lib/libDiagnosticMessagesClient.dylib\ndyld[33985]: <1E8A4F9E-3954-3458-B3BB-BE97F961C105> /usr/lib/libxml2.2.dylib\ndyld[33985]: <56AE2857-29E0-34E9-B2C3-EE8E951EEFC5> /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices\ndyld[33985]: /usr/lib/liblangid.dylib\ndyld[33985]: <12372585-DF92-33EF-B632-714FAA13260A> /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit\ndyld[33985]: /System/Library/Frameworks/Combine.framework/Versions/A/Combine\ndyld[33985]: <6098453F-4D7E-38B4-8ADC-02C9FF51E14A> /System/Library/PrivateFrameworks/CollectionsInternal.framework/Versions/A/CollectionsInternal\ndyld[33985]: <9A1279D4-575A-3E48-A460-A631A3F82D18> /System/Library/PrivateFrameworks/ReflectionInternal.framework/Versions/A/ReflectionInternal\ndyld[33985]: <6D89CD71-A86D-3D78-A64B-96AB79550F79> /System/Library/PrivateFrameworks/RuntimeInternal.framework/Versions/A/RuntimeInternal\ndyld[33985]: <4975D13C-2AC5-3473-85C0-98054A81D7C6> /usr/lib/swift/libswiftCoreFoundation.dylib\ndyld[33985]: <1DB56DA9-CF6B-3023-ABDF-5A37CB79223C> /usr/lib/swift/libswiftDarwin.dylib\ndyld[33985]: /usr/lib/swift/libswiftDispatch.dylib\ndyld[33985]: <06A92787-4440-3757-AF32-F2B331C753A2> /usr/lib/swift/libswiftIOKit.dylib\ndyld[33985]: <7CD9BDE7-F36B-3471-9295-38E181D6D9E5> /usr/lib/swift/libswiftSystem.dylib\ndyld[33985]: <24AEDAC1-C1EE-30F4-8818-72EBF8969D0C> /usr/lib/swift/libswiftXPC.dylib\ndyld[33985]: <52F59382-A6A6-3F55-8A85-D9FB822D370F> /usr/lib/swift/libswift_Builtin_float.dylib\ndyld[33985]: <8E168857-47F4-349F-A718-A18DB144FCB0> /usr/lib/swift/libswift_Concurrency.dylib\ndyld[33985]: <85246B9A-A757-3F67-B792-3A2F7BB2BB25> /usr/lib/swift/libswift_DarwinFoundation1.dylib\ndyld[33985]: <8DF0116D-DFC9-3906-9DF6-F1DBC47E324B> /usr/lib/swift/libswift_StringProcessing.dylib\ndyld[33985]: /usr/lib/swift/libswiftos.dylib\ndyld[33985]: <1C7E652B-6B94-3180-93A6-EF8DBA3A5448> /System/Library/Frameworks/Network.framework/Versions/A/Network\ndyld[33985]: <4C6139EE-BF87-37A6-B226-830A6FDC36F8> /System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo\ndyld[33985]: <9D0387FC-E8F6-3004-9C95-CA68EA715C8B> /System/Library/Frameworks/Security.framework/Versions/A/Security\ndyld[33985]: <633BCB5F-F063-3D5A-B52A-F72AE236824B> /usr/lib/libbsm.0.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer\ndyld[33985]: <10A4E63B-A1EB-31CC-B3E1-DB4FE115FC84> /System/Library/PrivateFrameworks/BackgroundSystemTasks.framework/Versions/A/BackgroundSystemTasks\ndyld[33985]: /System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics\ndyld[33985]: <7C50137B-2ABD-3819-B033-AE65B05A6085> /System/Library/PrivateFrameworks/CoreWiFi.framework/Versions/A/CoreWiFi\ndyld[33985]: /System/Library/PrivateFrameworks/LoggingSupport.framework/Versions/A/LoggingSupport\ndyld[33985]: <91A461DE-C8E8-3868-B393-BA6E5A17DF2A> /System/Library/PrivateFrameworks/MobileAsset.framework/Versions/A/MobileAsset\ndyld[33985]: /System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog\ndyld[33985]: /System/Library/PrivateFrameworks/Rapport.framework/Versions/A/Rapport\ndyld[33985]: /System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices\ndyld[33985]: <9F52706C-75BD-34AF-A29E-C26608124ACC> /System/Library/Frameworks/SwiftData.framework/Versions/A/SwiftData\ndyld[33985]: <259877CE-4E2C-34A9-A07F-FEE2999D7B2F> /System/Library/PrivateFrameworks/Symptoms.framework/Versions/A/Frameworks/SymptomAnalytics.framework/Versions/A/SymptomAnalytics\ndyld[33985]: /System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers\ndyld[33985]: <4A78C569-FF0D-398B-9C25-33453F0CEC40> /System/Library/PrivateFrameworks/UserManagement.framework/Versions/A/UserManagement\ndyld[33985]: <5BF55637-F306-3D79-B5A1-DB8A871DAD4B> /usr/lib/libboringssl.dylib\ndyld[33985]: <831C79C1-8DBE-31A3-AA4E-8E2B041488D6> /usr/lib/libcupolicy.dylib\ndyld[33985]: <88925A0C-4960-3F6D-AF3A-B1983F7B3D18> /usr/lib/libdns_services.dylib\ndyld[33985]: /usr/lib/libnetworkextension.dylib\ndyld[33985]: <9753F471-40DD-3B9E-9D64-8D07C1B06BC9> /System/Library/Frameworks/NetworkExtension.framework/Versions/A/NetworkExtension\ndyld[33985]: /usr/lib/libnwswifttls.dylib\ndyld[33985]: <6F59933A-6618-33F1-BE52-E7FC3BF7A1EF> /usr/lib/libpcap.A.dylib\ndyld[33985]: <5E89267F-C684-348D-8356-F9DAD8B4CB13> /usr/lib/libquic.dylib\ndyld[33985]: /usr/lib/libusrtcp.dylib\ndyld[33985]: /usr/lib/libMobileGestalt.dylib\ndyld[33985]: /usr/lib/libapple_nghttp2.dylib\ndyld[33985]: <6937D729-7EF4-3972-9E12-694C17C1C1AB> /usr/lib/libcoretls_cfhelpers.dylib\ndyld[33985]: /usr/lib/libsqlite3.dylib\ndyld[33985]: <1617DBB1-2BFF-3619-903C-2FBB31348FB6> /System/Library/PrivateFrameworks/AtomicsInternal.framework/Versions/A/AtomicsInternal\ndyld[33985]: <41F66F01-A342-3091-A832-0B2B645C922B> /System/Library/PrivateFrameworks/InternalSwiftProtobuf.framework/Versions/A/InternalSwiftProtobuf\ndyld[33985]: <2EDB2E62-942F-3AB5-82AF-8E1328544E17> /usr/lib/swift/libswiftDistributed.dylib\ndyld[33985]: /usr/lib/swift/libswiftObservation.dylib\ndyld[33985]: /usr/lib/swift/libswiftSynchronization.dylib\ndyld[33985]: <9CD7B1E1-3E47-339C-A193-2392E3E0ED23> /usr/lib/system/libcache.dylib\ndyld[33985]: <3B110564-5278-3CB0-85F1-2CE8431FF935> /usr/lib/system/libcommonCrypto.dylib\ndyld[33985]: <6FB345CA-7F5C-3263-A23F-143F7539FD8A> /usr/lib/system/libcompiler_rt.dylib\ndyld[33985]: /usr/lib/system/libcopyfile.dylib\ndyld[33985]: <0642DDAD-4771-3C82-805C-E7C6701C1461> /usr/lib/system/libcorecrypto.dylib\ndyld[33985]: /usr/lib/system/libdispatch.dylib\ndyld[33985]: <957F93B3-8805-39C7-9C51-EDD1715F550E> /usr/lib/system/libdyld.dylib\ndyld[33985]: <7E863FCA-F3FF-32C7-8A8C-F983E946AFC3> /usr/lib/system/libkeymgr.dylib\ndyld[33985]: <949131E5-BDA2-39BA-AA50-62651BB51802> /usr/lib/system/libmacho.dylib\ndyld[33985]: /usr/lib/system/libquarantine.dylib\ndyld[33985]: <7460B5AE-469A-36A0-A7EC-6C7D69628E86> /usr/lib/system/libremovefile.dylib\ndyld[33985]: <54439739-33EE-3273-839F-CBA67D7F5CB1> /usr/lib/system/libsystem_asl.dylib\ndyld[33985]: /usr/lib/system/libsystem_blocks.dylib\ndyld[33985]: /usr/lib/system/libsystem_c.dylib\ndyld[33985]: /usr/lib/system/libsystem_collections.dylib\ndyld[33985]: /usr/lib/system/libsystem_configuration.dylib\ndyld[33985]: <14B2A47F-19C8-392F-8FDB-FE8AE375DD41> /usr/lib/system/libsystem_containermanager.dylib\ndyld[33985]: /usr/lib/system/libsystem_coreservices.dylib\ndyld[33985]: <8E07D22E-CE5A-38A0-B091-5B0338C326F5> /usr/lib/system/libsystem_darwin.dylib\ndyld[33985]: <971A4F65-493D-39F3-846D-0D33FA2769FD> /usr/lib/system/libsystem_darwindirectory.dylib\ndyld[33985]: <305F4398-E688-3384-B351-02D865EC8A04> /usr/lib/system/libsystem_dnssd.dylib\ndyld[33985]: <750CA446-92EA-3A56-9A7B-CC0841686C50> /usr/lib/system/libsystem_eligibility.dylib\ndyld[33985]: /usr/lib/system/libsystem_featureflags.dylib\ndyld[33985]: <9B5FB84B-31AD-3EA7-8F89-8C700D369DC8> /usr/lib/system/libsystem_info.dylib\ndyld[33985]: /usr/lib/system/libsystem_m.dylib\ndyld[33985]: /usr/lib/system/libsystem_malloc.dylib\ndyld[33985]: <9C7B1EEB-47BE-3791-93A9-CFC693CB9417> /usr/lib/system/libsystem_networkextension.dylib\ndyld[33985]: <15799128-6CBD-30D6-A2BB-B9D02B4470C0> /usr/lib/system/libsystem_notify.dylib\ndyld[33985]: <54688162-B50D-3D31-A1E8-7B9766D3530D> /usr/lib/system/libsystem_sandbox.dylib\ndyld[33985]: /usr/lib/system/libsystem_sanitizers.dylib\ndyld[33985]: /usr/lib/system/libsystem_secinit.dylib\ndyld[33985]: /usr/lib/system/libsystem_kernel.dylib\ndyld[33985]: /usr/lib/system/libsystem_platform.dylib\ndyld[33985]: /usr/lib/system/libsystem_pthread.dylib\ndyld[33985]: <229122B9-B8B1-3F2F-870E-8650AE3C4FB5> /usr/lib/system/libsystem_symptoms.dylib\ndyld[33985]: <93F1DD8C-6CD9-32B9-B222-D23DA5D161B4> /usr/lib/system/libsystem_trace.dylib\ndyld[33985]: <7194FF5B-A6C5-3D67-B00A-90209F10D603> /usr/lib/system/libsystem_trial.dylib\ndyld[33985]: <05FD0014-55B1-3B8A-A6BA-6C7A389C4123> /usr/lib/system/libunwind.dylib\ndyld[33985]: <33E44C2D-D65E-37A6-B85F-1A4CF524A050> /usr/lib/system/libxpc.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/XPCSupport.framework/Versions/A/XPCSupport\ndyld[33985]: /System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement\ndyld[33985]: /System/Library/PrivateFrameworks/AppleKeyStore.framework/Versions/A/AppleKeyStore\ndyld[33985]: /usr/lib/libCoreEntitlements.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity\ndyld[33985]: <81F4A8BA-C80F-3B53-82E7-57F6928609C5> /System/Library/PrivateFrameworks/CloudServices.framework/Versions/A/CloudServices\ndyld[33985]: <737479F2-7B20-3DB6-B9F4-0DAA1B73E9D0> /System/Library/PrivateFrameworks/SymptomDiagnosticReporter.framework/Versions/A/SymptomDiagnosticReporter\ndyld[33985]: /System/Library/PrivateFrameworks/CrashReporterSupport.framework/Versions/A/CrashReporterSupport\ndyld[33985]: /System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression\ndyld[33985]: <0EAB1F4A-9275-3FED-8EA6-E962ACDDEE5D> /usr/lib/libcoretls.dylib\ndyld[33985]: <7E84FD3B-E90E-317E-AC19-17B70AC809E5> /usr/lib/libpam.2.dylib\ndyld[33985]: /usr/lib/libxar.1.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS\ndyld[33985]: /System/Library/PrivateFrameworks/SwiftASN1Internal.framework/Versions/A/SwiftASN1Internal\ndyld[33985]: /usr/lib/libutil.dylib\ndyld[33985]: <8E04C57D-3651-386E-83D5-4728B732F214> /usr/lib/libenergytrace.dylib\ndyld[33985]: /usr/lib/system/libkxld.dylib\ndyld[33985]: <2BC48182-F354-3AB0-8F18-0C60CAAFE398> /System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer\ndyld[33985]: <5556FD64-9D47-3547-961E-3A27681F3C51> /System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface\ndyld[33985]: <6A4A85F4-3D12-3C4C-85EC-D53D61379F28> /usr/lib/libheimdal-asn1.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce\ndyld[33985]: /System/Library/PrivateFrameworks/OctagonTrust.framework/Versions/A/OctagonTrust\ndyld[33985]: /System/Library/PrivateFrameworks/AppleIDAuthSupport.framework/Versions/A/AppleIDAuthSupport\ndyld[33985]: <9A86DB3F-CC62-3E89-B872-35D04CFFBE42> /System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation\ndyld[33985]: /System/Library/PrivateFrameworks/KeychainCircle.framework/Versions/A/KeychainCircle\ndyld[33985]: <336E2CAC-84D2-34DC-8AE3-7FE688C609EA> /System/Library/PrivateFrameworks/AuthKit.framework/Versions/A/AuthKit\ndyld[33985]: /System/Library/PrivateFrameworks/AAAFoundation.framework/Versions/A/AAAFoundation\ndyld[33985]: /System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag\ndyld[33985]: <79000980-1797-3115-B74B-60FA1E9C3C73> /System/Library/PrivateFrameworks/CorePhoneNumbers.framework/Versions/A/CorePhoneNumbers\ndyld[33985]: <21723046-939E-302F-883C-9DB417452E3A> /System/Library/PrivateFrameworks/MultiverseSupport.framework/Versions/A/MultiverseSupport\ndyld[33985]: <823F3D1A-65F1-3CC5-96B1-750263B8DB36> /System/Library/PrivateFrameworks/RemoteServiceDiscovery.framework/Versions/A/RemoteServiceDiscovery\ndyld[33985]: /System/Library/PrivateFrameworks/DiskManagement.framework/Versions/A/DiskManagement\ndyld[33985]: <5F6B668E-00B2-3BEC-959F-26BD6B50D42B> /System/Library/Frameworks/Accounts.framework/Versions/A/Accounts\ndyld[33985]: /System/Library/PrivateFrameworks/URLFormatting.framework/Versions/A/URLFormatting\ndyld[33985]: <91BDD1F8-831B-3B01-86BA-6BBCB43373C4> /System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary\ndyld[33985]: <885F9C72-1018-368B-AD36-E8A42E87FD91> /System/Library/PrivateFrameworks/RemoteXPC.framework/Versions/A/RemoteXPC\ndyld[33985]: /usr/lib/libFDR.dylib\ndyld[33985]: <24D28E7F-A1AE-3031-8679-A0D6C6D68A86> /usr/lib/libamsupport.dylib\ndyld[33985]: <29367004-5D60-38DB-831F-9E5EE9364B21> /usr/lib/libReverseProxyDevice.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/MSUDataAccessor.framework/Versions/A/MSUDataAccessor\ndyld[33985]: <9594FBFB-D49D-3DF6-8820-564633EAEC2B> /System/Library/PrivateFrameworks/AppleDeviceQuerySupport.framework/Versions/A/AppleDeviceQuerySupport\ndyld[33985]: <44CD8313-2D5B-3A34-BACA-EF8800803B4A> /System/Library/PrivateFrameworks/AOSKit.framework/Versions/A/AOSKit\ndyld[33985]: <5198BFE1-41D2-33D5-A9E0-C63F81A512D3> /System/Library/PrivateFrameworks/AppSSOCore.framework/Versions/A/AppSSOCore\ndyld[33985]: <61B2B917-D14A-38AD-A439-16E1C635441A> /System/Library/PrivateFrameworks/AppSupport.framework/Versions/A/AppSupport\ndyld[33985]: <816EC446-7C41-3A2F-A582-7CB856797C09> /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation\ndyld[33985]: <38C8FBEC-DE88-33FE-B742-A192F22CC754> /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics\ndyld[33985]: /System/Library/PrivateFrameworks/DuetActivityScheduler.framework/Versions/A/DuetActivityScheduler\ndyld[33985]: <0E78989C-854F-3664-AD92-6B7B6D04191C> /System/Library/PrivateFrameworks/FTServices.framework/Versions/A/FTServices\ndyld[33985]: <277D18EF-39E4-3F72-99E8-8D3DF65ED1D0> /System/Library/Frameworks/GSS.framework/Versions/A/GSS\ndyld[33985]: <5ACC6C0E-51E9-3B5A-B24F-89B22D070878> /System/Library/PrivateFrameworks/InternationalSupport.framework/Versions/A/InternationalSupport\ndyld[33985]: /usr/lib/libMemoryResourceException.dylib\ndyld[33985]: <798012E0-3FFC-3B8D-AC74-E7B7DAEA7E66> /System/Library/PrivateFrameworks/NetworkScore.framework/Versions/A/NetworkScore\ndyld[33985]: <2C93123F-99C8-3B8D-AAE6-3A817BE0A2BF> /System/Library/PrivateFrameworks/NetworkServiceProxy.framework/Versions/A/NetworkServiceProxy\ndyld[33985]: /System/Library/PrivateFrameworks/StreamingExtractor.framework/Versions/A/StreamingExtractor\ndyld[33985]: <1F2EDC7B-8F28-3721-8A60-F6E1BCFC29A3> /System/Library/PrivateFrameworks/StreamingZip.framework/Versions/A/StreamingZip\ndyld[33985]: <5DA62AF9-3D46-3D17-A3EB-7026A2F006DF> /System/Library/PrivateFrameworks/SymptomReporter.framework/Versions/A/SymptomReporter\ndyld[33985]: /usr/lib/liblzma.5.dylib\ndyld[33985]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents\ndyld[33985]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore\ndyld[33985]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata\ndyld[33985]: <61677289-93B7-382F-86CA-B856361D293F> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices\ndyld[33985]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit\ndyld[33985]: <435D6243-695B-3543-A722-10106F5696BD> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE\ndyld[33985]: <01579E0C-9D85-3521-8916-4DDC990CD064> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices\ndyld[33985]: <6A26D479-5926-330B-9FB8-9B7A6BE8E239> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices\ndyld[33985]: <297AC970-E432-3BBD-986C-36782634062E> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList\ndyld[33985]: <6508C698-D587-3B5A-B95B-A3A3F78CE122> /usr/lib/libCheckFix.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC\ndyld[33985]: /System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP\ndyld[33985]: <29AA0F7F-26F4-35B3-96DF-8A67B00A58AB> /System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities\ndyld[33985]: <9171DD7D-3994-3963-9A28-BC163BF97DE6> /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate\ndyld[33985]: /usr/lib/libmecab.dylib\ndyld[33985]: <1CA9048E-57DD-30F4-A3E6-FE6E97D5BF82> /usr/lib/libCRFSuite.dylib\ndyld[33985]: <74E55DD6-720D-39E4-897E-EB4328E1946D> /usr/lib/libgermantok.dylib\ndyld[33985]: <92FAD15C-EEA5-34E9-B309-75A1CD1B620B> /usr/lib/libThaiTokenizer.dylib\ndyld[33985]: <2B16DF37-A596-3D8A-AE47-33E580EB1354> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage\ndyld[33985]: <8203944D-B53E-3D7E-A481-3C676CAE1B6A> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib\ndyld[33985]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib\ndyld[33985]: <08508E7B-096D-31AB-9C66-191C877ED62F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/Libraries/libCGInterfaces.dylib\ndyld[33985]: <8945E7B7-12AE-3FF4-AA3B-D4DF9A06FEE7> /System/Library/PrivateFrameworks/AccelerateGPU.framework/Versions/A/AccelerateGPU\ndyld[33985]: <23402175-D2CF-3B08-88D0-AFBBCF775FEF> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib\ndyld[33985]: <086CBEED-2F64-3E75-AB99-8C8C0E0A2F1C> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices\ndyld[33985]: <0616AF41-149E-3F4A-906E-56E2642457BE> /System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo\ndyld[33985]: <873404F1-CC9D-30F9-AE06-8EA58D292005> /System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync\ndyld[33985]: /System/Library/Frameworks/CoreText.framework/Versions/A/CoreText\ndyld[33985]: /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO\ndyld[33985]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS\ndyld[33985]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices\ndyld[33985]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore\ndyld[33985]: <59BBF27B-1D89-3D35-9210-8386EFA15A8D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD\ndyld[33985]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy\ndyld[33985]: <9CDA611B-254A-3779-9356-369485134C2D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis\ndyld[33985]: <0C8F41C6-6D93-3DB3-B522-CA8CFF5C3B33> /System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight\ndyld[33985]: <9E126CE0-FBB2-3B15-953F-CCDC758E34FB> /System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib\ndyld[33985]: <07CF779F-8F51-3764-B486-23D76868FF91> /System/Library/PrivateFrameworks/BiomeLibrary.framework/Versions/A/BiomeLibrary\ndyld[33985]: <959C748F-8851-3A25-BFFA-5FEA80296965> /System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard\ndyld[33985]: /System/Library/PrivateFrameworks/BoardServices.framework/Versions/A/BoardServices\ndyld[33985]: /System/Library/PrivateFrameworks/BackBoardServices.framework/Versions/A/BackBoardServices\ndyld[33985]: <7F763DF9-EA7F-3938-B599-DCCF4605E610> /System/Library/PrivateFrameworks/BackBoardHIDEventFoundation.framework/Versions/A/BackBoardHIDEventFoundation\ndyld[33985]: /System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay\ndyld[33985]: /System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox\ndyld[33985]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders\ndyld[33985]: /System/Library/PrivateFrameworks/ProDisplayLibrary.framework/Versions/A/ProDisplayLibrary\ndyld[33985]: <1E529C1A-B09C-3EB7-A286-CE00E292D561> /System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator\ndyld[33985]: <493E76D9-74D4-333B-A3B2-E5F9BC86429D> /System/Library/Frameworks/Metal.framework/Versions/A/Metal\ndyld[33985]: /System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator\ndyld[33985]: /System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia\ndyld[33985]: /System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient\ndyld[33985]: <98CB7012-30E5-3BDD-8C84-CDBDA9DB3017> /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore\ndyld[33985]: <57F7BB9C-649D-3360-AA86-A502815D77FA> /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport\ndyld[33985]: <625F222D-6394-39B9-A1F2-12B9EA56DD85> /usr/lib/swift/libswiftAccelerate.dylib\ndyld[33985]: /usr/lib/swift/libswiftCoreAudio.dylib\ndyld[33985]: /usr/lib/swift/libswiftCoreMedia.dylib\ndyld[33985]: <7235A6A9-49B2-3B94-9DD6-C987019CDBF2> /usr/lib/swift/libswiftMetal.dylib\ndyld[33985]: <9670AE5C-271A-3DCB-9A0A-8E3A7CCC2726> /usr/lib/swift/libswiftOSLog.dylib\ndyld[33985]: <63444A8C-9E8C-3778-820D-1E0C88CA2DF7> /usr/lib/swift/libswiftQuartzCore.dylib\ndyld[33985]: /usr/lib/swift/libswiftUniformTypeIdentifiers.dylib\ndyld[33985]: <9247A5B6-A883-3A07-BEE7-A223840317A4> /usr/lib/swift/libswiftVideoToolbox.dylib\ndyld[33985]: /usr/lib/swift/libswiftsimd.dylib\ndyld[33985]: <2110407D-EFB4-373E-B963-9C92E26594B2> /System/Library/PrivateFrameworks/BiomeStreams.framework/Versions/A/BiomeStreams\ndyld[33985]: /System/Library/PrivateFrameworks/BiomeStorage.framework/Versions/A/BiomeStorage\ndyld[33985]: <455A5553-E683-30B4-A906-1F14E75F6E61> /System/Library/PrivateFrameworks/BiomeFoundation.framework/Versions/A/BiomeFoundation\ndyld[33985]: /System/Library/PrivateFrameworks/IntelligencePlatformLibrary.framework/Versions/A/IntelligencePlatformLibrary\ndyld[33985]: <42CDC0E6-51BA-3804-BD3E-EDF87FC74034> /System/Library/PrivateFrameworks/PoirotSchematizer.framework/Versions/A/PoirotSchematizer\ndyld[33985]: <2362E209-EC61-3FFC-9486-1244BB29BE82> /System/Library/PrivateFrameworks/BiomeSync.framework/Versions/A/BiomeSync\ndyld[33985]: /System/Library/PrivateFrameworks/BiomeDSL.framework/Versions/A/BiomeDSL\ndyld[33985]: <0F03104F-FC8B-3ADD-8850-4B7029E2B56E> /System/Library/PrivateFrameworks/BiomePubSub.framework/Versions/A/BiomePubSub\ndyld[33985]: <73EE1A0A-0D29-3104-98CB-BEFEDA53F7C0> /System/Library/PrivateFrameworks/ProactiveSupport.framework/Versions/A/ProactiveSupport\ndyld[33985]: /System/Library/PrivateFrameworks/FeatureFlags.framework/Versions/A/FeatureFlags\ndyld[33985]: /System/Library/PrivateFrameworks/PoirotUDFs.framework/Versions/A/PoirotUDFs\ndyld[33985]: /usr/lib/swift/libswift_DarwinFoundation2.dylib\ndyld[33985]: <8D2C31B5-FB10-3BF6-8566-F0DCD56C8582> /usr/lib/swift/libswift_DarwinFoundation3.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime\ndyld[33985]: <858910C5-1D4A-37B7-BF0E-EE02E24A2ACD> /System/Library/PrivateFrameworks/InternationalTextSearch.framework/Versions/A/InternationalTextSearch\ndyld[33985]: <13271AA6-33EA-369B-B2D1-6EC528C820E7> /System/Library/PrivateFrameworks/SoftwareUpdateCoreSupport.framework/Versions/A/SoftwareUpdateCoreSupport\ndyld[33985]: <2CA857AF-D999-34DC-94A1-3AC0E5B80416> /System/Library/PrivateFrameworks/SoftwareUpdateCoreConnect.framework/Versions/A/SoftwareUpdateCoreConnect\ndyld[33985]: /usr/lib/libbootpolicy.dylib\ndyld[33985]: /usr/lib/libpartition2_dynamic.dylib\ndyld[33985]: <9A8926C8-36A6-3DB4-A485-059C1F630984> /usr/lib/libAppleArchive.dylib\ndyld[33985]: <5FFE1FFA-6BD0-32AF-A815-7543731CA763> /usr/lib/libbz2.1.0.dylib\ndyld[33985]: <06728C4D-5750-308F-8290-EAF7BE91F4BB> /System/Library/PrivateFrameworks/OSAnalytics.framework/Versions/A/OSAnalytics\ndyld[33985]: <2FA711C7-F764-363A-BF03-295E0DA88B79> /System/Library/PrivateFrameworks/DeviceRecovery.framework/Versions/A/DeviceRecovery\ndyld[33985]: <59136324-34E6-3367-92BB-659346907A04> /System/Library/PrivateFrameworks/CoreSymbolication.framework/Versions/A/CoreSymbolication\ndyld[33985]: <724D42FC-F4FD-39C7-A1BF-D0AD086231F4> /System/Library/PrivateFrameworks/Symbolication.framework/Versions/A/Symbolication\ndyld[33985]: <7C923545-F3BB-3215-9720-85196358D9F1> /System/Library/PrivateFrameworks/DebugSymbols.framework/Versions/A/DebugSymbols\ndyld[33985]: <566F2D7D-0F3B-3290-A739-7A40A151F0BE> /System/Library/PrivateFrameworks/MallocStackLogging.framework/Versions/A/MallocStackLogging\ndyld[33985]: <7B63C2BF-8C7C-3ECA-ACD9-F1B75DBE018C> /usr/lib/swift/libswift_RegexParser.dylib\ndyld[33985]: <4646F780-1D5E-3EE7-B00A-64619293CC18> /usr/lib/libiconv.2.dylib\ndyld[33985]: <1940124C-0D73-35D2-9D94-A75F116088A0> /usr/lib/libcharset.1.dylib\ndyld[33985]: <24779350-BC29-3465-AAB3-F7CD0DA5844A> /System/Library/PrivateFrameworks/PoirotSQLite.framework/Versions/A/PoirotSQLite\ndyld[33985]: <2091B02D-8D55-3DC4-8097-60C193D03C85> /System/Library/PrivateFrameworks/CascadeSets.framework/Versions/A/CascadeSets\ndyld[33985]: <7F00413A-4D40-3DBF-8FD5-859B23E6DC03> /System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG\ndyld[33985]: /usr/lib/libexpat.1.dylib\ndyld[33985]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib\ndyld[33985]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib\ndyld[33985]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib\ndyld[33985]: <7304F8B3-8E0F-3813-BFAF-9A565CEA0A11> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib\ndyld[33985]: <01AAD3B4-D6BA-36D9-BA6F-D494D2AC161D> /usr/lib/libate.dylib\ndyld[33985]: <8EA6CA42-AA01-3C0F-9672-4917481BAAAE> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib\ndyld[33985]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib\ndyld[33985]: <526C249F-FF2E-3DC4-A639-B41A032E8CCE> /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libllvm-flatbuffers.dylib\ndyld[33985]: <1FDD3B19-C04A-3EE7-B7DF-E1F89954A696> /System/Library/PrivateFrameworks/FramePacing.framework/Versions/A/FramePacing\ndyld[33985]: <2C410B78-B9A5-30DC-8D83-FFEC1277F34C> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libGPUCompilerUtils.dylib\ndyld[33985]: <90CFC86E-833E-3E9F-BAAC-2B61BD750DA6> /System/Library/PrivateFrameworks/CoreDuetContext.framework/Versions/A/CoreDuetContext\ndyld[33985]: <838F99F9-D3FA-335B-9767-B5D04A3FACA6> /System/Library/PrivateFrameworks/CoreDuet.framework/Versions/A/CoreDuet\ndyld[33985]: <712AD9C1-44D2-36F4-BA8E-15038521462B> /System/Library/Frameworks/CoreData.framework/Versions/A/CoreData\ndyld[33985]: <9805BB7B-12C9-39F5-9070-C5B8BFCAE2AF> /System/Library/Frameworks/CoreLocation.framework/Versions/A/CoreLocation\ndyld[33985]: /System/Library/Frameworks/Intents.framework/Versions/A/Intents\ndyld[33985]: /System/Library/Frameworks/_LocationEssentials.framework/Versions/A/_LocationEssentials\ndyld[33985]: <1DAFDDDA-BB7B-320E-BCFC-B7C22886D486> /System/Library/PrivateFrameworks/GeoServices.framework/Versions/A/GeoServices\ndyld[33985]: /System/Library/PrivateFrameworks/LocationSupport.framework/Versions/A/LocationSupport\ndyld[33985]: <515FDCCC-535A-398B-BBD3-3D35565F5423> /System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth\ndyld[33985]: <38EE3C42-06D6-3A46-A420-DF701A4EA911> /System/Library/PrivateFrameworks/GeoServicesCore.framework/Versions/A/GeoServicesCore\ndyld[33985]: <8D0ECDD1-24B6-3B8D-9CF9-CDC55FC64490> /System/Library/PrivateFrameworks/PhoneNumbers.framework/Versions/A/PhoneNumbers\ndyld[33985]: <3D533C35-3A2A-3672-92EF-5FEE9EE739AC> /System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation\ndyld[33985]: <2B5FB7B0-844C-3D84-9EFD-020B285B0F8D> /System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport\ndyld[33985]: <62740FDD-2B16-3319-B5C9-022D45C6B03A> /System/Library/PrivateFrameworks/OSEligibility.framework/Versions/A/OSEligibility\ndyld[33985]: <10C63D59-07BC-3518-87A0-83CAC48D8A70> /System/Library/PrivateFrameworks/IconServices.framework/Versions/A/IconServices\ndyld[33985]: <8EF56F82-8CCE-3811-AD16-6D0939187B45> /System/Library/Frameworks/LightweightCodeRequirements.framework/Versions/A/LightweightCodeRequirements\ndyld[33985]: /System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit\ndyld[33985]: <1946F8FE-0ABC-3F8F-9116-5451ECABD14C> /System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices\ndyld[33985]: /System/Library/PrivateFrameworks/IconFoundation.framework/Versions/A/IconFoundation\ndyld[33985]: /System/Library/PrivateFrameworks/AssistantServices.framework/Versions/A/AssistantServices\ndyld[33985]: <6A34A62A-16D4-34F0-B34B-2D96B53C20AD> /System/Library/PrivateFrameworks/IconRendering.framework/Versions/A/IconRendering\ndyld[33985]: /System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI\ndyld[33985]: <0943679D-FF88-3F18-BE4B-D8B4827AB0B5> /System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage\ndyld[33985]: <968B5A5F-9749-3527-AF2A-66B599785308> /System/Library/PrivateFrameworks/SFSymbols.framework/Versions/A/SFSymbols\ndyld[33985]: /System/Library/Frameworks/DeveloperToolsSupport.framework/Versions/A/DeveloperToolsSupport\ndyld[33985]: <92090A92-DFAF-3EBC-886C-655EC158A53F> /System/Library/PrivateFrameworks/RenderBox.framework/Versions/A/RenderBox\ndyld[33985]: <986D57A7-BFF1-3DAA-8EB1-17CCAA76C731> /System/Library/PrivateFrameworks/CoreSVG.framework/Versions/A/CoreSVG\ndyld[33985]: /System/Library/PrivateFrameworks/TextureIO.framework/Versions/A/TextureIO\ndyld[33985]: /usr/lib/swift/libswiftCoreImage.dylib\ndyld[33985]: <77D85BA0-FE1C-3B5A-92DB-70A30202C990> /System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer\ndyld[33985]: /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL\ndyld[33985]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib\ndyld[33985]: <6CEF3932-AAC9-3F8E-905D-A826F2884C9A> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib\ndyld[33985]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib\ndyld[33985]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib\ndyld[33985]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib\ndyld[33985]: <07CB5D41-C2F3-3C33-951F-67B2C8B8B662> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices\ndyld[33985]: <5D3E7FFF-AC8E-3D6F-8E99-B199E593D270> /System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG\ndyld[33985]: <49E7449E-1385-3B53-94CC-36EFC31E98FE> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib\ndyld[33985]: <23C577A8-DB0B-3A0A-9058-1289483C262A> /usr/lib/libhvf.dylib\ndyld[33985]: <11E757EC-72FB-3C53-8ED7-641428AB6169> /System/Library/PrivateFrameworks/ParsingInternal.framework/Versions/A/ParsingInternal\ndyld[33985]: /System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib\ndyld[33985]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore\ndyld[33985]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage\ndyld[33985]: <199F6401-91D0-36E9-9EA9-D4B44ED1CE3A> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork\ndyld[33985]: <4D134FE3-50EE-39D5-9699-04B4B673DD35> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix\ndyld[33985]: <2E7E2722-3821-3DBF-B25A-6EA45D1A8FD4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector\ndyld[33985]: <3E1FE9EA-34A2-3545-B639-48B1FE1FD3D4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray\ndyld[33985]: <3103E210-FF5C-3677-BDD3-59FF17A6ACEC> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions\ndyld[33985]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSBenchmarkLoop.framework/Versions/A/MPSBenchmarkLoop\ndyld[33985]: <31F90368-23A5-39BB-822B-C8470C4479AE> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSHost.framework/Versions/A/MPSHost\ndyld[33985]: <9C416BB2-0882-315C-AF23-F476E34983BC> /System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools\ndyld[33985]: /System/Library/PrivateFrameworks/IOAccelMemoryInfo.framework/Versions/A/IOAccelMemoryInfo\ndyld[33985]: /System/Library/PrivateFrameworks/kperf.framework/Versions/A/kperf\ndyld[33985]: <03470B3A-A004-39A0-B6A4-F2A4AFFFCDD3> /System/Library/PrivateFrameworks/GPURawCounter.framework/Versions/A/GPURawCounter\ndyld[33985]: <4D8F39C6-B221-3AF1-BB40-CAEB0A174D61> /System/Library/PrivateFrameworks/ASEProcessing.framework/Versions/A/ASEProcessing\ndyld[33985]: /System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing\ndyld[33985]: <1B4C0154-843C-3CEE-9628-22978082DD2D> /System/Library/PrivateFrameworks/SILManager.framework/Versions/A/SILManager\ndyld[33985]: /System/Library/PrivateFrameworks/Tightbeam.framework/Versions/A/Tightbeam\ndyld[33985]: <856ACB2A-3334-3BA6-AAC8-8F344E7CDB83> /usr/lib/swift/libswiftCompression.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/AFKUser.framework/Versions/A/AFKUser\ndyld[33985]: <2186F196-EE17-3A59-B9DA-D6823BEDD35B> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI\ndyld[33985]: <086BB8AD-E317-3FC4-9E44-0D7C6036E8D7> /System/Library/PrivateFrameworks/SAObjects.framework/Versions/A/SAObjects\ndyld[33985]: /System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox\ndyld[33985]: /System/Library/PrivateFrameworks/MediaRemote.framework/Versions/A/MediaRemote\ndyld[33985]: <7F1A25E4-ED0A-3502-AABA-26EDB4A0D2A7> /System/Library/Frameworks/UserNotifications.framework/Versions/A/UserNotifications\ndyld[33985]: <8A5E0FF6-3116-3082-A0AD-20DCD6C5E1B4> /System/Library/PrivateFrameworks/SiriInstrumentation.framework/Versions/A/SiriInstrumentation\ndyld[33985]: <600E036E-9B18-35BE-B40B-E8D2D53AC90D> /System/Library/PrivateFrameworks/SiriAnalytics.framework/Versions/A/SiriAnalytics\ndyld[33985]: <619E6770-766A-3629-9AF8-F32C009375E9> /System/Library/PrivateFrameworks/SiriTTSService.framework/Versions/A/SiriTTSService\ndyld[33985]: <71CAE70A-72AD-3F74-834D-3519B545C08D> /System/Library/PrivateFrameworks/SiriCrossDeviceArbitration.framework/Versions/A/SiriCrossDeviceArbitration\ndyld[33985]: /System/Library/PrivateFrameworks/FeedbackLogger.framework/Versions/A/FeedbackLogger\ndyld[33985]: <55FBCBE4-1032-3017-BA49-D734B82405DF> /System/Library/PrivateFrameworks/FaceTimeNameUtility.framework/Versions/A/FaceTimeNameUtility\ndyld[33985]: /System/Library/PrivateFrameworks/SiriCrossDeviceArbitrationFeedback.framework/Versions/A/SiriCrossDeviceArbitrationFeedback\ndyld[33985]: <368BC882-02B9-38AB-89B4-F62430F2B8EB> /usr/lib/swift/libswiftCoreLocation.dylib\ndyld[33985]: /usr/lib/swift/libswiftAVFoundation.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/UIKitServices.framework/Versions/A/UIKitServices\ndyld[33985]: <54A2CBB8-623D-3629-904A-D0399ED13547> /System/Library/PrivateFrameworks/UnifiedAssetFramework.framework/Versions/A/UnifiedAssetFramework\ndyld[33985]: <8AF1606D-5C93-3B80-BC81-60C5688628E2> /System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore\ndyld[33985]: /System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession\ndyld[33985]: <52BD9E26-B356-3EAA-9AD7-7FF700C61A91> /System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI\ndyld[33985]: <3FF99846-E48C-3C9A-814C-35B45E5F60EC> /usr/lib/libAudioStatistics.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk\ndyld[33985]: /System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio\ndyld[33985]: <75F77FEC-BE14-3C97-93DA-403C3B529D3B> /usr/lib/libAudioToolboxUtility.dylib\ndyld[33985]: <7AE04E20-83FD-3B1B-8846-E9869AD98DB5> /usr/lib/swift/libswiftCoreMIDI.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata\ndyld[33985]: /System/Library/PrivateFrameworks/AudioDSPGraph.framework/Versions/A/AudioDSPGraph\ndyld[33985]: <6108A12D-286B-3CF2-B848-B0E7A0189DCC> /System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy\ndyld[33985]: <655F6374-6CE8-3D0E-994E-4D7C37F78E89> /usr/lib/libSMC.dylib\ndyld[33985]: <912BFF10-FB8F-3D52-9941-ACDCE1CAE36A> /usr/lib/libperfcheck.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/AudioAnalytics.framework/Versions/A/AudioAnalytics\ndyld[33985]: <869F0693-0E82-38C1-8920-C782E71735CA> /System/Library/Frameworks/OSLog.framework/Versions/A/OSLog\ndyld[33985]: <173A632F-20F3-30C1-BC55-EFFE977BBB8E> /usr/lib/libmis.dylib\ndyld[33985]: <52A7AD42-9DE0-393B-A6FB-A7CB6FF8F3A5> /System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience\ndyld[33985]: <1A63E9E1-2D64-3AF4-9CCD-6EF042397F84> /System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore\ndyld[33985]: <04DC06C1-2BFA-3FEE-9429-A33E41721A3E> /usr/lib/libspindump.dylib\ndyld[33985]: <7CC1BC36-42C1-39A3-AA4F-F9C8ABCE0CD5> /System/Library/PrivateFrameworks/AudioAccessoryServices.framework/Versions/A/AudioAccessoryServices\ndyld[33985]: /System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils\ndyld[33985]: /System/Library/PrivateFrameworks/HID.framework/Versions/A/HID\ndyld[33985]: <920D8AA6-CDCD-3E0F-AD66-F0673ACABD3F> /System/Library/PrivateFrameworks/Sharing.framework/Versions/A/Sharing\ndyld[33985]: <3DD8C4CA-23E9-35CF-AD67-549DD72D3344> /System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras\ndyld[33985]: <236517AD-8D16-3E62-8603-EBE7B65ACACA> /System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211\ndyld[33985]: <42F76533-D8DD-3A24-A08A-103C51428797> /System/Library/PrivateFrameworks/IDSFoundation.framework/Versions/A/IDSFoundation\ndyld[33985]: <116D159B-163E-3F91-9591-30FF8B6EB537> /System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Apple80211\ndyld[33985]: /System/Library/Frameworks/CoreWLAN.framework/Versions/A/CoreWLAN\ndyld[33985]: <1ABB6C50-A5DE-3744-8F07-8C3B5617B0B9> /System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth\ndyld[33985]: <82D79BDA-26A0-3A44-AAC8-911411801FDE> /usr/lib/swift/libswiftRegexBuilder.dylib\ndyld[33985]: <41E45E0C-2E88-3605-B213-F7CD760A9FF4> /System/Library/PrivateFrameworks/IMFoundation.framework/Versions/A/IMFoundation\ndyld[33985]: <40F60A3F-90A9-3F07-A99D-005E3158C6F6> /System/Library/PrivateFrameworks/Marco.framework/Versions/A/Marco\ndyld[33985]: <59FFD032-1427-39F6-BC16-A6877582A243> /System/Library/PrivateFrameworks/CommonUtilities.framework/Versions/A/CommonUtilities\ndyld[33985]: <6C3E51F8-D809-3AA1-8695-B75714C2D39A> /System/Library/PrivateFrameworks/Engram.framework/Versions/A/Engram\ndyld[33985]: /System/Library/PrivateFrameworks/XPCDistributed.framework/Versions/A/XPCDistributed\ndyld[33985]: <12066854-2BE4-35DF-BA9F-B38221C980FD> /usr/lib/libtidy.A.dylib\ndyld[33985]: <63F598E2-AF8A-3F29-BE11-3F14DB377A5B> /System/Library/PrivateFrameworks/Bom.framework/Versions/A/Bom\ndyld[33985]: /usr/lib/libParallelCompression.dylib\ndyld[33985]: <9E06CB59-0638-3C9F-B202-264E739433AC> /usr/lib/libIOReport.dylib\ndyld[33985]: <15EEE715-2670-3288-AFA8-504BAD951B4F> /System/Library/PrivateFrameworks/WiFiPeerToPeer.framework/Versions/A/WiFiPeerToPeer\ndyld[33985]: <3854272C-7B14-3A3C-9BB1-F0FBA394708A> /System/Library/PrivateFrameworks/Centauri.framework/Versions/A/Centauri\ndyld[33985]: <946B1484-B180-3451-A452-B54BF5A6D392> /System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon\ndyld[33985]: <2B49C295-4EA2-3DE3-90B4-DC03A96F2657> /usr/lib/libmrc.dylib\ndyld[33985]: <6661265C-7B78-3158-9011-4BFDFFEF7807> /System/Library/PrivateFrameworks/IPConfiguration.framework/Versions/A/IPConfiguration\ndyld[33985]: /System/Library/PrivateFrameworks/Netrb.framework/Versions/A/Netrb\ndyld[33985]: /System/Library/PrivateFrameworks/FrontBoardServices.framework/Versions/A/FrontBoardServices\ndyld[33985]: /System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData\ndyld[33985]: <757FEDFF-841C-3D62-B703-CDE79E929363> /System/Library/PrivateFrameworks/GraphicsServices.framework/Versions/A/GraphicsServices\ndyld[33985]: <093EF25B-5305-3611-B068-E65071858F52> /System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit\ndyld[33985]: /System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory\ndyld[33985]: <3B7FD4C1-D1D4-3DA9-B2F8-3D4094679D76> /System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory\ndyld[33985]: <016C5057-625C-30B1-AD32-7BC9D082F05B> /System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio\ndyld[33985]: /System/Library/Frameworks/AVRouting.framework/Versions/A/AVRouting\ndyld[33985]: <5240B3A0-D035-345E-A636-BC3A92C847C4> /usr/lib/libAccessibility.dylib\ndyld[33985]: <1FB2BCFD-D9FC-385A-A0EA-E2B5052E27F5> /System/Library/PrivateFrameworks/MediaServices.framework/Versions/A/MediaServices\ndyld[33985]: /System/Library/PrivateFrameworks/IDS.framework/Versions/A/IDS\ndyld[33985]: /System/Library/PrivateFrameworks/IsolatedCoreAudioClient.framework/Versions/A/IsolatedCoreAudioClient\ndyld[33985]: <7CC0621B-3B88-3533-A3FB-52E6214486EE> /System/Library/PrivateFrameworks/CoreAudioOrchestration.framework/Versions/A/CoreAudioOrchestration\ndyld[33985]: /System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox\ndyld[33985]: /System/Library/PrivateFrameworks/CoreAVCHD.framework/Versions/A/CoreAVCHD\ndyld[33985]: <74D313A5-4D99-35D1-A4C9-B76AB6457EF0> /System/Library/Frameworks/MediaAccessibility.framework/Versions/A/MediaAccessibility\ndyld[33985]: <87F549F4-73CC-302B-ABDB-D3CCFADABFA9> /System/Library/PrivateFrameworks/Mangrove.framework/Versions/A/Mangrove\ndyld[33985]: <214294AE-C7B7-3C9A-A4F8-201C989F9779> /System/Library/PrivateFrameworks/CMPhoto.framework/Versions/A/CMPhoto\ndyld[33985]: <5F090F48-E481-3737-8E75-362E5D274879> /System/Library/Frameworks/CoreTelephony.framework/Versions/A/CoreTelephony\ndyld[33985]: <9ACFCA55-82CB-33DB-AD00-443576099FDB> /System/Library/PrivateFrameworks/CoreAUC.framework/Versions/A/CoreAUC\ndyld[33985]: <71A0C0AD-67F3-36F9-BF73-6DD5D7424AF7> /System/Library/PrivateFrameworks/AppleJPEGXL.framework/Versions/A/AppleJPEGXL\ndyld[33985]: <825E8416-E246-338E-A5CF-AA81A1B01DD9> /System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport\ndyld[33985]: <7CF84496-675C-3241-B0EF-E83C95F188FA> /System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA\ndyld[33985]: <63A6BBA0-CD50-30F8-9CD2-81B59264EA13> /usr/lib/libTelephonyUtilDynamic.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler\ndyld[33985]: /System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment\ndyld[33985]: /System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay\ndyld[33985]: /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit\ndyld[33985]: /System/Library/PrivateFrameworks/CryptoKitCBridging.framework/Versions/A/CryptoKitCBridging\ndyld[33985]: <714063A8-D81E-3B22-9B36-88948A979E7F> /System/Library/Frameworks/CryptoTokenKit.framework/Versions/A/CryptoTokenKit\ndyld[33985]: <86858734-8B4D-38E6-AAA7-B7A046A7CB2A> /System/Library/Frameworks/LocalAuthentication.framework/Versions/A/LocalAuthentication\ndyld[33985]: /System/Library/PrivateFrameworks/LocalAuthenticationCore.framework/Versions/A/LocalAuthenticationCore\ndyld[33985]: /System/Library/PrivateFrameworks/LocalAuthenticationCredentialServices.framework/Versions/A/LocalAuthenticationCredentialServices\ndyld[33985]: /System/Library/Frameworks/LocalAuthentication.framework/Support/SharedUtils.framework/Versions/A/SharedUtils\ndyld[33985]: <80C3E2D4-B6B8-3C62-B257-27DEEBAD4935> /usr/lib/libcsfde.dylib\ndyld[33985]: <650E155C-1FE3-36ED-8D84-157D380F7F95> /usr/lib/libCoreStorage.dylib\ndyld[33985]: <46DD93AF-BACD-309B-AD51-9CC47C78CA2C> /System/Library/PrivateFrameworks/MediaKit.framework/Versions/A/MediaKit\ndyld[33985]: /System/Library/Frameworks/DiscRecording.framework/Versions/A/DiscRecording\ndyld[33985]: <1F8700BE-BD91-3B94-AC32-A5F10CEFEE35> /System/Library/PrivateFrameworks/ProtectedCloudStorage.framework/Versions/A/ProtectedCloudStorage\ndyld[33985]: /System/Library/PrivateFrameworks/EFILogin.framework/Versions/A/EFILogin\ndyld[33985]: /System/Library/PrivateFrameworks/PersistentConnection.framework/Versions/A/PersistentConnection\ndyld[33985]: <6B0D099C-AC56-35DB-90F1-88A09E587FCB> /System/Library/PrivateFrameworks/SonicFoundation.framework/Versions/A/SonicFoundation\ndyld[33985]: /System/Library/PrivateFrameworks/AsyncAlgorithmsInternal.framework/Versions/A/AsyncAlgorithmsInternal\ndyld[33985]: <2CA62C12-37B5-345A-BF79-5D05F43F6BFB> /System/Library/PrivateFrameworks/FTAWD.framework/Versions/A/FTAWD\ndyld[33985]: <0A1C4D11-C108-35E9-A921-86ED86CF7446> /System/Library/PrivateFrameworks/Dendrite.framework/Versions/A/Dendrite\ndyld[33985]: /usr/lib/libtailspin.dylib\ndyld[33985]: <5FEA8C08-1577-3296-BC9C-7F3203E8EBFB> /System/Library/PrivateFrameworks/Osprey.framework/Versions/A/Osprey\ndyld[33985]: /System/Library/PrivateFrameworks/SiriTTS.framework/Versions/A/SiriTTS\ndyld[33985]: <0C005C4D-CA12-389C-9CCE-C4ED05B187E8> /System/Library/Frameworks/NaturalLanguage.framework/Versions/A/NaturalLanguage\ndyld[33985]: <0E502870-00F4-35D4-AF82-E7059244798E> /System/Library/PrivateFrameworks/GenerativeModels.framework/Versions/A/GenerativeModels\ndyld[33985]: <68474F39-798D-325B-B52F-3DE214F279AE> /System/Library/PrivateFrameworks/SiriPowerInstrumentation.framework/Versions/A/SiriPowerInstrumentation\ndyld[33985]: <5E36265A-7670-3D39-A2B8-71DA0AA131CF> /usr/lib/swift/libswiftNaturalLanguage.dylib\ndyld[33985]: <6794652C-86F0-37EB-838D-483177685E26> /System/Library/PrivateFrameworks/TailspinSymbolication.framework/Versions/A/TailspinSymbolication\ndyld[33985]: <089C1A34-2F4E-3649-94AA-B28A7ECB008B> /System/Library/PrivateFrameworks/Darwinup.framework/Versions/A/Darwinup\ndyld[33985]: /System/Library/PrivateFrameworks/SignpostSupport.framework/Versions/A/SignpostSupport\ndyld[33985]: <8C10B437-C282-37F5-834F-E7179C700373> /System/Library/PrivateFrameworks/FeatureFlagsSupport.framework/Versions/A/FeatureFlagsSupport\ndyld[33985]: /System/Library/PrivateFrameworks/ktrace.framework/Versions/A/ktrace\ndyld[33985]: /System/Library/PrivateFrameworks/SampleAnalysis.framework/Versions/A/SampleAnalysis\ndyld[33985]: <400B0E96-4869-37BE-9832-1A14C386148B> /System/Library/PrivateFrameworks/kperfdata.framework/Versions/A/kperfdata\ndyld[33985]: <7E3E0CF7-905A-3244-A0C9-0ADCC2E16415> /usr/lib/libdscsym.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity\ndyld[33985]: <6FBA9099-E428-3571-B940-0D210B6D0861> /System/Library/PrivateFrameworks/BulkSymbolication.framework/Versions/A/BulkSymbolication\ndyld[33985]: <90E600A3-0A27-348A-AA57-D1DF4FB305E8> /usr/lib/libTLE.dylib\ndyld[33985]: <2D1B971F-6A7F-32D0-8B0F-F8FA3A13E8F1> /System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper\ndyld[33985]: <8F2949A6-43A0-30A2-B5D2-945949C5AA01> /System/Library/PrivateFrameworks/Espresso.framework/Versions/A/Espresso\ndyld[33985]: /System/Library/Frameworks/CoreML.framework/Versions/A/CoreML\ndyld[33985]: /usr/lib/libedit.3.dylib\ndyld[33985]: <465A74BC-F20D-3C05-9441-7E08EAA49FAF> /System/Library/PrivateFrameworks/ANECompiler.framework/Versions/A/ANECompiler\ndyld[33985]: /System/Library/PrivateFrameworks/AppleNeuralEngine.framework/Versions/A/AppleNeuralEngine\ndyld[33985]: <97C5C585-F5EE-323A-B949-69EAE9080871> /System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL\ndyld[33985]: <7401E849-7B2E-39A9-99D3-5CB0A6BBDFFE> /System/Library/Frameworks/MetalPerformanceShadersGraph.framework/Versions/A/MetalPerformanceShadersGraph\ndyld[33985]: /System/Library/PrivateFrameworks/MLCompilerServices.framework/Versions/A/MLCompilerServices\ndyld[33985]: /System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices\ndyld[33985]: <9EB04E94-EE2D-38A5-A214-00AF73DBE4E9> /usr/lib/libncurses.5.4.dylib\ndyld[33985]: /usr/lib/libsandbox.1.dylib\ndyld[33985]: <2F2EF0D7-2FE4-3A5A-8E4C-E1571C8D0C10> /usr/lib/libMatch.1.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/ODIE.framework/Versions/A/ODIE\ndyld[33985]: /System/Library/PrivateFrameworks/MLModelAsset.framework/Versions/A/MLModelAsset\ndyld[33985]: <22B4CD07-5C72-3CA4-9CD1-2C87686CDDE5> /System/Library/PrivateFrameworks/MLCompilerRuntime.framework/Versions/A/MLCompilerRuntime\ndyld[33985]: /System/Library/Frameworks/MLCompute.framework/Versions/A/MLCompute\ndyld[33985]: <6028DD46-8E5A-33F0-93B2-41FA480366CC> /System/Library/PrivateFrameworks/MLAssetIO.framework/Versions/A/MLAssetIO\ndyld[33985]: /usr/lib/swift/libswiftMLCompute.dylib\ndyld[33985]: <067E2603-4FEA-3CA5-8926-45F60681EDDB> /System/Library/PrivateFrameworks/AVFCore.framework/Versions/A/AVFCore\ndyld[33985]: /System/Library/PrivateFrameworks/AVFCapture.framework/Versions/A/AVFCapture\ndyld[33985]: <3B782AC2-00C4-3534-91D2-5C7242B32440> /System/Library/PrivateFrameworks/CMImaging.framework/Versions/A/CMImaging\ndyld[33985]: <1772C40D-6EF4-3F81-BA00-6EE8B05039A6> /System/Library/PrivateFrameworks/Quagga.framework/Versions/A/Quagga\ndyld[33985]: <57A10B70-C3C9-34C6-8D22-F5118B63E2F0> /System/Library/PrivateFrameworks/CMCapture.framework/Versions/A/CMCapture\ndyld[33985]: <1035C1AB-5058-3AFA-8D77-514901516251> /System/Library/Frameworks/CoreMediaIO.framework/Versions/A/CoreMediaIO\ndyld[33985]: /System/Library/PrivateFrameworks/CMCaptureDevice.framework/Versions/A/CMCaptureDevice\ndyld[33985]: <1F873909-B3B8-3D55-9673-9AFA86BB085B> /System/Library/PrivateFrameworks/CoreBrightness.framework/Versions/A/CoreBrightness\ndyld[33985]: /System/Library/PrivateFrameworks/CinematicFraming.framework/Versions/A/CinematicFraming\ndyld[33985]: <882BC08E-B1E1-3E52-AE8A-AC22A1BF2BE8> /System/Library/PrivateFrameworks/ModelManagerServices.framework/Versions/A/ModelManagerServices\ndyld[33985]: <3E83115F-D04B-3C8D-8646-35204AA2DB84> /System/Library/PrivateFrameworks/CPMS.framework/Versions/A/CPMS\ndyld[33985]: /System/Library/PrivateFrameworks/SystemStatus.framework/Versions/A/SystemStatus\ndyld[33985]: <2E109991-45C6-3783-8A36-B6A8070AAD67> /System/Library/Frameworks/CoreMotion.framework/Versions/A/CoreMotion\ndyld[33985]: /System/Library/PrivateFrameworks/TimeSync.framework/Versions/A/TimeSync\ndyld[33985]: <9B3D4CA3-7BCF-36C9-AA99-27BDFE7854CD> /System/Library/PrivateFrameworks/DistributedSensing.framework/Versions/A/DistributedSensing\ndyld[33985]: /System/Library/PrivateFrameworks/MobileBluetooth.framework/Versions/A/MobileBluetooth\ndyld[33985]: <0BAB3589-8D81-3C60-9F05-9D207E89F4B6> /System/Library/PrivateFrameworks/IOKitten.framework/Versions/A/IOKitten\ndyld[33985]: <8A2C8C17-E138-3B34-8643-ED4FB1C9049E> /System/Library/PrivateFrameworks/LocationLogEncryption.framework/Versions/A/LocationLogEncryption\ndyld[33985]: <05EC9C98-7211-39C9-B376-796F37327801> /System/Library/PrivateFrameworks/AppleIntelligenceReporting.framework/Versions/A/AppleIntelligenceReporting\ndyld[33985]: <47AAECAD-C28C-352E-BB86-7F292E0BFBC6> /System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji\ndyld[33985]: <327536E3-A27C-38C2-A67F-D6488D04CCEE> /System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling\ndyld[33985]: <95BA357E-906A-3183-A402-A41D486B5AB3> /System/Library/PrivateFrameworks/Montreal.framework/Versions/A/Montreal\ndyld[33985]: /usr/lib/libcmph.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/GenerativeModelsFoundation.framework/Versions/A/GenerativeModelsFoundation\ndyld[33985]: /System/Library/PrivateFrameworks/TokenGeneration.framework/Versions/A/TokenGeneration\ndyld[33985]: <418985BB-52A3-34D4-8379-40DC4C63AA32> /System/Library/PrivateFrameworks/GenerativeFunctions.framework/Versions/A/GenerativeFunctions\ndyld[33985]: <63FD423F-836C-3034-BA48-100AE09A9140> /System/Library/PrivateFrameworks/GenerativeFunctionsFoundation.framework/Versions/A/GenerativeFunctionsFoundation\ndyld[33985]: /System/Library/PrivateFrameworks/ModelCatalog.framework/Versions/A/ModelCatalog\ndyld[33985]: <67C3B698-8279-30F1-9167-4730E6F41F5A> /System/Library/PrivateFrameworks/SensitiveContentAnalysisML.framework/Versions/A/SensitiveContentAnalysisML\ndyld[33985]: /System/Library/PrivateFrameworks/GenerativeFunctionsInstrumentation.framework/Versions/A/GenerativeFunctionsInstrumentation\ndyld[33985]: /System/Library/PrivateFrameworks/PromptKit.framework/Versions/A/PromptKit\ndyld[33985]: <12245228-2B9A-3B24-8C5E-10111D68BE65> /System/Library/PrivateFrameworks/ProactiveDaemonSupport.framework/Versions/A/ProactiveDaemonSupport\ndyld[33985]: <3166486F-3F65-31DB-8018-779FFA32DC71> /System/Library/PrivateFrameworks/TokenGenerationCore.framework/Versions/A/TokenGenerationCore\ndyld[33985]: <53B3126E-7B01-30DD-961A-510E9CFC3CF1> /System/Library/PrivateFrameworks/Trial.framework/Versions/A/Trial\ndyld[33985]: /System/Library/PrivateFrameworks/TrialProto.framework/Versions/A/TrialProto\ndyld[33985]: /System/Library/PrivateFrameworks/AppleFlatBuffers.framework/Versions/A/AppleFlatBuffers\ndyld[33985]: <642E3357-AB6D-3039-A818-EDB5D6A189C2> /System/Library/PrivateFrameworks/SentencePieceInternal.framework/Versions/A/SentencePieceInternal\ndyld[33985]: <10F83439-3A9F-316B-992E-451A72876715> /System/Library/Frameworks/Vision.framework/Versions/A/Vision\ndyld[33985]: /System/Library/PrivateFrameworks/CoreSceneUnderstanding.framework/Versions/A/CoreSceneUnderstanding\ndyld[33985]: <4E70B4ED-C8E0-3636-80E4-0939FE56BB63> /System/Library/PrivateFrameworks/VisionCore.framework/Versions/A/VisionCore\ndyld[33985]: /System/Library/PrivateFrameworks/DataDetectorsCore.framework/Versions/A/DataDetectorsCore\ndyld[33985]: /System/Library/Frameworks/Vision.framework/libfaceCore.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/Futhark.framework/Versions/A/Futhark\ndyld[33985]: /System/Library/PrivateFrameworks/InertiaCam.framework/Versions/A/InertiaCam\ndyld[33985]: /System/Library/PrivateFrameworks/TextRecognition.framework/Versions/A/TextRecognition\ndyld[33985]: <73F6F860-69AF-3162-86E0-A683642287D6> /System/Library/Frameworks/DataDetection.framework/Versions/A/DataDetection\ndyld[33985]: <1D5DF9CA-41FC-3B7B-B19F-2C435F3A66F2> /System/Library/PrivateFrameworks/TextInput.framework/Versions/A/TextInput\ndyld[33985]: /System/Library/PrivateFrameworks/CVNLP.framework/Versions/A/CVNLP\ndyld[33985]: <58AC6CAB-5B91-367F-932F-BD0939BBD125> /System/Library/PrivateFrameworks/IntentsFoundation.framework/Versions/A/IntentsFoundation\ndyld[33985]: <27479D70-8BF6-3D3C-B528-1BB9B1B98391> /System/Library/PrivateFrameworks/ApplePushService.framework/Versions/A/ApplePushService\ndyld[33985]: <676D50CC-8455-3267-B8E8-CA31B8EF8F91> /System/Library/Frameworks/CloudKit.framework/Versions/A/CloudKit\ndyld[33985]: /System/Library/PrivateFrameworks/CoreDuetDaemonProtocol.framework/Versions/A/CoreDuetDaemonProtocol\ndyld[33985]: <962EA390-008E-3DDC-B2A5-7B2DAF6E8786> /System/Library/PrivateFrameworks/DeviceIdentity.framework/Versions/A/DeviceIdentity\ndyld[33985]: /System/Library/Frameworks/SharedWithYouCore.framework/Versions/A/SharedWithYouCore\ndyld[33985]: <121798D0-5254-3547-8F96-0F2AF8D84250> /System/Library/PrivateFrameworks/CloudTelemetry.framework/Versions/A/CloudTelemetry\ndyld[33985]: <768DFB9E-7FB3-3998-A3AF-BEF0C6C740A7> /System/Library/PrivateFrameworks/AppleAccount.framework/Versions/A/AppleAccount\ndyld[33985]: /System/Library/PrivateFrameworks/CacheDelete.framework/Versions/A/CacheDelete\ndyld[33985]: /System/Library/PrivateFrameworks/C2.framework/Versions/A/C2\ndyld[33985]: <23871A43-55FD-3D0C-B29F-B143A64D1D8D> /System/Library/PrivateFrameworks/CloudCoreInternal.framework/Versions/A/CloudCoreInternal\ndyld[33985]: /System/Library/PrivateFrameworks/CloudAsset.framework/Versions/A/CloudAsset\ndyld[33985]: <36607924-B1B2-39ED-B6D1-29683EFB67A0> /System/Library/Frameworks/PushKit.framework/Versions/A/PushKit\ndyld[33985]: <26865685-385E-3120-9886-2082EEC20B20> /System/Library/Frameworks/CoreTransferable.framework/Versions/A/CoreTransferable\ndyld[33985]: <5EA68C5E-69B0-3011-9D66-AEF49D82B29D> /System/Library/Frameworks/FileProvider.framework/Versions/A/FileProvider\ndyld[33985]: /System/Library/PrivateFrameworks/GenerationalStorage.framework/Versions/A/GenerationalStorage\ndyld[33985]: /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/DesktopServicesPriv\ndyld[33985]: <024DBF34-DF66-3164-825E-F77F85462E66> /System/Library/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth\ndyld[33985]: <87907862-52FF-3F24-AC29-7C1678BCD277> /System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport\ndyld[33985]: /System/Library/PrivateFrameworks/CloudTelemetryTools.framework/Versions/A/CloudTelemetryTools\ndyld[33985]: /System/Library/PrivateFrameworks/CloudTelemetryShared.dylib\ndyld[33985]: <7BEBC9F1-212D-37F4-B601-A7AAD12F7225> /System/Library/PrivateFrameworks/RTCReporting.framework/Versions/A/RTCReporting\ndyld[33985]: <7A222E30-8DD4-3B1D-B820-4EA33B797525> /System/Library/PrivateFrameworks/AAAFoundationSwift.framework/Versions/A/AAAFoundationSwift\ndyld[33985]: <9D724BE7-0B01-39F3-82BE-BCEDC6EBAC8A> /System/Library/PrivateFrameworks/AppleIDSSOAuthentication.framework/Versions/A/AppleIDSSOAuthentication\ndyld[33985]: <659AFBBD-E22E-3474-BCFE-298DA57B1464> /System/Library/PrivateFrameworks/UIFoundation.framework/Versions/A/UIFoundation\ndyld[33985]: <0368EA7D-01B2-3AA9-A6D6-A2A0850AC800> /System/Library/PrivateFrameworks/HIDDisplay.framework/Versions/A/HIDDisplay\ndyld[33985]: <6A5A8E21-A9E6-32A2-9BDB-8013F002AEF8> /usr/lib/libcups.2.dylib\ndyld[33985]: /System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos\ndyld[33985]: <4AB71911-9300-30D4-88CF-D20EFD75ACE6> /usr/lib/libresolv.9.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal\ndyld[33985]: <0CB2E7E3-E96F-343B-A4E7-545E74AF0255> /System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib\ndyld[33985]: <097F7235-CA53-3644-BB95-F6F912B4F2C7> /System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth\ndyld[33985]: /System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities\ndyld[33985]: /System/Library/PrivateFrameworks/AttributeGraph.framework/Versions/A/AttributeGraph\ndyld[33985]: /usr/lib/libAXSafeCategoryBundle.dylib\ndyld[33985]: /System/Library/Frameworks/TabularData.framework/Versions/A/TabularData\ndyld[33985]: <841D5662-2CB9-3A27-ADA7-E33AC5E45199> /System/Library/PrivateFrameworks/ArgumentParserInternal.framework/Versions/A/ArgumentParserInternal\ndyld[33985]: <4C851329-A9F4-3E9E-9E48-07FF4120DCF9> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib\ndyld[33985]: <5015CD96-C046-364D-AAE3-1F439044468B> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib\ndyld[33985]: <407BCF3E-A91F-3A7F-8B8C-DBB8E807990F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib\ndyld[33985]: <669ABE12-838F-3F14-8456-D60DE5DF8EB8> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib\ndyld[33985]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib\ndyld[33985]: <54A103BA-7D04-32DB-B204-179E2E0290CA> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib\ndyld[33985]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib\ndyld[33985]: <1ACDAA8A-EB43-37C7-B661-39B1C0E05290> /System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary\ndyld[33985]: <12479A32-B72F-3A09-BB03-BA56C37853B5> /System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore\ndyld[33985]: /usr/lib/libapp_launch_measurement.dylib\ndyld[33985]: <4F3BEA3B-A363-3D04-B903-9B613C993CA1> /System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices\ndyld[33985]: <6C426EA5-7F1E-333E-BB5D-74465EFED12B> /usr/lib/libxslt.1.dylib\ndyld[33985]: <627D64D5-2D3C-3EC6-B4AB-FEF4DEE40871> /System/Library/PrivateFrameworks/FindMyDevice.framework/Versions/A/FindMyDevice\ndyld[33985]: <930F9F83-A947-3788-9FBA-49872FC3AF8D> /System/Library/PrivateFrameworks/FMCoreLite.framework/Versions/A/FMCoreLite\ndyld[33985]: <5F356BA6-47B5-382B-B54A-1550BB138A62> /System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement\ndyld[33985]: <38DAF669-429F-384F-87D6-8550842EEB5E> /System/Library/PrivateFrameworks/CryptoKitPrivate.framework/Versions/A/CryptoKitPrivate\ndyld[33985]: <5B73C216-2ACE-3F8C-A2B3-7D35D5D0395A> /System/Library/PrivateFrameworks/CaptiveNetwork.framework/Versions/A/CaptiveNetwork\ndyld[33985]: /System/Library/PrivateFrameworks/EAP8021X.framework/Versions/A/EAP8021X\ndyld[33985]: <36F215D1-A2C0-32CA-ADD8-6D85AB48A772> /System/Library/Frameworks/QuickLookThumbnailing.framework/Versions/A/QuickLookThumbnailing\ndyld[33985]: /System/Library/PrivateFrameworks/DiskImages.framework/Versions/A/DiskImages\ndyld[33985]: <49121861-2603-3B0A-B664-BAD9E729BE5D> /System/Library/Frameworks/NetFS.framework/Versions/A/NetFS\ndyld[33985]: <2E99AD96-DC1C-3643-9988-273AB6844EFC> /usr/lib/libcurl.4.dylib\ndyld[33985]: <46D13DA8-E7BD-37DC-91DD-D5E6CE00C2B8> /usr/lib/libcrypto.46.dylib\ndyld[33985]: <07D5F4C6-1A13-344C-882B-0B0A08048DE5> /usr/lib/libssl.48.dylib\ndyld[33985]: <8CABDD64-E6C6-3B77-B839-2E2B875CE0FE> /System/Library/Frameworks/LDAP.framework/Versions/A/LDAP\ndyld[33985]: <96C0BAAA-7FE6-3277-AFBC-31926F5935EE> /System/Library/PrivateFrameworks/TrustEvaluationAgent.framework/Versions/A/TrustEvaluationAgent\ndyld[33985]: <7CF2A32E-72DD-34F7-B179-A17ED3D7DD75> /usr/lib/libsasl2.2.dylib\ndyld[33985]: /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa\ndyld[33985]: /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit\ndyld[33985]: <4840B78C-D96B-35B9-85C7-E5889C44A7C4> /System/Library/PrivateFrameworks/CollectionViewCore.framework/Versions/A/CollectionViewCore\ndyld[33985]: /System/Library/PrivateFrameworks/XCTTargetBootstrap.framework/Versions/A/XCTTargetBootstrap\ndyld[33985]: <0D6F3043-5372-3B15-96BC-6F45AD86F410> /System/Library/PrivateFrameworks/UserActivity.framework/Versions/A/UserActivity\ndyld[33985]: <7CDC68D4-0845-3053-AB80-C5A1F354060F> /System/Library/PrivateFrameworks/FrontBoard.framework/Versions/A/FrontBoard\ndyld[33985]: /System/Library/PrivateFrameworks/UIIntelligenceSupport.framework/Versions/A/UIIntelligenceSupport\ndyld[33985]: <9EB0840F-B045-3529-9467-1470A3C6CA02> /System/Library/Frameworks/SwiftUICore.framework/Versions/A/SwiftUICore\ndyld[33985]: <84FB5635-42EE-3BB5-B7CD-8A354AD7DB0A> /System/Library/PrivateFrameworks/WritingTools.framework/Versions/A/WritingTools\ndyld[33985]: <6ECD36F7-0A2E-3631-A57F-FD0152173454> /System/Library/PrivateFrameworks/WindowManagement.framework/Versions/A/WindowManagement\ndyld[33985]: /System/Library/PrivateFrameworks/FocusEngine.framework/Versions/A/FocusEngine\ndyld[33985]: <2BC041DF-695A-32EE-B164-1C35C2AFFC53> /System/Library/PrivateFrameworks/DesignLibrary.framework/Versions/A/DesignLibrary\ndyld[33985]: /System/Library/PrivateFrameworks/DFRFoundation.framework/Versions/A/DFRFoundation\ndyld[33985]: <365E81B9-B9BA-3F4F-83FD-86A35CFBC8AC> /System/Library/PrivateFrameworks/UpdateCycle.framework/Versions/A/UpdateCycle\ndyld[33985]: <38408482-CE3B-359E-9465-7FEB4BB79B54> /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox\ndyld[33985]: /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/SpeechRecognition.framework/Versions/A/SpeechRecognition\ndyld[33985]: <74C54353-A613-35A2-857A-737BB48906F9> /System/Library/PrivateFrameworks/PerformanceAnalysis.framework/Versions/A/PerformanceAnalysis\ndyld[33985]: <22008BA9-C61B-3FAD-A1A0-F6A1CD220343> /System/Library/Frameworks/Accessibility.framework/Versions/A/Accessibility\ndyld[33985]: <028E944B-66C4-39E2-A436-FB93FB6CED4E> /System/Library/Frameworks/Symbols.framework/Versions/A/Symbols\ndyld[33985]: /System/Library/PrivateFrameworks/Gestures.framework/Versions/A/Gestures\ndyld[33985]: <713AEF7A-43B6-3735-8AB5-361F5EE06BBA> /usr/lib/swift/libswiftSpatial.dylib\ndyld[33985]: /usr/lib/swift/libswiftCoreGraphics.dylib\ndyld[33985]: <14A11A94-6A52-3D24-9267-42EDAEEC5FDD> /usr/lib/swift/libswiftFoundation.dylib\ndyld[33985]: <76A7FE10-AD26-3505-A8CC-D37AC1C24D32> /usr/lib/swift/libswiftSwiftOnoneSupport.dylib\ndyld[33985]: <4B5C0268-23EB-3E20-8F57-CDECFB6E3205> /usr/lib/swift/libswiftsys_time.dylib\ndyld[33985]: /System/Library/PrivateFrameworks/CoreMaterial.framework/Versions/A/CoreMaterial\ndyld[33985]: <68B7C15F-537C-3FEF-838B-DC548772A45F> /usr/lib/libSpatial.dylib\ndyld[33985]: <183FD4D6-D766-34FC-B8E1-7C4D17435AC3> /System/Library/SubFrameworks/UIUtilities.framework/Versions/A/UIUtilities\ndyld[33985]: move loaded to delayed: libcmark-gfm.dylib\ndyld[33985]: move loaded to delayed: BackgroundSystemTasks\ndyld[33985]: move loaded to delayed: SymptomAnalytics\ndyld[33985]: move loaded to delayed: libcupolicy.dylib\ndyld[33985]: move loaded to delayed: libnetworkextension.dylib\ndyld[33985]: move loaded to delayed: NetworkExtension\ndyld[33985]: move loaded to delayed: libnwswifttls.dylib\ndyld[33985]: move loaded to delayed: libpcap.A.dylib\ndyld[33985]: move loaded to delayed: XPCSupport\ndyld[33985]: move loaded to delayed: CloudServices\ndyld[33985]: move loaded to delayed: OctagonTrust\ndyld[33985]: move loaded to delayed: AppleIDAuthSupport\ndyld[33985]: move loaded to delayed: KeychainCircle\ndyld[33985]: move loaded to delayed: AuthKit\ndyld[33985]: move loaded to delayed: AAAFoundation\ndyld[33985]: move loaded to delayed: MultiverseSupport\ndyld[33985]: move loaded to delayed: DiskManagement\ndyld[33985]: move loaded to delayed: URLFormatting\ndyld[33985]: move loaded to delayed: AOSKit\ndyld[33985]: move loaded to delayed: AppSSOCore\ndyld[33985]: move loaded to delayed: DuetActivityScheduler\ndyld[33985]: move loaded to delayed: FTServices\ndyld[33985]: move loaded to delayed: libMemoryResourceException.dylib\ndyld[33985]: move loaded to delayed: NetworkScore\ndyld[33985]: move loaded to delayed: NetworkServiceProxy\ndyld[33985]: move loaded to delayed: StreamingExtractor\ndyld[33985]: move loaded to delayed: SymptomReporter\ndyld[33985]: move loaded to delayed: libCGInterfaces.dylib\ndyld[33985]: move loaded to delayed: AccelerateGPU\ndyld[33985]: move loaded to delayed: CoreDuetContext\ndyld[33985]: move loaded to delayed: CoreDuet\ndyld[33985]: move loaded to delayed: CoreLocation\ndyld[33985]: move loaded to delayed: Intents\ndyld[33985]: move loaded to delayed: AssistantServices\ndyld[33985]: move loaded to delayed: SAObjects\ndyld[33985]: move loaded to delayed: MediaRemote\ndyld[33985]: move loaded to delayed: SiriTTSService\ndyld[33985]: move loaded to delayed: SiriCrossDeviceArbitration\ndyld[33985]: move loaded to delayed: FaceTimeNameUtility\ndyld[33985]: move loaded to delayed: SiriCrossDeviceArbitrationFeedback\ndyld[33985]: move loaded to delayed: libswiftCoreLocation.dylib\ndyld[33985]: move loaded to delayed: UIKitServices\ndyld[33985]: move loaded to delayed: AudioDSPGraph\ndyld[33985]: move loaded to delayed: AudioAccessoryServices\ndyld[33985]: move loaded to delayed: Sharing\ndyld[33985]: move loaded to delayed: IDSFoundation\ndyld[33985]: move loaded to delayed: Apple80211\ndyld[33985]: move loaded to delayed: CoreWLAN\ndyld[33985]: move loaded to delayed: IMFoundation\ndyld[33985]: move loaded to delayed: Marco\ndyld[33985]: move loaded to delayed: CommonUtilities\ndyld[33985]: move loaded to delayed: Engram\ndyld[33985]: move loaded to delayed: XPCDistributed\ndyld[33985]: move loaded to delayed: libtidy.A.dylib\ndyld[33985]: move loaded to delayed: Bom\ndyld[33985]: move loaded to delayed: libParallelCompression.dylib\ndyld[33985]: move loaded to delayed: MediaServices\ndyld[33985]: move loaded to delayed: IDS\ndyld[33985]: move loaded to delayed: LocalAuthentication\ndyld[33985]: move loaded to delayed: LocalAuthenticationCore\ndyld[33985]: move loaded to delayed: LocalAuthenticationCredentialServices\ndyld[33985]: move loaded to delayed: SharedUtils\ndyld[33985]: move loaded to delayed: libcsfde.dylib\ndyld[33985]: move loaded to delayed: libCoreStorage.dylib\ndyld[33985]: move loaded to delayed: ProtectedCloudStorage\ndyld[33985]: move loaded to delayed: EFILogin\ndyld[33985]: move loaded to delayed: PersistentConnection\ndyld[33985]: move loaded to delayed: SonicFoundation\ndyld[33985]: move loaded to delayed: AsyncAlgorithmsInternal\ndyld[33985]: move loaded to delayed: FTAWD\ndyld[33985]: move loaded to delayed: libtailspin.dylib\ndyld[33985]: move loaded to delayed: Osprey\ndyld[33985]: move loaded to delayed: SiriTTS\ndyld[33985]: move loaded to delayed: SiriPowerInstrumentation\ndyld[33985]: move loaded to delayed: TailspinSymbolication\ndyld[33985]: move loaded to delayed: Darwinup\ndyld[33985]: move loaded to delayed: SignpostSupport\ndyld[33985]: move loaded to delayed: FeatureFlagsSupport\ndyld[33985]: move loaded to delayed: ktrace\ndyld[33985]: move loaded to delayed: SampleAnalysis\ndyld[33985]: move loaded to delayed: kperfdata\ndyld[33985]: move loaded to delayed: libdscsym.dylib\ndyld[33985]: move loaded to delayed: BulkSymbolication\ndyld[33985]: move loaded to delayed: IntentsFoundation\ndyld[33985]: move loaded to delayed: ApplePushService\ndyld[33985]: move loaded to delayed: CloudKit\ndyld[33985]: move loaded to delayed: CoreDuetDaemonProtocol\ndyld[33985]: move loaded to delayed: DeviceIdentity\ndyld[33985]: move loaded to delayed: SharedWithYouCore\ndyld[33985]: move loaded to delayed: CloudTelemetry\ndyld[33985]: move loaded to delayed: AppleAccount\ndyld[33985]: move loaded to delayed: CacheDelete\ndyld[33985]: move loaded to delayed: C2\ndyld[33985]: move loaded to delayed: CloudCoreInternal\ndyld[33985]: move loaded to delayed: CloudAsset\ndyld[33985]: move loaded to delayed: PushKit\ndyld[33985]: move loaded to delayed: FileProvider\ndyld[33985]: move loaded to delayed: GenerationalStorage\ndyld[33985]: move loaded to delayed: DesktopServicesPriv\ndyld[33985]: move loaded to delayed: CloudTelemetryTools\ndyld[33985]: move loaded to delayed: CloudTelemetryShared.dylib\ndyld[33985]: move loaded to delayed: RTCReporting\ndyld[33985]: move loaded to delayed: AAAFoundationSwift\ndyld[33985]: move loaded to delayed: AppleIDSSOAuthentication\ndyld[33985]: move loaded to delayed: FindMyDevice\ndyld[33985]: move loaded to delayed: FMCoreLite\ndyld[33985]: move loaded to delayed: ServiceManagement\ndyld[33985]: move loaded to delayed: CryptoKitPrivate\ndyld[33985]: move loaded to delayed: CaptiveNetwork\ndyld[33985]: move loaded to delayed: EAP8021X\ndyld[33985]: move loaded to delayed: QuickLookThumbnailing\ndyld[33985]: <9B0A2398-8610-35D6-B7F3-B76933F2AAF4> /System/Library/Extensions/AGXMetalG16G_B0.bundle/Contents/MacOS/AGXMetalG16G_B0\ndyld[33985]: <9235E8EC-599D-386F-8A8A-6B6A92D33369> /System/Library/PrivateFrameworks/IOGPU.framework/Versions/A/IOGPU\n" + }, + { + "command": [ + "/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/webscene_angle_probe", + "metal", + "2" + ], + "passed": true, + "exitCode": 0, + "stdout": "{\"schemaVersion\":1,\"probe\":\"angle\",\"status\":\"passed\",\"hardwareAccelerated\":true,\"backend\":\"metal\",\"esMajor\":2,\"adapter\":\"ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)\",\"vendor\":\"Google Inc. (Apple)\",\"driver\":\"OpenGL ES 2.0 (ANGLE 2.1.1 git hash: 082d85ba19ef)\",\"hardwareEvidence\":\"Explicit ANGLE hardware device on native Metal/D3D11 backend\",\"verifiedPixels\":68,\"webglCompatibleContext\":true,\"robustResourceInitialization\":true,\"diagnosticReadback\":true,\"expectedRGBA\":[51,102,153,255],\"tolerance\":1}\n", + "loaderTrace": "dyld[33987]: <4A19C6E0-06A8-3EFB-8467-594471CDCC90> /private/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/webscene_angle_probe\ndyld[33987]: <4C4C44AF-5555-3144-A120-3E4412E00745> /private/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/libEGL.dylib\ndyld[33987]: <4C4C4498-5555-3144-A17E-05A77F85C5AE> /private/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/libGLESv2.dylib\ndyld[33987]: <493E76D9-74D4-333B-A3B2-E5F9BC86429D> /System/Library/Frameworks/Metal.framework/Versions/A/Metal\ndyld[33987]: <6CD959AA-4825-306A-864A-BD69EC5F2DC0> /usr/lib/libDiagnosticMessagesClient.dylib\ndyld[33987]: <526C249F-FF2E-3DC4-A639-B41A032E8CCE> /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libllvm-flatbuffers.dylib\ndyld[33987]: <12372585-DF92-33EF-B632-714FAA13260A> /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit\ndyld[33987]: /System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator\ndyld[33987]: <5FFE1FFA-6BD0-32AF-A815-7543731CA763> /usr/lib/libbz2.1.0.dylib\ndyld[33987]: <1FDD3B19-C04A-3EE7-B7DF-E1F89954A696> /System/Library/PrivateFrameworks/FramePacing.framework/Versions/A/FramePacing\ndyld[33987]: /usr/lib/libMobileGestalt.dylib\ndyld[33987]: <56AE2857-29E0-34E9-B2C3-EE8E951EEFC5> /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices\ndyld[33987]: /usr/lib/libcompression.dylib\ndyld[33987]: <2C410B78-B9A5-30DC-8D83-FFEC1277F34C> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib\ndyld[33987]: <5556FD64-9D47-3547-961E-3A27681F3C51> /System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface\ndyld[33987]: <91DACE39-FA28-3191-818D-1FCC6A0E615A> /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation\ndyld[33987]: <9D0387FC-E8F6-3004-9C95-CA68EA715C8B> /System/Library/Frameworks/Security.framework/Versions/A/Security\ndyld[33987]: /System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics\ndyld[33987]: /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libGPUCompilerUtils.dylib\ndyld[33987]: <03BD9E32-CF0A-37B0-898A-3CE8DE06D842> /usr/lib/libobjc.A.dylib\ndyld[33987]: /usr/lib/libc++.1.dylib\ndyld[33987]: <4FED5EE2-5D3E-35B1-A170-9859C4B683BB> /usr/lib/libSystem.B.dylib\ndyld[33987]: <9B672762-7B1F-30BC-96DE-F176B372D66D> /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation\ndyld[33987]: <9CD7B1E1-3E47-339C-A193-2392E3E0ED23> /usr/lib/system/libcache.dylib\ndyld[33987]: <3B110564-5278-3CB0-85F1-2CE8431FF935> /usr/lib/system/libcommonCrypto.dylib\ndyld[33987]: <6FB345CA-7F5C-3263-A23F-143F7539FD8A> /usr/lib/system/libcompiler_rt.dylib\ndyld[33987]: /usr/lib/system/libcopyfile.dylib\ndyld[33987]: <0642DDAD-4771-3C82-805C-E7C6701C1461> /usr/lib/system/libcorecrypto.dylib\ndyld[33987]: /usr/lib/system/libdispatch.dylib\ndyld[33987]: <957F93B3-8805-39C7-9C51-EDD1715F550E> /usr/lib/system/libdyld.dylib\ndyld[33987]: <7E863FCA-F3FF-32C7-8A8C-F983E946AFC3> /usr/lib/system/libkeymgr.dylib\ndyld[33987]: <949131E5-BDA2-39BA-AA50-62651BB51802> /usr/lib/system/libmacho.dylib\ndyld[33987]: /usr/lib/system/libquarantine.dylib\ndyld[33987]: <7460B5AE-469A-36A0-A7EC-6C7D69628E86> /usr/lib/system/libremovefile.dylib\ndyld[33987]: <54439739-33EE-3273-839F-CBA67D7F5CB1> /usr/lib/system/libsystem_asl.dylib\ndyld[33987]: /usr/lib/system/libsystem_blocks.dylib\ndyld[33987]: /usr/lib/system/libsystem_c.dylib\ndyld[33987]: /usr/lib/system/libsystem_collections.dylib\ndyld[33987]: /usr/lib/system/libsystem_configuration.dylib\ndyld[33987]: <14B2A47F-19C8-392F-8FDB-FE8AE375DD41> /usr/lib/system/libsystem_containermanager.dylib\ndyld[33987]: /usr/lib/system/libsystem_coreservices.dylib\ndyld[33987]: <8E07D22E-CE5A-38A0-B091-5B0338C326F5> /usr/lib/system/libsystem_darwin.dylib\ndyld[33987]: <971A4F65-493D-39F3-846D-0D33FA2769FD> /usr/lib/system/libsystem_darwindirectory.dylib\ndyld[33987]: <305F4398-E688-3384-B351-02D865EC8A04> /usr/lib/system/libsystem_dnssd.dylib\ndyld[33987]: <750CA446-92EA-3A56-9A7B-CC0841686C50> /usr/lib/system/libsystem_eligibility.dylib\ndyld[33987]: /usr/lib/system/libsystem_featureflags.dylib\ndyld[33987]: <9B5FB84B-31AD-3EA7-8F89-8C700D369DC8> /usr/lib/system/libsystem_info.dylib\ndyld[33987]: /usr/lib/system/libsystem_m.dylib\ndyld[33987]: /usr/lib/system/libsystem_malloc.dylib\ndyld[33987]: <9C7B1EEB-47BE-3791-93A9-CFC693CB9417> /usr/lib/system/libsystem_networkextension.dylib\ndyld[33987]: <15799128-6CBD-30D6-A2BB-B9D02B4470C0> /usr/lib/system/libsystem_notify.dylib\ndyld[33987]: <54688162-B50D-3D31-A1E8-7B9766D3530D> /usr/lib/system/libsystem_sandbox.dylib\ndyld[33987]: /usr/lib/system/libsystem_sanitizers.dylib\ndyld[33987]: /usr/lib/system/libsystem_secinit.dylib\ndyld[33987]: /usr/lib/system/libsystem_kernel.dylib\ndyld[33987]: /usr/lib/system/libsystem_platform.dylib\ndyld[33987]: /usr/lib/system/libsystem_pthread.dylib\ndyld[33987]: <229122B9-B8B1-3F2F-870E-8650AE3C4FB5> /usr/lib/system/libsystem_symptoms.dylib\ndyld[33987]: <93F1DD8C-6CD9-32B9-B222-D23DA5D161B4> /usr/lib/system/libsystem_trace.dylib\ndyld[33987]: <7194FF5B-A6C5-3D67-B00A-90209F10D603> /usr/lib/system/libsystem_trial.dylib\ndyld[33987]: <05FD0014-55B1-3B8A-A6BA-6C7A389C4123> /usr/lib/system/libunwind.dylib\ndyld[33987]: <33E44C2D-D65E-37A6-B85F-1A4CF524A050> /usr/lib/system/libxpc.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/XPCSupport.framework/Versions/A/XPCSupport\ndyld[33987]: <83794FB3-DE9B-3D23-AB5E-2C1D5D30F134> /usr/lib/swift/libswiftCore.dylib\ndyld[33987]: /usr/lib/libc++abi.dylib\ndyld[33987]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/libRosetta.dylib\ndyld[33987]: <4FD234EA-2C18-3C25-8BD0-B1F4805C6675> /usr/lib/swift/libswiftObjectiveC.dylib\ndyld[33987]: <9E3C7597-446F-3C50-9930-2425D9252C0C> /usr/lib/libswiftPrespecialized.dylib\ndyld[33987]: <1479C415-3678-3968-AC77-06373490860E> /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration\ndyld[33987]: <13EDE3A5-A7D9-3FB8-B0C2-2FB7F7272B34> /usr/lib/libz.1.dylib\ndyld[33987]: <54AD73AF-852E-3CD6-8B7D-E73BE79857D3> /System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout\ndyld[33987]: <1A2A9A41-5269-3B0C-BCEE-B446966CE366> /usr/lib/libcmark-gfm.dylib\ndyld[33987]: <820D290D-51A0-3064-A1F2-4F0AAF7E6BF4> /usr/lib/libfakelink.dylib\ndyld[33987]: <4A3B95C5-AA2E-338C-9398-56895AF82D97> /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork\ndyld[33987]: <332C4B80-5B3C-34E7-AD1F-F6131E607F95> /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration\ndyld[33987]: <0048DB96-1737-3FC5-AF0C-AF784FA24A03> /usr/lib/libarchive.2.dylib\ndyld[33987]: <53A3E31E-06A8-325E-B5A8-316B88AA3C92> /usr/lib/libicucore.A.dylib\ndyld[33987]: <1E8A4F9E-3954-3458-B3BB-BE97F961C105> /usr/lib/libxml2.2.dylib\ndyld[33987]: /usr/lib/liblangid.dylib\ndyld[33987]: /System/Library/Frameworks/Combine.framework/Versions/A/Combine\ndyld[33987]: <6098453F-4D7E-38B4-8ADC-02C9FF51E14A> /System/Library/PrivateFrameworks/CollectionsInternal.framework/Versions/A/CollectionsInternal\ndyld[33987]: <9A1279D4-575A-3E48-A460-A631A3F82D18> /System/Library/PrivateFrameworks/ReflectionInternal.framework/Versions/A/ReflectionInternal\ndyld[33987]: <6D89CD71-A86D-3D78-A64B-96AB79550F79> /System/Library/PrivateFrameworks/RuntimeInternal.framework/Versions/A/RuntimeInternal\ndyld[33987]: <4109E8DD-0A81-310C-B1B3-23B87186D0D8> /System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking\ndyld[33987]: <4975D13C-2AC5-3473-85C0-98054A81D7C6> /usr/lib/swift/libswiftCoreFoundation.dylib\ndyld[33987]: <1DB56DA9-CF6B-3023-ABDF-5A37CB79223C> /usr/lib/swift/libswiftDarwin.dylib\ndyld[33987]: /usr/lib/swift/libswiftDispatch.dylib\ndyld[33987]: <06A92787-4440-3757-AF32-F2B331C753A2> /usr/lib/swift/libswiftIOKit.dylib\ndyld[33987]: <7CD9BDE7-F36B-3471-9295-38E181D6D9E5> /usr/lib/swift/libswiftSystem.dylib\ndyld[33987]: <24AEDAC1-C1EE-30F4-8818-72EBF8969D0C> /usr/lib/swift/libswiftXPC.dylib\ndyld[33987]: <52F59382-A6A6-3F55-8A85-D9FB822D370F> /usr/lib/swift/libswift_Builtin_float.dylib\ndyld[33987]: <8E168857-47F4-349F-A718-A18DB144FCB0> /usr/lib/swift/libswift_Concurrency.dylib\ndyld[33987]: <85246B9A-A757-3F67-B792-3A2F7BB2BB25> /usr/lib/swift/libswift_DarwinFoundation1.dylib\ndyld[33987]: <8DF0116D-DFC9-3906-9DF6-F1DBC47E324B> /usr/lib/swift/libswift_StringProcessing.dylib\ndyld[33987]: /usr/lib/swift/libswiftos.dylib\ndyld[33987]: <7D56DA94-31EB-35F0-B886-4010C075E035> /System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal\ndyld[33987]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/liboah.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/GenerationalStorage.framework/Versions/A/GenerationalStorage\ndyld[33987]: /System/Library/PrivateFrameworks/CacheDelete.framework/Versions/A/CacheDelete\ndyld[33987]: <36F215D1-A2C0-32CA-ADD8-6D85AB48A772> /System/Library/Frameworks/QuickLookThumbnailing.framework/Versions/A/QuickLookThumbnailing\ndyld[33987]: <5EA68C5E-69B0-3011-9D66-AEF49D82B29D> /System/Library/Frameworks/FileProvider.framework/Versions/A/FileProvider\ndyld[33987]: /System/Library/PrivateFrameworks/DiskImages.framework/Versions/A/DiskImages\ndyld[33987]: <49121861-2603-3B0A-B664-BAD9E729BE5D> /System/Library/Frameworks/NetFS.framework/Versions/A/NetFS\ndyld[33987]: /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/DesktopServicesPriv\ndyld[33987]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents\ndyld[33987]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore\ndyld[33987]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata\ndyld[33987]: <61677289-93B7-382F-86CA-B856361D293F> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices\ndyld[33987]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit\ndyld[33987]: <435D6243-695B-3543-A722-10106F5696BD> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE\ndyld[33987]: <01579E0C-9D85-3521-8916-4DDC990CD064> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices\ndyld[33987]: <6A26D479-5926-330B-9FB8-9B7A6BE8E239> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices\ndyld[33987]: <297AC970-E432-3BBD-986C-36782634062E> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList\ndyld[33987]: /usr/lib/libapple_nghttp2.dylib\ndyld[33987]: /usr/lib/libsqlite3.dylib\ndyld[33987]: <5F6B668E-00B2-3BEC-959F-26BD6B50D42B> /System/Library/Frameworks/Accounts.framework/Versions/A/Accounts\ndyld[33987]: <44CD8313-2D5B-3A34-BACA-EF8800803B4A> /System/Library/PrivateFrameworks/AOSKit.framework/Versions/A/AOSKit\ndyld[33987]: <5198BFE1-41D2-33D5-A9E0-C63F81A512D3> /System/Library/PrivateFrameworks/AppSSOCore.framework/Versions/A/AppSSOCore\ndyld[33987]: <61B2B917-D14A-38AD-A439-16E1C635441A> /System/Library/PrivateFrameworks/AppSupport.framework/Versions/A/AppSupport\ndyld[33987]: <816EC446-7C41-3A2F-A582-7CB856797C09> /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation\ndyld[33987]: <10A4E63B-A1EB-31CC-B3E1-DB4FE115FC84> /System/Library/PrivateFrameworks/BackgroundSystemTasks.framework/Versions/A/BackgroundSystemTasks\ndyld[33987]: <38C8FBEC-DE88-33FE-B742-A192F22CC754> /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics\ndyld[33987]: /System/Library/PrivateFrameworks/DuetActivityScheduler.framework/Versions/A/DuetActivityScheduler\ndyld[33987]: <0E78989C-854F-3664-AD92-6B7B6D04191C> /System/Library/PrivateFrameworks/FTServices.framework/Versions/A/FTServices\ndyld[33987]: <277D18EF-39E4-3F72-99E8-8D3DF65ED1D0> /System/Library/Frameworks/GSS.framework/Versions/A/GSS\ndyld[33987]: <5ACC6C0E-51E9-3B5A-B24F-89B22D070878> /System/Library/PrivateFrameworks/InternationalSupport.framework/Versions/A/InternationalSupport\ndyld[33987]: /usr/lib/libMemoryResourceException.dylib\ndyld[33987]: <798012E0-3FFC-3B8D-AC74-E7B7DAEA7E66> /System/Library/PrivateFrameworks/NetworkScore.framework/Versions/A/NetworkScore\ndyld[33987]: <2C93123F-99C8-3B8D-AAE6-3A817BE0A2BF> /System/Library/PrivateFrameworks/NetworkServiceProxy.framework/Versions/A/NetworkServiceProxy\ndyld[33987]: /System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices\ndyld[33987]: /System/Library/PrivateFrameworks/StreamingExtractor.framework/Versions/A/StreamingExtractor\ndyld[33987]: <1F2EDC7B-8F28-3721-8A60-F6E1BCFC29A3> /System/Library/PrivateFrameworks/StreamingZip.framework/Versions/A/StreamingZip\ndyld[33987]: <5DA62AF9-3D46-3D17-A3EB-7026A2F006DF> /System/Library/PrivateFrameworks/SymptomReporter.framework/Versions/A/SymptomReporter\ndyld[33987]: <8E04C57D-3651-386E-83D5-4728B732F214> /usr/lib/libenergytrace.dylib\ndyld[33987]: /usr/lib/libnetworkextension.dylib\ndyld[33987]: <1C7E652B-6B94-3180-93A6-EF8DBA3A5448> /System/Library/Frameworks/Network.framework/Versions/A/Network\ndyld[33987]: <633BCB5F-F063-3D5A-B52A-F72AE236824B> /usr/lib/libbsm.0.dylib\ndyld[33987]: /usr/lib/system/libkxld.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/AppleKeyStore.framework/Versions/A/AppleKeyStore\ndyld[33987]: /usr/lib/libCoreEntitlements.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity\ndyld[33987]: /System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer\ndyld[33987]: <81F4A8BA-C80F-3B53-82E7-57F6928609C5> /System/Library/PrivateFrameworks/CloudServices.framework/Versions/A/CloudServices\ndyld[33987]: <737479F2-7B20-3DB6-B9F4-0DAA1B73E9D0> /System/Library/PrivateFrameworks/SymptomDiagnosticReporter.framework/Versions/A/SymptomDiagnosticReporter\ndyld[33987]: /System/Library/PrivateFrameworks/CrashReporterSupport.framework/Versions/A/CrashReporterSupport\ndyld[33987]: /System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression\ndyld[33987]: <0EAB1F4A-9275-3FED-8EA6-E962ACDDEE5D> /usr/lib/libcoretls.dylib\ndyld[33987]: <6937D729-7EF4-3972-9E12-694C17C1C1AB> /usr/lib/libcoretls_cfhelpers.dylib\ndyld[33987]: <7E84FD3B-E90E-317E-AC19-17B70AC809E5> /usr/lib/libpam.2.dylib\ndyld[33987]: /usr/lib/libxar.1.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS\ndyld[33987]: /System/Library/PrivateFrameworks/SwiftASN1Internal.framework/Versions/A/SwiftASN1Internal\ndyld[33987]: /usr/lib/libutil.dylib\ndyld[33987]: <4C6139EE-BF87-37A6-B226-830A6FDC36F8> /System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo\ndyld[33987]: <2BC48182-F354-3AB0-8F18-0C60CAAFE398> /System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer\ndyld[33987]: <7C50137B-2ABD-3819-B033-AE65B05A6085> /System/Library/PrivateFrameworks/CoreWiFi.framework/Versions/A/CoreWiFi\ndyld[33987]: /System/Library/PrivateFrameworks/LoggingSupport.framework/Versions/A/LoggingSupport\ndyld[33987]: <91A461DE-C8E8-3868-B393-BA6E5A17DF2A> /System/Library/PrivateFrameworks/MobileAsset.framework/Versions/A/MobileAsset\ndyld[33987]: /System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog\ndyld[33987]: /System/Library/PrivateFrameworks/Rapport.framework/Versions/A/Rapport\ndyld[33987]: <9F52706C-75BD-34AF-A29E-C26608124ACC> /System/Library/Frameworks/SwiftData.framework/Versions/A/SwiftData\ndyld[33987]: <259877CE-4E2C-34A9-A07F-FEE2999D7B2F> /System/Library/PrivateFrameworks/Symptoms.framework/Versions/A/Frameworks/SymptomAnalytics.framework/Versions/A/SymptomAnalytics\ndyld[33987]: /System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers\ndyld[33987]: <4A78C569-FF0D-398B-9C25-33453F0CEC40> /System/Library/PrivateFrameworks/UserManagement.framework/Versions/A/UserManagement\ndyld[33987]: <5BF55637-F306-3D79-B5A1-DB8A871DAD4B> /usr/lib/libboringssl.dylib\ndyld[33987]: <831C79C1-8DBE-31A3-AA4E-8E2B041488D6> /usr/lib/libcupolicy.dylib\ndyld[33987]: <88925A0C-4960-3F6D-AF3A-B1983F7B3D18> /usr/lib/libdns_services.dylib\ndyld[33987]: <9753F471-40DD-3B9E-9D64-8D07C1B06BC9> /System/Library/Frameworks/NetworkExtension.framework/Versions/A/NetworkExtension\ndyld[33987]: /usr/lib/libnwswifttls.dylib\ndyld[33987]: <6F59933A-6618-33F1-BE52-E7FC3BF7A1EF> /usr/lib/libpcap.A.dylib\ndyld[33987]: <5E89267F-C684-348D-8356-F9DAD8B4CB13> /usr/lib/libquic.dylib\ndyld[33987]: /usr/lib/libusrtcp.dylib\ndyld[33987]: <1617DBB1-2BFF-3619-903C-2FBB31348FB6> /System/Library/PrivateFrameworks/AtomicsInternal.framework/Versions/A/AtomicsInternal\ndyld[33987]: <41F66F01-A342-3091-A832-0B2B645C922B> /System/Library/PrivateFrameworks/InternalSwiftProtobuf.framework/Versions/A/InternalSwiftProtobuf\ndyld[33987]: <2EDB2E62-942F-3AB5-82AF-8E1328544E17> /usr/lib/swift/libswiftDistributed.dylib\ndyld[33987]: /usr/lib/swift/libswiftObservation.dylib\ndyld[33987]: /usr/lib/swift/libswiftSynchronization.dylib\ndyld[33987]: <91BDD1F8-831B-3B01-86BA-6BBCB43373C4> /System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary\ndyld[33987]: <90CFC86E-833E-3E9F-BAAC-2B61BD750DA6> /System/Library/PrivateFrameworks/CoreDuetContext.framework/Versions/A/CoreDuetContext\ndyld[33987]: <07CF779F-8F51-3764-B486-23D76868FF91> /System/Library/PrivateFrameworks/BiomeLibrary.framework/Versions/A/BiomeLibrary\ndyld[33987]: <838F99F9-D3FA-335B-9767-B5D04A3FACA6> /System/Library/PrivateFrameworks/CoreDuet.framework/Versions/A/CoreDuet\ndyld[33987]: <2110407D-EFB4-373E-B963-9C92E26594B2> /System/Library/PrivateFrameworks/BiomeStreams.framework/Versions/A/BiomeStreams\ndyld[33987]: <455A5553-E683-30B4-A906-1F14E75F6E61> /System/Library/PrivateFrameworks/BiomeFoundation.framework/Versions/A/BiomeFoundation\ndyld[33987]: <0F03104F-FC8B-3ADD-8850-4B7029E2B56E> /System/Library/PrivateFrameworks/BiomePubSub.framework/Versions/A/BiomePubSub\ndyld[33987]: <712AD9C1-44D2-36F4-BA8E-15038521462B> /System/Library/Frameworks/CoreData.framework/Versions/A/CoreData\ndyld[33987]: <73EE1A0A-0D29-3104-98CB-BEFEDA53F7C0> /System/Library/PrivateFrameworks/ProactiveSupport.framework/Versions/A/ProactiveSupport\ndyld[33987]: <9805BB7B-12C9-39F5-9070-C5B8BFCAE2AF> /System/Library/Frameworks/CoreLocation.framework/Versions/A/CoreLocation\ndyld[33987]: /System/Library/Frameworks/Intents.framework/Versions/A/Intents\ndyld[33987]: /System/Library/Frameworks/_LocationEssentials.framework/Versions/A/_LocationEssentials\ndyld[33987]: /usr/lib/liblzma.5.dylib\ndyld[33987]: <9171DD7D-3994-3963-9A28-BC163BF97DE6> /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate\ndyld[33987]: /System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag\ndyld[33987]: <858910C5-1D4A-37B7-BF0E-EE02E24A2ACD> /System/Library/PrivateFrameworks/InternationalTextSearch.framework/Versions/A/InternationalTextSearch\ndyld[33987]: <13271AA6-33EA-369B-B2D1-6EC528C820E7> /System/Library/PrivateFrameworks/SoftwareUpdateCoreSupport.framework/Versions/A/SoftwareUpdateCoreSupport\ndyld[33987]: <2CA857AF-D999-34DC-94A1-3AC0E5B80416> /System/Library/PrivateFrameworks/SoftwareUpdateCoreConnect.framework/Versions/A/SoftwareUpdateCoreConnect\ndyld[33987]: <823F3D1A-65F1-3CC5-96B1-750263B8DB36> /System/Library/PrivateFrameworks/RemoteServiceDiscovery.framework/Versions/A/RemoteServiceDiscovery\ndyld[33987]: /System/Library/PrivateFrameworks/MSUDataAccessor.framework/Versions/A/MSUDataAccessor\ndyld[33987]: /usr/lib/libbootpolicy.dylib\ndyld[33987]: <885F9C72-1018-368B-AD36-E8A42E87FD91> /System/Library/PrivateFrameworks/RemoteXPC.framework/Versions/A/RemoteXPC\ndyld[33987]: /usr/lib/libFDR.dylib\ndyld[33987]: <24D28E7F-A1AE-3031-8679-A0D6C6D68A86> /usr/lib/libamsupport.dylib\ndyld[33987]: <29367004-5D60-38DB-831F-9E5EE9364B21> /usr/lib/libReverseProxyDevice.dylib\ndyld[33987]: <9594FBFB-D49D-3DF6-8820-564633EAEC2B> /System/Library/PrivateFrameworks/AppleDeviceQuerySupport.framework/Versions/A/AppleDeviceQuerySupport\ndyld[33987]: /usr/lib/libpartition2_dynamic.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce\ndyld[33987]: <9A8926C8-36A6-3DB4-A485-059C1F630984> /usr/lib/libAppleArchive.dylib\ndyld[33987]: <2B16DF37-A596-3D8A-AE47-33E580EB1354> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage\ndyld[33987]: <8203944D-B53E-3D7E-A481-3C676CAE1B6A> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib\ndyld[33987]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib\ndyld[33987]: <08508E7B-096D-31AB-9C66-191C877ED62F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/Libraries/libCGInterfaces.dylib\ndyld[33987]: <8945E7B7-12AE-3FF4-AA3B-D4DF9A06FEE7> /System/Library/PrivateFrameworks/AccelerateGPU.framework/Versions/A/AccelerateGPU\ndyld[33987]: <23402175-D2CF-3B08-88D0-AFBBCF775FEF> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib\ndyld[33987]: <086CBEED-2F64-3E75-AB99-8C8C0E0A2F1C> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices\ndyld[33987]: <0616AF41-149E-3F4A-906E-56E2642457BE> /System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo\ndyld[33987]: <873404F1-CC9D-30F9-AE06-8EA58D292005> /System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync\ndyld[33987]: /System/Library/Frameworks/CoreText.framework/Versions/A/CoreText\ndyld[33987]: /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO\ndyld[33987]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS\ndyld[33987]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices\ndyld[33987]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore\ndyld[33987]: <59BBF27B-1D89-3D35-9210-8386EFA15A8D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD\ndyld[33987]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy\ndyld[33987]: <9CDA611B-254A-3779-9356-369485134C2D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis\ndyld[33987]: <0C8F41C6-6D93-3DB3-B522-CA8CFF5C3B33> /System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight\ndyld[33987]: <9E126CE0-FBB2-3B15-953F-CCDC758E34FB> /System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib\ndyld[33987]: <959C748F-8851-3A25-BFFA-5FEA80296965> /System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard\ndyld[33987]: /System/Library/PrivateFrameworks/BoardServices.framework/Versions/A/BoardServices\ndyld[33987]: /System/Library/PrivateFrameworks/BackBoardServices.framework/Versions/A/BackBoardServices\ndyld[33987]: <7F763DF9-EA7F-3938-B599-DCCF4605E610> /System/Library/PrivateFrameworks/BackBoardHIDEventFoundation.framework/Versions/A/BackBoardHIDEventFoundation\ndyld[33987]: /System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay\ndyld[33987]: /System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox\ndyld[33987]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders\ndyld[33987]: /System/Library/PrivateFrameworks/ProDisplayLibrary.framework/Versions/A/ProDisplayLibrary\ndyld[33987]: <1E529C1A-B09C-3EB7-A286-CE00E292D561> /System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator\ndyld[33987]: /System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia\ndyld[33987]: /System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC\ndyld[33987]: /System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient\ndyld[33987]: <98CB7012-30E5-3BDD-8C84-CDBDA9DB3017> /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore\ndyld[33987]: <57F7BB9C-649D-3360-AA86-A502815D77FA> /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport\ndyld[33987]: <625F222D-6394-39B9-A1F2-12B9EA56DD85> /usr/lib/swift/libswiftAccelerate.dylib\ndyld[33987]: /usr/lib/swift/libswiftCoreAudio.dylib\ndyld[33987]: /usr/lib/swift/libswiftCoreMedia.dylib\ndyld[33987]: <7235A6A9-49B2-3B94-9DD6-C987019CDBF2> /usr/lib/swift/libswiftMetal.dylib\ndyld[33987]: <9670AE5C-271A-3DCB-9A0A-8E3A7CCC2726> /usr/lib/swift/libswiftOSLog.dylib\ndyld[33987]: <63444A8C-9E8C-3778-820D-1E0C88CA2DF7> /usr/lib/swift/libswiftQuartzCore.dylib\ndyld[33987]: /usr/lib/swift/libswiftUniformTypeIdentifiers.dylib\ndyld[33987]: <9247A5B6-A883-3A07-BEE7-A223840317A4> /usr/lib/swift/libswiftVideoToolbox.dylib\ndyld[33987]: /usr/lib/swift/libswiftsimd.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/BiomeStorage.framework/Versions/A/BiomeStorage\ndyld[33987]: /System/Library/PrivateFrameworks/IntelligencePlatformLibrary.framework/Versions/A/IntelligencePlatformLibrary\ndyld[33987]: <42CDC0E6-51BA-3804-BD3E-EDF87FC74034> /System/Library/PrivateFrameworks/PoirotSchematizer.framework/Versions/A/PoirotSchematizer\ndyld[33987]: <2362E209-EC61-3FFC-9486-1244BB29BE82> /System/Library/PrivateFrameworks/BiomeSync.framework/Versions/A/BiomeSync\ndyld[33987]: /System/Library/PrivateFrameworks/BiomeDSL.framework/Versions/A/BiomeDSL\ndyld[33987]: /System/Library/PrivateFrameworks/FeatureFlags.framework/Versions/A/FeatureFlags\ndyld[33987]: /System/Library/PrivateFrameworks/PoirotUDFs.framework/Versions/A/PoirotUDFs\ndyld[33987]: /usr/lib/swift/libswift_DarwinFoundation2.dylib\ndyld[33987]: <8D2C31B5-FB10-3BF6-8566-F0DCD56C8582> /usr/lib/swift/libswift_DarwinFoundation3.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime\ndyld[33987]: <4646F780-1D5E-3EE7-B00A-64619293CC18> /usr/lib/libiconv.2.dylib\ndyld[33987]: <1940124C-0D73-35D2-9D94-A75F116088A0> /usr/lib/libcharset.1.dylib\ndyld[33987]: <24779350-BC29-3465-AAB3-F7CD0DA5844A> /System/Library/PrivateFrameworks/PoirotSQLite.framework/Versions/A/PoirotSQLite\ndyld[33987]: <7B63C2BF-8C7C-3ECA-ACD9-F1B75DBE018C> /usr/lib/swift/libswift_RegexParser.dylib\ndyld[33987]: <2091B02D-8D55-3DC4-8097-60C193D03C85> /System/Library/PrivateFrameworks/CascadeSets.framework/Versions/A/CascadeSets\ndyld[33987]: <79000980-1797-3115-B74B-60FA1E9C3C73> /System/Library/PrivateFrameworks/CorePhoneNumbers.framework/Versions/A/CorePhoneNumbers\ndyld[33987]: <7F00413A-4D40-3DBF-8FD5-859B23E6DC03> /System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG\ndyld[33987]: /usr/lib/libexpat.1.dylib\ndyld[33987]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib\ndyld[33987]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib\ndyld[33987]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib\ndyld[33987]: <7304F8B3-8E0F-3813-BFAF-9A565CEA0A11> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib\ndyld[33987]: <01AAD3B4-D6BA-36D9-BA6F-D494D2AC161D> /usr/lib/libate.dylib\ndyld[33987]: <8EA6CA42-AA01-3C0F-9672-4917481BAAAE> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib\ndyld[33987]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib\ndyld[33987]: <757FEDFF-841C-3D62-B703-CDE79E929363> /System/Library/PrivateFrameworks/GraphicsServices.framework/Versions/A/GraphicsServices\ndyld[33987]: /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL\ndyld[33987]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib\ndyld[33987]: <6CEF3932-AAC9-3F8E-905D-A826F2884C9A> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib\ndyld[33987]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib\ndyld[33987]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib\ndyld[33987]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib\ndyld[33987]: <07CB5D41-C2F3-3C33-951F-67B2C8B8B662> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler\ndyld[33987]: /System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment\ndyld[33987]: /System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay\ndyld[33987]: <825E8416-E246-338E-A5CF-AA81A1B01DD9> /System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport\ndyld[33987]: <7CF84496-675C-3241-B0EF-E83C95F188FA> /System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA\ndyld[33987]: <3D533C35-3A2A-3672-92EF-5FEE9EE739AC> /System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation\ndyld[33987]: /System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore\ndyld[33987]: <04DC06C1-2BFA-3FEE-9429-A33E41721A3E> /usr/lib/libspindump.dylib\ndyld[33987]: /System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio\ndyld[33987]: <2B5FB7B0-844C-3D84-9EFD-020B285B0F8D> /System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport\ndyld[33987]: /System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata\ndyld[33987]: /System/Library/PrivateFrameworks/AudioDSPGraph.framework/Versions/A/AudioDSPGraph\ndyld[33987]: <8AF1606D-5C93-3B80-BC81-60C5688628E2> /System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore\ndyld[33987]: /System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk\ndyld[33987]: <3FF99846-E48C-3C9A-814C-35B45E5F60EC> /usr/lib/libAudioStatistics.dylib\ndyld[33987]: <6108A12D-286B-3CF2-B848-B0E7A0189DCC> /System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy\ndyld[33987]: <655F6374-6CE8-3D0E-994E-4D7C37F78E89> /usr/lib/libSMC.dylib\ndyld[33987]: <7AE04E20-83FD-3B1B-8846-E9869AD98DB5> /usr/lib/swift/libswiftCoreMIDI.dylib\ndyld[33987]: <52BD9E26-B356-3EAA-9AD7-7FF700C61A91> /System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI\ndyld[33987]: <75F77FEC-BE14-3C97-93DA-403C3B529D3B> /usr/lib/libAudioToolboxUtility.dylib\ndyld[33987]: <912BFF10-FB8F-3D52-9941-ACDCE1CAE36A> /usr/lib/libperfcheck.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/AudioAnalytics.framework/Versions/A/AudioAnalytics\ndyld[33987]: <869F0693-0E82-38C1-8920-C782E71735CA> /System/Library/Frameworks/OSLog.framework/Versions/A/OSLog\ndyld[33987]: <62740FDD-2B16-3319-B5C9-022D45C6B03A> /System/Library/PrivateFrameworks/OSEligibility.framework/Versions/A/OSEligibility\ndyld[33987]: <10C63D59-07BC-3518-87A0-83CAC48D8A70> /System/Library/PrivateFrameworks/IconServices.framework/Versions/A/IconServices\ndyld[33987]: <8EF56F82-8CCE-3811-AD16-6D0939187B45> /System/Library/Frameworks/LightweightCodeRequirements.framework/Versions/A/LightweightCodeRequirements\ndyld[33987]: /System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit\ndyld[33987]: <1946F8FE-0ABC-3F8F-9116-5451ECABD14C> /System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices\ndyld[33987]: /System/Library/PrivateFrameworks/IconFoundation.framework/Versions/A/IconFoundation\ndyld[33987]: /System/Library/PrivateFrameworks/AssistantServices.framework/Versions/A/AssistantServices\ndyld[33987]: <6A34A62A-16D4-34F0-B34B-2D96B53C20AD> /System/Library/PrivateFrameworks/IconRendering.framework/Versions/A/IconRendering\ndyld[33987]: /System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI\ndyld[33987]: <0943679D-FF88-3F18-BE4B-D8B4827AB0B5> /System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage\ndyld[33987]: <968B5A5F-9749-3527-AF2A-66B599785308> /System/Library/PrivateFrameworks/SFSymbols.framework/Versions/A/SFSymbols\ndyld[33987]: /System/Library/Frameworks/DeveloperToolsSupport.framework/Versions/A/DeveloperToolsSupport\ndyld[33987]: <92090A92-DFAF-3EBC-886C-655EC158A53F> /System/Library/PrivateFrameworks/RenderBox.framework/Versions/A/RenderBox\ndyld[33987]: <986D57A7-BFF1-3DAA-8EB1-17CCAA76C731> /System/Library/PrivateFrameworks/CoreSVG.framework/Versions/A/CoreSVG\ndyld[33987]: /System/Library/PrivateFrameworks/TextureIO.framework/Versions/A/TextureIO\ndyld[33987]: /usr/lib/swift/libswiftCoreImage.dylib\ndyld[33987]: <77D85BA0-FE1C-3B5A-92DB-70A30202C990> /System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer\ndyld[33987]: /System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices\ndyld[33987]: <5D3E7FFF-AC8E-3D6F-8E99-B199E593D270> /System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG\ndyld[33987]: <49E7449E-1385-3B53-94CC-36EFC31E98FE> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib\ndyld[33987]: <23C577A8-DB0B-3A0A-9058-1289483C262A> /usr/lib/libhvf.dylib\ndyld[33987]: <11E757EC-72FB-3C53-8ED7-641428AB6169> /System/Library/PrivateFrameworks/ParsingInternal.framework/Versions/A/ParsingInternal\ndyld[33987]: /System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib\ndyld[33987]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore\ndyld[33987]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage\ndyld[33987]: <199F6401-91D0-36E9-9EA9-D4B44ED1CE3A> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork\ndyld[33987]: <4D134FE3-50EE-39D5-9699-04B4B673DD35> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix\ndyld[33987]: <2E7E2722-3821-3DBF-B25A-6EA45D1A8FD4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector\ndyld[33987]: <3E1FE9EA-34A2-3545-B639-48B1FE1FD3D4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray\ndyld[33987]: <3103E210-FF5C-3677-BDD3-59FF17A6ACEC> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions\ndyld[33987]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSBenchmarkLoop.framework/Versions/A/MPSBenchmarkLoop\ndyld[33987]: <31F90368-23A5-39BB-822B-C8470C4479AE> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSHost.framework/Versions/A/MPSHost\ndyld[33987]: <9C416BB2-0882-315C-AF23-F476E34983BC> /System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools\ndyld[33987]: /System/Library/PrivateFrameworks/IOAccelMemoryInfo.framework/Versions/A/IOAccelMemoryInfo\ndyld[33987]: /System/Library/PrivateFrameworks/kperf.framework/Versions/A/kperf\ndyld[33987]: <03470B3A-A004-39A0-B6A4-F2A4AFFFCDD3> /System/Library/PrivateFrameworks/GPURawCounter.framework/Versions/A/GPURawCounter\ndyld[33987]: <4D8F39C6-B221-3AF1-BB40-CAEB0A174D61> /System/Library/PrivateFrameworks/ASEProcessing.framework/Versions/A/ASEProcessing\ndyld[33987]: <724D42FC-F4FD-39C7-A1BF-D0AD086231F4> /System/Library/PrivateFrameworks/Symbolication.framework/Versions/A/Symbolication\ndyld[33987]: /System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing\ndyld[33987]: <1B4C0154-843C-3CEE-9628-22978082DD2D> /System/Library/PrivateFrameworks/SILManager.framework/Versions/A/SILManager\ndyld[33987]: <59136324-34E6-3367-92BB-659346907A04> /System/Library/PrivateFrameworks/CoreSymbolication.framework/Versions/A/CoreSymbolication\ndyld[33987]: <566F2D7D-0F3B-3290-A739-7A40A151F0BE> /System/Library/PrivateFrameworks/MallocStackLogging.framework/Versions/A/MallocStackLogging\ndyld[33987]: <7C923545-F3BB-3215-9720-85196358D9F1> /System/Library/PrivateFrameworks/DebugSymbols.framework/Versions/A/DebugSymbols\ndyld[33987]: <06728C4D-5750-308F-8290-EAF7BE91F4BB> /System/Library/PrivateFrameworks/OSAnalytics.framework/Versions/A/OSAnalytics\ndyld[33987]: <2FA711C7-F764-363A-BF03-295E0DA88B79> /System/Library/PrivateFrameworks/DeviceRecovery.framework/Versions/A/DeviceRecovery\ndyld[33987]: /System/Library/PrivateFrameworks/Tightbeam.framework/Versions/A/Tightbeam\ndyld[33987]: <856ACB2A-3334-3BA6-AAC8-8F344E7CDB83> /usr/lib/swift/libswiftCompression.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/AFKUser.framework/Versions/A/AFKUser\ndyld[33987]: <2186F196-EE17-3A59-B9DA-D6823BEDD35B> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI\ndyld[33987]: <086BB8AD-E317-3FC4-9E44-0D7C6036E8D7> /System/Library/PrivateFrameworks/SAObjects.framework/Versions/A/SAObjects\ndyld[33987]: /System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox\ndyld[33987]: /System/Library/PrivateFrameworks/MediaRemote.framework/Versions/A/MediaRemote\ndyld[33987]: <7F1A25E4-ED0A-3502-AABA-26EDB4A0D2A7> /System/Library/Frameworks/UserNotifications.framework/Versions/A/UserNotifications\ndyld[33987]: <8A5E0FF6-3116-3082-A0AD-20DCD6C5E1B4> /System/Library/PrivateFrameworks/SiriInstrumentation.framework/Versions/A/SiriInstrumentation\ndyld[33987]: <600E036E-9B18-35BE-B40B-E8D2D53AC90D> /System/Library/PrivateFrameworks/SiriAnalytics.framework/Versions/A/SiriAnalytics\ndyld[33987]: <619E6770-766A-3629-9AF8-F32C009375E9> /System/Library/PrivateFrameworks/SiriTTSService.framework/Versions/A/SiriTTSService\ndyld[33987]: <71CAE70A-72AD-3F74-834D-3519B545C08D> /System/Library/PrivateFrameworks/SiriCrossDeviceArbitration.framework/Versions/A/SiriCrossDeviceArbitration\ndyld[33987]: /System/Library/PrivateFrameworks/FeedbackLogger.framework/Versions/A/FeedbackLogger\ndyld[33987]: <55FBCBE4-1032-3017-BA49-D734B82405DF> /System/Library/PrivateFrameworks/FaceTimeNameUtility.framework/Versions/A/FaceTimeNameUtility\ndyld[33987]: <336E2CAC-84D2-34DC-8AE3-7FE688C609EA> /System/Library/PrivateFrameworks/AuthKit.framework/Versions/A/AuthKit\ndyld[33987]: /System/Library/PrivateFrameworks/SiriCrossDeviceArbitrationFeedback.framework/Versions/A/SiriCrossDeviceArbitrationFeedback\ndyld[33987]: <368BC882-02B9-38AB-89B4-F62430F2B8EB> /usr/lib/swift/libswiftCoreLocation.dylib\ndyld[33987]: /usr/lib/swift/libswiftAVFoundation.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/UIKitServices.framework/Versions/A/UIKitServices\ndyld[33987]: <54A2CBB8-623D-3629-904A-D0399ED13547> /System/Library/PrivateFrameworks/UnifiedAssetFramework.framework/Versions/A/UnifiedAssetFramework\ndyld[33987]: /System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession\ndyld[33987]: <52A7AD42-9DE0-393B-A6FB-A7CB6FF8F3A5> /System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience\ndyld[33987]: <1A63E9E1-2D64-3AF4-9CCD-6EF042397F84> /System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib\ndyld[33987]: <7CC1BC36-42C1-39A3-AA4F-F9C8ABCE0CD5> /System/Library/PrivateFrameworks/AudioAccessoryServices.framework/Versions/A/AudioAccessoryServices\ndyld[33987]: <515FDCCC-535A-398B-BBD3-3D35565F5423> /System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth\ndyld[33987]: /System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils\ndyld[33987]: /System/Library/PrivateFrameworks/HID.framework/Versions/A/HID\ndyld[33987]: <920D8AA6-CDCD-3E0F-AD66-F0673ACABD3F> /System/Library/PrivateFrameworks/Sharing.framework/Versions/A/Sharing\ndyld[33987]: <3DD8C4CA-23E9-35CF-AD67-549DD72D3344> /System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras\ndyld[33987]: <236517AD-8D16-3E62-8603-EBE7B65ACACA> /System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211\ndyld[33987]: <42F76533-D8DD-3A24-A08A-103C51428797> /System/Library/PrivateFrameworks/IDSFoundation.framework/Versions/A/IDSFoundation\ndyld[33987]: <116D159B-163E-3F91-9591-30FF8B6EB537> /System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Apple80211\ndyld[33987]: /System/Library/Frameworks/CoreWLAN.framework/Versions/A/CoreWLAN\ndyld[33987]: <1ABB6C50-A5DE-3744-8F07-8C3B5617B0B9> /System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth\ndyld[33987]: <82D79BDA-26A0-3A44-AAC8-911411801FDE> /usr/lib/swift/libswiftRegexBuilder.dylib\ndyld[33987]: <41E45E0C-2E88-3605-B213-F7CD760A9FF4> /System/Library/PrivateFrameworks/IMFoundation.framework/Versions/A/IMFoundation\ndyld[33987]: <40F60A3F-90A9-3F07-A99D-005E3158C6F6> /System/Library/PrivateFrameworks/Marco.framework/Versions/A/Marco\ndyld[33987]: <59FFD032-1427-39F6-BC16-A6877582A243> /System/Library/PrivateFrameworks/CommonUtilities.framework/Versions/A/CommonUtilities\ndyld[33987]: <6C3E51F8-D809-3AA1-8695-B75714C2D39A> /System/Library/PrivateFrameworks/Engram.framework/Versions/A/Engram\ndyld[33987]: /System/Library/PrivateFrameworks/XPCDistributed.framework/Versions/A/XPCDistributed\ndyld[33987]: <12066854-2BE4-35DF-BA9F-B38221C980FD> /usr/lib/libtidy.A.dylib\ndyld[33987]: <63F598E2-AF8A-3F29-BE11-3F14DB377A5B> /System/Library/PrivateFrameworks/Bom.framework/Versions/A/Bom\ndyld[33987]: /usr/lib/libParallelCompression.dylib\ndyld[33987]: <9E06CB59-0638-3C9F-B202-264E739433AC> /usr/lib/libIOReport.dylib\ndyld[33987]: <15EEE715-2670-3288-AFA8-504BAD951B4F> /System/Library/PrivateFrameworks/WiFiPeerToPeer.framework/Versions/A/WiFiPeerToPeer\ndyld[33987]: <9A86DB3F-CC62-3E89-B872-35D04CFFBE42> /System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation\ndyld[33987]: <3854272C-7B14-3A3C-9BB1-F0FBA394708A> /System/Library/PrivateFrameworks/Centauri.framework/Versions/A/Centauri\ndyld[33987]: <946B1484-B180-3451-A452-B54BF5A6D392> /System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon\ndyld[33987]: <2B49C295-4EA2-3DE3-90B4-DC03A96F2657> /usr/lib/libmrc.dylib\ndyld[33987]: <6661265C-7B78-3158-9011-4BFDFFEF7807> /System/Library/PrivateFrameworks/IPConfiguration.framework/Versions/A/IPConfiguration\ndyld[33987]: /System/Library/PrivateFrameworks/Netrb.framework/Versions/A/Netrb\ndyld[33987]: /System/Library/PrivateFrameworks/FrontBoardServices.framework/Versions/A/FrontBoardServices\ndyld[33987]: <74E55DD6-720D-39E4-897E-EB4328E1946D> /usr/lib/libgermantok.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData\ndyld[33987]: <21723046-939E-302F-883C-9DB417452E3A> /System/Library/PrivateFrameworks/MultiverseSupport.framework/Versions/A/MultiverseSupport\ndyld[33987]: /System/Library/PrivateFrameworks/DiskManagement.framework/Versions/A/DiskManagement\ndyld[33987]: /System/Library/PrivateFrameworks/AppleIDAuthSupport.framework/Versions/A/AppleIDAuthSupport\ndyld[33987]: /System/Library/PrivateFrameworks/AAAFoundation.framework/Versions/A/AAAFoundation\ndyld[33987]: /System/Library/PrivateFrameworks/KeychainCircle.framework/Versions/A/KeychainCircle\ndyld[33987]: /System/Library/PrivateFrameworks/URLFormatting.framework/Versions/A/URLFormatting\ndyld[33987]: <80C3E2D4-B6B8-3C62-B257-27DEEBAD4935> /usr/lib/libcsfde.dylib\ndyld[33987]: <650E155C-1FE3-36ED-8D84-157D380F7F95> /usr/lib/libCoreStorage.dylib\ndyld[33987]: <46DD93AF-BACD-309B-AD51-9CC47C78CA2C> /System/Library/PrivateFrameworks/MediaKit.framework/Versions/A/MediaKit\ndyld[33987]: /System/Library/Frameworks/DiscRecording.framework/Versions/A/DiscRecording\ndyld[33987]: <1F8700BE-BD91-3B94-AC32-A5F10CEFEE35> /System/Library/PrivateFrameworks/ProtectedCloudStorage.framework/Versions/A/ProtectedCloudStorage\ndyld[33987]: /System/Library/PrivateFrameworks/EFILogin.framework/Versions/A/EFILogin\ndyld[33987]: <6A4A85F4-3D12-3C4C-85EC-D53D61379F28> /usr/lib/libheimdal-asn1.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/OctagonTrust.framework/Versions/A/OctagonTrust\ndyld[33987]: <093EF25B-5305-3611-B068-E65071858F52> /System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit\ndyld[33987]: /System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory\ndyld[33987]: <3B7FD4C1-D1D4-3DA9-B2F8-3D4094679D76> /System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory\ndyld[33987]: <1DAFDDDA-BB7B-320E-BCFC-B7C22886D486> /System/Library/PrivateFrameworks/GeoServices.framework/Versions/A/GeoServices\ndyld[33987]: /System/Library/PrivateFrameworks/LocationSupport.framework/Versions/A/LocationSupport\ndyld[33987]: <38EE3C42-06D6-3A46-A420-DF701A4EA911> /System/Library/PrivateFrameworks/GeoServicesCore.framework/Versions/A/GeoServicesCore\ndyld[33987]: <8D0ECDD1-24B6-3B8D-9CF9-CDC55FC64490> /System/Library/PrivateFrameworks/PhoneNumbers.framework/Versions/A/PhoneNumbers\ndyld[33987]: <8A2C8C17-E138-3B34-8643-ED4FB1C9049E> /System/Library/PrivateFrameworks/LocationLogEncryption.framework/Versions/A/LocationLogEncryption\ndyld[33987]: <016C5057-625C-30B1-AD32-7BC9D082F05B> /System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio\ndyld[33987]: /System/Library/Frameworks/AVRouting.framework/Versions/A/AVRouting\ndyld[33987]: <5240B3A0-D035-345E-A636-BC3A92C847C4> /usr/lib/libAccessibility.dylib\ndyld[33987]: <1FB2BCFD-D9FC-385A-A0EA-E2B5052E27F5> /System/Library/PrivateFrameworks/MediaServices.framework/Versions/A/MediaServices\ndyld[33987]: /System/Library/PrivateFrameworks/IDS.framework/Versions/A/IDS\ndyld[33987]: /System/Library/PrivateFrameworks/IsolatedCoreAudioClient.framework/Versions/A/IsolatedCoreAudioClient\ndyld[33987]: <7CC0621B-3B88-3533-A3FB-52E6214486EE> /System/Library/PrivateFrameworks/CoreAudioOrchestration.framework/Versions/A/CoreAudioOrchestration\ndyld[33987]: /System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox\ndyld[33987]: /System/Library/PrivateFrameworks/CoreAVCHD.framework/Versions/A/CoreAVCHD\ndyld[33987]: <74D313A5-4D99-35D1-A4C9-B76AB6457EF0> /System/Library/Frameworks/MediaAccessibility.framework/Versions/A/MediaAccessibility\ndyld[33987]: <87F549F4-73CC-302B-ABDB-D3CCFADABFA9> /System/Library/PrivateFrameworks/Mangrove.framework/Versions/A/Mangrove\ndyld[33987]: <214294AE-C7B7-3C9A-A4F8-201C989F9779> /System/Library/PrivateFrameworks/CMPhoto.framework/Versions/A/CMPhoto\ndyld[33987]: <5F090F48-E481-3737-8E75-362E5D274879> /System/Library/Frameworks/CoreTelephony.framework/Versions/A/CoreTelephony\ndyld[33987]: <9ACFCA55-82CB-33DB-AD00-443576099FDB> /System/Library/PrivateFrameworks/CoreAUC.framework/Versions/A/CoreAUC\ndyld[33987]: <71A0C0AD-67F3-36F9-BF73-6DD5D7424AF7> /System/Library/PrivateFrameworks/AppleJPEGXL.framework/Versions/A/AppleJPEGXL\ndyld[33987]: <63A6BBA0-CD50-30F8-9CD2-81B59264EA13> /usr/lib/libTelephonyUtilDynamic.dylib\ndyld[33987]: /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit\ndyld[33987]: /System/Library/PrivateFrameworks/CryptoKitCBridging.framework/Versions/A/CryptoKitCBridging\ndyld[33987]: <714063A8-D81E-3B22-9B36-88948A979E7F> /System/Library/Frameworks/CryptoTokenKit.framework/Versions/A/CryptoTokenKit\ndyld[33987]: <86858734-8B4D-38E6-AAA7-B7A046A7CB2A> /System/Library/Frameworks/LocalAuthentication.framework/Versions/A/LocalAuthentication\ndyld[33987]: /System/Library/PrivateFrameworks/LocalAuthenticationCore.framework/Versions/A/LocalAuthenticationCore\ndyld[33987]: /System/Library/PrivateFrameworks/LocalAuthenticationCredentialServices.framework/Versions/A/LocalAuthenticationCredentialServices\ndyld[33987]: /System/Library/Frameworks/LocalAuthentication.framework/Support/SharedUtils.framework/Versions/A/SharedUtils\ndyld[33987]: /System/Library/PrivateFrameworks/PersistentConnection.framework/Versions/A/PersistentConnection\ndyld[33987]: <6B0D099C-AC56-35DB-90F1-88A09E587FCB> /System/Library/PrivateFrameworks/SonicFoundation.framework/Versions/A/SonicFoundation\ndyld[33987]: /System/Library/PrivateFrameworks/AsyncAlgorithmsInternal.framework/Versions/A/AsyncAlgorithmsInternal\ndyld[33987]: <2CA62C12-37B5-345A-BF79-5D05F43F6BFB> /System/Library/PrivateFrameworks/FTAWD.framework/Versions/A/FTAWD\ndyld[33987]: <0A1C4D11-C108-35E9-A921-86ED86CF7446> /System/Library/PrivateFrameworks/Dendrite.framework/Versions/A/Dendrite\ndyld[33987]: /usr/lib/libtailspin.dylib\ndyld[33987]: <5FEA8C08-1577-3296-BC9C-7F3203E8EBFB> /System/Library/PrivateFrameworks/Osprey.framework/Versions/A/Osprey\ndyld[33987]: /System/Library/PrivateFrameworks/SiriTTS.framework/Versions/A/SiriTTS\ndyld[33987]: <0C005C4D-CA12-389C-9CCE-C4ED05B187E8> /System/Library/Frameworks/NaturalLanguage.framework/Versions/A/NaturalLanguage\ndyld[33987]: <0E502870-00F4-35D4-AF82-E7059244798E> /System/Library/PrivateFrameworks/GenerativeModels.framework/Versions/A/GenerativeModels\ndyld[33987]: <68474F39-798D-325B-B52F-3DE214F279AE> /System/Library/PrivateFrameworks/SiriPowerInstrumentation.framework/Versions/A/SiriPowerInstrumentation\ndyld[33987]: <5E36265A-7670-3D39-A2B8-71DA0AA131CF> /usr/lib/swift/libswiftNaturalLanguage.dylib\ndyld[33987]: <6794652C-86F0-37EB-838D-483177685E26> /System/Library/PrivateFrameworks/TailspinSymbolication.framework/Versions/A/TailspinSymbolication\ndyld[33987]: <089C1A34-2F4E-3649-94AA-B28A7ECB008B> /System/Library/PrivateFrameworks/Darwinup.framework/Versions/A/Darwinup\ndyld[33987]: /System/Library/PrivateFrameworks/SignpostSupport.framework/Versions/A/SignpostSupport\ndyld[33987]: <8C10B437-C282-37F5-834F-E7179C700373> /System/Library/PrivateFrameworks/FeatureFlagsSupport.framework/Versions/A/FeatureFlagsSupport\ndyld[33987]: /System/Library/PrivateFrameworks/ktrace.framework/Versions/A/ktrace\ndyld[33987]: /System/Library/PrivateFrameworks/SampleAnalysis.framework/Versions/A/SampleAnalysis\ndyld[33987]: <400B0E96-4869-37BE-9832-1A14C386148B> /System/Library/PrivateFrameworks/kperfdata.framework/Versions/A/kperfdata\ndyld[33987]: <7E3E0CF7-905A-3244-A0C9-0ADCC2E16415> /usr/lib/libdscsym.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity\ndyld[33987]: <6FBA9099-E428-3571-B940-0D210B6D0861> /System/Library/PrivateFrameworks/BulkSymbolication.framework/Versions/A/BulkSymbolication\ndyld[33987]: <90E600A3-0A27-348A-AA57-D1DF4FB305E8> /usr/lib/libTLE.dylib\ndyld[33987]: <173A632F-20F3-30C1-BC55-EFFE977BBB8E> /usr/lib/libmis.dylib\ndyld[33987]: <2D1B971F-6A7F-32D0-8B0F-F8FA3A13E8F1> /System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper\ndyld[33987]: <8F2949A6-43A0-30A2-B5D2-945949C5AA01> /System/Library/PrivateFrameworks/Espresso.framework/Versions/A/Espresso\ndyld[33987]: /System/Library/Frameworks/CoreML.framework/Versions/A/CoreML\ndyld[33987]: /usr/lib/libedit.3.dylib\ndyld[33987]: <465A74BC-F20D-3C05-9441-7E08EAA49FAF> /System/Library/PrivateFrameworks/ANECompiler.framework/Versions/A/ANECompiler\ndyld[33987]: /System/Library/PrivateFrameworks/AppleNeuralEngine.framework/Versions/A/AppleNeuralEngine\ndyld[33987]: <97C5C585-F5EE-323A-B949-69EAE9080871> /System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL\ndyld[33987]: <7401E849-7B2E-39A9-99D3-5CB0A6BBDFFE> /System/Library/Frameworks/MetalPerformanceShadersGraph.framework/Versions/A/MetalPerformanceShadersGraph\ndyld[33987]: /System/Library/PrivateFrameworks/MLCompilerServices.framework/Versions/A/MLCompilerServices\ndyld[33987]: /System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices\ndyld[33987]: <9EB04E94-EE2D-38A5-A214-00AF73DBE4E9> /usr/lib/libncurses.5.4.dylib\ndyld[33987]: /usr/lib/libsandbox.1.dylib\ndyld[33987]: <2F2EF0D7-2FE4-3A5A-8E4C-E1571C8D0C10> /usr/lib/libMatch.1.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/ODIE.framework/Versions/A/ODIE\ndyld[33987]: /System/Library/PrivateFrameworks/MLModelAsset.framework/Versions/A/MLModelAsset\ndyld[33987]: <22B4CD07-5C72-3CA4-9CD1-2C87686CDDE5> /System/Library/PrivateFrameworks/MLCompilerRuntime.framework/Versions/A/MLCompilerRuntime\ndyld[33987]: /System/Library/Frameworks/MLCompute.framework/Versions/A/MLCompute\ndyld[33987]: <6028DD46-8E5A-33F0-93B2-41FA480366CC> /System/Library/PrivateFrameworks/MLAssetIO.framework/Versions/A/MLAssetIO\ndyld[33987]: /usr/lib/swift/libswiftMLCompute.dylib\ndyld[33987]: <067E2603-4FEA-3CA5-8926-45F60681EDDB> /System/Library/PrivateFrameworks/AVFCore.framework/Versions/A/AVFCore\ndyld[33987]: /System/Library/PrivateFrameworks/AVFCapture.framework/Versions/A/AVFCapture\ndyld[33987]: <3B782AC2-00C4-3534-91D2-5C7242B32440> /System/Library/PrivateFrameworks/CMImaging.framework/Versions/A/CMImaging\ndyld[33987]: <1772C40D-6EF4-3F81-BA00-6EE8B05039A6> /System/Library/PrivateFrameworks/Quagga.framework/Versions/A/Quagga\ndyld[33987]: <57A10B70-C3C9-34C6-8D22-F5118B63E2F0> /System/Library/PrivateFrameworks/CMCapture.framework/Versions/A/CMCapture\ndyld[33987]: <1035C1AB-5058-3AFA-8D77-514901516251> /System/Library/Frameworks/CoreMediaIO.framework/Versions/A/CoreMediaIO\ndyld[33987]: /System/Library/PrivateFrameworks/CMCaptureDevice.framework/Versions/A/CMCaptureDevice\ndyld[33987]: <1F873909-B3B8-3D55-9673-9AFA86BB085B> /System/Library/PrivateFrameworks/CoreBrightness.framework/Versions/A/CoreBrightness\ndyld[33987]: /System/Library/PrivateFrameworks/CinematicFraming.framework/Versions/A/CinematicFraming\ndyld[33987]: <882BC08E-B1E1-3E52-AE8A-AC22A1BF2BE8> /System/Library/PrivateFrameworks/ModelManagerServices.framework/Versions/A/ModelManagerServices\ndyld[33987]: <3E83115F-D04B-3C8D-8646-35204AA2DB84> /System/Library/PrivateFrameworks/CPMS.framework/Versions/A/CPMS\ndyld[33987]: /System/Library/PrivateFrameworks/SystemStatus.framework/Versions/A/SystemStatus\ndyld[33987]: <2E109991-45C6-3783-8A36-B6A8070AAD67> /System/Library/Frameworks/CoreMotion.framework/Versions/A/CoreMotion\ndyld[33987]: /System/Library/PrivateFrameworks/TimeSync.framework/Versions/A/TimeSync\ndyld[33987]: <9B3D4CA3-7BCF-36C9-AA99-27BDFE7854CD> /System/Library/PrivateFrameworks/DistributedSensing.framework/Versions/A/DistributedSensing\ndyld[33987]: /System/Library/PrivateFrameworks/MobileBluetooth.framework/Versions/A/MobileBluetooth\ndyld[33987]: <0BAB3589-8D81-3C60-9F05-9D207E89F4B6> /System/Library/PrivateFrameworks/IOKitten.framework/Versions/A/IOKitten\ndyld[33987]: <05EC9C98-7211-39C9-B376-796F37327801> /System/Library/PrivateFrameworks/AppleIntelligenceReporting.framework/Versions/A/AppleIntelligenceReporting\ndyld[33987]: <47AAECAD-C28C-352E-BB86-7F292E0BFBC6> /System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji\ndyld[33987]: <1CA9048E-57DD-30F4-A3E6-FE6E97D5BF82> /usr/lib/libCRFSuite.dylib\ndyld[33987]: <327536E3-A27C-38C2-A67F-D6488D04CCEE> /System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling\ndyld[33987]: /System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP\ndyld[33987]: <95BA357E-906A-3183-A402-A41D486B5AB3> /System/Library/PrivateFrameworks/Montreal.framework/Versions/A/Montreal\ndyld[33987]: /usr/lib/libcmph.dylib\ndyld[33987]: /usr/lib/libmecab.dylib\ndyld[33987]: <92FAD15C-EEA5-34E9-B309-75A1CD1B620B> /usr/lib/libThaiTokenizer.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/GenerativeModelsFoundation.framework/Versions/A/GenerativeModelsFoundation\ndyld[33987]: /System/Library/PrivateFrameworks/TokenGeneration.framework/Versions/A/TokenGeneration\ndyld[33987]: <418985BB-52A3-34D4-8379-40DC4C63AA32> /System/Library/PrivateFrameworks/GenerativeFunctions.framework/Versions/A/GenerativeFunctions\ndyld[33987]: <63FD423F-836C-3034-BA48-100AE09A9140> /System/Library/PrivateFrameworks/GenerativeFunctionsFoundation.framework/Versions/A/GenerativeFunctionsFoundation\ndyld[33987]: /System/Library/PrivateFrameworks/ModelCatalog.framework/Versions/A/ModelCatalog\ndyld[33987]: <67C3B698-8279-30F1-9167-4730E6F41F5A> /System/Library/PrivateFrameworks/SensitiveContentAnalysisML.framework/Versions/A/SensitiveContentAnalysisML\ndyld[33987]: /System/Library/PrivateFrameworks/GenerativeFunctionsInstrumentation.framework/Versions/A/GenerativeFunctionsInstrumentation\ndyld[33987]: /System/Library/PrivateFrameworks/PromptKit.framework/Versions/A/PromptKit\ndyld[33987]: <12245228-2B9A-3B24-8C5E-10111D68BE65> /System/Library/PrivateFrameworks/ProactiveDaemonSupport.framework/Versions/A/ProactiveDaemonSupport\ndyld[33987]: <3166486F-3F65-31DB-8018-779FFA32DC71> /System/Library/PrivateFrameworks/TokenGenerationCore.framework/Versions/A/TokenGenerationCore\ndyld[33987]: <53B3126E-7B01-30DD-961A-510E9CFC3CF1> /System/Library/PrivateFrameworks/Trial.framework/Versions/A/Trial\ndyld[33987]: /System/Library/PrivateFrameworks/TrialProto.framework/Versions/A/TrialProto\ndyld[33987]: /System/Library/PrivateFrameworks/AppleFlatBuffers.framework/Versions/A/AppleFlatBuffers\ndyld[33987]: <642E3357-AB6D-3039-A818-EDB5D6A189C2> /System/Library/PrivateFrameworks/SentencePieceInternal.framework/Versions/A/SentencePieceInternal\ndyld[33987]: <10F83439-3A9F-316B-992E-451A72876715> /System/Library/Frameworks/Vision.framework/Versions/A/Vision\ndyld[33987]: /System/Library/PrivateFrameworks/CoreSceneUnderstanding.framework/Versions/A/CoreSceneUnderstanding\ndyld[33987]: <4E70B4ED-C8E0-3636-80E4-0939FE56BB63> /System/Library/PrivateFrameworks/VisionCore.framework/Versions/A/VisionCore\ndyld[33987]: /System/Library/PrivateFrameworks/DataDetectorsCore.framework/Versions/A/DataDetectorsCore\ndyld[33987]: /System/Library/Frameworks/Vision.framework/libfaceCore.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/Futhark.framework/Versions/A/Futhark\ndyld[33987]: /System/Library/PrivateFrameworks/InertiaCam.framework/Versions/A/InertiaCam\ndyld[33987]: /System/Library/PrivateFrameworks/TextRecognition.framework/Versions/A/TextRecognition\ndyld[33987]: <73F6F860-69AF-3162-86E0-A683642287D6> /System/Library/Frameworks/DataDetection.framework/Versions/A/DataDetection\ndyld[33987]: <1D5DF9CA-41FC-3B7B-B19F-2C435F3A66F2> /System/Library/PrivateFrameworks/TextInput.framework/Versions/A/TextInput\ndyld[33987]: /System/Library/PrivateFrameworks/CVNLP.framework/Versions/A/CVNLP\ndyld[33987]: <58AC6CAB-5B91-367F-932F-BD0939BBD125> /System/Library/PrivateFrameworks/IntentsFoundation.framework/Versions/A/IntentsFoundation\ndyld[33987]: <0368EA7D-01B2-3AA9-A6D6-A2A0850AC800> /System/Library/PrivateFrameworks/HIDDisplay.framework/Versions/A/HIDDisplay\ndyld[33987]: <6A5A8E21-A9E6-32A2-9BDB-8013F002AEF8> /usr/lib/libcups.2.dylib\ndyld[33987]: /System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos\ndyld[33987]: <4AB71911-9300-30D4-88CF-D20EFD75ACE6> /usr/lib/libresolv.9.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal\ndyld[33987]: <0CB2E7E3-E96F-343B-A4E7-545E74AF0255> /System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib\ndyld[33987]: <097F7235-CA53-3644-BB95-F6F912B4F2C7> /System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth\ndyld[33987]: /System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities\ndyld[33987]: /System/Library/PrivateFrameworks/AttributeGraph.framework/Versions/A/AttributeGraph\ndyld[33987]: /usr/lib/libAXSafeCategoryBundle.dylib\ndyld[33987]: /System/Library/Frameworks/TabularData.framework/Versions/A/TabularData\ndyld[33987]: <841D5662-2CB9-3A27-ADA7-E33AC5E45199> /System/Library/PrivateFrameworks/ArgumentParserInternal.framework/Versions/A/ArgumentParserInternal\ndyld[33987]: <4C851329-A9F4-3E9E-9E48-07FF4120DCF9> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib\ndyld[33987]: <5015CD96-C046-364D-AAE3-1F439044468B> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib\ndyld[33987]: <407BCF3E-A91F-3A7F-8B8C-DBB8E807990F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib\ndyld[33987]: <669ABE12-838F-3F14-8456-D60DE5DF8EB8> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib\ndyld[33987]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib\ndyld[33987]: <54A103BA-7D04-32DB-B204-179E2E0290CA> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib\ndyld[33987]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib\ndyld[33987]: <27479D70-8BF6-3D3C-B528-1BB9B1B98391> /System/Library/PrivateFrameworks/ApplePushService.framework/Versions/A/ApplePushService\ndyld[33987]: <676D50CC-8455-3267-B8E8-CA31B8EF8F91> /System/Library/Frameworks/CloudKit.framework/Versions/A/CloudKit\ndyld[33987]: /System/Library/PrivateFrameworks/CoreDuetDaemonProtocol.framework/Versions/A/CoreDuetDaemonProtocol\ndyld[33987]: <962EA390-008E-3DDC-B2A5-7B2DAF6E8786> /System/Library/PrivateFrameworks/DeviceIdentity.framework/Versions/A/DeviceIdentity\ndyld[33987]: /System/Library/Frameworks/SharedWithYouCore.framework/Versions/A/SharedWithYouCore\ndyld[33987]: <121798D0-5254-3547-8F96-0F2AF8D84250> /System/Library/PrivateFrameworks/CloudTelemetry.framework/Versions/A/CloudTelemetry\ndyld[33987]: <768DFB9E-7FB3-3998-A3AF-BEF0C6C740A7> /System/Library/PrivateFrameworks/AppleAccount.framework/Versions/A/AppleAccount\ndyld[33987]: /System/Library/PrivateFrameworks/C2.framework/Versions/A/C2\ndyld[33987]: <23871A43-55FD-3D0C-B29F-B143A64D1D8D> /System/Library/PrivateFrameworks/CloudCoreInternal.framework/Versions/A/CloudCoreInternal\ndyld[33987]: /System/Library/PrivateFrameworks/CloudAsset.framework/Versions/A/CloudAsset\ndyld[33987]: <36607924-B1B2-39ED-B6D1-29683EFB67A0> /System/Library/Frameworks/PushKit.framework/Versions/A/PushKit\ndyld[33987]: <26865685-385E-3120-9886-2082EEC20B20> /System/Library/Frameworks/CoreTransferable.framework/Versions/A/CoreTransferable\ndyld[33987]: <024DBF34-DF66-3164-825E-F77F85462E66> /System/Library/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth\ndyld[33987]: <87907862-52FF-3F24-AC29-7C1678BCD277> /System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport\ndyld[33987]: /System/Library/PrivateFrameworks/CloudTelemetryTools.framework/Versions/A/CloudTelemetryTools\ndyld[33987]: /System/Library/PrivateFrameworks/CloudTelemetryShared.dylib\ndyld[33987]: <7BEBC9F1-212D-37F4-B601-A7AAD12F7225> /System/Library/PrivateFrameworks/RTCReporting.framework/Versions/A/RTCReporting\ndyld[33987]: <7A222E30-8DD4-3B1D-B820-4EA33B797525> /System/Library/PrivateFrameworks/AAAFoundationSwift.framework/Versions/A/AAAFoundationSwift\ndyld[33987]: <9D724BE7-0B01-39F3-82BE-BCEDC6EBAC8A> /System/Library/PrivateFrameworks/AppleIDSSOAuthentication.framework/Versions/A/AppleIDSSOAuthentication\ndyld[33987]: <659AFBBD-E22E-3474-BCFE-298DA57B1464> /System/Library/PrivateFrameworks/UIFoundation.framework/Versions/A/UIFoundation\ndyld[33987]: <5B73C216-2ACE-3F8C-A2B3-7D35D5D0395A> /System/Library/PrivateFrameworks/CaptiveNetwork.framework/Versions/A/CaptiveNetwork\ndyld[33987]: /System/Library/PrivateFrameworks/EAP8021X.framework/Versions/A/EAP8021X\ndyld[33987]: <38DAF669-429F-384F-87D6-8550842EEB5E> /System/Library/PrivateFrameworks/CryptoKitPrivate.framework/Versions/A/CryptoKitPrivate\ndyld[33987]: <627D64D5-2D3C-3EC6-B4AB-FEF4DEE40871> /System/Library/PrivateFrameworks/FindMyDevice.framework/Versions/A/FindMyDevice\ndyld[33987]: <930F9F83-A947-3788-9FBA-49872FC3AF8D> /System/Library/PrivateFrameworks/FMCoreLite.framework/Versions/A/FMCoreLite\ndyld[33987]: <5F356BA6-47B5-382B-B54A-1550BB138A62> /System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement\ndyld[33987]: <6508C698-D587-3B5A-B95B-A3A3F78CE122> /usr/lib/libCheckFix.dylib\ndyld[33987]: <29AA0F7F-26F4-35B3-96DF-8A67B00A58AB> /System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities\ndyld[33987]: <1ACDAA8A-EB43-37C7-B661-39B1C0E05290> /System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary\ndyld[33987]: <12479A32-B72F-3A09-BB03-BA56C37853B5> /System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore\ndyld[33987]: /usr/lib/libapp_launch_measurement.dylib\ndyld[33987]: <4F3BEA3B-A363-3D04-B903-9B613C993CA1> /System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices\ndyld[33987]: <6C426EA5-7F1E-333E-BB5D-74465EFED12B> /usr/lib/libxslt.1.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement\ndyld[33987]: <2E99AD96-DC1C-3643-9988-273AB6844EFC> /usr/lib/libcurl.4.dylib\ndyld[33987]: <46D13DA8-E7BD-37DC-91DD-D5E6CE00C2B8> /usr/lib/libcrypto.46.dylib\ndyld[33987]: <07D5F4C6-1A13-344C-882B-0B0A08048DE5> /usr/lib/libssl.48.dylib\ndyld[33987]: <8CABDD64-E6C6-3B77-B839-2E2B875CE0FE> /System/Library/Frameworks/LDAP.framework/Versions/A/LDAP\ndyld[33987]: <96C0BAAA-7FE6-3277-AFBC-31926F5935EE> /System/Library/PrivateFrameworks/TrustEvaluationAgent.framework/Versions/A/TrustEvaluationAgent\ndyld[33987]: <7CF2A32E-72DD-34F7-B179-A17ED3D7DD75> /usr/lib/libsasl2.2.dylib\ndyld[33987]: /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa\ndyld[33987]: /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit\ndyld[33987]: <4840B78C-D96B-35B9-85C7-E5889C44A7C4> /System/Library/PrivateFrameworks/CollectionViewCore.framework/Versions/A/CollectionViewCore\ndyld[33987]: /System/Library/PrivateFrameworks/XCTTargetBootstrap.framework/Versions/A/XCTTargetBootstrap\ndyld[33987]: <0D6F3043-5372-3B15-96BC-6F45AD86F410> /System/Library/PrivateFrameworks/UserActivity.framework/Versions/A/UserActivity\ndyld[33987]: <7CDC68D4-0845-3053-AB80-C5A1F354060F> /System/Library/PrivateFrameworks/FrontBoard.framework/Versions/A/FrontBoard\ndyld[33987]: /System/Library/PrivateFrameworks/UIIntelligenceSupport.framework/Versions/A/UIIntelligenceSupport\ndyld[33987]: <9EB0840F-B045-3529-9467-1470A3C6CA02> /System/Library/Frameworks/SwiftUICore.framework/Versions/A/SwiftUICore\ndyld[33987]: <84FB5635-42EE-3BB5-B7CD-8A354AD7DB0A> /System/Library/PrivateFrameworks/WritingTools.framework/Versions/A/WritingTools\ndyld[33987]: <6ECD36F7-0A2E-3631-A57F-FD0152173454> /System/Library/PrivateFrameworks/WindowManagement.framework/Versions/A/WindowManagement\ndyld[33987]: /System/Library/PrivateFrameworks/FocusEngine.framework/Versions/A/FocusEngine\ndyld[33987]: <2BC041DF-695A-32EE-B164-1C35C2AFFC53> /System/Library/PrivateFrameworks/DesignLibrary.framework/Versions/A/DesignLibrary\ndyld[33987]: /System/Library/PrivateFrameworks/DFRFoundation.framework/Versions/A/DFRFoundation\ndyld[33987]: <365E81B9-B9BA-3F4F-83FD-86A35CFBC8AC> /System/Library/PrivateFrameworks/UpdateCycle.framework/Versions/A/UpdateCycle\ndyld[33987]: <38408482-CE3B-359E-9465-7FEB4BB79B54> /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox\ndyld[33987]: /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/SpeechRecognition.framework/Versions/A/SpeechRecognition\ndyld[33987]: <74C54353-A613-35A2-857A-737BB48906F9> /System/Library/PrivateFrameworks/PerformanceAnalysis.framework/Versions/A/PerformanceAnalysis\ndyld[33987]: <22008BA9-C61B-3FAD-A1A0-F6A1CD220343> /System/Library/Frameworks/Accessibility.framework/Versions/A/Accessibility\ndyld[33987]: <028E944B-66C4-39E2-A436-FB93FB6CED4E> /System/Library/Frameworks/Symbols.framework/Versions/A/Symbols\ndyld[33987]: /System/Library/PrivateFrameworks/Gestures.framework/Versions/A/Gestures\ndyld[33987]: <713AEF7A-43B6-3735-8AB5-361F5EE06BBA> /usr/lib/swift/libswiftSpatial.dylib\ndyld[33987]: /usr/lib/swift/libswiftCoreGraphics.dylib\ndyld[33987]: <14A11A94-6A52-3D24-9267-42EDAEEC5FDD> /usr/lib/swift/libswiftFoundation.dylib\ndyld[33987]: <76A7FE10-AD26-3505-A8CC-D37AC1C24D32> /usr/lib/swift/libswiftSwiftOnoneSupport.dylib\ndyld[33987]: <4B5C0268-23EB-3E20-8F57-CDECFB6E3205> /usr/lib/swift/libswiftsys_time.dylib\ndyld[33987]: /System/Library/PrivateFrameworks/CoreMaterial.framework/Versions/A/CoreMaterial\ndyld[33987]: <68B7C15F-537C-3FEF-838B-DC548772A45F> /usr/lib/libSpatial.dylib\ndyld[33987]: <183FD4D6-D766-34FC-B8E1-7C4D17435AC3> /System/Library/SubFrameworks/UIUtilities.framework/Versions/A/UIUtilities\ndyld[33987]: move loaded to delayed: XPCSupport\ndyld[33987]: move loaded to delayed: libcmark-gfm.dylib\ndyld[33987]: move loaded to delayed: GenerationalStorage\ndyld[33987]: move loaded to delayed: CacheDelete\ndyld[33987]: move loaded to delayed: QuickLookThumbnailing\ndyld[33987]: move loaded to delayed: FileProvider\ndyld[33987]: move loaded to delayed: DesktopServicesPriv\ndyld[33987]: move loaded to delayed: AOSKit\ndyld[33987]: move loaded to delayed: AppSSOCore\ndyld[33987]: move loaded to delayed: BackgroundSystemTasks\ndyld[33987]: move loaded to delayed: DuetActivityScheduler\ndyld[33987]: move loaded to delayed: FTServices\ndyld[33987]: move loaded to delayed: libMemoryResourceException.dylib\ndyld[33987]: move loaded to delayed: NetworkScore\ndyld[33987]: move loaded to delayed: NetworkServiceProxy\ndyld[33987]: move loaded to delayed: StreamingExtractor\ndyld[33987]: move loaded to delayed: SymptomReporter\ndyld[33987]: move loaded to delayed: libnetworkextension.dylib\ndyld[33987]: move loaded to delayed: CloudServices\ndyld[33987]: move loaded to delayed: SymptomAnalytics\ndyld[33987]: move loaded to delayed: libcupolicy.dylib\ndyld[33987]: move loaded to delayed: NetworkExtension\ndyld[33987]: move loaded to delayed: libnwswifttls.dylib\ndyld[33987]: move loaded to delayed: libpcap.A.dylib\ndyld[33987]: move loaded to delayed: CoreDuetContext\ndyld[33987]: move loaded to delayed: CoreDuet\ndyld[33987]: move loaded to delayed: CoreLocation\ndyld[33987]: move loaded to delayed: Intents\ndyld[33987]: move loaded to delayed: libCGInterfaces.dylib\ndyld[33987]: move loaded to delayed: AccelerateGPU\ndyld[33987]: move loaded to delayed: AudioDSPGraph\ndyld[33987]: move loaded to delayed: AssistantServices\ndyld[33987]: move loaded to delayed: SAObjects\ndyld[33987]: move loaded to delayed: MediaRemote\ndyld[33987]: move loaded to delayed: SiriTTSService\ndyld[33987]: move loaded to delayed: SiriCrossDeviceArbitration\ndyld[33987]: move loaded to delayed: FaceTimeNameUtility\ndyld[33987]: move loaded to delayed: AuthKit\ndyld[33987]: move loaded to delayed: SiriCrossDeviceArbitrationFeedback\ndyld[33987]: move loaded to delayed: libswiftCoreLocation.dylib\ndyld[33987]: move loaded to delayed: UIKitServices\ndyld[33987]: move loaded to delayed: AudioAccessoryServices\ndyld[33987]: move loaded to delayed: Sharing\ndyld[33987]: move loaded to delayed: IDSFoundation\ndyld[33987]: move loaded to delayed: Apple80211\ndyld[33987]: move loaded to delayed: CoreWLAN\ndyld[33987]: move loaded to delayed: IMFoundation\ndyld[33987]: move loaded to delayed: Marco\ndyld[33987]: move loaded to delayed: CommonUtilities\ndyld[33987]: move loaded to delayed: Engram\ndyld[33987]: move loaded to delayed: XPCDistributed\ndyld[33987]: move loaded to delayed: libtidy.A.dylib\ndyld[33987]: move loaded to delayed: Bom\ndyld[33987]: move loaded to delayed: libParallelCompression.dylib\ndyld[33987]: move loaded to delayed: MultiverseSupport\ndyld[33987]: move loaded to delayed: DiskManagement\ndyld[33987]: move loaded to delayed: AppleIDAuthSupport\ndyld[33987]: move loaded to delayed: AAAFoundation\ndyld[33987]: move loaded to delayed: KeychainCircle\ndyld[33987]: move loaded to delayed: URLFormatting\ndyld[33987]: move loaded to delayed: libcsfde.dylib\ndyld[33987]: move loaded to delayed: libCoreStorage.dylib\ndyld[33987]: move loaded to delayed: ProtectedCloudStorage\ndyld[33987]: move loaded to delayed: EFILogin\ndyld[33987]: move loaded to delayed: OctagonTrust\ndyld[33987]: move loaded to delayed: MediaServices\ndyld[33987]: move loaded to delayed: IDS\ndyld[33987]: move loaded to delayed: LocalAuthentication\ndyld[33987]: move loaded to delayed: LocalAuthenticationCore\ndyld[33987]: move loaded to delayed: LocalAuthenticationCredentialServices\ndyld[33987]: move loaded to delayed: SharedUtils\ndyld[33987]: move loaded to delayed: PersistentConnection\ndyld[33987]: move loaded to delayed: SonicFoundation\ndyld[33987]: move loaded to delayed: AsyncAlgorithmsInternal\ndyld[33987]: move loaded to delayed: FTAWD\ndyld[33987]: move loaded to delayed: libtailspin.dylib\ndyld[33987]: move loaded to delayed: Osprey\ndyld[33987]: move loaded to delayed: SiriTTS\ndyld[33987]: move loaded to delayed: SiriPowerInstrumentation\ndyld[33987]: move loaded to delayed: TailspinSymbolication\ndyld[33987]: move loaded to delayed: Darwinup\ndyld[33987]: move loaded to delayed: SignpostSupport\ndyld[33987]: move loaded to delayed: FeatureFlagsSupport\ndyld[33987]: move loaded to delayed: ktrace\ndyld[33987]: move loaded to delayed: SampleAnalysis\ndyld[33987]: move loaded to delayed: kperfdata\ndyld[33987]: move loaded to delayed: libdscsym.dylib\ndyld[33987]: move loaded to delayed: BulkSymbolication\ndyld[33987]: move loaded to delayed: IntentsFoundation\ndyld[33987]: move loaded to delayed: ApplePushService\ndyld[33987]: move loaded to delayed: CloudKit\ndyld[33987]: move loaded to delayed: CoreDuetDaemonProtocol\ndyld[33987]: move loaded to delayed: DeviceIdentity\ndyld[33987]: move loaded to delayed: SharedWithYouCore\ndyld[33987]: move loaded to delayed: CloudTelemetry\ndyld[33987]: move loaded to delayed: AppleAccount\ndyld[33987]: move loaded to delayed: C2\ndyld[33987]: move loaded to delayed: CloudCoreInternal\ndyld[33987]: move loaded to delayed: CloudAsset\ndyld[33987]: move loaded to delayed: PushKit\ndyld[33987]: move loaded to delayed: CloudTelemetryTools\ndyld[33987]: move loaded to delayed: CloudTelemetryShared.dylib\ndyld[33987]: move loaded to delayed: RTCReporting\ndyld[33987]: move loaded to delayed: AAAFoundationSwift\ndyld[33987]: move loaded to delayed: AppleIDSSOAuthentication\ndyld[33987]: move loaded to delayed: CaptiveNetwork\ndyld[33987]: move loaded to delayed: EAP8021X\ndyld[33987]: move loaded to delayed: CryptoKitPrivate\ndyld[33987]: move loaded to delayed: FindMyDevice\ndyld[33987]: move loaded to delayed: FMCoreLite\ndyld[33987]: move loaded to delayed: ServiceManagement\ndyld[33987]: <9B0A2398-8610-35D6-B7F3-B76933F2AAF4> /System/Library/Extensions/AGXMetalG16G_B0.bundle/Contents/MacOS/AGXMetalG16G_B0\ndyld[33987]: <9235E8EC-599D-386F-8A8A-6B6A92D33369> /System/Library/PrivateFrameworks/IOGPU.framework/Versions/A/IOGPU\n" + }, + { + "command": [ + "/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/webscene_angle_probe", + "metal", + "3" + ], + "passed": true, + "exitCode": 0, + "stdout": "{\"schemaVersion\":1,\"probe\":\"angle\",\"status\":\"passed\",\"hardwareAccelerated\":true,\"backend\":\"metal\",\"esMajor\":3,\"adapter\":\"ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)\",\"vendor\":\"Google Inc. (Apple)\",\"driver\":\"OpenGL ES 3.0 (ANGLE 2.1.1 git hash: 082d85ba19ef)\",\"hardwareEvidence\":\"Explicit ANGLE hardware device on native Metal/D3D11 backend\",\"verifiedPixels\":68,\"webglCompatibleContext\":true,\"robustResourceInitialization\":true,\"diagnosticReadback\":true,\"expectedRGBA\":[51,102,153,255],\"tolerance\":1}\n", + "loaderTrace": "dyld[33988]: <4A19C6E0-06A8-3EFB-8467-594471CDCC90> /private/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/webscene_angle_probe\ndyld[33988]: <4C4C44AF-5555-3144-A120-3E4412E00745> /private/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/libEGL.dylib\ndyld[33988]: <4C4C4498-5555-3144-A17E-05A77F85C5AE> /private/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/libGLESv2.dylib\ndyld[33988]: <493E76D9-74D4-333B-A3B2-E5F9BC86429D> /System/Library/Frameworks/Metal.framework/Versions/A/Metal\ndyld[33988]: <6CD959AA-4825-306A-864A-BD69EC5F2DC0> /usr/lib/libDiagnosticMessagesClient.dylib\ndyld[33988]: <526C249F-FF2E-3DC4-A639-B41A032E8CCE> /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libllvm-flatbuffers.dylib\ndyld[33988]: <12372585-DF92-33EF-B632-714FAA13260A> /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit\ndyld[33988]: /System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator\ndyld[33988]: <5FFE1FFA-6BD0-32AF-A815-7543731CA763> /usr/lib/libbz2.1.0.dylib\ndyld[33988]: <1FDD3B19-C04A-3EE7-B7DF-E1F89954A696> /System/Library/PrivateFrameworks/FramePacing.framework/Versions/A/FramePacing\ndyld[33988]: /usr/lib/libMobileGestalt.dylib\ndyld[33988]: <56AE2857-29E0-34E9-B2C3-EE8E951EEFC5> /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices\ndyld[33988]: /usr/lib/libcompression.dylib\ndyld[33988]: <2C410B78-B9A5-30DC-8D83-FFEC1277F34C> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib\ndyld[33988]: <5556FD64-9D47-3547-961E-3A27681F3C51> /System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface\ndyld[33988]: <91DACE39-FA28-3191-818D-1FCC6A0E615A> /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation\ndyld[33988]: <9D0387FC-E8F6-3004-9C95-CA68EA715C8B> /System/Library/Frameworks/Security.framework/Versions/A/Security\ndyld[33988]: /System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics\ndyld[33988]: /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libGPUCompilerUtils.dylib\ndyld[33988]: <03BD9E32-CF0A-37B0-898A-3CE8DE06D842> /usr/lib/libobjc.A.dylib\ndyld[33988]: /usr/lib/libc++.1.dylib\ndyld[33988]: <4FED5EE2-5D3E-35B1-A170-9859C4B683BB> /usr/lib/libSystem.B.dylib\ndyld[33988]: <9B672762-7B1F-30BC-96DE-F176B372D66D> /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation\ndyld[33988]: <9CD7B1E1-3E47-339C-A193-2392E3E0ED23> /usr/lib/system/libcache.dylib\ndyld[33988]: <3B110564-5278-3CB0-85F1-2CE8431FF935> /usr/lib/system/libcommonCrypto.dylib\ndyld[33988]: <6FB345CA-7F5C-3263-A23F-143F7539FD8A> /usr/lib/system/libcompiler_rt.dylib\ndyld[33988]: /usr/lib/system/libcopyfile.dylib\ndyld[33988]: <0642DDAD-4771-3C82-805C-E7C6701C1461> /usr/lib/system/libcorecrypto.dylib\ndyld[33988]: /usr/lib/system/libdispatch.dylib\ndyld[33988]: <957F93B3-8805-39C7-9C51-EDD1715F550E> /usr/lib/system/libdyld.dylib\ndyld[33988]: <7E863FCA-F3FF-32C7-8A8C-F983E946AFC3> /usr/lib/system/libkeymgr.dylib\ndyld[33988]: <949131E5-BDA2-39BA-AA50-62651BB51802> /usr/lib/system/libmacho.dylib\ndyld[33988]: /usr/lib/system/libquarantine.dylib\ndyld[33988]: <7460B5AE-469A-36A0-A7EC-6C7D69628E86> /usr/lib/system/libremovefile.dylib\ndyld[33988]: <54439739-33EE-3273-839F-CBA67D7F5CB1> /usr/lib/system/libsystem_asl.dylib\ndyld[33988]: /usr/lib/system/libsystem_blocks.dylib\ndyld[33988]: /usr/lib/system/libsystem_c.dylib\ndyld[33988]: /usr/lib/system/libsystem_collections.dylib\ndyld[33988]: /usr/lib/system/libsystem_configuration.dylib\ndyld[33988]: <14B2A47F-19C8-392F-8FDB-FE8AE375DD41> /usr/lib/system/libsystem_containermanager.dylib\ndyld[33988]: /usr/lib/system/libsystem_coreservices.dylib\ndyld[33988]: <8E07D22E-CE5A-38A0-B091-5B0338C326F5> /usr/lib/system/libsystem_darwin.dylib\ndyld[33988]: <971A4F65-493D-39F3-846D-0D33FA2769FD> /usr/lib/system/libsystem_darwindirectory.dylib\ndyld[33988]: <305F4398-E688-3384-B351-02D865EC8A04> /usr/lib/system/libsystem_dnssd.dylib\ndyld[33988]: <750CA446-92EA-3A56-9A7B-CC0841686C50> /usr/lib/system/libsystem_eligibility.dylib\ndyld[33988]: /usr/lib/system/libsystem_featureflags.dylib\ndyld[33988]: <9B5FB84B-31AD-3EA7-8F89-8C700D369DC8> /usr/lib/system/libsystem_info.dylib\ndyld[33988]: /usr/lib/system/libsystem_m.dylib\ndyld[33988]: /usr/lib/system/libsystem_malloc.dylib\ndyld[33988]: <9C7B1EEB-47BE-3791-93A9-CFC693CB9417> /usr/lib/system/libsystem_networkextension.dylib\ndyld[33988]: <15799128-6CBD-30D6-A2BB-B9D02B4470C0> /usr/lib/system/libsystem_notify.dylib\ndyld[33988]: <54688162-B50D-3D31-A1E8-7B9766D3530D> /usr/lib/system/libsystem_sandbox.dylib\ndyld[33988]: /usr/lib/system/libsystem_sanitizers.dylib\ndyld[33988]: /usr/lib/system/libsystem_secinit.dylib\ndyld[33988]: /usr/lib/system/libsystem_kernel.dylib\ndyld[33988]: /usr/lib/system/libsystem_platform.dylib\ndyld[33988]: /usr/lib/system/libsystem_pthread.dylib\ndyld[33988]: <229122B9-B8B1-3F2F-870E-8650AE3C4FB5> /usr/lib/system/libsystem_symptoms.dylib\ndyld[33988]: <93F1DD8C-6CD9-32B9-B222-D23DA5D161B4> /usr/lib/system/libsystem_trace.dylib\ndyld[33988]: <7194FF5B-A6C5-3D67-B00A-90209F10D603> /usr/lib/system/libsystem_trial.dylib\ndyld[33988]: <05FD0014-55B1-3B8A-A6BA-6C7A389C4123> /usr/lib/system/libunwind.dylib\ndyld[33988]: <33E44C2D-D65E-37A6-B85F-1A4CF524A050> /usr/lib/system/libxpc.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/XPCSupport.framework/Versions/A/XPCSupport\ndyld[33988]: <83794FB3-DE9B-3D23-AB5E-2C1D5D30F134> /usr/lib/swift/libswiftCore.dylib\ndyld[33988]: /usr/lib/libc++abi.dylib\ndyld[33988]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/libRosetta.dylib\ndyld[33988]: <4FD234EA-2C18-3C25-8BD0-B1F4805C6675> /usr/lib/swift/libswiftObjectiveC.dylib\ndyld[33988]: <9E3C7597-446F-3C50-9930-2425D9252C0C> /usr/lib/libswiftPrespecialized.dylib\ndyld[33988]: <1479C415-3678-3968-AC77-06373490860E> /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration\ndyld[33988]: <13EDE3A5-A7D9-3FB8-B0C2-2FB7F7272B34> /usr/lib/libz.1.dylib\ndyld[33988]: <54AD73AF-852E-3CD6-8B7D-E73BE79857D3> /System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout\ndyld[33988]: <1A2A9A41-5269-3B0C-BCEE-B446966CE366> /usr/lib/libcmark-gfm.dylib\ndyld[33988]: <820D290D-51A0-3064-A1F2-4F0AAF7E6BF4> /usr/lib/libfakelink.dylib\ndyld[33988]: <4A3B95C5-AA2E-338C-9398-56895AF82D97> /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork\ndyld[33988]: <332C4B80-5B3C-34E7-AD1F-F6131E607F95> /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration\ndyld[33988]: <0048DB96-1737-3FC5-AF0C-AF784FA24A03> /usr/lib/libarchive.2.dylib\ndyld[33988]: <53A3E31E-06A8-325E-B5A8-316B88AA3C92> /usr/lib/libicucore.A.dylib\ndyld[33988]: <1E8A4F9E-3954-3458-B3BB-BE97F961C105> /usr/lib/libxml2.2.dylib\ndyld[33988]: /usr/lib/liblangid.dylib\ndyld[33988]: /System/Library/Frameworks/Combine.framework/Versions/A/Combine\ndyld[33988]: <6098453F-4D7E-38B4-8ADC-02C9FF51E14A> /System/Library/PrivateFrameworks/CollectionsInternal.framework/Versions/A/CollectionsInternal\ndyld[33988]: <9A1279D4-575A-3E48-A460-A631A3F82D18> /System/Library/PrivateFrameworks/ReflectionInternal.framework/Versions/A/ReflectionInternal\ndyld[33988]: <6D89CD71-A86D-3D78-A64B-96AB79550F79> /System/Library/PrivateFrameworks/RuntimeInternal.framework/Versions/A/RuntimeInternal\ndyld[33988]: <4109E8DD-0A81-310C-B1B3-23B87186D0D8> /System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking\ndyld[33988]: <4975D13C-2AC5-3473-85C0-98054A81D7C6> /usr/lib/swift/libswiftCoreFoundation.dylib\ndyld[33988]: <1DB56DA9-CF6B-3023-ABDF-5A37CB79223C> /usr/lib/swift/libswiftDarwin.dylib\ndyld[33988]: /usr/lib/swift/libswiftDispatch.dylib\ndyld[33988]: <06A92787-4440-3757-AF32-F2B331C753A2> /usr/lib/swift/libswiftIOKit.dylib\ndyld[33988]: <7CD9BDE7-F36B-3471-9295-38E181D6D9E5> /usr/lib/swift/libswiftSystem.dylib\ndyld[33988]: <24AEDAC1-C1EE-30F4-8818-72EBF8969D0C> /usr/lib/swift/libswiftXPC.dylib\ndyld[33988]: <52F59382-A6A6-3F55-8A85-D9FB822D370F> /usr/lib/swift/libswift_Builtin_float.dylib\ndyld[33988]: <8E168857-47F4-349F-A718-A18DB144FCB0> /usr/lib/swift/libswift_Concurrency.dylib\ndyld[33988]: <85246B9A-A757-3F67-B792-3A2F7BB2BB25> /usr/lib/swift/libswift_DarwinFoundation1.dylib\ndyld[33988]: <8DF0116D-DFC9-3906-9DF6-F1DBC47E324B> /usr/lib/swift/libswift_StringProcessing.dylib\ndyld[33988]: /usr/lib/swift/libswiftos.dylib\ndyld[33988]: <7D56DA94-31EB-35F0-B886-4010C075E035> /System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal\ndyld[33988]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/liboah.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/GenerationalStorage.framework/Versions/A/GenerationalStorage\ndyld[33988]: /System/Library/PrivateFrameworks/CacheDelete.framework/Versions/A/CacheDelete\ndyld[33988]: <36F215D1-A2C0-32CA-ADD8-6D85AB48A772> /System/Library/Frameworks/QuickLookThumbnailing.framework/Versions/A/QuickLookThumbnailing\ndyld[33988]: <5EA68C5E-69B0-3011-9D66-AEF49D82B29D> /System/Library/Frameworks/FileProvider.framework/Versions/A/FileProvider\ndyld[33988]: /System/Library/PrivateFrameworks/DiskImages.framework/Versions/A/DiskImages\ndyld[33988]: <49121861-2603-3B0A-B664-BAD9E729BE5D> /System/Library/Frameworks/NetFS.framework/Versions/A/NetFS\ndyld[33988]: /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/DesktopServicesPriv\ndyld[33988]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents\ndyld[33988]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore\ndyld[33988]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata\ndyld[33988]: <61677289-93B7-382F-86CA-B856361D293F> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices\ndyld[33988]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit\ndyld[33988]: <435D6243-695B-3543-A722-10106F5696BD> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE\ndyld[33988]: <01579E0C-9D85-3521-8916-4DDC990CD064> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices\ndyld[33988]: <6A26D479-5926-330B-9FB8-9B7A6BE8E239> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices\ndyld[33988]: <297AC970-E432-3BBD-986C-36782634062E> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList\ndyld[33988]: /usr/lib/libapple_nghttp2.dylib\ndyld[33988]: /usr/lib/libsqlite3.dylib\ndyld[33988]: <5F6B668E-00B2-3BEC-959F-26BD6B50D42B> /System/Library/Frameworks/Accounts.framework/Versions/A/Accounts\ndyld[33988]: <44CD8313-2D5B-3A34-BACA-EF8800803B4A> /System/Library/PrivateFrameworks/AOSKit.framework/Versions/A/AOSKit\ndyld[33988]: <5198BFE1-41D2-33D5-A9E0-C63F81A512D3> /System/Library/PrivateFrameworks/AppSSOCore.framework/Versions/A/AppSSOCore\ndyld[33988]: <61B2B917-D14A-38AD-A439-16E1C635441A> /System/Library/PrivateFrameworks/AppSupport.framework/Versions/A/AppSupport\ndyld[33988]: <816EC446-7C41-3A2F-A582-7CB856797C09> /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation\ndyld[33988]: <10A4E63B-A1EB-31CC-B3E1-DB4FE115FC84> /System/Library/PrivateFrameworks/BackgroundSystemTasks.framework/Versions/A/BackgroundSystemTasks\ndyld[33988]: <38C8FBEC-DE88-33FE-B742-A192F22CC754> /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics\ndyld[33988]: /System/Library/PrivateFrameworks/DuetActivityScheduler.framework/Versions/A/DuetActivityScheduler\ndyld[33988]: <0E78989C-854F-3664-AD92-6B7B6D04191C> /System/Library/PrivateFrameworks/FTServices.framework/Versions/A/FTServices\ndyld[33988]: <277D18EF-39E4-3F72-99E8-8D3DF65ED1D0> /System/Library/Frameworks/GSS.framework/Versions/A/GSS\ndyld[33988]: <5ACC6C0E-51E9-3B5A-B24F-89B22D070878> /System/Library/PrivateFrameworks/InternationalSupport.framework/Versions/A/InternationalSupport\ndyld[33988]: /usr/lib/libMemoryResourceException.dylib\ndyld[33988]: <798012E0-3FFC-3B8D-AC74-E7B7DAEA7E66> /System/Library/PrivateFrameworks/NetworkScore.framework/Versions/A/NetworkScore\ndyld[33988]: <2C93123F-99C8-3B8D-AAE6-3A817BE0A2BF> /System/Library/PrivateFrameworks/NetworkServiceProxy.framework/Versions/A/NetworkServiceProxy\ndyld[33988]: /System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices\ndyld[33988]: /System/Library/PrivateFrameworks/StreamingExtractor.framework/Versions/A/StreamingExtractor\ndyld[33988]: <1F2EDC7B-8F28-3721-8A60-F6E1BCFC29A3> /System/Library/PrivateFrameworks/StreamingZip.framework/Versions/A/StreamingZip\ndyld[33988]: <5DA62AF9-3D46-3D17-A3EB-7026A2F006DF> /System/Library/PrivateFrameworks/SymptomReporter.framework/Versions/A/SymptomReporter\ndyld[33988]: <8E04C57D-3651-386E-83D5-4728B732F214> /usr/lib/libenergytrace.dylib\ndyld[33988]: /usr/lib/libnetworkextension.dylib\ndyld[33988]: <1C7E652B-6B94-3180-93A6-EF8DBA3A5448> /System/Library/Frameworks/Network.framework/Versions/A/Network\ndyld[33988]: <633BCB5F-F063-3D5A-B52A-F72AE236824B> /usr/lib/libbsm.0.dylib\ndyld[33988]: /usr/lib/system/libkxld.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/AppleKeyStore.framework/Versions/A/AppleKeyStore\ndyld[33988]: /usr/lib/libCoreEntitlements.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity\ndyld[33988]: /System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer\ndyld[33988]: <81F4A8BA-C80F-3B53-82E7-57F6928609C5> /System/Library/PrivateFrameworks/CloudServices.framework/Versions/A/CloudServices\ndyld[33988]: <737479F2-7B20-3DB6-B9F4-0DAA1B73E9D0> /System/Library/PrivateFrameworks/SymptomDiagnosticReporter.framework/Versions/A/SymptomDiagnosticReporter\ndyld[33988]: /System/Library/PrivateFrameworks/CrashReporterSupport.framework/Versions/A/CrashReporterSupport\ndyld[33988]: /System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression\ndyld[33988]: <0EAB1F4A-9275-3FED-8EA6-E962ACDDEE5D> /usr/lib/libcoretls.dylib\ndyld[33988]: <6937D729-7EF4-3972-9E12-694C17C1C1AB> /usr/lib/libcoretls_cfhelpers.dylib\ndyld[33988]: <7E84FD3B-E90E-317E-AC19-17B70AC809E5> /usr/lib/libpam.2.dylib\ndyld[33988]: /usr/lib/libxar.1.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS\ndyld[33988]: /System/Library/PrivateFrameworks/SwiftASN1Internal.framework/Versions/A/SwiftASN1Internal\ndyld[33988]: /usr/lib/libutil.dylib\ndyld[33988]: <4C6139EE-BF87-37A6-B226-830A6FDC36F8> /System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo\ndyld[33988]: <2BC48182-F354-3AB0-8F18-0C60CAAFE398> /System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer\ndyld[33988]: <7C50137B-2ABD-3819-B033-AE65B05A6085> /System/Library/PrivateFrameworks/CoreWiFi.framework/Versions/A/CoreWiFi\ndyld[33988]: /System/Library/PrivateFrameworks/LoggingSupport.framework/Versions/A/LoggingSupport\ndyld[33988]: <91A461DE-C8E8-3868-B393-BA6E5A17DF2A> /System/Library/PrivateFrameworks/MobileAsset.framework/Versions/A/MobileAsset\ndyld[33988]: /System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog\ndyld[33988]: /System/Library/PrivateFrameworks/Rapport.framework/Versions/A/Rapport\ndyld[33988]: <9F52706C-75BD-34AF-A29E-C26608124ACC> /System/Library/Frameworks/SwiftData.framework/Versions/A/SwiftData\ndyld[33988]: <259877CE-4E2C-34A9-A07F-FEE2999D7B2F> /System/Library/PrivateFrameworks/Symptoms.framework/Versions/A/Frameworks/SymptomAnalytics.framework/Versions/A/SymptomAnalytics\ndyld[33988]: /System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers\ndyld[33988]: <4A78C569-FF0D-398B-9C25-33453F0CEC40> /System/Library/PrivateFrameworks/UserManagement.framework/Versions/A/UserManagement\ndyld[33988]: <5BF55637-F306-3D79-B5A1-DB8A871DAD4B> /usr/lib/libboringssl.dylib\ndyld[33988]: <831C79C1-8DBE-31A3-AA4E-8E2B041488D6> /usr/lib/libcupolicy.dylib\ndyld[33988]: <88925A0C-4960-3F6D-AF3A-B1983F7B3D18> /usr/lib/libdns_services.dylib\ndyld[33988]: <9753F471-40DD-3B9E-9D64-8D07C1B06BC9> /System/Library/Frameworks/NetworkExtension.framework/Versions/A/NetworkExtension\ndyld[33988]: /usr/lib/libnwswifttls.dylib\ndyld[33988]: <6F59933A-6618-33F1-BE52-E7FC3BF7A1EF> /usr/lib/libpcap.A.dylib\ndyld[33988]: <5E89267F-C684-348D-8356-F9DAD8B4CB13> /usr/lib/libquic.dylib\ndyld[33988]: /usr/lib/libusrtcp.dylib\ndyld[33988]: <1617DBB1-2BFF-3619-903C-2FBB31348FB6> /System/Library/PrivateFrameworks/AtomicsInternal.framework/Versions/A/AtomicsInternal\ndyld[33988]: <41F66F01-A342-3091-A832-0B2B645C922B> /System/Library/PrivateFrameworks/InternalSwiftProtobuf.framework/Versions/A/InternalSwiftProtobuf\ndyld[33988]: <2EDB2E62-942F-3AB5-82AF-8E1328544E17> /usr/lib/swift/libswiftDistributed.dylib\ndyld[33988]: /usr/lib/swift/libswiftObservation.dylib\ndyld[33988]: /usr/lib/swift/libswiftSynchronization.dylib\ndyld[33988]: <91BDD1F8-831B-3B01-86BA-6BBCB43373C4> /System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary\ndyld[33988]: <90CFC86E-833E-3E9F-BAAC-2B61BD750DA6> /System/Library/PrivateFrameworks/CoreDuetContext.framework/Versions/A/CoreDuetContext\ndyld[33988]: <07CF779F-8F51-3764-B486-23D76868FF91> /System/Library/PrivateFrameworks/BiomeLibrary.framework/Versions/A/BiomeLibrary\ndyld[33988]: <838F99F9-D3FA-335B-9767-B5D04A3FACA6> /System/Library/PrivateFrameworks/CoreDuet.framework/Versions/A/CoreDuet\ndyld[33988]: <2110407D-EFB4-373E-B963-9C92E26594B2> /System/Library/PrivateFrameworks/BiomeStreams.framework/Versions/A/BiomeStreams\ndyld[33988]: <455A5553-E683-30B4-A906-1F14E75F6E61> /System/Library/PrivateFrameworks/BiomeFoundation.framework/Versions/A/BiomeFoundation\ndyld[33988]: <0F03104F-FC8B-3ADD-8850-4B7029E2B56E> /System/Library/PrivateFrameworks/BiomePubSub.framework/Versions/A/BiomePubSub\ndyld[33988]: <712AD9C1-44D2-36F4-BA8E-15038521462B> /System/Library/Frameworks/CoreData.framework/Versions/A/CoreData\ndyld[33988]: <73EE1A0A-0D29-3104-98CB-BEFEDA53F7C0> /System/Library/PrivateFrameworks/ProactiveSupport.framework/Versions/A/ProactiveSupport\ndyld[33988]: <9805BB7B-12C9-39F5-9070-C5B8BFCAE2AF> /System/Library/Frameworks/CoreLocation.framework/Versions/A/CoreLocation\ndyld[33988]: /System/Library/Frameworks/Intents.framework/Versions/A/Intents\ndyld[33988]: /System/Library/Frameworks/_LocationEssentials.framework/Versions/A/_LocationEssentials\ndyld[33988]: /usr/lib/liblzma.5.dylib\ndyld[33988]: <9171DD7D-3994-3963-9A28-BC163BF97DE6> /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate\ndyld[33988]: /System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag\ndyld[33988]: <858910C5-1D4A-37B7-BF0E-EE02E24A2ACD> /System/Library/PrivateFrameworks/InternationalTextSearch.framework/Versions/A/InternationalTextSearch\ndyld[33988]: <13271AA6-33EA-369B-B2D1-6EC528C820E7> /System/Library/PrivateFrameworks/SoftwareUpdateCoreSupport.framework/Versions/A/SoftwareUpdateCoreSupport\ndyld[33988]: <2CA857AF-D999-34DC-94A1-3AC0E5B80416> /System/Library/PrivateFrameworks/SoftwareUpdateCoreConnect.framework/Versions/A/SoftwareUpdateCoreConnect\ndyld[33988]: <823F3D1A-65F1-3CC5-96B1-750263B8DB36> /System/Library/PrivateFrameworks/RemoteServiceDiscovery.framework/Versions/A/RemoteServiceDiscovery\ndyld[33988]: /System/Library/PrivateFrameworks/MSUDataAccessor.framework/Versions/A/MSUDataAccessor\ndyld[33988]: /usr/lib/libbootpolicy.dylib\ndyld[33988]: <885F9C72-1018-368B-AD36-E8A42E87FD91> /System/Library/PrivateFrameworks/RemoteXPC.framework/Versions/A/RemoteXPC\ndyld[33988]: /usr/lib/libFDR.dylib\ndyld[33988]: <24D28E7F-A1AE-3031-8679-A0D6C6D68A86> /usr/lib/libamsupport.dylib\ndyld[33988]: <29367004-5D60-38DB-831F-9E5EE9364B21> /usr/lib/libReverseProxyDevice.dylib\ndyld[33988]: <9594FBFB-D49D-3DF6-8820-564633EAEC2B> /System/Library/PrivateFrameworks/AppleDeviceQuerySupport.framework/Versions/A/AppleDeviceQuerySupport\ndyld[33988]: /usr/lib/libpartition2_dynamic.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce\ndyld[33988]: <9A8926C8-36A6-3DB4-A485-059C1F630984> /usr/lib/libAppleArchive.dylib\ndyld[33988]: <2B16DF37-A596-3D8A-AE47-33E580EB1354> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage\ndyld[33988]: <8203944D-B53E-3D7E-A481-3C676CAE1B6A> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib\ndyld[33988]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib\ndyld[33988]: <08508E7B-096D-31AB-9C66-191C877ED62F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/Libraries/libCGInterfaces.dylib\ndyld[33988]: <8945E7B7-12AE-3FF4-AA3B-D4DF9A06FEE7> /System/Library/PrivateFrameworks/AccelerateGPU.framework/Versions/A/AccelerateGPU\ndyld[33988]: <23402175-D2CF-3B08-88D0-AFBBCF775FEF> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib\ndyld[33988]: <086CBEED-2F64-3E75-AB99-8C8C0E0A2F1C> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices\ndyld[33988]: <0616AF41-149E-3F4A-906E-56E2642457BE> /System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo\ndyld[33988]: <873404F1-CC9D-30F9-AE06-8EA58D292005> /System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync\ndyld[33988]: /System/Library/Frameworks/CoreText.framework/Versions/A/CoreText\ndyld[33988]: /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO\ndyld[33988]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS\ndyld[33988]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices\ndyld[33988]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore\ndyld[33988]: <59BBF27B-1D89-3D35-9210-8386EFA15A8D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD\ndyld[33988]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy\ndyld[33988]: <9CDA611B-254A-3779-9356-369485134C2D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis\ndyld[33988]: <0C8F41C6-6D93-3DB3-B522-CA8CFF5C3B33> /System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight\ndyld[33988]: <9E126CE0-FBB2-3B15-953F-CCDC758E34FB> /System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib\ndyld[33988]: <959C748F-8851-3A25-BFFA-5FEA80296965> /System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard\ndyld[33988]: /System/Library/PrivateFrameworks/BoardServices.framework/Versions/A/BoardServices\ndyld[33988]: /System/Library/PrivateFrameworks/BackBoardServices.framework/Versions/A/BackBoardServices\ndyld[33988]: <7F763DF9-EA7F-3938-B599-DCCF4605E610> /System/Library/PrivateFrameworks/BackBoardHIDEventFoundation.framework/Versions/A/BackBoardHIDEventFoundation\ndyld[33988]: /System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay\ndyld[33988]: /System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox\ndyld[33988]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders\ndyld[33988]: /System/Library/PrivateFrameworks/ProDisplayLibrary.framework/Versions/A/ProDisplayLibrary\ndyld[33988]: <1E529C1A-B09C-3EB7-A286-CE00E292D561> /System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator\ndyld[33988]: /System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia\ndyld[33988]: /System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC\ndyld[33988]: /System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient\ndyld[33988]: <98CB7012-30E5-3BDD-8C84-CDBDA9DB3017> /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore\ndyld[33988]: <57F7BB9C-649D-3360-AA86-A502815D77FA> /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport\ndyld[33988]: <625F222D-6394-39B9-A1F2-12B9EA56DD85> /usr/lib/swift/libswiftAccelerate.dylib\ndyld[33988]: /usr/lib/swift/libswiftCoreAudio.dylib\ndyld[33988]: /usr/lib/swift/libswiftCoreMedia.dylib\ndyld[33988]: <7235A6A9-49B2-3B94-9DD6-C987019CDBF2> /usr/lib/swift/libswiftMetal.dylib\ndyld[33988]: <9670AE5C-271A-3DCB-9A0A-8E3A7CCC2726> /usr/lib/swift/libswiftOSLog.dylib\ndyld[33988]: <63444A8C-9E8C-3778-820D-1E0C88CA2DF7> /usr/lib/swift/libswiftQuartzCore.dylib\ndyld[33988]: /usr/lib/swift/libswiftUniformTypeIdentifiers.dylib\ndyld[33988]: <9247A5B6-A883-3A07-BEE7-A223840317A4> /usr/lib/swift/libswiftVideoToolbox.dylib\ndyld[33988]: /usr/lib/swift/libswiftsimd.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/BiomeStorage.framework/Versions/A/BiomeStorage\ndyld[33988]: /System/Library/PrivateFrameworks/IntelligencePlatformLibrary.framework/Versions/A/IntelligencePlatformLibrary\ndyld[33988]: <42CDC0E6-51BA-3804-BD3E-EDF87FC74034> /System/Library/PrivateFrameworks/PoirotSchematizer.framework/Versions/A/PoirotSchematizer\ndyld[33988]: <2362E209-EC61-3FFC-9486-1244BB29BE82> /System/Library/PrivateFrameworks/BiomeSync.framework/Versions/A/BiomeSync\ndyld[33988]: /System/Library/PrivateFrameworks/BiomeDSL.framework/Versions/A/BiomeDSL\ndyld[33988]: /System/Library/PrivateFrameworks/FeatureFlags.framework/Versions/A/FeatureFlags\ndyld[33988]: /System/Library/PrivateFrameworks/PoirotUDFs.framework/Versions/A/PoirotUDFs\ndyld[33988]: /usr/lib/swift/libswift_DarwinFoundation2.dylib\ndyld[33988]: <8D2C31B5-FB10-3BF6-8566-F0DCD56C8582> /usr/lib/swift/libswift_DarwinFoundation3.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime\ndyld[33988]: <4646F780-1D5E-3EE7-B00A-64619293CC18> /usr/lib/libiconv.2.dylib\ndyld[33988]: <1940124C-0D73-35D2-9D94-A75F116088A0> /usr/lib/libcharset.1.dylib\ndyld[33988]: <24779350-BC29-3465-AAB3-F7CD0DA5844A> /System/Library/PrivateFrameworks/PoirotSQLite.framework/Versions/A/PoirotSQLite\ndyld[33988]: <7B63C2BF-8C7C-3ECA-ACD9-F1B75DBE018C> /usr/lib/swift/libswift_RegexParser.dylib\ndyld[33988]: <2091B02D-8D55-3DC4-8097-60C193D03C85> /System/Library/PrivateFrameworks/CascadeSets.framework/Versions/A/CascadeSets\ndyld[33988]: <79000980-1797-3115-B74B-60FA1E9C3C73> /System/Library/PrivateFrameworks/CorePhoneNumbers.framework/Versions/A/CorePhoneNumbers\ndyld[33988]: <7F00413A-4D40-3DBF-8FD5-859B23E6DC03> /System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG\ndyld[33988]: /usr/lib/libexpat.1.dylib\ndyld[33988]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib\ndyld[33988]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib\ndyld[33988]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib\ndyld[33988]: <7304F8B3-8E0F-3813-BFAF-9A565CEA0A11> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib\ndyld[33988]: <01AAD3B4-D6BA-36D9-BA6F-D494D2AC161D> /usr/lib/libate.dylib\ndyld[33988]: <8EA6CA42-AA01-3C0F-9672-4917481BAAAE> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib\ndyld[33988]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib\ndyld[33988]: <757FEDFF-841C-3D62-B703-CDE79E929363> /System/Library/PrivateFrameworks/GraphicsServices.framework/Versions/A/GraphicsServices\ndyld[33988]: /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL\ndyld[33988]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib\ndyld[33988]: <6CEF3932-AAC9-3F8E-905D-A826F2884C9A> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib\ndyld[33988]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib\ndyld[33988]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib\ndyld[33988]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib\ndyld[33988]: <07CB5D41-C2F3-3C33-951F-67B2C8B8B662> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler\ndyld[33988]: /System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment\ndyld[33988]: /System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay\ndyld[33988]: <825E8416-E246-338E-A5CF-AA81A1B01DD9> /System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport\ndyld[33988]: <7CF84496-675C-3241-B0EF-E83C95F188FA> /System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA\ndyld[33988]: <3D533C35-3A2A-3672-92EF-5FEE9EE739AC> /System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation\ndyld[33988]: /System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore\ndyld[33988]: <04DC06C1-2BFA-3FEE-9429-A33E41721A3E> /usr/lib/libspindump.dylib\ndyld[33988]: /System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio\ndyld[33988]: <2B5FB7B0-844C-3D84-9EFD-020B285B0F8D> /System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport\ndyld[33988]: /System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata\ndyld[33988]: /System/Library/PrivateFrameworks/AudioDSPGraph.framework/Versions/A/AudioDSPGraph\ndyld[33988]: <8AF1606D-5C93-3B80-BC81-60C5688628E2> /System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore\ndyld[33988]: /System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk\ndyld[33988]: <3FF99846-E48C-3C9A-814C-35B45E5F60EC> /usr/lib/libAudioStatistics.dylib\ndyld[33988]: <6108A12D-286B-3CF2-B848-B0E7A0189DCC> /System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy\ndyld[33988]: <655F6374-6CE8-3D0E-994E-4D7C37F78E89> /usr/lib/libSMC.dylib\ndyld[33988]: <7AE04E20-83FD-3B1B-8846-E9869AD98DB5> /usr/lib/swift/libswiftCoreMIDI.dylib\ndyld[33988]: <52BD9E26-B356-3EAA-9AD7-7FF700C61A91> /System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI\ndyld[33988]: <75F77FEC-BE14-3C97-93DA-403C3B529D3B> /usr/lib/libAudioToolboxUtility.dylib\ndyld[33988]: <912BFF10-FB8F-3D52-9941-ACDCE1CAE36A> /usr/lib/libperfcheck.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/AudioAnalytics.framework/Versions/A/AudioAnalytics\ndyld[33988]: <869F0693-0E82-38C1-8920-C782E71735CA> /System/Library/Frameworks/OSLog.framework/Versions/A/OSLog\ndyld[33988]: <62740FDD-2B16-3319-B5C9-022D45C6B03A> /System/Library/PrivateFrameworks/OSEligibility.framework/Versions/A/OSEligibility\ndyld[33988]: <10C63D59-07BC-3518-87A0-83CAC48D8A70> /System/Library/PrivateFrameworks/IconServices.framework/Versions/A/IconServices\ndyld[33988]: <8EF56F82-8CCE-3811-AD16-6D0939187B45> /System/Library/Frameworks/LightweightCodeRequirements.framework/Versions/A/LightweightCodeRequirements\ndyld[33988]: /System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit\ndyld[33988]: <1946F8FE-0ABC-3F8F-9116-5451ECABD14C> /System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices\ndyld[33988]: /System/Library/PrivateFrameworks/IconFoundation.framework/Versions/A/IconFoundation\ndyld[33988]: /System/Library/PrivateFrameworks/AssistantServices.framework/Versions/A/AssistantServices\ndyld[33988]: <6A34A62A-16D4-34F0-B34B-2D96B53C20AD> /System/Library/PrivateFrameworks/IconRendering.framework/Versions/A/IconRendering\ndyld[33988]: /System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI\ndyld[33988]: <0943679D-FF88-3F18-BE4B-D8B4827AB0B5> /System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage\ndyld[33988]: <968B5A5F-9749-3527-AF2A-66B599785308> /System/Library/PrivateFrameworks/SFSymbols.framework/Versions/A/SFSymbols\ndyld[33988]: /System/Library/Frameworks/DeveloperToolsSupport.framework/Versions/A/DeveloperToolsSupport\ndyld[33988]: <92090A92-DFAF-3EBC-886C-655EC158A53F> /System/Library/PrivateFrameworks/RenderBox.framework/Versions/A/RenderBox\ndyld[33988]: <986D57A7-BFF1-3DAA-8EB1-17CCAA76C731> /System/Library/PrivateFrameworks/CoreSVG.framework/Versions/A/CoreSVG\ndyld[33988]: /System/Library/PrivateFrameworks/TextureIO.framework/Versions/A/TextureIO\ndyld[33988]: /usr/lib/swift/libswiftCoreImage.dylib\ndyld[33988]: <77D85BA0-FE1C-3B5A-92DB-70A30202C990> /System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer\ndyld[33988]: /System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices\ndyld[33988]: <5D3E7FFF-AC8E-3D6F-8E99-B199E593D270> /System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG\ndyld[33988]: <49E7449E-1385-3B53-94CC-36EFC31E98FE> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib\ndyld[33988]: <23C577A8-DB0B-3A0A-9058-1289483C262A> /usr/lib/libhvf.dylib\ndyld[33988]: <11E757EC-72FB-3C53-8ED7-641428AB6169> /System/Library/PrivateFrameworks/ParsingInternal.framework/Versions/A/ParsingInternal\ndyld[33988]: /System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib\ndyld[33988]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore\ndyld[33988]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage\ndyld[33988]: <199F6401-91D0-36E9-9EA9-D4B44ED1CE3A> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork\ndyld[33988]: <4D134FE3-50EE-39D5-9699-04B4B673DD35> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix\ndyld[33988]: <2E7E2722-3821-3DBF-B25A-6EA45D1A8FD4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector\ndyld[33988]: <3E1FE9EA-34A2-3545-B639-48B1FE1FD3D4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray\ndyld[33988]: <3103E210-FF5C-3677-BDD3-59FF17A6ACEC> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions\ndyld[33988]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSBenchmarkLoop.framework/Versions/A/MPSBenchmarkLoop\ndyld[33988]: <31F90368-23A5-39BB-822B-C8470C4479AE> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSHost.framework/Versions/A/MPSHost\ndyld[33988]: <9C416BB2-0882-315C-AF23-F476E34983BC> /System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools\ndyld[33988]: /System/Library/PrivateFrameworks/IOAccelMemoryInfo.framework/Versions/A/IOAccelMemoryInfo\ndyld[33988]: /System/Library/PrivateFrameworks/kperf.framework/Versions/A/kperf\ndyld[33988]: <03470B3A-A004-39A0-B6A4-F2A4AFFFCDD3> /System/Library/PrivateFrameworks/GPURawCounter.framework/Versions/A/GPURawCounter\ndyld[33988]: <4D8F39C6-B221-3AF1-BB40-CAEB0A174D61> /System/Library/PrivateFrameworks/ASEProcessing.framework/Versions/A/ASEProcessing\ndyld[33988]: <724D42FC-F4FD-39C7-A1BF-D0AD086231F4> /System/Library/PrivateFrameworks/Symbolication.framework/Versions/A/Symbolication\ndyld[33988]: /System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing\ndyld[33988]: <1B4C0154-843C-3CEE-9628-22978082DD2D> /System/Library/PrivateFrameworks/SILManager.framework/Versions/A/SILManager\ndyld[33988]: <59136324-34E6-3367-92BB-659346907A04> /System/Library/PrivateFrameworks/CoreSymbolication.framework/Versions/A/CoreSymbolication\ndyld[33988]: <566F2D7D-0F3B-3290-A739-7A40A151F0BE> /System/Library/PrivateFrameworks/MallocStackLogging.framework/Versions/A/MallocStackLogging\ndyld[33988]: <7C923545-F3BB-3215-9720-85196358D9F1> /System/Library/PrivateFrameworks/DebugSymbols.framework/Versions/A/DebugSymbols\ndyld[33988]: <06728C4D-5750-308F-8290-EAF7BE91F4BB> /System/Library/PrivateFrameworks/OSAnalytics.framework/Versions/A/OSAnalytics\ndyld[33988]: <2FA711C7-F764-363A-BF03-295E0DA88B79> /System/Library/PrivateFrameworks/DeviceRecovery.framework/Versions/A/DeviceRecovery\ndyld[33988]: /System/Library/PrivateFrameworks/Tightbeam.framework/Versions/A/Tightbeam\ndyld[33988]: <856ACB2A-3334-3BA6-AAC8-8F344E7CDB83> /usr/lib/swift/libswiftCompression.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/AFKUser.framework/Versions/A/AFKUser\ndyld[33988]: <2186F196-EE17-3A59-B9DA-D6823BEDD35B> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI\ndyld[33988]: <086BB8AD-E317-3FC4-9E44-0D7C6036E8D7> /System/Library/PrivateFrameworks/SAObjects.framework/Versions/A/SAObjects\ndyld[33988]: /System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox\ndyld[33988]: /System/Library/PrivateFrameworks/MediaRemote.framework/Versions/A/MediaRemote\ndyld[33988]: <7F1A25E4-ED0A-3502-AABA-26EDB4A0D2A7> /System/Library/Frameworks/UserNotifications.framework/Versions/A/UserNotifications\ndyld[33988]: <8A5E0FF6-3116-3082-A0AD-20DCD6C5E1B4> /System/Library/PrivateFrameworks/SiriInstrumentation.framework/Versions/A/SiriInstrumentation\ndyld[33988]: <600E036E-9B18-35BE-B40B-E8D2D53AC90D> /System/Library/PrivateFrameworks/SiriAnalytics.framework/Versions/A/SiriAnalytics\ndyld[33988]: <619E6770-766A-3629-9AF8-F32C009375E9> /System/Library/PrivateFrameworks/SiriTTSService.framework/Versions/A/SiriTTSService\ndyld[33988]: <71CAE70A-72AD-3F74-834D-3519B545C08D> /System/Library/PrivateFrameworks/SiriCrossDeviceArbitration.framework/Versions/A/SiriCrossDeviceArbitration\ndyld[33988]: /System/Library/PrivateFrameworks/FeedbackLogger.framework/Versions/A/FeedbackLogger\ndyld[33988]: <55FBCBE4-1032-3017-BA49-D734B82405DF> /System/Library/PrivateFrameworks/FaceTimeNameUtility.framework/Versions/A/FaceTimeNameUtility\ndyld[33988]: <336E2CAC-84D2-34DC-8AE3-7FE688C609EA> /System/Library/PrivateFrameworks/AuthKit.framework/Versions/A/AuthKit\ndyld[33988]: /System/Library/PrivateFrameworks/SiriCrossDeviceArbitrationFeedback.framework/Versions/A/SiriCrossDeviceArbitrationFeedback\ndyld[33988]: <368BC882-02B9-38AB-89B4-F62430F2B8EB> /usr/lib/swift/libswiftCoreLocation.dylib\ndyld[33988]: /usr/lib/swift/libswiftAVFoundation.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/UIKitServices.framework/Versions/A/UIKitServices\ndyld[33988]: <54A2CBB8-623D-3629-904A-D0399ED13547> /System/Library/PrivateFrameworks/UnifiedAssetFramework.framework/Versions/A/UnifiedAssetFramework\ndyld[33988]: /System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession\ndyld[33988]: <52A7AD42-9DE0-393B-A6FB-A7CB6FF8F3A5> /System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience\ndyld[33988]: <1A63E9E1-2D64-3AF4-9CCD-6EF042397F84> /System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib\ndyld[33988]: <7CC1BC36-42C1-39A3-AA4F-F9C8ABCE0CD5> /System/Library/PrivateFrameworks/AudioAccessoryServices.framework/Versions/A/AudioAccessoryServices\ndyld[33988]: <515FDCCC-535A-398B-BBD3-3D35565F5423> /System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth\ndyld[33988]: /System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils\ndyld[33988]: /System/Library/PrivateFrameworks/HID.framework/Versions/A/HID\ndyld[33988]: <920D8AA6-CDCD-3E0F-AD66-F0673ACABD3F> /System/Library/PrivateFrameworks/Sharing.framework/Versions/A/Sharing\ndyld[33988]: <3DD8C4CA-23E9-35CF-AD67-549DD72D3344> /System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras\ndyld[33988]: <236517AD-8D16-3E62-8603-EBE7B65ACACA> /System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211\ndyld[33988]: <42F76533-D8DD-3A24-A08A-103C51428797> /System/Library/PrivateFrameworks/IDSFoundation.framework/Versions/A/IDSFoundation\ndyld[33988]: <116D159B-163E-3F91-9591-30FF8B6EB537> /System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Apple80211\ndyld[33988]: /System/Library/Frameworks/CoreWLAN.framework/Versions/A/CoreWLAN\ndyld[33988]: <1ABB6C50-A5DE-3744-8F07-8C3B5617B0B9> /System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth\ndyld[33988]: <82D79BDA-26A0-3A44-AAC8-911411801FDE> /usr/lib/swift/libswiftRegexBuilder.dylib\ndyld[33988]: <41E45E0C-2E88-3605-B213-F7CD760A9FF4> /System/Library/PrivateFrameworks/IMFoundation.framework/Versions/A/IMFoundation\ndyld[33988]: <40F60A3F-90A9-3F07-A99D-005E3158C6F6> /System/Library/PrivateFrameworks/Marco.framework/Versions/A/Marco\ndyld[33988]: <59FFD032-1427-39F6-BC16-A6877582A243> /System/Library/PrivateFrameworks/CommonUtilities.framework/Versions/A/CommonUtilities\ndyld[33988]: <6C3E51F8-D809-3AA1-8695-B75714C2D39A> /System/Library/PrivateFrameworks/Engram.framework/Versions/A/Engram\ndyld[33988]: /System/Library/PrivateFrameworks/XPCDistributed.framework/Versions/A/XPCDistributed\ndyld[33988]: <12066854-2BE4-35DF-BA9F-B38221C980FD> /usr/lib/libtidy.A.dylib\ndyld[33988]: <63F598E2-AF8A-3F29-BE11-3F14DB377A5B> /System/Library/PrivateFrameworks/Bom.framework/Versions/A/Bom\ndyld[33988]: /usr/lib/libParallelCompression.dylib\ndyld[33988]: <9E06CB59-0638-3C9F-B202-264E739433AC> /usr/lib/libIOReport.dylib\ndyld[33988]: <15EEE715-2670-3288-AFA8-504BAD951B4F> /System/Library/PrivateFrameworks/WiFiPeerToPeer.framework/Versions/A/WiFiPeerToPeer\ndyld[33988]: <9A86DB3F-CC62-3E89-B872-35D04CFFBE42> /System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation\ndyld[33988]: <3854272C-7B14-3A3C-9BB1-F0FBA394708A> /System/Library/PrivateFrameworks/Centauri.framework/Versions/A/Centauri\ndyld[33988]: <946B1484-B180-3451-A452-B54BF5A6D392> /System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon\ndyld[33988]: <2B49C295-4EA2-3DE3-90B4-DC03A96F2657> /usr/lib/libmrc.dylib\ndyld[33988]: <6661265C-7B78-3158-9011-4BFDFFEF7807> /System/Library/PrivateFrameworks/IPConfiguration.framework/Versions/A/IPConfiguration\ndyld[33988]: /System/Library/PrivateFrameworks/Netrb.framework/Versions/A/Netrb\ndyld[33988]: /System/Library/PrivateFrameworks/FrontBoardServices.framework/Versions/A/FrontBoardServices\ndyld[33988]: <74E55DD6-720D-39E4-897E-EB4328E1946D> /usr/lib/libgermantok.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData\ndyld[33988]: <21723046-939E-302F-883C-9DB417452E3A> /System/Library/PrivateFrameworks/MultiverseSupport.framework/Versions/A/MultiverseSupport\ndyld[33988]: /System/Library/PrivateFrameworks/DiskManagement.framework/Versions/A/DiskManagement\ndyld[33988]: /System/Library/PrivateFrameworks/AppleIDAuthSupport.framework/Versions/A/AppleIDAuthSupport\ndyld[33988]: /System/Library/PrivateFrameworks/AAAFoundation.framework/Versions/A/AAAFoundation\ndyld[33988]: /System/Library/PrivateFrameworks/KeychainCircle.framework/Versions/A/KeychainCircle\ndyld[33988]: /System/Library/PrivateFrameworks/URLFormatting.framework/Versions/A/URLFormatting\ndyld[33988]: <80C3E2D4-B6B8-3C62-B257-27DEEBAD4935> /usr/lib/libcsfde.dylib\ndyld[33988]: <650E155C-1FE3-36ED-8D84-157D380F7F95> /usr/lib/libCoreStorage.dylib\ndyld[33988]: <46DD93AF-BACD-309B-AD51-9CC47C78CA2C> /System/Library/PrivateFrameworks/MediaKit.framework/Versions/A/MediaKit\ndyld[33988]: /System/Library/Frameworks/DiscRecording.framework/Versions/A/DiscRecording\ndyld[33988]: <1F8700BE-BD91-3B94-AC32-A5F10CEFEE35> /System/Library/PrivateFrameworks/ProtectedCloudStorage.framework/Versions/A/ProtectedCloudStorage\ndyld[33988]: /System/Library/PrivateFrameworks/EFILogin.framework/Versions/A/EFILogin\ndyld[33988]: <6A4A85F4-3D12-3C4C-85EC-D53D61379F28> /usr/lib/libheimdal-asn1.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/OctagonTrust.framework/Versions/A/OctagonTrust\ndyld[33988]: <093EF25B-5305-3611-B068-E65071858F52> /System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit\ndyld[33988]: /System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory\ndyld[33988]: <3B7FD4C1-D1D4-3DA9-B2F8-3D4094679D76> /System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory\ndyld[33988]: <1DAFDDDA-BB7B-320E-BCFC-B7C22886D486> /System/Library/PrivateFrameworks/GeoServices.framework/Versions/A/GeoServices\ndyld[33988]: /System/Library/PrivateFrameworks/LocationSupport.framework/Versions/A/LocationSupport\ndyld[33988]: <38EE3C42-06D6-3A46-A420-DF701A4EA911> /System/Library/PrivateFrameworks/GeoServicesCore.framework/Versions/A/GeoServicesCore\ndyld[33988]: <8D0ECDD1-24B6-3B8D-9CF9-CDC55FC64490> /System/Library/PrivateFrameworks/PhoneNumbers.framework/Versions/A/PhoneNumbers\ndyld[33988]: <8A2C8C17-E138-3B34-8643-ED4FB1C9049E> /System/Library/PrivateFrameworks/LocationLogEncryption.framework/Versions/A/LocationLogEncryption\ndyld[33988]: <016C5057-625C-30B1-AD32-7BC9D082F05B> /System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio\ndyld[33988]: /System/Library/Frameworks/AVRouting.framework/Versions/A/AVRouting\ndyld[33988]: <5240B3A0-D035-345E-A636-BC3A92C847C4> /usr/lib/libAccessibility.dylib\ndyld[33988]: <1FB2BCFD-D9FC-385A-A0EA-E2B5052E27F5> /System/Library/PrivateFrameworks/MediaServices.framework/Versions/A/MediaServices\ndyld[33988]: /System/Library/PrivateFrameworks/IDS.framework/Versions/A/IDS\ndyld[33988]: /System/Library/PrivateFrameworks/IsolatedCoreAudioClient.framework/Versions/A/IsolatedCoreAudioClient\ndyld[33988]: <7CC0621B-3B88-3533-A3FB-52E6214486EE> /System/Library/PrivateFrameworks/CoreAudioOrchestration.framework/Versions/A/CoreAudioOrchestration\ndyld[33988]: /System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox\ndyld[33988]: /System/Library/PrivateFrameworks/CoreAVCHD.framework/Versions/A/CoreAVCHD\ndyld[33988]: <74D313A5-4D99-35D1-A4C9-B76AB6457EF0> /System/Library/Frameworks/MediaAccessibility.framework/Versions/A/MediaAccessibility\ndyld[33988]: <87F549F4-73CC-302B-ABDB-D3CCFADABFA9> /System/Library/PrivateFrameworks/Mangrove.framework/Versions/A/Mangrove\ndyld[33988]: <214294AE-C7B7-3C9A-A4F8-201C989F9779> /System/Library/PrivateFrameworks/CMPhoto.framework/Versions/A/CMPhoto\ndyld[33988]: <5F090F48-E481-3737-8E75-362E5D274879> /System/Library/Frameworks/CoreTelephony.framework/Versions/A/CoreTelephony\ndyld[33988]: <9ACFCA55-82CB-33DB-AD00-443576099FDB> /System/Library/PrivateFrameworks/CoreAUC.framework/Versions/A/CoreAUC\ndyld[33988]: <71A0C0AD-67F3-36F9-BF73-6DD5D7424AF7> /System/Library/PrivateFrameworks/AppleJPEGXL.framework/Versions/A/AppleJPEGXL\ndyld[33988]: <63A6BBA0-CD50-30F8-9CD2-81B59264EA13> /usr/lib/libTelephonyUtilDynamic.dylib\ndyld[33988]: /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit\ndyld[33988]: /System/Library/PrivateFrameworks/CryptoKitCBridging.framework/Versions/A/CryptoKitCBridging\ndyld[33988]: <714063A8-D81E-3B22-9B36-88948A979E7F> /System/Library/Frameworks/CryptoTokenKit.framework/Versions/A/CryptoTokenKit\ndyld[33988]: <86858734-8B4D-38E6-AAA7-B7A046A7CB2A> /System/Library/Frameworks/LocalAuthentication.framework/Versions/A/LocalAuthentication\ndyld[33988]: /System/Library/PrivateFrameworks/LocalAuthenticationCore.framework/Versions/A/LocalAuthenticationCore\ndyld[33988]: /System/Library/PrivateFrameworks/LocalAuthenticationCredentialServices.framework/Versions/A/LocalAuthenticationCredentialServices\ndyld[33988]: /System/Library/Frameworks/LocalAuthentication.framework/Support/SharedUtils.framework/Versions/A/SharedUtils\ndyld[33988]: /System/Library/PrivateFrameworks/PersistentConnection.framework/Versions/A/PersistentConnection\ndyld[33988]: <6B0D099C-AC56-35DB-90F1-88A09E587FCB> /System/Library/PrivateFrameworks/SonicFoundation.framework/Versions/A/SonicFoundation\ndyld[33988]: /System/Library/PrivateFrameworks/AsyncAlgorithmsInternal.framework/Versions/A/AsyncAlgorithmsInternal\ndyld[33988]: <2CA62C12-37B5-345A-BF79-5D05F43F6BFB> /System/Library/PrivateFrameworks/FTAWD.framework/Versions/A/FTAWD\ndyld[33988]: <0A1C4D11-C108-35E9-A921-86ED86CF7446> /System/Library/PrivateFrameworks/Dendrite.framework/Versions/A/Dendrite\ndyld[33988]: /usr/lib/libtailspin.dylib\ndyld[33988]: <5FEA8C08-1577-3296-BC9C-7F3203E8EBFB> /System/Library/PrivateFrameworks/Osprey.framework/Versions/A/Osprey\ndyld[33988]: /System/Library/PrivateFrameworks/SiriTTS.framework/Versions/A/SiriTTS\ndyld[33988]: <0C005C4D-CA12-389C-9CCE-C4ED05B187E8> /System/Library/Frameworks/NaturalLanguage.framework/Versions/A/NaturalLanguage\ndyld[33988]: <0E502870-00F4-35D4-AF82-E7059244798E> /System/Library/PrivateFrameworks/GenerativeModels.framework/Versions/A/GenerativeModels\ndyld[33988]: <68474F39-798D-325B-B52F-3DE214F279AE> /System/Library/PrivateFrameworks/SiriPowerInstrumentation.framework/Versions/A/SiriPowerInstrumentation\ndyld[33988]: <5E36265A-7670-3D39-A2B8-71DA0AA131CF> /usr/lib/swift/libswiftNaturalLanguage.dylib\ndyld[33988]: <6794652C-86F0-37EB-838D-483177685E26> /System/Library/PrivateFrameworks/TailspinSymbolication.framework/Versions/A/TailspinSymbolication\ndyld[33988]: <089C1A34-2F4E-3649-94AA-B28A7ECB008B> /System/Library/PrivateFrameworks/Darwinup.framework/Versions/A/Darwinup\ndyld[33988]: /System/Library/PrivateFrameworks/SignpostSupport.framework/Versions/A/SignpostSupport\ndyld[33988]: <8C10B437-C282-37F5-834F-E7179C700373> /System/Library/PrivateFrameworks/FeatureFlagsSupport.framework/Versions/A/FeatureFlagsSupport\ndyld[33988]: /System/Library/PrivateFrameworks/ktrace.framework/Versions/A/ktrace\ndyld[33988]: /System/Library/PrivateFrameworks/SampleAnalysis.framework/Versions/A/SampleAnalysis\ndyld[33988]: <400B0E96-4869-37BE-9832-1A14C386148B> /System/Library/PrivateFrameworks/kperfdata.framework/Versions/A/kperfdata\ndyld[33988]: <7E3E0CF7-905A-3244-A0C9-0ADCC2E16415> /usr/lib/libdscsym.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity\ndyld[33988]: <6FBA9099-E428-3571-B940-0D210B6D0861> /System/Library/PrivateFrameworks/BulkSymbolication.framework/Versions/A/BulkSymbolication\ndyld[33988]: <90E600A3-0A27-348A-AA57-D1DF4FB305E8> /usr/lib/libTLE.dylib\ndyld[33988]: <173A632F-20F3-30C1-BC55-EFFE977BBB8E> /usr/lib/libmis.dylib\ndyld[33988]: <2D1B971F-6A7F-32D0-8B0F-F8FA3A13E8F1> /System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper\ndyld[33988]: <8F2949A6-43A0-30A2-B5D2-945949C5AA01> /System/Library/PrivateFrameworks/Espresso.framework/Versions/A/Espresso\ndyld[33988]: /System/Library/Frameworks/CoreML.framework/Versions/A/CoreML\ndyld[33988]: /usr/lib/libedit.3.dylib\ndyld[33988]: <465A74BC-F20D-3C05-9441-7E08EAA49FAF> /System/Library/PrivateFrameworks/ANECompiler.framework/Versions/A/ANECompiler\ndyld[33988]: /System/Library/PrivateFrameworks/AppleNeuralEngine.framework/Versions/A/AppleNeuralEngine\ndyld[33988]: <97C5C585-F5EE-323A-B949-69EAE9080871> /System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL\ndyld[33988]: <7401E849-7B2E-39A9-99D3-5CB0A6BBDFFE> /System/Library/Frameworks/MetalPerformanceShadersGraph.framework/Versions/A/MetalPerformanceShadersGraph\ndyld[33988]: /System/Library/PrivateFrameworks/MLCompilerServices.framework/Versions/A/MLCompilerServices\ndyld[33988]: /System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices\ndyld[33988]: <9EB04E94-EE2D-38A5-A214-00AF73DBE4E9> /usr/lib/libncurses.5.4.dylib\ndyld[33988]: /usr/lib/libsandbox.1.dylib\ndyld[33988]: <2F2EF0D7-2FE4-3A5A-8E4C-E1571C8D0C10> /usr/lib/libMatch.1.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/ODIE.framework/Versions/A/ODIE\ndyld[33988]: /System/Library/PrivateFrameworks/MLModelAsset.framework/Versions/A/MLModelAsset\ndyld[33988]: <22B4CD07-5C72-3CA4-9CD1-2C87686CDDE5> /System/Library/PrivateFrameworks/MLCompilerRuntime.framework/Versions/A/MLCompilerRuntime\ndyld[33988]: /System/Library/Frameworks/MLCompute.framework/Versions/A/MLCompute\ndyld[33988]: <6028DD46-8E5A-33F0-93B2-41FA480366CC> /System/Library/PrivateFrameworks/MLAssetIO.framework/Versions/A/MLAssetIO\ndyld[33988]: /usr/lib/swift/libswiftMLCompute.dylib\ndyld[33988]: <067E2603-4FEA-3CA5-8926-45F60681EDDB> /System/Library/PrivateFrameworks/AVFCore.framework/Versions/A/AVFCore\ndyld[33988]: /System/Library/PrivateFrameworks/AVFCapture.framework/Versions/A/AVFCapture\ndyld[33988]: <3B782AC2-00C4-3534-91D2-5C7242B32440> /System/Library/PrivateFrameworks/CMImaging.framework/Versions/A/CMImaging\ndyld[33988]: <1772C40D-6EF4-3F81-BA00-6EE8B05039A6> /System/Library/PrivateFrameworks/Quagga.framework/Versions/A/Quagga\ndyld[33988]: <57A10B70-C3C9-34C6-8D22-F5118B63E2F0> /System/Library/PrivateFrameworks/CMCapture.framework/Versions/A/CMCapture\ndyld[33988]: <1035C1AB-5058-3AFA-8D77-514901516251> /System/Library/Frameworks/CoreMediaIO.framework/Versions/A/CoreMediaIO\ndyld[33988]: /System/Library/PrivateFrameworks/CMCaptureDevice.framework/Versions/A/CMCaptureDevice\ndyld[33988]: <1F873909-B3B8-3D55-9673-9AFA86BB085B> /System/Library/PrivateFrameworks/CoreBrightness.framework/Versions/A/CoreBrightness\ndyld[33988]: /System/Library/PrivateFrameworks/CinematicFraming.framework/Versions/A/CinematicFraming\ndyld[33988]: <882BC08E-B1E1-3E52-AE8A-AC22A1BF2BE8> /System/Library/PrivateFrameworks/ModelManagerServices.framework/Versions/A/ModelManagerServices\ndyld[33988]: <3E83115F-D04B-3C8D-8646-35204AA2DB84> /System/Library/PrivateFrameworks/CPMS.framework/Versions/A/CPMS\ndyld[33988]: /System/Library/PrivateFrameworks/SystemStatus.framework/Versions/A/SystemStatus\ndyld[33988]: <2E109991-45C6-3783-8A36-B6A8070AAD67> /System/Library/Frameworks/CoreMotion.framework/Versions/A/CoreMotion\ndyld[33988]: /System/Library/PrivateFrameworks/TimeSync.framework/Versions/A/TimeSync\ndyld[33988]: <9B3D4CA3-7BCF-36C9-AA99-27BDFE7854CD> /System/Library/PrivateFrameworks/DistributedSensing.framework/Versions/A/DistributedSensing\ndyld[33988]: /System/Library/PrivateFrameworks/MobileBluetooth.framework/Versions/A/MobileBluetooth\ndyld[33988]: <0BAB3589-8D81-3C60-9F05-9D207E89F4B6> /System/Library/PrivateFrameworks/IOKitten.framework/Versions/A/IOKitten\ndyld[33988]: <05EC9C98-7211-39C9-B376-796F37327801> /System/Library/PrivateFrameworks/AppleIntelligenceReporting.framework/Versions/A/AppleIntelligenceReporting\ndyld[33988]: <47AAECAD-C28C-352E-BB86-7F292E0BFBC6> /System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji\ndyld[33988]: <1CA9048E-57DD-30F4-A3E6-FE6E97D5BF82> /usr/lib/libCRFSuite.dylib\ndyld[33988]: <327536E3-A27C-38C2-A67F-D6488D04CCEE> /System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling\ndyld[33988]: /System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP\ndyld[33988]: <95BA357E-906A-3183-A402-A41D486B5AB3> /System/Library/PrivateFrameworks/Montreal.framework/Versions/A/Montreal\ndyld[33988]: /usr/lib/libcmph.dylib\ndyld[33988]: /usr/lib/libmecab.dylib\ndyld[33988]: <92FAD15C-EEA5-34E9-B309-75A1CD1B620B> /usr/lib/libThaiTokenizer.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/GenerativeModelsFoundation.framework/Versions/A/GenerativeModelsFoundation\ndyld[33988]: /System/Library/PrivateFrameworks/TokenGeneration.framework/Versions/A/TokenGeneration\ndyld[33988]: <418985BB-52A3-34D4-8379-40DC4C63AA32> /System/Library/PrivateFrameworks/GenerativeFunctions.framework/Versions/A/GenerativeFunctions\ndyld[33988]: <63FD423F-836C-3034-BA48-100AE09A9140> /System/Library/PrivateFrameworks/GenerativeFunctionsFoundation.framework/Versions/A/GenerativeFunctionsFoundation\ndyld[33988]: /System/Library/PrivateFrameworks/ModelCatalog.framework/Versions/A/ModelCatalog\ndyld[33988]: <67C3B698-8279-30F1-9167-4730E6F41F5A> /System/Library/PrivateFrameworks/SensitiveContentAnalysisML.framework/Versions/A/SensitiveContentAnalysisML\ndyld[33988]: /System/Library/PrivateFrameworks/GenerativeFunctionsInstrumentation.framework/Versions/A/GenerativeFunctionsInstrumentation\ndyld[33988]: /System/Library/PrivateFrameworks/PromptKit.framework/Versions/A/PromptKit\ndyld[33988]: <12245228-2B9A-3B24-8C5E-10111D68BE65> /System/Library/PrivateFrameworks/ProactiveDaemonSupport.framework/Versions/A/ProactiveDaemonSupport\ndyld[33988]: <3166486F-3F65-31DB-8018-779FFA32DC71> /System/Library/PrivateFrameworks/TokenGenerationCore.framework/Versions/A/TokenGenerationCore\ndyld[33988]: <53B3126E-7B01-30DD-961A-510E9CFC3CF1> /System/Library/PrivateFrameworks/Trial.framework/Versions/A/Trial\ndyld[33988]: /System/Library/PrivateFrameworks/TrialProto.framework/Versions/A/TrialProto\ndyld[33988]: /System/Library/PrivateFrameworks/AppleFlatBuffers.framework/Versions/A/AppleFlatBuffers\ndyld[33988]: <642E3357-AB6D-3039-A818-EDB5D6A189C2> /System/Library/PrivateFrameworks/SentencePieceInternal.framework/Versions/A/SentencePieceInternal\ndyld[33988]: <10F83439-3A9F-316B-992E-451A72876715> /System/Library/Frameworks/Vision.framework/Versions/A/Vision\ndyld[33988]: /System/Library/PrivateFrameworks/CoreSceneUnderstanding.framework/Versions/A/CoreSceneUnderstanding\ndyld[33988]: <4E70B4ED-C8E0-3636-80E4-0939FE56BB63> /System/Library/PrivateFrameworks/VisionCore.framework/Versions/A/VisionCore\ndyld[33988]: /System/Library/PrivateFrameworks/DataDetectorsCore.framework/Versions/A/DataDetectorsCore\ndyld[33988]: /System/Library/Frameworks/Vision.framework/libfaceCore.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/Futhark.framework/Versions/A/Futhark\ndyld[33988]: /System/Library/PrivateFrameworks/InertiaCam.framework/Versions/A/InertiaCam\ndyld[33988]: /System/Library/PrivateFrameworks/TextRecognition.framework/Versions/A/TextRecognition\ndyld[33988]: <73F6F860-69AF-3162-86E0-A683642287D6> /System/Library/Frameworks/DataDetection.framework/Versions/A/DataDetection\ndyld[33988]: <1D5DF9CA-41FC-3B7B-B19F-2C435F3A66F2> /System/Library/PrivateFrameworks/TextInput.framework/Versions/A/TextInput\ndyld[33988]: /System/Library/PrivateFrameworks/CVNLP.framework/Versions/A/CVNLP\ndyld[33988]: <58AC6CAB-5B91-367F-932F-BD0939BBD125> /System/Library/PrivateFrameworks/IntentsFoundation.framework/Versions/A/IntentsFoundation\ndyld[33988]: <0368EA7D-01B2-3AA9-A6D6-A2A0850AC800> /System/Library/PrivateFrameworks/HIDDisplay.framework/Versions/A/HIDDisplay\ndyld[33988]: <6A5A8E21-A9E6-32A2-9BDB-8013F002AEF8> /usr/lib/libcups.2.dylib\ndyld[33988]: /System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos\ndyld[33988]: <4AB71911-9300-30D4-88CF-D20EFD75ACE6> /usr/lib/libresolv.9.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal\ndyld[33988]: <0CB2E7E3-E96F-343B-A4E7-545E74AF0255> /System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib\ndyld[33988]: <097F7235-CA53-3644-BB95-F6F912B4F2C7> /System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth\ndyld[33988]: /System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities\ndyld[33988]: /System/Library/PrivateFrameworks/AttributeGraph.framework/Versions/A/AttributeGraph\ndyld[33988]: /usr/lib/libAXSafeCategoryBundle.dylib\ndyld[33988]: /System/Library/Frameworks/TabularData.framework/Versions/A/TabularData\ndyld[33988]: <841D5662-2CB9-3A27-ADA7-E33AC5E45199> /System/Library/PrivateFrameworks/ArgumentParserInternal.framework/Versions/A/ArgumentParserInternal\ndyld[33988]: <4C851329-A9F4-3E9E-9E48-07FF4120DCF9> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib\ndyld[33988]: <5015CD96-C046-364D-AAE3-1F439044468B> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib\ndyld[33988]: <407BCF3E-A91F-3A7F-8B8C-DBB8E807990F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib\ndyld[33988]: <669ABE12-838F-3F14-8456-D60DE5DF8EB8> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib\ndyld[33988]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib\ndyld[33988]: <54A103BA-7D04-32DB-B204-179E2E0290CA> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib\ndyld[33988]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib\ndyld[33988]: <27479D70-8BF6-3D3C-B528-1BB9B1B98391> /System/Library/PrivateFrameworks/ApplePushService.framework/Versions/A/ApplePushService\ndyld[33988]: <676D50CC-8455-3267-B8E8-CA31B8EF8F91> /System/Library/Frameworks/CloudKit.framework/Versions/A/CloudKit\ndyld[33988]: /System/Library/PrivateFrameworks/CoreDuetDaemonProtocol.framework/Versions/A/CoreDuetDaemonProtocol\ndyld[33988]: <962EA390-008E-3DDC-B2A5-7B2DAF6E8786> /System/Library/PrivateFrameworks/DeviceIdentity.framework/Versions/A/DeviceIdentity\ndyld[33988]: /System/Library/Frameworks/SharedWithYouCore.framework/Versions/A/SharedWithYouCore\ndyld[33988]: <121798D0-5254-3547-8F96-0F2AF8D84250> /System/Library/PrivateFrameworks/CloudTelemetry.framework/Versions/A/CloudTelemetry\ndyld[33988]: <768DFB9E-7FB3-3998-A3AF-BEF0C6C740A7> /System/Library/PrivateFrameworks/AppleAccount.framework/Versions/A/AppleAccount\ndyld[33988]: /System/Library/PrivateFrameworks/C2.framework/Versions/A/C2\ndyld[33988]: <23871A43-55FD-3D0C-B29F-B143A64D1D8D> /System/Library/PrivateFrameworks/CloudCoreInternal.framework/Versions/A/CloudCoreInternal\ndyld[33988]: /System/Library/PrivateFrameworks/CloudAsset.framework/Versions/A/CloudAsset\ndyld[33988]: <36607924-B1B2-39ED-B6D1-29683EFB67A0> /System/Library/Frameworks/PushKit.framework/Versions/A/PushKit\ndyld[33988]: <26865685-385E-3120-9886-2082EEC20B20> /System/Library/Frameworks/CoreTransferable.framework/Versions/A/CoreTransferable\ndyld[33988]: <024DBF34-DF66-3164-825E-F77F85462E66> /System/Library/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth\ndyld[33988]: <87907862-52FF-3F24-AC29-7C1678BCD277> /System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport\ndyld[33988]: /System/Library/PrivateFrameworks/CloudTelemetryTools.framework/Versions/A/CloudTelemetryTools\ndyld[33988]: /System/Library/PrivateFrameworks/CloudTelemetryShared.dylib\ndyld[33988]: <7BEBC9F1-212D-37F4-B601-A7AAD12F7225> /System/Library/PrivateFrameworks/RTCReporting.framework/Versions/A/RTCReporting\ndyld[33988]: <7A222E30-8DD4-3B1D-B820-4EA33B797525> /System/Library/PrivateFrameworks/AAAFoundationSwift.framework/Versions/A/AAAFoundationSwift\ndyld[33988]: <9D724BE7-0B01-39F3-82BE-BCEDC6EBAC8A> /System/Library/PrivateFrameworks/AppleIDSSOAuthentication.framework/Versions/A/AppleIDSSOAuthentication\ndyld[33988]: <659AFBBD-E22E-3474-BCFE-298DA57B1464> /System/Library/PrivateFrameworks/UIFoundation.framework/Versions/A/UIFoundation\ndyld[33988]: <5B73C216-2ACE-3F8C-A2B3-7D35D5D0395A> /System/Library/PrivateFrameworks/CaptiveNetwork.framework/Versions/A/CaptiveNetwork\ndyld[33988]: /System/Library/PrivateFrameworks/EAP8021X.framework/Versions/A/EAP8021X\ndyld[33988]: <38DAF669-429F-384F-87D6-8550842EEB5E> /System/Library/PrivateFrameworks/CryptoKitPrivate.framework/Versions/A/CryptoKitPrivate\ndyld[33988]: <627D64D5-2D3C-3EC6-B4AB-FEF4DEE40871> /System/Library/PrivateFrameworks/FindMyDevice.framework/Versions/A/FindMyDevice\ndyld[33988]: <930F9F83-A947-3788-9FBA-49872FC3AF8D> /System/Library/PrivateFrameworks/FMCoreLite.framework/Versions/A/FMCoreLite\ndyld[33988]: <5F356BA6-47B5-382B-B54A-1550BB138A62> /System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement\ndyld[33988]: <6508C698-D587-3B5A-B95B-A3A3F78CE122> /usr/lib/libCheckFix.dylib\ndyld[33988]: <29AA0F7F-26F4-35B3-96DF-8A67B00A58AB> /System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities\ndyld[33988]: <1ACDAA8A-EB43-37C7-B661-39B1C0E05290> /System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary\ndyld[33988]: <12479A32-B72F-3A09-BB03-BA56C37853B5> /System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore\ndyld[33988]: /usr/lib/libapp_launch_measurement.dylib\ndyld[33988]: <4F3BEA3B-A363-3D04-B903-9B613C993CA1> /System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices\ndyld[33988]: <6C426EA5-7F1E-333E-BB5D-74465EFED12B> /usr/lib/libxslt.1.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement\ndyld[33988]: <2E99AD96-DC1C-3643-9988-273AB6844EFC> /usr/lib/libcurl.4.dylib\ndyld[33988]: <46D13DA8-E7BD-37DC-91DD-D5E6CE00C2B8> /usr/lib/libcrypto.46.dylib\ndyld[33988]: <07D5F4C6-1A13-344C-882B-0B0A08048DE5> /usr/lib/libssl.48.dylib\ndyld[33988]: <8CABDD64-E6C6-3B77-B839-2E2B875CE0FE> /System/Library/Frameworks/LDAP.framework/Versions/A/LDAP\ndyld[33988]: <96C0BAAA-7FE6-3277-AFBC-31926F5935EE> /System/Library/PrivateFrameworks/TrustEvaluationAgent.framework/Versions/A/TrustEvaluationAgent\ndyld[33988]: <7CF2A32E-72DD-34F7-B179-A17ED3D7DD75> /usr/lib/libsasl2.2.dylib\ndyld[33988]: /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa\ndyld[33988]: /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit\ndyld[33988]: <4840B78C-D96B-35B9-85C7-E5889C44A7C4> /System/Library/PrivateFrameworks/CollectionViewCore.framework/Versions/A/CollectionViewCore\ndyld[33988]: /System/Library/PrivateFrameworks/XCTTargetBootstrap.framework/Versions/A/XCTTargetBootstrap\ndyld[33988]: <0D6F3043-5372-3B15-96BC-6F45AD86F410> /System/Library/PrivateFrameworks/UserActivity.framework/Versions/A/UserActivity\ndyld[33988]: <7CDC68D4-0845-3053-AB80-C5A1F354060F> /System/Library/PrivateFrameworks/FrontBoard.framework/Versions/A/FrontBoard\ndyld[33988]: /System/Library/PrivateFrameworks/UIIntelligenceSupport.framework/Versions/A/UIIntelligenceSupport\ndyld[33988]: <9EB0840F-B045-3529-9467-1470A3C6CA02> /System/Library/Frameworks/SwiftUICore.framework/Versions/A/SwiftUICore\ndyld[33988]: <84FB5635-42EE-3BB5-B7CD-8A354AD7DB0A> /System/Library/PrivateFrameworks/WritingTools.framework/Versions/A/WritingTools\ndyld[33988]: <6ECD36F7-0A2E-3631-A57F-FD0152173454> /System/Library/PrivateFrameworks/WindowManagement.framework/Versions/A/WindowManagement\ndyld[33988]: /System/Library/PrivateFrameworks/FocusEngine.framework/Versions/A/FocusEngine\ndyld[33988]: <2BC041DF-695A-32EE-B164-1C35C2AFFC53> /System/Library/PrivateFrameworks/DesignLibrary.framework/Versions/A/DesignLibrary\ndyld[33988]: /System/Library/PrivateFrameworks/DFRFoundation.framework/Versions/A/DFRFoundation\ndyld[33988]: <365E81B9-B9BA-3F4F-83FD-86A35CFBC8AC> /System/Library/PrivateFrameworks/UpdateCycle.framework/Versions/A/UpdateCycle\ndyld[33988]: <38408482-CE3B-359E-9465-7FEB4BB79B54> /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox\ndyld[33988]: /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/SpeechRecognition.framework/Versions/A/SpeechRecognition\ndyld[33988]: <74C54353-A613-35A2-857A-737BB48906F9> /System/Library/PrivateFrameworks/PerformanceAnalysis.framework/Versions/A/PerformanceAnalysis\ndyld[33988]: <22008BA9-C61B-3FAD-A1A0-F6A1CD220343> /System/Library/Frameworks/Accessibility.framework/Versions/A/Accessibility\ndyld[33988]: <028E944B-66C4-39E2-A436-FB93FB6CED4E> /System/Library/Frameworks/Symbols.framework/Versions/A/Symbols\ndyld[33988]: /System/Library/PrivateFrameworks/Gestures.framework/Versions/A/Gestures\ndyld[33988]: <713AEF7A-43B6-3735-8AB5-361F5EE06BBA> /usr/lib/swift/libswiftSpatial.dylib\ndyld[33988]: /usr/lib/swift/libswiftCoreGraphics.dylib\ndyld[33988]: <14A11A94-6A52-3D24-9267-42EDAEEC5FDD> /usr/lib/swift/libswiftFoundation.dylib\ndyld[33988]: <76A7FE10-AD26-3505-A8CC-D37AC1C24D32> /usr/lib/swift/libswiftSwiftOnoneSupport.dylib\ndyld[33988]: <4B5C0268-23EB-3E20-8F57-CDECFB6E3205> /usr/lib/swift/libswiftsys_time.dylib\ndyld[33988]: /System/Library/PrivateFrameworks/CoreMaterial.framework/Versions/A/CoreMaterial\ndyld[33988]: <68B7C15F-537C-3FEF-838B-DC548772A45F> /usr/lib/libSpatial.dylib\ndyld[33988]: <183FD4D6-D766-34FC-B8E1-7C4D17435AC3> /System/Library/SubFrameworks/UIUtilities.framework/Versions/A/UIUtilities\ndyld[33988]: move loaded to delayed: XPCSupport\ndyld[33988]: move loaded to delayed: libcmark-gfm.dylib\ndyld[33988]: move loaded to delayed: GenerationalStorage\ndyld[33988]: move loaded to delayed: CacheDelete\ndyld[33988]: move loaded to delayed: QuickLookThumbnailing\ndyld[33988]: move loaded to delayed: FileProvider\ndyld[33988]: move loaded to delayed: DesktopServicesPriv\ndyld[33988]: move loaded to delayed: AOSKit\ndyld[33988]: move loaded to delayed: AppSSOCore\ndyld[33988]: move loaded to delayed: BackgroundSystemTasks\ndyld[33988]: move loaded to delayed: DuetActivityScheduler\ndyld[33988]: move loaded to delayed: FTServices\ndyld[33988]: move loaded to delayed: libMemoryResourceException.dylib\ndyld[33988]: move loaded to delayed: NetworkScore\ndyld[33988]: move loaded to delayed: NetworkServiceProxy\ndyld[33988]: move loaded to delayed: StreamingExtractor\ndyld[33988]: move loaded to delayed: SymptomReporter\ndyld[33988]: move loaded to delayed: libnetworkextension.dylib\ndyld[33988]: move loaded to delayed: CloudServices\ndyld[33988]: move loaded to delayed: SymptomAnalytics\ndyld[33988]: move loaded to delayed: libcupolicy.dylib\ndyld[33988]: move loaded to delayed: NetworkExtension\ndyld[33988]: move loaded to delayed: libnwswifttls.dylib\ndyld[33988]: move loaded to delayed: libpcap.A.dylib\ndyld[33988]: move loaded to delayed: CoreDuetContext\ndyld[33988]: move loaded to delayed: CoreDuet\ndyld[33988]: move loaded to delayed: CoreLocation\ndyld[33988]: move loaded to delayed: Intents\ndyld[33988]: move loaded to delayed: libCGInterfaces.dylib\ndyld[33988]: move loaded to delayed: AccelerateGPU\ndyld[33988]: move loaded to delayed: AudioDSPGraph\ndyld[33988]: move loaded to delayed: AssistantServices\ndyld[33988]: move loaded to delayed: SAObjects\ndyld[33988]: move loaded to delayed: MediaRemote\ndyld[33988]: move loaded to delayed: SiriTTSService\ndyld[33988]: move loaded to delayed: SiriCrossDeviceArbitration\ndyld[33988]: move loaded to delayed: FaceTimeNameUtility\ndyld[33988]: move loaded to delayed: AuthKit\ndyld[33988]: move loaded to delayed: SiriCrossDeviceArbitrationFeedback\ndyld[33988]: move loaded to delayed: libswiftCoreLocation.dylib\ndyld[33988]: move loaded to delayed: UIKitServices\ndyld[33988]: move loaded to delayed: AudioAccessoryServices\ndyld[33988]: move loaded to delayed: Sharing\ndyld[33988]: move loaded to delayed: IDSFoundation\ndyld[33988]: move loaded to delayed: Apple80211\ndyld[33988]: move loaded to delayed: CoreWLAN\ndyld[33988]: move loaded to delayed: IMFoundation\ndyld[33988]: move loaded to delayed: Marco\ndyld[33988]: move loaded to delayed: CommonUtilities\ndyld[33988]: move loaded to delayed: Engram\ndyld[33988]: move loaded to delayed: XPCDistributed\ndyld[33988]: move loaded to delayed: libtidy.A.dylib\ndyld[33988]: move loaded to delayed: Bom\ndyld[33988]: move loaded to delayed: libParallelCompression.dylib\ndyld[33988]: move loaded to delayed: MultiverseSupport\ndyld[33988]: move loaded to delayed: DiskManagement\ndyld[33988]: move loaded to delayed: AppleIDAuthSupport\ndyld[33988]: move loaded to delayed: AAAFoundation\ndyld[33988]: move loaded to delayed: KeychainCircle\ndyld[33988]: move loaded to delayed: URLFormatting\ndyld[33988]: move loaded to delayed: libcsfde.dylib\ndyld[33988]: move loaded to delayed: libCoreStorage.dylib\ndyld[33988]: move loaded to delayed: ProtectedCloudStorage\ndyld[33988]: move loaded to delayed: EFILogin\ndyld[33988]: move loaded to delayed: OctagonTrust\ndyld[33988]: move loaded to delayed: MediaServices\ndyld[33988]: move loaded to delayed: IDS\ndyld[33988]: move loaded to delayed: LocalAuthentication\ndyld[33988]: move loaded to delayed: LocalAuthenticationCore\ndyld[33988]: move loaded to delayed: LocalAuthenticationCredentialServices\ndyld[33988]: move loaded to delayed: SharedUtils\ndyld[33988]: move loaded to delayed: PersistentConnection\ndyld[33988]: move loaded to delayed: SonicFoundation\ndyld[33988]: move loaded to delayed: AsyncAlgorithmsInternal\ndyld[33988]: move loaded to delayed: FTAWD\ndyld[33988]: move loaded to delayed: libtailspin.dylib\ndyld[33988]: move loaded to delayed: Osprey\ndyld[33988]: move loaded to delayed: SiriTTS\ndyld[33988]: move loaded to delayed: SiriPowerInstrumentation\ndyld[33988]: move loaded to delayed: TailspinSymbolication\ndyld[33988]: move loaded to delayed: Darwinup\ndyld[33988]: move loaded to delayed: SignpostSupport\ndyld[33988]: move loaded to delayed: FeatureFlagsSupport\ndyld[33988]: move loaded to delayed: ktrace\ndyld[33988]: move loaded to delayed: SampleAnalysis\ndyld[33988]: move loaded to delayed: kperfdata\ndyld[33988]: move loaded to delayed: libdscsym.dylib\ndyld[33988]: move loaded to delayed: BulkSymbolication\ndyld[33988]: move loaded to delayed: IntentsFoundation\ndyld[33988]: move loaded to delayed: ApplePushService\ndyld[33988]: move loaded to delayed: CloudKit\ndyld[33988]: move loaded to delayed: CoreDuetDaemonProtocol\ndyld[33988]: move loaded to delayed: DeviceIdentity\ndyld[33988]: move loaded to delayed: SharedWithYouCore\ndyld[33988]: move loaded to delayed: CloudTelemetry\ndyld[33988]: move loaded to delayed: AppleAccount\ndyld[33988]: move loaded to delayed: C2\ndyld[33988]: move loaded to delayed: CloudCoreInternal\ndyld[33988]: move loaded to delayed: CloudAsset\ndyld[33988]: move loaded to delayed: PushKit\ndyld[33988]: move loaded to delayed: CloudTelemetryTools\ndyld[33988]: move loaded to delayed: CloudTelemetryShared.dylib\ndyld[33988]: move loaded to delayed: RTCReporting\ndyld[33988]: move loaded to delayed: AAAFoundationSwift\ndyld[33988]: move loaded to delayed: AppleIDSSOAuthentication\ndyld[33988]: move loaded to delayed: CaptiveNetwork\ndyld[33988]: move loaded to delayed: EAP8021X\ndyld[33988]: move loaded to delayed: CryptoKitPrivate\ndyld[33988]: move loaded to delayed: FindMyDevice\ndyld[33988]: move loaded to delayed: FMCoreLite\ndyld[33988]: move loaded to delayed: ServiceManagement\ndyld[33988]: <9B0A2398-8610-35D6-B7F3-B76933F2AAF4> /System/Library/Extensions/AGXMetalG16G_B0.bundle/Contents/MacOS/AGXMetalG16G_B0\ndyld[33988]: <9235E8EC-599D-386F-8A8A-6B6A92D33369> /System/Library/PrivateFrameworks/IOGPU.framework/Versions/A/IOGPU\n" + }, + { + "command": [ + "/opt/homebrew/opt/python@3.14/bin/python3.14", + "-c", + "import ctypes; library=ctypes.CDLL('/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/native-enabled/libwebscene_native_engine.dylib'); print(library.webscene_engine_get_abi_version())" + ], + "passed": true, + "exitCode": 0, + "stdout": "3\n", + "loaderTrace": "dyld[33989]: <8D7882C5-027F-3692-BFE3-C897D8F59412> /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/bin/python3.14\ndyld[33989]: <1DEC725C-63A6-3B9C-A038-DC35832D65CB> /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python\ndyld[33989]: <9B672762-7B1F-30BC-96DE-F176B372D66D> /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation\ndyld[33989]: <03BD9E32-CF0A-37B0-898A-3CE8DE06D842> /usr/lib/libobjc.A.dylib\ndyld[33989]: <7D56DA94-31EB-35F0-B886-4010C075E035> /System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal\ndyld[33989]: <91DACE39-FA28-3191-818D-1FCC6A0E615A> /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation\ndyld[33989]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/liboah.dylib\ndyld[33989]: <820D290D-51A0-3064-A1F2-4F0AAF7E6BF4> /usr/lib/libfakelink.dylib\ndyld[33989]: <53A3E31E-06A8-325E-B5A8-316B88AA3C92> /usr/lib/libicucore.A.dylib\ndyld[33989]: <4FED5EE2-5D3E-35B1-A170-9859C4B683BB> /usr/lib/libSystem.B.dylib\ndyld[33989]: <4109E8DD-0A81-310C-B1B3-23B87186D0D8> /System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking\ndyld[33989]: <83794FB3-DE9B-3D23-AB5E-2C1D5D30F134> /usr/lib/swift/libswiftCore.dylib\ndyld[33989]: /usr/lib/libc++abi.dylib\ndyld[33989]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/libRosetta.dylib\ndyld[33989]: /usr/lib/libc++.1.dylib\ndyld[33989]: <4FD234EA-2C18-3C25-8BD0-B1F4805C6675> /usr/lib/swift/libswiftObjectiveC.dylib\ndyld[33989]: <9E3C7597-446F-3C50-9930-2425D9252C0C> /usr/lib/libswiftPrespecialized.dylib\ndyld[33989]: <1479C415-3678-3968-AC77-06373490860E> /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration\ndyld[33989]: <13EDE3A5-A7D9-3FB8-B0C2-2FB7F7272B34> /usr/lib/libz.1.dylib\ndyld[33989]: <54AD73AF-852E-3CD6-8B7D-E73BE79857D3> /System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout\ndyld[33989]: <1A2A9A41-5269-3B0C-BCEE-B446966CE366> /usr/lib/libcmark-gfm.dylib\ndyld[33989]: /usr/lib/libcompression.dylib\ndyld[33989]: <4A3B95C5-AA2E-338C-9398-56895AF82D97> /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork\ndyld[33989]: <332C4B80-5B3C-34E7-AD1F-F6131E607F95> /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration\ndyld[33989]: <0048DB96-1737-3FC5-AF0C-AF784FA24A03> /usr/lib/libarchive.2.dylib\ndyld[33989]: <6CD959AA-4825-306A-864A-BD69EC5F2DC0> /usr/lib/libDiagnosticMessagesClient.dylib\ndyld[33989]: <1E8A4F9E-3954-3458-B3BB-BE97F961C105> /usr/lib/libxml2.2.dylib\ndyld[33989]: <56AE2857-29E0-34E9-B2C3-EE8E951EEFC5> /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices\ndyld[33989]: /usr/lib/liblangid.dylib\ndyld[33989]: <12372585-DF92-33EF-B632-714FAA13260A> /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit\ndyld[33989]: /System/Library/Frameworks/Combine.framework/Versions/A/Combine\ndyld[33989]: <6098453F-4D7E-38B4-8ADC-02C9FF51E14A> /System/Library/PrivateFrameworks/CollectionsInternal.framework/Versions/A/CollectionsInternal\ndyld[33989]: <9A1279D4-575A-3E48-A460-A631A3F82D18> /System/Library/PrivateFrameworks/ReflectionInternal.framework/Versions/A/ReflectionInternal\ndyld[33989]: <6D89CD71-A86D-3D78-A64B-96AB79550F79> /System/Library/PrivateFrameworks/RuntimeInternal.framework/Versions/A/RuntimeInternal\ndyld[33989]: <4975D13C-2AC5-3473-85C0-98054A81D7C6> /usr/lib/swift/libswiftCoreFoundation.dylib\ndyld[33989]: <1DB56DA9-CF6B-3023-ABDF-5A37CB79223C> /usr/lib/swift/libswiftDarwin.dylib\ndyld[33989]: /usr/lib/swift/libswiftDispatch.dylib\ndyld[33989]: <06A92787-4440-3757-AF32-F2B331C753A2> /usr/lib/swift/libswiftIOKit.dylib\ndyld[33989]: <7CD9BDE7-F36B-3471-9295-38E181D6D9E5> /usr/lib/swift/libswiftSystem.dylib\ndyld[33989]: <24AEDAC1-C1EE-30F4-8818-72EBF8969D0C> /usr/lib/swift/libswiftXPC.dylib\ndyld[33989]: <52F59382-A6A6-3F55-8A85-D9FB822D370F> /usr/lib/swift/libswift_Builtin_float.dylib\ndyld[33989]: <8E168857-47F4-349F-A718-A18DB144FCB0> /usr/lib/swift/libswift_Concurrency.dylib\ndyld[33989]: <85246B9A-A757-3F67-B792-3A2F7BB2BB25> /usr/lib/swift/libswift_DarwinFoundation1.dylib\ndyld[33989]: <8DF0116D-DFC9-3906-9DF6-F1DBC47E324B> /usr/lib/swift/libswift_StringProcessing.dylib\ndyld[33989]: /usr/lib/swift/libswiftos.dylib\ndyld[33989]: <1C7E652B-6B94-3180-93A6-EF8DBA3A5448> /System/Library/Frameworks/Network.framework/Versions/A/Network\ndyld[33989]: <4C6139EE-BF87-37A6-B226-830A6FDC36F8> /System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo\ndyld[33989]: <9D0387FC-E8F6-3004-9C95-CA68EA715C8B> /System/Library/Frameworks/Security.framework/Versions/A/Security\ndyld[33989]: <633BCB5F-F063-3D5A-B52A-F72AE236824B> /usr/lib/libbsm.0.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer\ndyld[33989]: <10A4E63B-A1EB-31CC-B3E1-DB4FE115FC84> /System/Library/PrivateFrameworks/BackgroundSystemTasks.framework/Versions/A/BackgroundSystemTasks\ndyld[33989]: /System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics\ndyld[33989]: <7C50137B-2ABD-3819-B033-AE65B05A6085> /System/Library/PrivateFrameworks/CoreWiFi.framework/Versions/A/CoreWiFi\ndyld[33989]: /System/Library/PrivateFrameworks/LoggingSupport.framework/Versions/A/LoggingSupport\ndyld[33989]: <91A461DE-C8E8-3868-B393-BA6E5A17DF2A> /System/Library/PrivateFrameworks/MobileAsset.framework/Versions/A/MobileAsset\ndyld[33989]: /System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog\ndyld[33989]: /System/Library/PrivateFrameworks/Rapport.framework/Versions/A/Rapport\ndyld[33989]: /System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices\ndyld[33989]: <9F52706C-75BD-34AF-A29E-C26608124ACC> /System/Library/Frameworks/SwiftData.framework/Versions/A/SwiftData\ndyld[33989]: <259877CE-4E2C-34A9-A07F-FEE2999D7B2F> /System/Library/PrivateFrameworks/Symptoms.framework/Versions/A/Frameworks/SymptomAnalytics.framework/Versions/A/SymptomAnalytics\ndyld[33989]: /System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers\ndyld[33989]: <4A78C569-FF0D-398B-9C25-33453F0CEC40> /System/Library/PrivateFrameworks/UserManagement.framework/Versions/A/UserManagement\ndyld[33989]: <5BF55637-F306-3D79-B5A1-DB8A871DAD4B> /usr/lib/libboringssl.dylib\ndyld[33989]: <831C79C1-8DBE-31A3-AA4E-8E2B041488D6> /usr/lib/libcupolicy.dylib\ndyld[33989]: <88925A0C-4960-3F6D-AF3A-B1983F7B3D18> /usr/lib/libdns_services.dylib\ndyld[33989]: /usr/lib/libnetworkextension.dylib\ndyld[33989]: <9753F471-40DD-3B9E-9D64-8D07C1B06BC9> /System/Library/Frameworks/NetworkExtension.framework/Versions/A/NetworkExtension\ndyld[33989]: /usr/lib/libnwswifttls.dylib\ndyld[33989]: <6F59933A-6618-33F1-BE52-E7FC3BF7A1EF> /usr/lib/libpcap.A.dylib\ndyld[33989]: <5E89267F-C684-348D-8356-F9DAD8B4CB13> /usr/lib/libquic.dylib\ndyld[33989]: /usr/lib/libusrtcp.dylib\ndyld[33989]: /usr/lib/libMobileGestalt.dylib\ndyld[33989]: /usr/lib/libapple_nghttp2.dylib\ndyld[33989]: <6937D729-7EF4-3972-9E12-694C17C1C1AB> /usr/lib/libcoretls_cfhelpers.dylib\ndyld[33989]: /usr/lib/libsqlite3.dylib\ndyld[33989]: <1617DBB1-2BFF-3619-903C-2FBB31348FB6> /System/Library/PrivateFrameworks/AtomicsInternal.framework/Versions/A/AtomicsInternal\ndyld[33989]: <41F66F01-A342-3091-A832-0B2B645C922B> /System/Library/PrivateFrameworks/InternalSwiftProtobuf.framework/Versions/A/InternalSwiftProtobuf\ndyld[33989]: <2EDB2E62-942F-3AB5-82AF-8E1328544E17> /usr/lib/swift/libswiftDistributed.dylib\ndyld[33989]: /usr/lib/swift/libswiftObservation.dylib\ndyld[33989]: /usr/lib/swift/libswiftSynchronization.dylib\ndyld[33989]: <9CD7B1E1-3E47-339C-A193-2392E3E0ED23> /usr/lib/system/libcache.dylib\ndyld[33989]: <3B110564-5278-3CB0-85F1-2CE8431FF935> /usr/lib/system/libcommonCrypto.dylib\ndyld[33989]: <6FB345CA-7F5C-3263-A23F-143F7539FD8A> /usr/lib/system/libcompiler_rt.dylib\ndyld[33989]: /usr/lib/system/libcopyfile.dylib\ndyld[33989]: <0642DDAD-4771-3C82-805C-E7C6701C1461> /usr/lib/system/libcorecrypto.dylib\ndyld[33989]: /usr/lib/system/libdispatch.dylib\ndyld[33989]: <957F93B3-8805-39C7-9C51-EDD1715F550E> /usr/lib/system/libdyld.dylib\ndyld[33989]: <7E863FCA-F3FF-32C7-8A8C-F983E946AFC3> /usr/lib/system/libkeymgr.dylib\ndyld[33989]: <949131E5-BDA2-39BA-AA50-62651BB51802> /usr/lib/system/libmacho.dylib\ndyld[33989]: /usr/lib/system/libquarantine.dylib\ndyld[33989]: <7460B5AE-469A-36A0-A7EC-6C7D69628E86> /usr/lib/system/libremovefile.dylib\ndyld[33989]: <54439739-33EE-3273-839F-CBA67D7F5CB1> /usr/lib/system/libsystem_asl.dylib\ndyld[33989]: /usr/lib/system/libsystem_blocks.dylib\ndyld[33989]: /usr/lib/system/libsystem_c.dylib\ndyld[33989]: /usr/lib/system/libsystem_collections.dylib\ndyld[33989]: /usr/lib/system/libsystem_configuration.dylib\ndyld[33989]: <14B2A47F-19C8-392F-8FDB-FE8AE375DD41> /usr/lib/system/libsystem_containermanager.dylib\ndyld[33989]: /usr/lib/system/libsystem_coreservices.dylib\ndyld[33989]: <8E07D22E-CE5A-38A0-B091-5B0338C326F5> /usr/lib/system/libsystem_darwin.dylib\ndyld[33989]: <971A4F65-493D-39F3-846D-0D33FA2769FD> /usr/lib/system/libsystem_darwindirectory.dylib\ndyld[33989]: <305F4398-E688-3384-B351-02D865EC8A04> /usr/lib/system/libsystem_dnssd.dylib\ndyld[33989]: <750CA446-92EA-3A56-9A7B-CC0841686C50> /usr/lib/system/libsystem_eligibility.dylib\ndyld[33989]: /usr/lib/system/libsystem_featureflags.dylib\ndyld[33989]: <9B5FB84B-31AD-3EA7-8F89-8C700D369DC8> /usr/lib/system/libsystem_info.dylib\ndyld[33989]: /usr/lib/system/libsystem_m.dylib\ndyld[33989]: /usr/lib/system/libsystem_malloc.dylib\ndyld[33989]: <9C7B1EEB-47BE-3791-93A9-CFC693CB9417> /usr/lib/system/libsystem_networkextension.dylib\ndyld[33989]: <15799128-6CBD-30D6-A2BB-B9D02B4470C0> /usr/lib/system/libsystem_notify.dylib\ndyld[33989]: <54688162-B50D-3D31-A1E8-7B9766D3530D> /usr/lib/system/libsystem_sandbox.dylib\ndyld[33989]: /usr/lib/system/libsystem_sanitizers.dylib\ndyld[33989]: /usr/lib/system/libsystem_secinit.dylib\ndyld[33989]: /usr/lib/system/libsystem_kernel.dylib\ndyld[33989]: /usr/lib/system/libsystem_platform.dylib\ndyld[33989]: /usr/lib/system/libsystem_pthread.dylib\ndyld[33989]: <229122B9-B8B1-3F2F-870E-8650AE3C4FB5> /usr/lib/system/libsystem_symptoms.dylib\ndyld[33989]: <93F1DD8C-6CD9-32B9-B222-D23DA5D161B4> /usr/lib/system/libsystem_trace.dylib\ndyld[33989]: <7194FF5B-A6C5-3D67-B00A-90209F10D603> /usr/lib/system/libsystem_trial.dylib\ndyld[33989]: <05FD0014-55B1-3B8A-A6BA-6C7A389C4123> /usr/lib/system/libunwind.dylib\ndyld[33989]: <33E44C2D-D65E-37A6-B85F-1A4CF524A050> /usr/lib/system/libxpc.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/XPCSupport.framework/Versions/A/XPCSupport\ndyld[33989]: /System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement\ndyld[33989]: /System/Library/PrivateFrameworks/AppleKeyStore.framework/Versions/A/AppleKeyStore\ndyld[33989]: /usr/lib/libCoreEntitlements.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity\ndyld[33989]: <81F4A8BA-C80F-3B53-82E7-57F6928609C5> /System/Library/PrivateFrameworks/CloudServices.framework/Versions/A/CloudServices\ndyld[33989]: <737479F2-7B20-3DB6-B9F4-0DAA1B73E9D0> /System/Library/PrivateFrameworks/SymptomDiagnosticReporter.framework/Versions/A/SymptomDiagnosticReporter\ndyld[33989]: /System/Library/PrivateFrameworks/CrashReporterSupport.framework/Versions/A/CrashReporterSupport\ndyld[33989]: /System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression\ndyld[33989]: <0EAB1F4A-9275-3FED-8EA6-E962ACDDEE5D> /usr/lib/libcoretls.dylib\ndyld[33989]: <7E84FD3B-E90E-317E-AC19-17B70AC809E5> /usr/lib/libpam.2.dylib\ndyld[33989]: /usr/lib/libxar.1.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS\ndyld[33989]: /System/Library/PrivateFrameworks/SwiftASN1Internal.framework/Versions/A/SwiftASN1Internal\ndyld[33989]: /usr/lib/libutil.dylib\ndyld[33989]: <8E04C57D-3651-386E-83D5-4728B732F214> /usr/lib/libenergytrace.dylib\ndyld[33989]: /usr/lib/system/libkxld.dylib\ndyld[33989]: <2BC48182-F354-3AB0-8F18-0C60CAAFE398> /System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer\ndyld[33989]: <5556FD64-9D47-3547-961E-3A27681F3C51> /System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface\ndyld[33989]: <6A4A85F4-3D12-3C4C-85EC-D53D61379F28> /usr/lib/libheimdal-asn1.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce\ndyld[33989]: /System/Library/PrivateFrameworks/OctagonTrust.framework/Versions/A/OctagonTrust\ndyld[33989]: /System/Library/PrivateFrameworks/AppleIDAuthSupport.framework/Versions/A/AppleIDAuthSupport\ndyld[33989]: <9A86DB3F-CC62-3E89-B872-35D04CFFBE42> /System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/KeychainCircle.framework/Versions/A/KeychainCircle\ndyld[33989]: <336E2CAC-84D2-34DC-8AE3-7FE688C609EA> /System/Library/PrivateFrameworks/AuthKit.framework/Versions/A/AuthKit\ndyld[33989]: /System/Library/PrivateFrameworks/AAAFoundation.framework/Versions/A/AAAFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag\ndyld[33989]: <79000980-1797-3115-B74B-60FA1E9C3C73> /System/Library/PrivateFrameworks/CorePhoneNumbers.framework/Versions/A/CorePhoneNumbers\ndyld[33989]: <21723046-939E-302F-883C-9DB417452E3A> /System/Library/PrivateFrameworks/MultiverseSupport.framework/Versions/A/MultiverseSupport\ndyld[33989]: <823F3D1A-65F1-3CC5-96B1-750263B8DB36> /System/Library/PrivateFrameworks/RemoteServiceDiscovery.framework/Versions/A/RemoteServiceDiscovery\ndyld[33989]: /System/Library/PrivateFrameworks/DiskManagement.framework/Versions/A/DiskManagement\ndyld[33989]: <5F6B668E-00B2-3BEC-959F-26BD6B50D42B> /System/Library/Frameworks/Accounts.framework/Versions/A/Accounts\ndyld[33989]: /System/Library/PrivateFrameworks/URLFormatting.framework/Versions/A/URLFormatting\ndyld[33989]: <91BDD1F8-831B-3B01-86BA-6BBCB43373C4> /System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary\ndyld[33989]: <885F9C72-1018-368B-AD36-E8A42E87FD91> /System/Library/PrivateFrameworks/RemoteXPC.framework/Versions/A/RemoteXPC\ndyld[33989]: /usr/lib/libFDR.dylib\ndyld[33989]: <24D28E7F-A1AE-3031-8679-A0D6C6D68A86> /usr/lib/libamsupport.dylib\ndyld[33989]: <29367004-5D60-38DB-831F-9E5EE9364B21> /usr/lib/libReverseProxyDevice.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/MSUDataAccessor.framework/Versions/A/MSUDataAccessor\ndyld[33989]: <9594FBFB-D49D-3DF6-8820-564633EAEC2B> /System/Library/PrivateFrameworks/AppleDeviceQuerySupport.framework/Versions/A/AppleDeviceQuerySupport\ndyld[33989]: <44CD8313-2D5B-3A34-BACA-EF8800803B4A> /System/Library/PrivateFrameworks/AOSKit.framework/Versions/A/AOSKit\ndyld[33989]: <5198BFE1-41D2-33D5-A9E0-C63F81A512D3> /System/Library/PrivateFrameworks/AppSSOCore.framework/Versions/A/AppSSOCore\ndyld[33989]: <61B2B917-D14A-38AD-A439-16E1C635441A> /System/Library/PrivateFrameworks/AppSupport.framework/Versions/A/AppSupport\ndyld[33989]: <816EC446-7C41-3A2F-A582-7CB856797C09> /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation\ndyld[33989]: <38C8FBEC-DE88-33FE-B742-A192F22CC754> /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics\ndyld[33989]: /System/Library/PrivateFrameworks/DuetActivityScheduler.framework/Versions/A/DuetActivityScheduler\ndyld[33989]: <0E78989C-854F-3664-AD92-6B7B6D04191C> /System/Library/PrivateFrameworks/FTServices.framework/Versions/A/FTServices\ndyld[33989]: <277D18EF-39E4-3F72-99E8-8D3DF65ED1D0> /System/Library/Frameworks/GSS.framework/Versions/A/GSS\ndyld[33989]: <5ACC6C0E-51E9-3B5A-B24F-89B22D070878> /System/Library/PrivateFrameworks/InternationalSupport.framework/Versions/A/InternationalSupport\ndyld[33989]: /usr/lib/libMemoryResourceException.dylib\ndyld[33989]: <798012E0-3FFC-3B8D-AC74-E7B7DAEA7E66> /System/Library/PrivateFrameworks/NetworkScore.framework/Versions/A/NetworkScore\ndyld[33989]: <2C93123F-99C8-3B8D-AAE6-3A817BE0A2BF> /System/Library/PrivateFrameworks/NetworkServiceProxy.framework/Versions/A/NetworkServiceProxy\ndyld[33989]: /System/Library/PrivateFrameworks/StreamingExtractor.framework/Versions/A/StreamingExtractor\ndyld[33989]: <1F2EDC7B-8F28-3721-8A60-F6E1BCFC29A3> /System/Library/PrivateFrameworks/StreamingZip.framework/Versions/A/StreamingZip\ndyld[33989]: <5DA62AF9-3D46-3D17-A3EB-7026A2F006DF> /System/Library/PrivateFrameworks/SymptomReporter.framework/Versions/A/SymptomReporter\ndyld[33989]: /usr/lib/liblzma.5.dylib\ndyld[33989]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents\ndyld[33989]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore\ndyld[33989]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata\ndyld[33989]: <61677289-93B7-382F-86CA-B856361D293F> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices\ndyld[33989]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit\ndyld[33989]: <435D6243-695B-3543-A722-10106F5696BD> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE\ndyld[33989]: <01579E0C-9D85-3521-8916-4DDC990CD064> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices\ndyld[33989]: <6A26D479-5926-330B-9FB8-9B7A6BE8E239> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices\ndyld[33989]: <297AC970-E432-3BBD-986C-36782634062E> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList\ndyld[33989]: <6508C698-D587-3B5A-B95B-A3A3F78CE122> /usr/lib/libCheckFix.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC\ndyld[33989]: /System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP\ndyld[33989]: <29AA0F7F-26F4-35B3-96DF-8A67B00A58AB> /System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities\ndyld[33989]: <9171DD7D-3994-3963-9A28-BC163BF97DE6> /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate\ndyld[33989]: /usr/lib/libmecab.dylib\ndyld[33989]: <1CA9048E-57DD-30F4-A3E6-FE6E97D5BF82> /usr/lib/libCRFSuite.dylib\ndyld[33989]: <74E55DD6-720D-39E4-897E-EB4328E1946D> /usr/lib/libgermantok.dylib\ndyld[33989]: <92FAD15C-EEA5-34E9-B309-75A1CD1B620B> /usr/lib/libThaiTokenizer.dylib\ndyld[33989]: <2B16DF37-A596-3D8A-AE47-33E580EB1354> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage\ndyld[33989]: <8203944D-B53E-3D7E-A481-3C676CAE1B6A> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib\ndyld[33989]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib\ndyld[33989]: <08508E7B-096D-31AB-9C66-191C877ED62F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/Libraries/libCGInterfaces.dylib\ndyld[33989]: <8945E7B7-12AE-3FF4-AA3B-D4DF9A06FEE7> /System/Library/PrivateFrameworks/AccelerateGPU.framework/Versions/A/AccelerateGPU\ndyld[33989]: <23402175-D2CF-3B08-88D0-AFBBCF775FEF> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib\ndyld[33989]: <086CBEED-2F64-3E75-AB99-8C8C0E0A2F1C> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices\ndyld[33989]: <0616AF41-149E-3F4A-906E-56E2642457BE> /System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo\ndyld[33989]: <873404F1-CC9D-30F9-AE06-8EA58D292005> /System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync\ndyld[33989]: /System/Library/Frameworks/CoreText.framework/Versions/A/CoreText\ndyld[33989]: /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO\ndyld[33989]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS\ndyld[33989]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices\ndyld[33989]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore\ndyld[33989]: <59BBF27B-1D89-3D35-9210-8386EFA15A8D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD\ndyld[33989]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy\ndyld[33989]: <9CDA611B-254A-3779-9356-369485134C2D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis\ndyld[33989]: <0C8F41C6-6D93-3DB3-B522-CA8CFF5C3B33> /System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight\ndyld[33989]: <9E126CE0-FBB2-3B15-953F-CCDC758E34FB> /System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib\ndyld[33989]: <07CF779F-8F51-3764-B486-23D76868FF91> /System/Library/PrivateFrameworks/BiomeLibrary.framework/Versions/A/BiomeLibrary\ndyld[33989]: <959C748F-8851-3A25-BFFA-5FEA80296965> /System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard\ndyld[33989]: /System/Library/PrivateFrameworks/BoardServices.framework/Versions/A/BoardServices\ndyld[33989]: /System/Library/PrivateFrameworks/BackBoardServices.framework/Versions/A/BackBoardServices\ndyld[33989]: <7F763DF9-EA7F-3938-B599-DCCF4605E610> /System/Library/PrivateFrameworks/BackBoardHIDEventFoundation.framework/Versions/A/BackBoardHIDEventFoundation\ndyld[33989]: /System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay\ndyld[33989]: /System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox\ndyld[33989]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders\ndyld[33989]: /System/Library/PrivateFrameworks/ProDisplayLibrary.framework/Versions/A/ProDisplayLibrary\ndyld[33989]: <1E529C1A-B09C-3EB7-A286-CE00E292D561> /System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator\ndyld[33989]: <493E76D9-74D4-333B-A3B2-E5F9BC86429D> /System/Library/Frameworks/Metal.framework/Versions/A/Metal\ndyld[33989]: /System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator\ndyld[33989]: /System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia\ndyld[33989]: /System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient\ndyld[33989]: <98CB7012-30E5-3BDD-8C84-CDBDA9DB3017> /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore\ndyld[33989]: <57F7BB9C-649D-3360-AA86-A502815D77FA> /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport\ndyld[33989]: <625F222D-6394-39B9-A1F2-12B9EA56DD85> /usr/lib/swift/libswiftAccelerate.dylib\ndyld[33989]: /usr/lib/swift/libswiftCoreAudio.dylib\ndyld[33989]: /usr/lib/swift/libswiftCoreMedia.dylib\ndyld[33989]: <7235A6A9-49B2-3B94-9DD6-C987019CDBF2> /usr/lib/swift/libswiftMetal.dylib\ndyld[33989]: <9670AE5C-271A-3DCB-9A0A-8E3A7CCC2726> /usr/lib/swift/libswiftOSLog.dylib\ndyld[33989]: <63444A8C-9E8C-3778-820D-1E0C88CA2DF7> /usr/lib/swift/libswiftQuartzCore.dylib\ndyld[33989]: /usr/lib/swift/libswiftUniformTypeIdentifiers.dylib\ndyld[33989]: <9247A5B6-A883-3A07-BEE7-A223840317A4> /usr/lib/swift/libswiftVideoToolbox.dylib\ndyld[33989]: /usr/lib/swift/libswiftsimd.dylib\ndyld[33989]: <2110407D-EFB4-373E-B963-9C92E26594B2> /System/Library/PrivateFrameworks/BiomeStreams.framework/Versions/A/BiomeStreams\ndyld[33989]: /System/Library/PrivateFrameworks/BiomeStorage.framework/Versions/A/BiomeStorage\ndyld[33989]: <455A5553-E683-30B4-A906-1F14E75F6E61> /System/Library/PrivateFrameworks/BiomeFoundation.framework/Versions/A/BiomeFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/IntelligencePlatformLibrary.framework/Versions/A/IntelligencePlatformLibrary\ndyld[33989]: <42CDC0E6-51BA-3804-BD3E-EDF87FC74034> /System/Library/PrivateFrameworks/PoirotSchematizer.framework/Versions/A/PoirotSchematizer\ndyld[33989]: <2362E209-EC61-3FFC-9486-1244BB29BE82> /System/Library/PrivateFrameworks/BiomeSync.framework/Versions/A/BiomeSync\ndyld[33989]: /System/Library/PrivateFrameworks/BiomeDSL.framework/Versions/A/BiomeDSL\ndyld[33989]: <0F03104F-FC8B-3ADD-8850-4B7029E2B56E> /System/Library/PrivateFrameworks/BiomePubSub.framework/Versions/A/BiomePubSub\ndyld[33989]: <73EE1A0A-0D29-3104-98CB-BEFEDA53F7C0> /System/Library/PrivateFrameworks/ProactiveSupport.framework/Versions/A/ProactiveSupport\ndyld[33989]: /System/Library/PrivateFrameworks/FeatureFlags.framework/Versions/A/FeatureFlags\ndyld[33989]: /System/Library/PrivateFrameworks/PoirotUDFs.framework/Versions/A/PoirotUDFs\ndyld[33989]: /usr/lib/swift/libswift_DarwinFoundation2.dylib\ndyld[33989]: <8D2C31B5-FB10-3BF6-8566-F0DCD56C8582> /usr/lib/swift/libswift_DarwinFoundation3.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime\ndyld[33989]: <858910C5-1D4A-37B7-BF0E-EE02E24A2ACD> /System/Library/PrivateFrameworks/InternationalTextSearch.framework/Versions/A/InternationalTextSearch\ndyld[33989]: <13271AA6-33EA-369B-B2D1-6EC528C820E7> /System/Library/PrivateFrameworks/SoftwareUpdateCoreSupport.framework/Versions/A/SoftwareUpdateCoreSupport\ndyld[33989]: <2CA857AF-D999-34DC-94A1-3AC0E5B80416> /System/Library/PrivateFrameworks/SoftwareUpdateCoreConnect.framework/Versions/A/SoftwareUpdateCoreConnect\ndyld[33989]: /usr/lib/libbootpolicy.dylib\ndyld[33989]: /usr/lib/libpartition2_dynamic.dylib\ndyld[33989]: <9A8926C8-36A6-3DB4-A485-059C1F630984> /usr/lib/libAppleArchive.dylib\ndyld[33989]: <5FFE1FFA-6BD0-32AF-A815-7543731CA763> /usr/lib/libbz2.1.0.dylib\ndyld[33989]: <06728C4D-5750-308F-8290-EAF7BE91F4BB> /System/Library/PrivateFrameworks/OSAnalytics.framework/Versions/A/OSAnalytics\ndyld[33989]: <2FA711C7-F764-363A-BF03-295E0DA88B79> /System/Library/PrivateFrameworks/DeviceRecovery.framework/Versions/A/DeviceRecovery\ndyld[33989]: <59136324-34E6-3367-92BB-659346907A04> /System/Library/PrivateFrameworks/CoreSymbolication.framework/Versions/A/CoreSymbolication\ndyld[33989]: <724D42FC-F4FD-39C7-A1BF-D0AD086231F4> /System/Library/PrivateFrameworks/Symbolication.framework/Versions/A/Symbolication\ndyld[33989]: <7C923545-F3BB-3215-9720-85196358D9F1> /System/Library/PrivateFrameworks/DebugSymbols.framework/Versions/A/DebugSymbols\ndyld[33989]: <566F2D7D-0F3B-3290-A739-7A40A151F0BE> /System/Library/PrivateFrameworks/MallocStackLogging.framework/Versions/A/MallocStackLogging\ndyld[33989]: <7B63C2BF-8C7C-3ECA-ACD9-F1B75DBE018C> /usr/lib/swift/libswift_RegexParser.dylib\ndyld[33989]: <4646F780-1D5E-3EE7-B00A-64619293CC18> /usr/lib/libiconv.2.dylib\ndyld[33989]: <1940124C-0D73-35D2-9D94-A75F116088A0> /usr/lib/libcharset.1.dylib\ndyld[33989]: <24779350-BC29-3465-AAB3-F7CD0DA5844A> /System/Library/PrivateFrameworks/PoirotSQLite.framework/Versions/A/PoirotSQLite\ndyld[33989]: <2091B02D-8D55-3DC4-8097-60C193D03C85> /System/Library/PrivateFrameworks/CascadeSets.framework/Versions/A/CascadeSets\ndyld[33989]: <7F00413A-4D40-3DBF-8FD5-859B23E6DC03> /System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG\ndyld[33989]: /usr/lib/libexpat.1.dylib\ndyld[33989]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib\ndyld[33989]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib\ndyld[33989]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib\ndyld[33989]: <7304F8B3-8E0F-3813-BFAF-9A565CEA0A11> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib\ndyld[33989]: <01AAD3B4-D6BA-36D9-BA6F-D494D2AC161D> /usr/lib/libate.dylib\ndyld[33989]: <8EA6CA42-AA01-3C0F-9672-4917481BAAAE> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib\ndyld[33989]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib\ndyld[33989]: <526C249F-FF2E-3DC4-A639-B41A032E8CCE> /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libllvm-flatbuffers.dylib\ndyld[33989]: <1FDD3B19-C04A-3EE7-B7DF-E1F89954A696> /System/Library/PrivateFrameworks/FramePacing.framework/Versions/A/FramePacing\ndyld[33989]: <2C410B78-B9A5-30DC-8D83-FFEC1277F34C> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libGPUCompilerUtils.dylib\ndyld[33989]: <90CFC86E-833E-3E9F-BAAC-2B61BD750DA6> /System/Library/PrivateFrameworks/CoreDuetContext.framework/Versions/A/CoreDuetContext\ndyld[33989]: <838F99F9-D3FA-335B-9767-B5D04A3FACA6> /System/Library/PrivateFrameworks/CoreDuet.framework/Versions/A/CoreDuet\ndyld[33989]: <712AD9C1-44D2-36F4-BA8E-15038521462B> /System/Library/Frameworks/CoreData.framework/Versions/A/CoreData\ndyld[33989]: <9805BB7B-12C9-39F5-9070-C5B8BFCAE2AF> /System/Library/Frameworks/CoreLocation.framework/Versions/A/CoreLocation\ndyld[33989]: /System/Library/Frameworks/Intents.framework/Versions/A/Intents\ndyld[33989]: /System/Library/Frameworks/_LocationEssentials.framework/Versions/A/_LocationEssentials\ndyld[33989]: <1DAFDDDA-BB7B-320E-BCFC-B7C22886D486> /System/Library/PrivateFrameworks/GeoServices.framework/Versions/A/GeoServices\ndyld[33989]: /System/Library/PrivateFrameworks/LocationSupport.framework/Versions/A/LocationSupport\ndyld[33989]: <515FDCCC-535A-398B-BBD3-3D35565F5423> /System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth\ndyld[33989]: <38EE3C42-06D6-3A46-A420-DF701A4EA911> /System/Library/PrivateFrameworks/GeoServicesCore.framework/Versions/A/GeoServicesCore\ndyld[33989]: <8D0ECDD1-24B6-3B8D-9CF9-CDC55FC64490> /System/Library/PrivateFrameworks/PhoneNumbers.framework/Versions/A/PhoneNumbers\ndyld[33989]: <3D533C35-3A2A-3672-92EF-5FEE9EE739AC> /System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation\ndyld[33989]: <2B5FB7B0-844C-3D84-9EFD-020B285B0F8D> /System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport\ndyld[33989]: <62740FDD-2B16-3319-B5C9-022D45C6B03A> /System/Library/PrivateFrameworks/OSEligibility.framework/Versions/A/OSEligibility\ndyld[33989]: <10C63D59-07BC-3518-87A0-83CAC48D8A70> /System/Library/PrivateFrameworks/IconServices.framework/Versions/A/IconServices\ndyld[33989]: <8EF56F82-8CCE-3811-AD16-6D0939187B45> /System/Library/Frameworks/LightweightCodeRequirements.framework/Versions/A/LightweightCodeRequirements\ndyld[33989]: /System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit\ndyld[33989]: <1946F8FE-0ABC-3F8F-9116-5451ECABD14C> /System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices\ndyld[33989]: /System/Library/PrivateFrameworks/IconFoundation.framework/Versions/A/IconFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/AssistantServices.framework/Versions/A/AssistantServices\ndyld[33989]: <6A34A62A-16D4-34F0-B34B-2D96B53C20AD> /System/Library/PrivateFrameworks/IconRendering.framework/Versions/A/IconRendering\ndyld[33989]: /System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI\ndyld[33989]: <0943679D-FF88-3F18-BE4B-D8B4827AB0B5> /System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage\ndyld[33989]: <968B5A5F-9749-3527-AF2A-66B599785308> /System/Library/PrivateFrameworks/SFSymbols.framework/Versions/A/SFSymbols\ndyld[33989]: /System/Library/Frameworks/DeveloperToolsSupport.framework/Versions/A/DeveloperToolsSupport\ndyld[33989]: <92090A92-DFAF-3EBC-886C-655EC158A53F> /System/Library/PrivateFrameworks/RenderBox.framework/Versions/A/RenderBox\ndyld[33989]: <986D57A7-BFF1-3DAA-8EB1-17CCAA76C731> /System/Library/PrivateFrameworks/CoreSVG.framework/Versions/A/CoreSVG\ndyld[33989]: /System/Library/PrivateFrameworks/TextureIO.framework/Versions/A/TextureIO\ndyld[33989]: /usr/lib/swift/libswiftCoreImage.dylib\ndyld[33989]: <77D85BA0-FE1C-3B5A-92DB-70A30202C990> /System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer\ndyld[33989]: /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL\ndyld[33989]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib\ndyld[33989]: <6CEF3932-AAC9-3F8E-905D-A826F2884C9A> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib\ndyld[33989]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib\ndyld[33989]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib\ndyld[33989]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib\ndyld[33989]: <07CB5D41-C2F3-3C33-951F-67B2C8B8B662> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices\ndyld[33989]: <5D3E7FFF-AC8E-3D6F-8E99-B199E593D270> /System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG\ndyld[33989]: <49E7449E-1385-3B53-94CC-36EFC31E98FE> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib\ndyld[33989]: <23C577A8-DB0B-3A0A-9058-1289483C262A> /usr/lib/libhvf.dylib\ndyld[33989]: <11E757EC-72FB-3C53-8ED7-641428AB6169> /System/Library/PrivateFrameworks/ParsingInternal.framework/Versions/A/ParsingInternal\ndyld[33989]: /System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib\ndyld[33989]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore\ndyld[33989]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage\ndyld[33989]: <199F6401-91D0-36E9-9EA9-D4B44ED1CE3A> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork\ndyld[33989]: <4D134FE3-50EE-39D5-9699-04B4B673DD35> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix\ndyld[33989]: <2E7E2722-3821-3DBF-B25A-6EA45D1A8FD4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector\ndyld[33989]: <3E1FE9EA-34A2-3545-B639-48B1FE1FD3D4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray\ndyld[33989]: <3103E210-FF5C-3677-BDD3-59FF17A6ACEC> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions\ndyld[33989]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSBenchmarkLoop.framework/Versions/A/MPSBenchmarkLoop\ndyld[33989]: <31F90368-23A5-39BB-822B-C8470C4479AE> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSHost.framework/Versions/A/MPSHost\ndyld[33989]: <9C416BB2-0882-315C-AF23-F476E34983BC> /System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools\ndyld[33989]: /System/Library/PrivateFrameworks/IOAccelMemoryInfo.framework/Versions/A/IOAccelMemoryInfo\ndyld[33989]: /System/Library/PrivateFrameworks/kperf.framework/Versions/A/kperf\ndyld[33989]: <03470B3A-A004-39A0-B6A4-F2A4AFFFCDD3> /System/Library/PrivateFrameworks/GPURawCounter.framework/Versions/A/GPURawCounter\ndyld[33989]: <4D8F39C6-B221-3AF1-BB40-CAEB0A174D61> /System/Library/PrivateFrameworks/ASEProcessing.framework/Versions/A/ASEProcessing\ndyld[33989]: /System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing\ndyld[33989]: <1B4C0154-843C-3CEE-9628-22978082DD2D> /System/Library/PrivateFrameworks/SILManager.framework/Versions/A/SILManager\ndyld[33989]: /System/Library/PrivateFrameworks/Tightbeam.framework/Versions/A/Tightbeam\ndyld[33989]: <856ACB2A-3334-3BA6-AAC8-8F344E7CDB83> /usr/lib/swift/libswiftCompression.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/AFKUser.framework/Versions/A/AFKUser\ndyld[33989]: <2186F196-EE17-3A59-B9DA-D6823BEDD35B> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI\ndyld[33989]: <086BB8AD-E317-3FC4-9E44-0D7C6036E8D7> /System/Library/PrivateFrameworks/SAObjects.framework/Versions/A/SAObjects\ndyld[33989]: /System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox\ndyld[33989]: /System/Library/PrivateFrameworks/MediaRemote.framework/Versions/A/MediaRemote\ndyld[33989]: <7F1A25E4-ED0A-3502-AABA-26EDB4A0D2A7> /System/Library/Frameworks/UserNotifications.framework/Versions/A/UserNotifications\ndyld[33989]: <8A5E0FF6-3116-3082-A0AD-20DCD6C5E1B4> /System/Library/PrivateFrameworks/SiriInstrumentation.framework/Versions/A/SiriInstrumentation\ndyld[33989]: <600E036E-9B18-35BE-B40B-E8D2D53AC90D> /System/Library/PrivateFrameworks/SiriAnalytics.framework/Versions/A/SiriAnalytics\ndyld[33989]: <619E6770-766A-3629-9AF8-F32C009375E9> /System/Library/PrivateFrameworks/SiriTTSService.framework/Versions/A/SiriTTSService\ndyld[33989]: <71CAE70A-72AD-3F74-834D-3519B545C08D> /System/Library/PrivateFrameworks/SiriCrossDeviceArbitration.framework/Versions/A/SiriCrossDeviceArbitration\ndyld[33989]: /System/Library/PrivateFrameworks/FeedbackLogger.framework/Versions/A/FeedbackLogger\ndyld[33989]: <55FBCBE4-1032-3017-BA49-D734B82405DF> /System/Library/PrivateFrameworks/FaceTimeNameUtility.framework/Versions/A/FaceTimeNameUtility\ndyld[33989]: /System/Library/PrivateFrameworks/SiriCrossDeviceArbitrationFeedback.framework/Versions/A/SiriCrossDeviceArbitrationFeedback\ndyld[33989]: <368BC882-02B9-38AB-89B4-F62430F2B8EB> /usr/lib/swift/libswiftCoreLocation.dylib\ndyld[33989]: /usr/lib/swift/libswiftAVFoundation.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/UIKitServices.framework/Versions/A/UIKitServices\ndyld[33989]: <54A2CBB8-623D-3629-904A-D0399ED13547> /System/Library/PrivateFrameworks/UnifiedAssetFramework.framework/Versions/A/UnifiedAssetFramework\ndyld[33989]: <8AF1606D-5C93-3B80-BC81-60C5688628E2> /System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore\ndyld[33989]: /System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession\ndyld[33989]: <52BD9E26-B356-3EAA-9AD7-7FF700C61A91> /System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI\ndyld[33989]: <3FF99846-E48C-3C9A-814C-35B45E5F60EC> /usr/lib/libAudioStatistics.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk\ndyld[33989]: /System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio\ndyld[33989]: <75F77FEC-BE14-3C97-93DA-403C3B529D3B> /usr/lib/libAudioToolboxUtility.dylib\ndyld[33989]: <7AE04E20-83FD-3B1B-8846-E9869AD98DB5> /usr/lib/swift/libswiftCoreMIDI.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata\ndyld[33989]: /System/Library/PrivateFrameworks/AudioDSPGraph.framework/Versions/A/AudioDSPGraph\ndyld[33989]: <6108A12D-286B-3CF2-B848-B0E7A0189DCC> /System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy\ndyld[33989]: <655F6374-6CE8-3D0E-994E-4D7C37F78E89> /usr/lib/libSMC.dylib\ndyld[33989]: <912BFF10-FB8F-3D52-9941-ACDCE1CAE36A> /usr/lib/libperfcheck.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/AudioAnalytics.framework/Versions/A/AudioAnalytics\ndyld[33989]: <869F0693-0E82-38C1-8920-C782E71735CA> /System/Library/Frameworks/OSLog.framework/Versions/A/OSLog\ndyld[33989]: <173A632F-20F3-30C1-BC55-EFFE977BBB8E> /usr/lib/libmis.dylib\ndyld[33989]: <52A7AD42-9DE0-393B-A6FB-A7CB6FF8F3A5> /System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience\ndyld[33989]: <1A63E9E1-2D64-3AF4-9CCD-6EF042397F84> /System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore\ndyld[33989]: <04DC06C1-2BFA-3FEE-9429-A33E41721A3E> /usr/lib/libspindump.dylib\ndyld[33989]: <7CC1BC36-42C1-39A3-AA4F-F9C8ABCE0CD5> /System/Library/PrivateFrameworks/AudioAccessoryServices.framework/Versions/A/AudioAccessoryServices\ndyld[33989]: /System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils\ndyld[33989]: /System/Library/PrivateFrameworks/HID.framework/Versions/A/HID\ndyld[33989]: <920D8AA6-CDCD-3E0F-AD66-F0673ACABD3F> /System/Library/PrivateFrameworks/Sharing.framework/Versions/A/Sharing\ndyld[33989]: <3DD8C4CA-23E9-35CF-AD67-549DD72D3344> /System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras\ndyld[33989]: <236517AD-8D16-3E62-8603-EBE7B65ACACA> /System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211\ndyld[33989]: <42F76533-D8DD-3A24-A08A-103C51428797> /System/Library/PrivateFrameworks/IDSFoundation.framework/Versions/A/IDSFoundation\ndyld[33989]: <116D159B-163E-3F91-9591-30FF8B6EB537> /System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Apple80211\ndyld[33989]: /System/Library/Frameworks/CoreWLAN.framework/Versions/A/CoreWLAN\ndyld[33989]: <1ABB6C50-A5DE-3744-8F07-8C3B5617B0B9> /System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth\ndyld[33989]: <82D79BDA-26A0-3A44-AAC8-911411801FDE> /usr/lib/swift/libswiftRegexBuilder.dylib\ndyld[33989]: <41E45E0C-2E88-3605-B213-F7CD760A9FF4> /System/Library/PrivateFrameworks/IMFoundation.framework/Versions/A/IMFoundation\ndyld[33989]: <40F60A3F-90A9-3F07-A99D-005E3158C6F6> /System/Library/PrivateFrameworks/Marco.framework/Versions/A/Marco\ndyld[33989]: <59FFD032-1427-39F6-BC16-A6877582A243> /System/Library/PrivateFrameworks/CommonUtilities.framework/Versions/A/CommonUtilities\ndyld[33989]: <6C3E51F8-D809-3AA1-8695-B75714C2D39A> /System/Library/PrivateFrameworks/Engram.framework/Versions/A/Engram\ndyld[33989]: /System/Library/PrivateFrameworks/XPCDistributed.framework/Versions/A/XPCDistributed\ndyld[33989]: <12066854-2BE4-35DF-BA9F-B38221C980FD> /usr/lib/libtidy.A.dylib\ndyld[33989]: <63F598E2-AF8A-3F29-BE11-3F14DB377A5B> /System/Library/PrivateFrameworks/Bom.framework/Versions/A/Bom\ndyld[33989]: /usr/lib/libParallelCompression.dylib\ndyld[33989]: <9E06CB59-0638-3C9F-B202-264E739433AC> /usr/lib/libIOReport.dylib\ndyld[33989]: <15EEE715-2670-3288-AFA8-504BAD951B4F> /System/Library/PrivateFrameworks/WiFiPeerToPeer.framework/Versions/A/WiFiPeerToPeer\ndyld[33989]: <3854272C-7B14-3A3C-9BB1-F0FBA394708A> /System/Library/PrivateFrameworks/Centauri.framework/Versions/A/Centauri\ndyld[33989]: <946B1484-B180-3451-A452-B54BF5A6D392> /System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon\ndyld[33989]: <2B49C295-4EA2-3DE3-90B4-DC03A96F2657> /usr/lib/libmrc.dylib\ndyld[33989]: <6661265C-7B78-3158-9011-4BFDFFEF7807> /System/Library/PrivateFrameworks/IPConfiguration.framework/Versions/A/IPConfiguration\ndyld[33989]: /System/Library/PrivateFrameworks/Netrb.framework/Versions/A/Netrb\ndyld[33989]: /System/Library/PrivateFrameworks/FrontBoardServices.framework/Versions/A/FrontBoardServices\ndyld[33989]: /System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData\ndyld[33989]: <757FEDFF-841C-3D62-B703-CDE79E929363> /System/Library/PrivateFrameworks/GraphicsServices.framework/Versions/A/GraphicsServices\ndyld[33989]: <093EF25B-5305-3611-B068-E65071858F52> /System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit\ndyld[33989]: /System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory\ndyld[33989]: <3B7FD4C1-D1D4-3DA9-B2F8-3D4094679D76> /System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory\ndyld[33989]: <016C5057-625C-30B1-AD32-7BC9D082F05B> /System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio\ndyld[33989]: /System/Library/Frameworks/AVRouting.framework/Versions/A/AVRouting\ndyld[33989]: <5240B3A0-D035-345E-A636-BC3A92C847C4> /usr/lib/libAccessibility.dylib\ndyld[33989]: <1FB2BCFD-D9FC-385A-A0EA-E2B5052E27F5> /System/Library/PrivateFrameworks/MediaServices.framework/Versions/A/MediaServices\ndyld[33989]: /System/Library/PrivateFrameworks/IDS.framework/Versions/A/IDS\ndyld[33989]: /System/Library/PrivateFrameworks/IsolatedCoreAudioClient.framework/Versions/A/IsolatedCoreAudioClient\ndyld[33989]: <7CC0621B-3B88-3533-A3FB-52E6214486EE> /System/Library/PrivateFrameworks/CoreAudioOrchestration.framework/Versions/A/CoreAudioOrchestration\ndyld[33989]: /System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox\ndyld[33989]: /System/Library/PrivateFrameworks/CoreAVCHD.framework/Versions/A/CoreAVCHD\ndyld[33989]: <74D313A5-4D99-35D1-A4C9-B76AB6457EF0> /System/Library/Frameworks/MediaAccessibility.framework/Versions/A/MediaAccessibility\ndyld[33989]: <87F549F4-73CC-302B-ABDB-D3CCFADABFA9> /System/Library/PrivateFrameworks/Mangrove.framework/Versions/A/Mangrove\ndyld[33989]: <214294AE-C7B7-3C9A-A4F8-201C989F9779> /System/Library/PrivateFrameworks/CMPhoto.framework/Versions/A/CMPhoto\ndyld[33989]: <5F090F48-E481-3737-8E75-362E5D274879> /System/Library/Frameworks/CoreTelephony.framework/Versions/A/CoreTelephony\ndyld[33989]: <9ACFCA55-82CB-33DB-AD00-443576099FDB> /System/Library/PrivateFrameworks/CoreAUC.framework/Versions/A/CoreAUC\ndyld[33989]: <71A0C0AD-67F3-36F9-BF73-6DD5D7424AF7> /System/Library/PrivateFrameworks/AppleJPEGXL.framework/Versions/A/AppleJPEGXL\ndyld[33989]: <825E8416-E246-338E-A5CF-AA81A1B01DD9> /System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport\ndyld[33989]: <7CF84496-675C-3241-B0EF-E83C95F188FA> /System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA\ndyld[33989]: <63A6BBA0-CD50-30F8-9CD2-81B59264EA13> /usr/lib/libTelephonyUtilDynamic.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler\ndyld[33989]: /System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment\ndyld[33989]: /System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay\ndyld[33989]: /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit\ndyld[33989]: /System/Library/PrivateFrameworks/CryptoKitCBridging.framework/Versions/A/CryptoKitCBridging\ndyld[33989]: <714063A8-D81E-3B22-9B36-88948A979E7F> /System/Library/Frameworks/CryptoTokenKit.framework/Versions/A/CryptoTokenKit\ndyld[33989]: <86858734-8B4D-38E6-AAA7-B7A046A7CB2A> /System/Library/Frameworks/LocalAuthentication.framework/Versions/A/LocalAuthentication\ndyld[33989]: /System/Library/PrivateFrameworks/LocalAuthenticationCore.framework/Versions/A/LocalAuthenticationCore\ndyld[33989]: /System/Library/PrivateFrameworks/LocalAuthenticationCredentialServices.framework/Versions/A/LocalAuthenticationCredentialServices\ndyld[33989]: /System/Library/Frameworks/LocalAuthentication.framework/Support/SharedUtils.framework/Versions/A/SharedUtils\ndyld[33989]: <80C3E2D4-B6B8-3C62-B257-27DEEBAD4935> /usr/lib/libcsfde.dylib\ndyld[33989]: <650E155C-1FE3-36ED-8D84-157D380F7F95> /usr/lib/libCoreStorage.dylib\ndyld[33989]: <46DD93AF-BACD-309B-AD51-9CC47C78CA2C> /System/Library/PrivateFrameworks/MediaKit.framework/Versions/A/MediaKit\ndyld[33989]: /System/Library/Frameworks/DiscRecording.framework/Versions/A/DiscRecording\ndyld[33989]: <1F8700BE-BD91-3B94-AC32-A5F10CEFEE35> /System/Library/PrivateFrameworks/ProtectedCloudStorage.framework/Versions/A/ProtectedCloudStorage\ndyld[33989]: /System/Library/PrivateFrameworks/EFILogin.framework/Versions/A/EFILogin\ndyld[33989]: /System/Library/PrivateFrameworks/PersistentConnection.framework/Versions/A/PersistentConnection\ndyld[33989]: <6B0D099C-AC56-35DB-90F1-88A09E587FCB> /System/Library/PrivateFrameworks/SonicFoundation.framework/Versions/A/SonicFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/AsyncAlgorithmsInternal.framework/Versions/A/AsyncAlgorithmsInternal\ndyld[33989]: <2CA62C12-37B5-345A-BF79-5D05F43F6BFB> /System/Library/PrivateFrameworks/FTAWD.framework/Versions/A/FTAWD\ndyld[33989]: <0A1C4D11-C108-35E9-A921-86ED86CF7446> /System/Library/PrivateFrameworks/Dendrite.framework/Versions/A/Dendrite\ndyld[33989]: /usr/lib/libtailspin.dylib\ndyld[33989]: <5FEA8C08-1577-3296-BC9C-7F3203E8EBFB> /System/Library/PrivateFrameworks/Osprey.framework/Versions/A/Osprey\ndyld[33989]: /System/Library/PrivateFrameworks/SiriTTS.framework/Versions/A/SiriTTS\ndyld[33989]: <0C005C4D-CA12-389C-9CCE-C4ED05B187E8> /System/Library/Frameworks/NaturalLanguage.framework/Versions/A/NaturalLanguage\ndyld[33989]: <0E502870-00F4-35D4-AF82-E7059244798E> /System/Library/PrivateFrameworks/GenerativeModels.framework/Versions/A/GenerativeModels\ndyld[33989]: <68474F39-798D-325B-B52F-3DE214F279AE> /System/Library/PrivateFrameworks/SiriPowerInstrumentation.framework/Versions/A/SiriPowerInstrumentation\ndyld[33989]: <5E36265A-7670-3D39-A2B8-71DA0AA131CF> /usr/lib/swift/libswiftNaturalLanguage.dylib\ndyld[33989]: <6794652C-86F0-37EB-838D-483177685E26> /System/Library/PrivateFrameworks/TailspinSymbolication.framework/Versions/A/TailspinSymbolication\ndyld[33989]: <089C1A34-2F4E-3649-94AA-B28A7ECB008B> /System/Library/PrivateFrameworks/Darwinup.framework/Versions/A/Darwinup\ndyld[33989]: /System/Library/PrivateFrameworks/SignpostSupport.framework/Versions/A/SignpostSupport\ndyld[33989]: <8C10B437-C282-37F5-834F-E7179C700373> /System/Library/PrivateFrameworks/FeatureFlagsSupport.framework/Versions/A/FeatureFlagsSupport\ndyld[33989]: /System/Library/PrivateFrameworks/ktrace.framework/Versions/A/ktrace\ndyld[33989]: /System/Library/PrivateFrameworks/SampleAnalysis.framework/Versions/A/SampleAnalysis\ndyld[33989]: <400B0E96-4869-37BE-9832-1A14C386148B> /System/Library/PrivateFrameworks/kperfdata.framework/Versions/A/kperfdata\ndyld[33989]: <7E3E0CF7-905A-3244-A0C9-0ADCC2E16415> /usr/lib/libdscsym.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity\ndyld[33989]: <6FBA9099-E428-3571-B940-0D210B6D0861> /System/Library/PrivateFrameworks/BulkSymbolication.framework/Versions/A/BulkSymbolication\ndyld[33989]: <90E600A3-0A27-348A-AA57-D1DF4FB305E8> /usr/lib/libTLE.dylib\ndyld[33989]: <2D1B971F-6A7F-32D0-8B0F-F8FA3A13E8F1> /System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper\ndyld[33989]: <8F2949A6-43A0-30A2-B5D2-945949C5AA01> /System/Library/PrivateFrameworks/Espresso.framework/Versions/A/Espresso\ndyld[33989]: /System/Library/Frameworks/CoreML.framework/Versions/A/CoreML\ndyld[33989]: /usr/lib/libedit.3.dylib\ndyld[33989]: <465A74BC-F20D-3C05-9441-7E08EAA49FAF> /System/Library/PrivateFrameworks/ANECompiler.framework/Versions/A/ANECompiler\ndyld[33989]: /System/Library/PrivateFrameworks/AppleNeuralEngine.framework/Versions/A/AppleNeuralEngine\ndyld[33989]: <97C5C585-F5EE-323A-B949-69EAE9080871> /System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL\ndyld[33989]: <7401E849-7B2E-39A9-99D3-5CB0A6BBDFFE> /System/Library/Frameworks/MetalPerformanceShadersGraph.framework/Versions/A/MetalPerformanceShadersGraph\ndyld[33989]: /System/Library/PrivateFrameworks/MLCompilerServices.framework/Versions/A/MLCompilerServices\ndyld[33989]: /System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices\ndyld[33989]: <9EB04E94-EE2D-38A5-A214-00AF73DBE4E9> /usr/lib/libncurses.5.4.dylib\ndyld[33989]: /usr/lib/libsandbox.1.dylib\ndyld[33989]: <2F2EF0D7-2FE4-3A5A-8E4C-E1571C8D0C10> /usr/lib/libMatch.1.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/ODIE.framework/Versions/A/ODIE\ndyld[33989]: /System/Library/PrivateFrameworks/MLModelAsset.framework/Versions/A/MLModelAsset\ndyld[33989]: <22B4CD07-5C72-3CA4-9CD1-2C87686CDDE5> /System/Library/PrivateFrameworks/MLCompilerRuntime.framework/Versions/A/MLCompilerRuntime\ndyld[33989]: /System/Library/Frameworks/MLCompute.framework/Versions/A/MLCompute\ndyld[33989]: <6028DD46-8E5A-33F0-93B2-41FA480366CC> /System/Library/PrivateFrameworks/MLAssetIO.framework/Versions/A/MLAssetIO\ndyld[33989]: /usr/lib/swift/libswiftMLCompute.dylib\ndyld[33989]: <067E2603-4FEA-3CA5-8926-45F60681EDDB> /System/Library/PrivateFrameworks/AVFCore.framework/Versions/A/AVFCore\ndyld[33989]: /System/Library/PrivateFrameworks/AVFCapture.framework/Versions/A/AVFCapture\ndyld[33989]: <3B782AC2-00C4-3534-91D2-5C7242B32440> /System/Library/PrivateFrameworks/CMImaging.framework/Versions/A/CMImaging\ndyld[33989]: <1772C40D-6EF4-3F81-BA00-6EE8B05039A6> /System/Library/PrivateFrameworks/Quagga.framework/Versions/A/Quagga\ndyld[33989]: <57A10B70-C3C9-34C6-8D22-F5118B63E2F0> /System/Library/PrivateFrameworks/CMCapture.framework/Versions/A/CMCapture\ndyld[33989]: <1035C1AB-5058-3AFA-8D77-514901516251> /System/Library/Frameworks/CoreMediaIO.framework/Versions/A/CoreMediaIO\ndyld[33989]: /System/Library/PrivateFrameworks/CMCaptureDevice.framework/Versions/A/CMCaptureDevice\ndyld[33989]: <1F873909-B3B8-3D55-9673-9AFA86BB085B> /System/Library/PrivateFrameworks/CoreBrightness.framework/Versions/A/CoreBrightness\ndyld[33989]: /System/Library/PrivateFrameworks/CinematicFraming.framework/Versions/A/CinematicFraming\ndyld[33989]: <882BC08E-B1E1-3E52-AE8A-AC22A1BF2BE8> /System/Library/PrivateFrameworks/ModelManagerServices.framework/Versions/A/ModelManagerServices\ndyld[33989]: <3E83115F-D04B-3C8D-8646-35204AA2DB84> /System/Library/PrivateFrameworks/CPMS.framework/Versions/A/CPMS\ndyld[33989]: /System/Library/PrivateFrameworks/SystemStatus.framework/Versions/A/SystemStatus\ndyld[33989]: <2E109991-45C6-3783-8A36-B6A8070AAD67> /System/Library/Frameworks/CoreMotion.framework/Versions/A/CoreMotion\ndyld[33989]: /System/Library/PrivateFrameworks/TimeSync.framework/Versions/A/TimeSync\ndyld[33989]: <9B3D4CA3-7BCF-36C9-AA99-27BDFE7854CD> /System/Library/PrivateFrameworks/DistributedSensing.framework/Versions/A/DistributedSensing\ndyld[33989]: /System/Library/PrivateFrameworks/MobileBluetooth.framework/Versions/A/MobileBluetooth\ndyld[33989]: <0BAB3589-8D81-3C60-9F05-9D207E89F4B6> /System/Library/PrivateFrameworks/IOKitten.framework/Versions/A/IOKitten\ndyld[33989]: <8A2C8C17-E138-3B34-8643-ED4FB1C9049E> /System/Library/PrivateFrameworks/LocationLogEncryption.framework/Versions/A/LocationLogEncryption\ndyld[33989]: <05EC9C98-7211-39C9-B376-796F37327801> /System/Library/PrivateFrameworks/AppleIntelligenceReporting.framework/Versions/A/AppleIntelligenceReporting\ndyld[33989]: <47AAECAD-C28C-352E-BB86-7F292E0BFBC6> /System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji\ndyld[33989]: <327536E3-A27C-38C2-A67F-D6488D04CCEE> /System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling\ndyld[33989]: <95BA357E-906A-3183-A402-A41D486B5AB3> /System/Library/PrivateFrameworks/Montreal.framework/Versions/A/Montreal\ndyld[33989]: /usr/lib/libcmph.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/GenerativeModelsFoundation.framework/Versions/A/GenerativeModelsFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/TokenGeneration.framework/Versions/A/TokenGeneration\ndyld[33989]: <418985BB-52A3-34D4-8379-40DC4C63AA32> /System/Library/PrivateFrameworks/GenerativeFunctions.framework/Versions/A/GenerativeFunctions\ndyld[33989]: <63FD423F-836C-3034-BA48-100AE09A9140> /System/Library/PrivateFrameworks/GenerativeFunctionsFoundation.framework/Versions/A/GenerativeFunctionsFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/ModelCatalog.framework/Versions/A/ModelCatalog\ndyld[33989]: <67C3B698-8279-30F1-9167-4730E6F41F5A> /System/Library/PrivateFrameworks/SensitiveContentAnalysisML.framework/Versions/A/SensitiveContentAnalysisML\ndyld[33989]: /System/Library/PrivateFrameworks/GenerativeFunctionsInstrumentation.framework/Versions/A/GenerativeFunctionsInstrumentation\ndyld[33989]: /System/Library/PrivateFrameworks/PromptKit.framework/Versions/A/PromptKit\ndyld[33989]: <12245228-2B9A-3B24-8C5E-10111D68BE65> /System/Library/PrivateFrameworks/ProactiveDaemonSupport.framework/Versions/A/ProactiveDaemonSupport\ndyld[33989]: <3166486F-3F65-31DB-8018-779FFA32DC71> /System/Library/PrivateFrameworks/TokenGenerationCore.framework/Versions/A/TokenGenerationCore\ndyld[33989]: <53B3126E-7B01-30DD-961A-510E9CFC3CF1> /System/Library/PrivateFrameworks/Trial.framework/Versions/A/Trial\ndyld[33989]: /System/Library/PrivateFrameworks/TrialProto.framework/Versions/A/TrialProto\ndyld[33989]: /System/Library/PrivateFrameworks/AppleFlatBuffers.framework/Versions/A/AppleFlatBuffers\ndyld[33989]: <642E3357-AB6D-3039-A818-EDB5D6A189C2> /System/Library/PrivateFrameworks/SentencePieceInternal.framework/Versions/A/SentencePieceInternal\ndyld[33989]: <10F83439-3A9F-316B-992E-451A72876715> /System/Library/Frameworks/Vision.framework/Versions/A/Vision\ndyld[33989]: /System/Library/PrivateFrameworks/CoreSceneUnderstanding.framework/Versions/A/CoreSceneUnderstanding\ndyld[33989]: <4E70B4ED-C8E0-3636-80E4-0939FE56BB63> /System/Library/PrivateFrameworks/VisionCore.framework/Versions/A/VisionCore\ndyld[33989]: /System/Library/PrivateFrameworks/DataDetectorsCore.framework/Versions/A/DataDetectorsCore\ndyld[33989]: /System/Library/Frameworks/Vision.framework/libfaceCore.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/Futhark.framework/Versions/A/Futhark\ndyld[33989]: /System/Library/PrivateFrameworks/InertiaCam.framework/Versions/A/InertiaCam\ndyld[33989]: /System/Library/PrivateFrameworks/TextRecognition.framework/Versions/A/TextRecognition\ndyld[33989]: <73F6F860-69AF-3162-86E0-A683642287D6> /System/Library/Frameworks/DataDetection.framework/Versions/A/DataDetection\ndyld[33989]: <1D5DF9CA-41FC-3B7B-B19F-2C435F3A66F2> /System/Library/PrivateFrameworks/TextInput.framework/Versions/A/TextInput\ndyld[33989]: /System/Library/PrivateFrameworks/CVNLP.framework/Versions/A/CVNLP\ndyld[33989]: <58AC6CAB-5B91-367F-932F-BD0939BBD125> /System/Library/PrivateFrameworks/IntentsFoundation.framework/Versions/A/IntentsFoundation\ndyld[33989]: <27479D70-8BF6-3D3C-B528-1BB9B1B98391> /System/Library/PrivateFrameworks/ApplePushService.framework/Versions/A/ApplePushService\ndyld[33989]: <676D50CC-8455-3267-B8E8-CA31B8EF8F91> /System/Library/Frameworks/CloudKit.framework/Versions/A/CloudKit\ndyld[33989]: /System/Library/PrivateFrameworks/CoreDuetDaemonProtocol.framework/Versions/A/CoreDuetDaemonProtocol\ndyld[33989]: <962EA390-008E-3DDC-B2A5-7B2DAF6E8786> /System/Library/PrivateFrameworks/DeviceIdentity.framework/Versions/A/DeviceIdentity\ndyld[33989]: /System/Library/Frameworks/SharedWithYouCore.framework/Versions/A/SharedWithYouCore\ndyld[33989]: <121798D0-5254-3547-8F96-0F2AF8D84250> /System/Library/PrivateFrameworks/CloudTelemetry.framework/Versions/A/CloudTelemetry\ndyld[33989]: <768DFB9E-7FB3-3998-A3AF-BEF0C6C740A7> /System/Library/PrivateFrameworks/AppleAccount.framework/Versions/A/AppleAccount\ndyld[33989]: /System/Library/PrivateFrameworks/CacheDelete.framework/Versions/A/CacheDelete\ndyld[33989]: /System/Library/PrivateFrameworks/C2.framework/Versions/A/C2\ndyld[33989]: <23871A43-55FD-3D0C-B29F-B143A64D1D8D> /System/Library/PrivateFrameworks/CloudCoreInternal.framework/Versions/A/CloudCoreInternal\ndyld[33989]: /System/Library/PrivateFrameworks/CloudAsset.framework/Versions/A/CloudAsset\ndyld[33989]: <36607924-B1B2-39ED-B6D1-29683EFB67A0> /System/Library/Frameworks/PushKit.framework/Versions/A/PushKit\ndyld[33989]: <26865685-385E-3120-9886-2082EEC20B20> /System/Library/Frameworks/CoreTransferable.framework/Versions/A/CoreTransferable\ndyld[33989]: <5EA68C5E-69B0-3011-9D66-AEF49D82B29D> /System/Library/Frameworks/FileProvider.framework/Versions/A/FileProvider\ndyld[33989]: /System/Library/PrivateFrameworks/GenerationalStorage.framework/Versions/A/GenerationalStorage\ndyld[33989]: /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/DesktopServicesPriv\ndyld[33989]: <024DBF34-DF66-3164-825E-F77F85462E66> /System/Library/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth\ndyld[33989]: <87907862-52FF-3F24-AC29-7C1678BCD277> /System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport\ndyld[33989]: /System/Library/PrivateFrameworks/CloudTelemetryTools.framework/Versions/A/CloudTelemetryTools\ndyld[33989]: /System/Library/PrivateFrameworks/CloudTelemetryShared.dylib\ndyld[33989]: <7BEBC9F1-212D-37F4-B601-A7AAD12F7225> /System/Library/PrivateFrameworks/RTCReporting.framework/Versions/A/RTCReporting\ndyld[33989]: <7A222E30-8DD4-3B1D-B820-4EA33B797525> /System/Library/PrivateFrameworks/AAAFoundationSwift.framework/Versions/A/AAAFoundationSwift\ndyld[33989]: <9D724BE7-0B01-39F3-82BE-BCEDC6EBAC8A> /System/Library/PrivateFrameworks/AppleIDSSOAuthentication.framework/Versions/A/AppleIDSSOAuthentication\ndyld[33989]: <659AFBBD-E22E-3474-BCFE-298DA57B1464> /System/Library/PrivateFrameworks/UIFoundation.framework/Versions/A/UIFoundation\ndyld[33989]: <0368EA7D-01B2-3AA9-A6D6-A2A0850AC800> /System/Library/PrivateFrameworks/HIDDisplay.framework/Versions/A/HIDDisplay\ndyld[33989]: <6A5A8E21-A9E6-32A2-9BDB-8013F002AEF8> /usr/lib/libcups.2.dylib\ndyld[33989]: /System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos\ndyld[33989]: <4AB71911-9300-30D4-88CF-D20EFD75ACE6> /usr/lib/libresolv.9.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal\ndyld[33989]: <0CB2E7E3-E96F-343B-A4E7-545E74AF0255> /System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib\ndyld[33989]: <097F7235-CA53-3644-BB95-F6F912B4F2C7> /System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth\ndyld[33989]: /System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities\ndyld[33989]: /System/Library/PrivateFrameworks/AttributeGraph.framework/Versions/A/AttributeGraph\ndyld[33989]: /usr/lib/libAXSafeCategoryBundle.dylib\ndyld[33989]: /System/Library/Frameworks/TabularData.framework/Versions/A/TabularData\ndyld[33989]: <841D5662-2CB9-3A27-ADA7-E33AC5E45199> /System/Library/PrivateFrameworks/ArgumentParserInternal.framework/Versions/A/ArgumentParserInternal\ndyld[33989]: <4C851329-A9F4-3E9E-9E48-07FF4120DCF9> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib\ndyld[33989]: <5015CD96-C046-364D-AAE3-1F439044468B> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib\ndyld[33989]: <407BCF3E-A91F-3A7F-8B8C-DBB8E807990F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib\ndyld[33989]: <669ABE12-838F-3F14-8456-D60DE5DF8EB8> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib\ndyld[33989]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib\ndyld[33989]: <54A103BA-7D04-32DB-B204-179E2E0290CA> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib\ndyld[33989]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib\ndyld[33989]: <1ACDAA8A-EB43-37C7-B661-39B1C0E05290> /System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary\ndyld[33989]: <12479A32-B72F-3A09-BB03-BA56C37853B5> /System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore\ndyld[33989]: /usr/lib/libapp_launch_measurement.dylib\ndyld[33989]: <4F3BEA3B-A363-3D04-B903-9B613C993CA1> /System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices\ndyld[33989]: <6C426EA5-7F1E-333E-BB5D-74465EFED12B> /usr/lib/libxslt.1.dylib\ndyld[33989]: <627D64D5-2D3C-3EC6-B4AB-FEF4DEE40871> /System/Library/PrivateFrameworks/FindMyDevice.framework/Versions/A/FindMyDevice\ndyld[33989]: <930F9F83-A947-3788-9FBA-49872FC3AF8D> /System/Library/PrivateFrameworks/FMCoreLite.framework/Versions/A/FMCoreLite\ndyld[33989]: <5F356BA6-47B5-382B-B54A-1550BB138A62> /System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement\ndyld[33989]: <38DAF669-429F-384F-87D6-8550842EEB5E> /System/Library/PrivateFrameworks/CryptoKitPrivate.framework/Versions/A/CryptoKitPrivate\ndyld[33989]: <5B73C216-2ACE-3F8C-A2B3-7D35D5D0395A> /System/Library/PrivateFrameworks/CaptiveNetwork.framework/Versions/A/CaptiveNetwork\ndyld[33989]: /System/Library/PrivateFrameworks/EAP8021X.framework/Versions/A/EAP8021X\ndyld[33989]: <36F215D1-A2C0-32CA-ADD8-6D85AB48A772> /System/Library/Frameworks/QuickLookThumbnailing.framework/Versions/A/QuickLookThumbnailing\ndyld[33989]: /System/Library/PrivateFrameworks/DiskImages.framework/Versions/A/DiskImages\ndyld[33989]: <49121861-2603-3B0A-B664-BAD9E729BE5D> /System/Library/Frameworks/NetFS.framework/Versions/A/NetFS\ndyld[33989]: <2E99AD96-DC1C-3643-9988-273AB6844EFC> /usr/lib/libcurl.4.dylib\ndyld[33989]: <46D13DA8-E7BD-37DC-91DD-D5E6CE00C2B8> /usr/lib/libcrypto.46.dylib\ndyld[33989]: <07D5F4C6-1A13-344C-882B-0B0A08048DE5> /usr/lib/libssl.48.dylib\ndyld[33989]: <8CABDD64-E6C6-3B77-B839-2E2B875CE0FE> /System/Library/Frameworks/LDAP.framework/Versions/A/LDAP\ndyld[33989]: <96C0BAAA-7FE6-3277-AFBC-31926F5935EE> /System/Library/PrivateFrameworks/TrustEvaluationAgent.framework/Versions/A/TrustEvaluationAgent\ndyld[33989]: <7CF2A32E-72DD-34F7-B179-A17ED3D7DD75> /usr/lib/libsasl2.2.dylib\ndyld[33989]: move loaded to delayed: libcmark-gfm.dylib\ndyld[33989]: move loaded to delayed: BackgroundSystemTasks\ndyld[33989]: move loaded to delayed: CoreWiFi\ndyld[33989]: move loaded to delayed: Rapport\ndyld[33989]: move loaded to delayed: SymptomAnalytics\ndyld[33989]: move loaded to delayed: libcupolicy.dylib\ndyld[33989]: move loaded to delayed: libnetworkextension.dylib\ndyld[33989]: move loaded to delayed: NetworkExtension\ndyld[33989]: move loaded to delayed: libnwswifttls.dylib\ndyld[33989]: move loaded to delayed: libpcap.A.dylib\ndyld[33989]: move loaded to delayed: XPCSupport\ndyld[33989]: move loaded to delayed: CloudServices\ndyld[33989]: move loaded to delayed: OctagonTrust\ndyld[33989]: move loaded to delayed: AppleIDAuthSupport\ndyld[33989]: move loaded to delayed: KeychainCircle\ndyld[33989]: move loaded to delayed: AuthKit\ndyld[33989]: move loaded to delayed: AAAFoundation\ndyld[33989]: move loaded to delayed: MultiverseSupport\ndyld[33989]: move loaded to delayed: DiskManagement\ndyld[33989]: move loaded to delayed: Accounts\ndyld[33989]: move loaded to delayed: URLFormatting\ndyld[33989]: move loaded to delayed: AOSKit\ndyld[33989]: move loaded to delayed: AppSSOCore\ndyld[33989]: move loaded to delayed: AVFoundation\ndyld[33989]: move loaded to delayed: DuetActivityScheduler\ndyld[33989]: move loaded to delayed: FTServices\ndyld[33989]: move loaded to delayed: InternationalSupport\ndyld[33989]: move loaded to delayed: libMemoryResourceException.dylib\ndyld[33989]: move loaded to delayed: NetworkScore\ndyld[33989]: move loaded to delayed: NetworkServiceProxy\ndyld[33989]: move loaded to delayed: StreamingExtractor\ndyld[33989]: move loaded to delayed: SymptomReporter\ndyld[33989]: move loaded to delayed: libCGInterfaces.dylib\ndyld[33989]: move loaded to delayed: AccelerateGPU\ndyld[33989]: move loaded to delayed: ApplicationServices\ndyld[33989]: move loaded to delayed: ATS\ndyld[33989]: move loaded to delayed: HIServices\ndyld[33989]: move loaded to delayed: PrintCore\ndyld[33989]: move loaded to delayed: QD\ndyld[33989]: move loaded to delayed: ColorSyncLegacy\ndyld[33989]: move loaded to delayed: SpeechSynthesis\ndyld[33989]: move loaded to delayed: CoreDuetContext\ndyld[33989]: move loaded to delayed: CoreDuet\ndyld[33989]: move loaded to delayed: CoreLocation\ndyld[33989]: move loaded to delayed: Intents\ndyld[33989]: move loaded to delayed: _LocationEssentials\ndyld[33989]: move loaded to delayed: GeoServices\ndyld[33989]: move loaded to delayed: LocationSupport\ndyld[33989]: move loaded to delayed: CoreBluetooth\ndyld[33989]: move loaded to delayed: GeoServicesCore\ndyld[33989]: move loaded to delayed: PhoneNumbers\ndyld[33989]: move loaded to delayed: IconServices\ndyld[33989]: move loaded to delayed: IconFoundation\ndyld[33989]: move loaded to delayed: AssistantServices\ndyld[33989]: move loaded to delayed: IconRendering\ndyld[33989]: move loaded to delayed: CoreUI\ndyld[33989]: move loaded to delayed: SFSymbols\ndyld[33989]: move loaded to delayed: DeveloperToolsSupport\ndyld[33989]: move loaded to delayed: RenderBox\ndyld[33989]: move loaded to delayed: CoreSVG\ndyld[33989]: move loaded to delayed: TextureIO\ndyld[33989]: move loaded to delayed: libswiftCoreImage.dylib\ndyld[33989]: move loaded to delayed: ATSUI\ndyld[33989]: move loaded to delayed: SAObjects\ndyld[33989]: move loaded to delayed: MediaRemote\ndyld[33989]: move loaded to delayed: UserNotifications\ndyld[33989]: move loaded to delayed: SiriInstrumentation\ndyld[33989]: move loaded to delayed: SiriAnalytics\ndyld[33989]: move loaded to delayed: SiriTTSService\ndyld[33989]: move loaded to delayed: SiriCrossDeviceArbitration\ndyld[33989]: move loaded to delayed: FeedbackLogger\ndyld[33989]: move loaded to delayed: FaceTimeNameUtility\ndyld[33989]: move loaded to delayed: SiriCrossDeviceArbitrationFeedback\ndyld[33989]: move loaded to delayed: libswiftCoreLocation.dylib\ndyld[33989]: move loaded to delayed: libswiftAVFoundation.dylib\ndyld[33989]: move loaded to delayed: UIKitServices\ndyld[33989]: move loaded to delayed: UnifiedAssetFramework\ndyld[33989]: move loaded to delayed: AudioDSPGraph\ndyld[33989]: move loaded to delayed: AudioAccessoryServices\ndyld[33989]: move loaded to delayed: CoreUtils\ndyld[33989]: move loaded to delayed: Sharing\ndyld[33989]: move loaded to delayed: CoreUtilsExtras\ndyld[33989]: move loaded to delayed: IO80211\ndyld[33989]: move loaded to delayed: IDSFoundation\ndyld[33989]: move loaded to delayed: Apple80211\ndyld[33989]: move loaded to delayed: CoreWLAN\ndyld[33989]: move loaded to delayed: IOBluetooth\ndyld[33989]: move loaded to delayed: libswiftRegexBuilder.dylib\ndyld[33989]: move loaded to delayed: IMFoundation\ndyld[33989]: move loaded to delayed: Marco\ndyld[33989]: move loaded to delayed: CommonUtilities\ndyld[33989]: move loaded to delayed: Engram\ndyld[33989]: move loaded to delayed: XPCDistributed\ndyld[33989]: move loaded to delayed: libtidy.A.dylib\ndyld[33989]: move loaded to delayed: Bom\ndyld[33989]: move loaded to delayed: libParallelCompression.dylib\ndyld[33989]: move loaded to delayed: libIOReport.dylib\ndyld[33989]: move loaded to delayed: WiFiPeerToPeer\ndyld[33989]: move loaded to delayed: Centauri\ndyld[33989]: move loaded to delayed: libmrc.dylib\ndyld[33989]: move loaded to delayed: IPConfiguration\ndyld[33989]: move loaded to delayed: Netrb\ndyld[33989]: move loaded to delayed: FrontBoardServices\ndyld[33989]: move loaded to delayed: AudioUnit\ndyld[33989]: move loaded to delayed: AVFAudio\ndyld[33989]: move loaded to delayed: AVRouting\ndyld[33989]: move loaded to delayed: libAccessibility.dylib\ndyld[33989]: move loaded to delayed: MediaServices\ndyld[33989]: move loaded to delayed: IDS\ndyld[33989]: move loaded to delayed: IsolatedCoreAudioClient\ndyld[33989]: move loaded to delayed: CoreAudioOrchestration\ndyld[33989]: move loaded to delayed: MediaToolbox\ndyld[33989]: move loaded to delayed: CoreAVCHD\ndyld[33989]: move loaded to delayed: MediaAccessibility\ndyld[33989]: move loaded to delayed: Mangrove\ndyld[33989]: move loaded to delayed: CMPhoto\ndyld[33989]: move loaded to delayed: CoreTelephony\ndyld[33989]: move loaded to delayed: CoreAUC\ndyld[33989]: move loaded to delayed: AppleJPEGXL\ndyld[33989]: move loaded to delayed: libTelephonyUtilDynamic.dylib\ndyld[33989]: move loaded to delayed: CryptoKit\ndyld[33989]: move loaded to delayed: CryptoKitCBridging\ndyld[33989]: move loaded to delayed: CryptoTokenKit\ndyld[33989]: move loaded to delayed: LocalAuthentication\ndyld[33989]: move loaded to delayed: LocalAuthenticationCore\ndyld[33989]: move loaded to delayed: LocalAuthenticationCredentialServices\ndyld[33989]: move loaded to delayed: SharedUtils\ndyld[33989]: move loaded to delayed: libcsfde.dylib\ndyld[33989]: move loaded to delayed: libCoreStorage.dylib\ndyld[33989]: move loaded to delayed: ProtectedCloudStorage\ndyld[33989]: move loaded to delayed: EFILogin\ndyld[33989]: move loaded to delayed: PersistentConnection\ndyld[33989]: move loaded to delayed: SonicFoundation\ndyld[33989]: move loaded to delayed: AsyncAlgorithmsInternal\ndyld[33989]: move loaded to delayed: FTAWD\ndyld[33989]: move loaded to delayed: Dendrite\ndyld[33989]: move loaded to delayed: libtailspin.dylib\ndyld[33989]: move loaded to delayed: Osprey\ndyld[33989]: move loaded to delayed: SiriTTS\ndyld[33989]: move loaded to delayed: NaturalLanguage\ndyld[33989]: move loaded to delayed: GenerativeModels\ndyld[33989]: move loaded to delayed: SiriPowerInstrumentation\ndyld[33989]: move loaded to delayed: libswiftNaturalLanguage.dylib\ndyld[33989]: move loaded to delayed: TailspinSymbolication\ndyld[33989]: move loaded to delayed: Darwinup\ndyld[33989]: move loaded to delayed: SignpostSupport\ndyld[33989]: move loaded to delayed: FeatureFlagsSupport\ndyld[33989]: move loaded to delayed: ktrace\ndyld[33989]: move loaded to delayed: SampleAnalysis\ndyld[33989]: move loaded to delayed: kperfdata\ndyld[33989]: move loaded to delayed: libdscsym.dylib\ndyld[33989]: move loaded to delayed: BulkSymbolication\ndyld[33989]: move loaded to delayed: Espresso\ndyld[33989]: move loaded to delayed: CoreML\ndyld[33989]: move loaded to delayed: libedit.3.dylib\ndyld[33989]: move loaded to delayed: ANECompiler\ndyld[33989]: move loaded to delayed: AppleNeuralEngine\ndyld[33989]: move loaded to delayed: MetalPerformanceShadersGraph\ndyld[33989]: move loaded to delayed: MLCompilerServices\ndyld[33989]: move loaded to delayed: ANEServices\ndyld[33989]: move loaded to delayed: libncurses.5.4.dylib\ndyld[33989]: move loaded to delayed: libsandbox.1.dylib\ndyld[33989]: move loaded to delayed: libMatch.1.dylib\ndyld[33989]: move loaded to delayed: ODIE\ndyld[33989]: move loaded to delayed: MLModelAsset\ndyld[33989]: move loaded to delayed: MLCompilerRuntime\ndyld[33989]: move loaded to delayed: MLCompute\ndyld[33989]: move loaded to delayed: MLAssetIO\ndyld[33989]: move loaded to delayed: libswiftMLCompute.dylib\ndyld[33989]: move loaded to delayed: AVFCore\ndyld[33989]: move loaded to delayed: AVFCapture\ndyld[33989]: move loaded to delayed: CMImaging\ndyld[33989]: move loaded to delayed: Quagga\ndyld[33989]: move loaded to delayed: CMCapture\ndyld[33989]: move loaded to delayed: CoreMediaIO\ndyld[33989]: move loaded to delayed: CMCaptureDevice\ndyld[33989]: move loaded to delayed: CoreBrightness\ndyld[33989]: move loaded to delayed: CinematicFraming\ndyld[33989]: move loaded to delayed: ModelManagerServices\ndyld[33989]: move loaded to delayed: CPMS\ndyld[33989]: move loaded to delayed: SystemStatus\ndyld[33989]: move loaded to delayed: CoreMotion\ndyld[33989]: move loaded to delayed: TimeSync\ndyld[33989]: move loaded to delayed: DistributedSensing\ndyld[33989]: move loaded to delayed: MobileBluetooth\ndyld[33989]: move loaded to delayed: IOKitten\ndyld[33989]: move loaded to delayed: LocationLogEncryption\ndyld[33989]: move loaded to delayed: AppleIntelligenceReporting\ndyld[33989]: move loaded to delayed: CoreEmoji\ndyld[33989]: move loaded to delayed: LanguageModeling\ndyld[33989]: move loaded to delayed: Montreal\ndyld[33989]: move loaded to delayed: libcmph.dylib\ndyld[33989]: move loaded to delayed: GenerativeModelsFoundation\ndyld[33989]: move loaded to delayed: TokenGeneration\ndyld[33989]: move loaded to delayed: GenerativeFunctions\ndyld[33989]: move loaded to delayed: GenerativeFunctionsFoundation\ndyld[33989]: move loaded to delayed: ModelCatalog\ndyld[33989]: move loaded to delayed: SensitiveContentAnalysisML\ndyld[33989]: move loaded to delayed: GenerativeFunctionsInstrumentation\ndyld[33989]: move loaded to delayed: PromptKit\ndyld[33989]: move loaded to delayed: ProactiveDaemonSupport\ndyld[33989]: move loaded to delayed: TokenGenerationCore\ndyld[33989]: move loaded to delayed: Trial\ndyld[33989]: move loaded to delayed: TrialProto\ndyld[33989]: move loaded to delayed: AppleFlatBuffers\ndyld[33989]: move loaded to delayed: SentencePieceInternal\ndyld[33989]: move loaded to delayed: Vision\ndyld[33989]: move loaded to delayed: CoreSceneUnderstanding\ndyld[33989]: move loaded to delayed: VisionCore\ndyld[33989]: move loaded to delayed: DataDetectorsCore\ndyld[33989]: move loaded to delayed: libfaceCore.dylib\ndyld[33989]: move loaded to delayed: Futhark\ndyld[33989]: move loaded to delayed: InertiaCam\ndyld[33989]: move loaded to delayed: TextRecognition\ndyld[33989]: move loaded to delayed: DataDetection\ndyld[33989]: move loaded to delayed: TextInput\ndyld[33989]: move loaded to delayed: CVNLP\ndyld[33989]: move loaded to delayed: IntentsFoundation\ndyld[33989]: move loaded to delayed: ApplePushService\ndyld[33989]: move loaded to delayed: CloudKit\ndyld[33989]: move loaded to delayed: CoreDuetDaemonProtocol\ndyld[33989]: move loaded to delayed: DeviceIdentity\ndyld[33989]: move loaded to delayed: SharedWithYouCore\ndyld[33989]: move loaded to delayed: CloudTelemetry\ndyld[33989]: move loaded to delayed: AppleAccount\ndyld[33989]: move loaded to delayed: CacheDelete\ndyld[33989]: move loaded to delayed: C2\ndyld[33989]: move loaded to delayed: CloudCoreInternal\ndyld[33989]: move loaded to delayed: CloudAsset\ndyld[33989]: move loaded to delayed: PushKit\ndyld[33989]: move loaded to delayed: CoreTransferable\ndyld[33989]: move loaded to delayed: FileProvider\ndyld[33989]: move loaded to delayed: GenerationalStorage\ndyld[33989]: move loaded to delayed: DesktopServicesPriv\ndyld[33989]: move loaded to delayed: CloudTelemetryTools\ndyld[33989]: move loaded to delayed: CloudTelemetryShared.dylib\ndyld[33989]: move loaded to delayed: RTCReporting\ndyld[33989]: move loaded to delayed: AAAFoundationSwift\ndyld[33989]: move loaded to delayed: AppleIDSSOAuthentication\ndyld[33989]: move loaded to delayed: UIFoundation\ndyld[33989]: move loaded to delayed: libcups.2.dylib\ndyld[33989]: move loaded to delayed: AXCoreUtilities\ndyld[33989]: move loaded to delayed: AttributeGraph\ndyld[33989]: move loaded to delayed: libAXSafeCategoryBundle.dylib\ndyld[33989]: move loaded to delayed: TabularData\ndyld[33989]: move loaded to delayed: ArgumentParserInternal\ndyld[33989]: move loaded to delayed: FindMyDevice\ndyld[33989]: move loaded to delayed: FMCoreLite\ndyld[33989]: move loaded to delayed: ServiceManagement\ndyld[33989]: move loaded to delayed: CryptoKitPrivate\ndyld[33989]: move loaded to delayed: CaptiveNetwork\ndyld[33989]: move loaded to delayed: EAP8021X\ndyld[33989]: move loaded to delayed: QuickLookThumbnailing\ndyld[33989]: <8A5F0D29-A245-3FB7-8531-4BFD4394BE26> /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python\ndyld[33989]: <1DEC725C-63A6-3B9C-A038-DC35832D65CB> /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python\ndyld[33989]: <9B672762-7B1F-30BC-96DE-F176B372D66D> /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation\ndyld[33989]: <03BD9E32-CF0A-37B0-898A-3CE8DE06D842> /usr/lib/libobjc.A.dylib\ndyld[33989]: <7D56DA94-31EB-35F0-B886-4010C075E035> /System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal\ndyld[33989]: <91DACE39-FA28-3191-818D-1FCC6A0E615A> /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation\ndyld[33989]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/liboah.dylib\ndyld[33989]: <820D290D-51A0-3064-A1F2-4F0AAF7E6BF4> /usr/lib/libfakelink.dylib\ndyld[33989]: <53A3E31E-06A8-325E-B5A8-316B88AA3C92> /usr/lib/libicucore.A.dylib\ndyld[33989]: <4FED5EE2-5D3E-35B1-A170-9859C4B683BB> /usr/lib/libSystem.B.dylib\ndyld[33989]: <4109E8DD-0A81-310C-B1B3-23B87186D0D8> /System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking\ndyld[33989]: <83794FB3-DE9B-3D23-AB5E-2C1D5D30F134> /usr/lib/swift/libswiftCore.dylib\ndyld[33989]: /usr/lib/libc++abi.dylib\ndyld[33989]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/libRosetta.dylib\ndyld[33989]: /usr/lib/libc++.1.dylib\ndyld[33989]: <4FD234EA-2C18-3C25-8BD0-B1F4805C6675> /usr/lib/swift/libswiftObjectiveC.dylib\ndyld[33989]: <9E3C7597-446F-3C50-9930-2425D9252C0C> /usr/lib/libswiftPrespecialized.dylib\ndyld[33989]: <1479C415-3678-3968-AC77-06373490860E> /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration\ndyld[33989]: <13EDE3A5-A7D9-3FB8-B0C2-2FB7F7272B34> /usr/lib/libz.1.dylib\ndyld[33989]: <54AD73AF-852E-3CD6-8B7D-E73BE79857D3> /System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout\ndyld[33989]: <1A2A9A41-5269-3B0C-BCEE-B446966CE366> /usr/lib/libcmark-gfm.dylib\ndyld[33989]: /usr/lib/libcompression.dylib\ndyld[33989]: <4A3B95C5-AA2E-338C-9398-56895AF82D97> /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork\ndyld[33989]: <332C4B80-5B3C-34E7-AD1F-F6131E607F95> /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration\ndyld[33989]: <0048DB96-1737-3FC5-AF0C-AF784FA24A03> /usr/lib/libarchive.2.dylib\ndyld[33989]: <6CD959AA-4825-306A-864A-BD69EC5F2DC0> /usr/lib/libDiagnosticMessagesClient.dylib\ndyld[33989]: <1E8A4F9E-3954-3458-B3BB-BE97F961C105> /usr/lib/libxml2.2.dylib\ndyld[33989]: <56AE2857-29E0-34E9-B2C3-EE8E951EEFC5> /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices\ndyld[33989]: /usr/lib/liblangid.dylib\ndyld[33989]: <12372585-DF92-33EF-B632-714FAA13260A> /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit\ndyld[33989]: /System/Library/Frameworks/Combine.framework/Versions/A/Combine\ndyld[33989]: <6098453F-4D7E-38B4-8ADC-02C9FF51E14A> /System/Library/PrivateFrameworks/CollectionsInternal.framework/Versions/A/CollectionsInternal\ndyld[33989]: <9A1279D4-575A-3E48-A460-A631A3F82D18> /System/Library/PrivateFrameworks/ReflectionInternal.framework/Versions/A/ReflectionInternal\ndyld[33989]: <6D89CD71-A86D-3D78-A64B-96AB79550F79> /System/Library/PrivateFrameworks/RuntimeInternal.framework/Versions/A/RuntimeInternal\ndyld[33989]: <4975D13C-2AC5-3473-85C0-98054A81D7C6> /usr/lib/swift/libswiftCoreFoundation.dylib\ndyld[33989]: <1DB56DA9-CF6B-3023-ABDF-5A37CB79223C> /usr/lib/swift/libswiftDarwin.dylib\ndyld[33989]: /usr/lib/swift/libswiftDispatch.dylib\ndyld[33989]: <06A92787-4440-3757-AF32-F2B331C753A2> /usr/lib/swift/libswiftIOKit.dylib\ndyld[33989]: <7CD9BDE7-F36B-3471-9295-38E181D6D9E5> /usr/lib/swift/libswiftSystem.dylib\ndyld[33989]: <24AEDAC1-C1EE-30F4-8818-72EBF8969D0C> /usr/lib/swift/libswiftXPC.dylib\ndyld[33989]: <52F59382-A6A6-3F55-8A85-D9FB822D370F> /usr/lib/swift/libswift_Builtin_float.dylib\ndyld[33989]: <8E168857-47F4-349F-A718-A18DB144FCB0> /usr/lib/swift/libswift_Concurrency.dylib\ndyld[33989]: <85246B9A-A757-3F67-B792-3A2F7BB2BB25> /usr/lib/swift/libswift_DarwinFoundation1.dylib\ndyld[33989]: <8DF0116D-DFC9-3906-9DF6-F1DBC47E324B> /usr/lib/swift/libswift_StringProcessing.dylib\ndyld[33989]: /usr/lib/swift/libswiftos.dylib\ndyld[33989]: <1C7E652B-6B94-3180-93A6-EF8DBA3A5448> /System/Library/Frameworks/Network.framework/Versions/A/Network\ndyld[33989]: <4C6139EE-BF87-37A6-B226-830A6FDC36F8> /System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo\ndyld[33989]: <9D0387FC-E8F6-3004-9C95-CA68EA715C8B> /System/Library/Frameworks/Security.framework/Versions/A/Security\ndyld[33989]: <633BCB5F-F063-3D5A-B52A-F72AE236824B> /usr/lib/libbsm.0.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer\ndyld[33989]: <10A4E63B-A1EB-31CC-B3E1-DB4FE115FC84> /System/Library/PrivateFrameworks/BackgroundSystemTasks.framework/Versions/A/BackgroundSystemTasks\ndyld[33989]: /System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics\ndyld[33989]: <7C50137B-2ABD-3819-B033-AE65B05A6085> /System/Library/PrivateFrameworks/CoreWiFi.framework/Versions/A/CoreWiFi\ndyld[33989]: /System/Library/PrivateFrameworks/LoggingSupport.framework/Versions/A/LoggingSupport\ndyld[33989]: <91A461DE-C8E8-3868-B393-BA6E5A17DF2A> /System/Library/PrivateFrameworks/MobileAsset.framework/Versions/A/MobileAsset\ndyld[33989]: /System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog\ndyld[33989]: /System/Library/PrivateFrameworks/Rapport.framework/Versions/A/Rapport\ndyld[33989]: /System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices\ndyld[33989]: <9F52706C-75BD-34AF-A29E-C26608124ACC> /System/Library/Frameworks/SwiftData.framework/Versions/A/SwiftData\ndyld[33989]: <259877CE-4E2C-34A9-A07F-FEE2999D7B2F> /System/Library/PrivateFrameworks/Symptoms.framework/Versions/A/Frameworks/SymptomAnalytics.framework/Versions/A/SymptomAnalytics\ndyld[33989]: /System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers\ndyld[33989]: <4A78C569-FF0D-398B-9C25-33453F0CEC40> /System/Library/PrivateFrameworks/UserManagement.framework/Versions/A/UserManagement\ndyld[33989]: <5BF55637-F306-3D79-B5A1-DB8A871DAD4B> /usr/lib/libboringssl.dylib\ndyld[33989]: <831C79C1-8DBE-31A3-AA4E-8E2B041488D6> /usr/lib/libcupolicy.dylib\ndyld[33989]: <88925A0C-4960-3F6D-AF3A-B1983F7B3D18> /usr/lib/libdns_services.dylib\ndyld[33989]: /usr/lib/libnetworkextension.dylib\ndyld[33989]: <9753F471-40DD-3B9E-9D64-8D07C1B06BC9> /System/Library/Frameworks/NetworkExtension.framework/Versions/A/NetworkExtension\ndyld[33989]: /usr/lib/libnwswifttls.dylib\ndyld[33989]: <6F59933A-6618-33F1-BE52-E7FC3BF7A1EF> /usr/lib/libpcap.A.dylib\ndyld[33989]: <5E89267F-C684-348D-8356-F9DAD8B4CB13> /usr/lib/libquic.dylib\ndyld[33989]: /usr/lib/libusrtcp.dylib\ndyld[33989]: /usr/lib/libMobileGestalt.dylib\ndyld[33989]: /usr/lib/libapple_nghttp2.dylib\ndyld[33989]: <6937D729-7EF4-3972-9E12-694C17C1C1AB> /usr/lib/libcoretls_cfhelpers.dylib\ndyld[33989]: /usr/lib/libsqlite3.dylib\ndyld[33989]: <1617DBB1-2BFF-3619-903C-2FBB31348FB6> /System/Library/PrivateFrameworks/AtomicsInternal.framework/Versions/A/AtomicsInternal\ndyld[33989]: <41F66F01-A342-3091-A832-0B2B645C922B> /System/Library/PrivateFrameworks/InternalSwiftProtobuf.framework/Versions/A/InternalSwiftProtobuf\ndyld[33989]: <2EDB2E62-942F-3AB5-82AF-8E1328544E17> /usr/lib/swift/libswiftDistributed.dylib\ndyld[33989]: /usr/lib/swift/libswiftObservation.dylib\ndyld[33989]: /usr/lib/swift/libswiftSynchronization.dylib\ndyld[33989]: <9CD7B1E1-3E47-339C-A193-2392E3E0ED23> /usr/lib/system/libcache.dylib\ndyld[33989]: <3B110564-5278-3CB0-85F1-2CE8431FF935> /usr/lib/system/libcommonCrypto.dylib\ndyld[33989]: <6FB345CA-7F5C-3263-A23F-143F7539FD8A> /usr/lib/system/libcompiler_rt.dylib\ndyld[33989]: /usr/lib/system/libcopyfile.dylib\ndyld[33989]: <0642DDAD-4771-3C82-805C-E7C6701C1461> /usr/lib/system/libcorecrypto.dylib\ndyld[33989]: /usr/lib/system/libdispatch.dylib\ndyld[33989]: <957F93B3-8805-39C7-9C51-EDD1715F550E> /usr/lib/system/libdyld.dylib\ndyld[33989]: <7E863FCA-F3FF-32C7-8A8C-F983E946AFC3> /usr/lib/system/libkeymgr.dylib\ndyld[33989]: <949131E5-BDA2-39BA-AA50-62651BB51802> /usr/lib/system/libmacho.dylib\ndyld[33989]: /usr/lib/system/libquarantine.dylib\ndyld[33989]: <7460B5AE-469A-36A0-A7EC-6C7D69628E86> /usr/lib/system/libremovefile.dylib\ndyld[33989]: <54439739-33EE-3273-839F-CBA67D7F5CB1> /usr/lib/system/libsystem_asl.dylib\ndyld[33989]: /usr/lib/system/libsystem_blocks.dylib\ndyld[33989]: /usr/lib/system/libsystem_c.dylib\ndyld[33989]: /usr/lib/system/libsystem_collections.dylib\ndyld[33989]: /usr/lib/system/libsystem_configuration.dylib\ndyld[33989]: <14B2A47F-19C8-392F-8FDB-FE8AE375DD41> /usr/lib/system/libsystem_containermanager.dylib\ndyld[33989]: /usr/lib/system/libsystem_coreservices.dylib\ndyld[33989]: <8E07D22E-CE5A-38A0-B091-5B0338C326F5> /usr/lib/system/libsystem_darwin.dylib\ndyld[33989]: <971A4F65-493D-39F3-846D-0D33FA2769FD> /usr/lib/system/libsystem_darwindirectory.dylib\ndyld[33989]: <305F4398-E688-3384-B351-02D865EC8A04> /usr/lib/system/libsystem_dnssd.dylib\ndyld[33989]: <750CA446-92EA-3A56-9A7B-CC0841686C50> /usr/lib/system/libsystem_eligibility.dylib\ndyld[33989]: /usr/lib/system/libsystem_featureflags.dylib\ndyld[33989]: <9B5FB84B-31AD-3EA7-8F89-8C700D369DC8> /usr/lib/system/libsystem_info.dylib\ndyld[33989]: /usr/lib/system/libsystem_m.dylib\ndyld[33989]: /usr/lib/system/libsystem_malloc.dylib\ndyld[33989]: <9C7B1EEB-47BE-3791-93A9-CFC693CB9417> /usr/lib/system/libsystem_networkextension.dylib\ndyld[33989]: <15799128-6CBD-30D6-A2BB-B9D02B4470C0> /usr/lib/system/libsystem_notify.dylib\ndyld[33989]: <54688162-B50D-3D31-A1E8-7B9766D3530D> /usr/lib/system/libsystem_sandbox.dylib\ndyld[33989]: /usr/lib/system/libsystem_sanitizers.dylib\ndyld[33989]: /usr/lib/system/libsystem_secinit.dylib\ndyld[33989]: /usr/lib/system/libsystem_kernel.dylib\ndyld[33989]: /usr/lib/system/libsystem_platform.dylib\ndyld[33989]: /usr/lib/system/libsystem_pthread.dylib\ndyld[33989]: <229122B9-B8B1-3F2F-870E-8650AE3C4FB5> /usr/lib/system/libsystem_symptoms.dylib\ndyld[33989]: <93F1DD8C-6CD9-32B9-B222-D23DA5D161B4> /usr/lib/system/libsystem_trace.dylib\ndyld[33989]: <7194FF5B-A6C5-3D67-B00A-90209F10D603> /usr/lib/system/libsystem_trial.dylib\ndyld[33989]: <05FD0014-55B1-3B8A-A6BA-6C7A389C4123> /usr/lib/system/libunwind.dylib\ndyld[33989]: <33E44C2D-D65E-37A6-B85F-1A4CF524A050> /usr/lib/system/libxpc.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/XPCSupport.framework/Versions/A/XPCSupport\ndyld[33989]: /System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement\ndyld[33989]: /System/Library/PrivateFrameworks/AppleKeyStore.framework/Versions/A/AppleKeyStore\ndyld[33989]: /usr/lib/libCoreEntitlements.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity\ndyld[33989]: <81F4A8BA-C80F-3B53-82E7-57F6928609C5> /System/Library/PrivateFrameworks/CloudServices.framework/Versions/A/CloudServices\ndyld[33989]: <737479F2-7B20-3DB6-B9F4-0DAA1B73E9D0> /System/Library/PrivateFrameworks/SymptomDiagnosticReporter.framework/Versions/A/SymptomDiagnosticReporter\ndyld[33989]: /System/Library/PrivateFrameworks/CrashReporterSupport.framework/Versions/A/CrashReporterSupport\ndyld[33989]: /System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression\ndyld[33989]: <0EAB1F4A-9275-3FED-8EA6-E962ACDDEE5D> /usr/lib/libcoretls.dylib\ndyld[33989]: <7E84FD3B-E90E-317E-AC19-17B70AC809E5> /usr/lib/libpam.2.dylib\ndyld[33989]: /usr/lib/libxar.1.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS\ndyld[33989]: /System/Library/PrivateFrameworks/SwiftASN1Internal.framework/Versions/A/SwiftASN1Internal\ndyld[33989]: /usr/lib/libutil.dylib\ndyld[33989]: <8E04C57D-3651-386E-83D5-4728B732F214> /usr/lib/libenergytrace.dylib\ndyld[33989]: /usr/lib/system/libkxld.dylib\ndyld[33989]: <2BC48182-F354-3AB0-8F18-0C60CAAFE398> /System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer\ndyld[33989]: <5556FD64-9D47-3547-961E-3A27681F3C51> /System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface\ndyld[33989]: <6A4A85F4-3D12-3C4C-85EC-D53D61379F28> /usr/lib/libheimdal-asn1.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce\ndyld[33989]: /System/Library/PrivateFrameworks/OctagonTrust.framework/Versions/A/OctagonTrust\ndyld[33989]: /System/Library/PrivateFrameworks/AppleIDAuthSupport.framework/Versions/A/AppleIDAuthSupport\ndyld[33989]: <9A86DB3F-CC62-3E89-B872-35D04CFFBE42> /System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/KeychainCircle.framework/Versions/A/KeychainCircle\ndyld[33989]: <336E2CAC-84D2-34DC-8AE3-7FE688C609EA> /System/Library/PrivateFrameworks/AuthKit.framework/Versions/A/AuthKit\ndyld[33989]: /System/Library/PrivateFrameworks/AAAFoundation.framework/Versions/A/AAAFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag\ndyld[33989]: <79000980-1797-3115-B74B-60FA1E9C3C73> /System/Library/PrivateFrameworks/CorePhoneNumbers.framework/Versions/A/CorePhoneNumbers\ndyld[33989]: <21723046-939E-302F-883C-9DB417452E3A> /System/Library/PrivateFrameworks/MultiverseSupport.framework/Versions/A/MultiverseSupport\ndyld[33989]: <823F3D1A-65F1-3CC5-96B1-750263B8DB36> /System/Library/PrivateFrameworks/RemoteServiceDiscovery.framework/Versions/A/RemoteServiceDiscovery\ndyld[33989]: /System/Library/PrivateFrameworks/DiskManagement.framework/Versions/A/DiskManagement\ndyld[33989]: <5F6B668E-00B2-3BEC-959F-26BD6B50D42B> /System/Library/Frameworks/Accounts.framework/Versions/A/Accounts\ndyld[33989]: /System/Library/PrivateFrameworks/URLFormatting.framework/Versions/A/URLFormatting\ndyld[33989]: <91BDD1F8-831B-3B01-86BA-6BBCB43373C4> /System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary\ndyld[33989]: <885F9C72-1018-368B-AD36-E8A42E87FD91> /System/Library/PrivateFrameworks/RemoteXPC.framework/Versions/A/RemoteXPC\ndyld[33989]: /usr/lib/libFDR.dylib\ndyld[33989]: <24D28E7F-A1AE-3031-8679-A0D6C6D68A86> /usr/lib/libamsupport.dylib\ndyld[33989]: <29367004-5D60-38DB-831F-9E5EE9364B21> /usr/lib/libReverseProxyDevice.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/MSUDataAccessor.framework/Versions/A/MSUDataAccessor\ndyld[33989]: <9594FBFB-D49D-3DF6-8820-564633EAEC2B> /System/Library/PrivateFrameworks/AppleDeviceQuerySupport.framework/Versions/A/AppleDeviceQuerySupport\ndyld[33989]: <44CD8313-2D5B-3A34-BACA-EF8800803B4A> /System/Library/PrivateFrameworks/AOSKit.framework/Versions/A/AOSKit\ndyld[33989]: <5198BFE1-41D2-33D5-A9E0-C63F81A512D3> /System/Library/PrivateFrameworks/AppSSOCore.framework/Versions/A/AppSSOCore\ndyld[33989]: <61B2B917-D14A-38AD-A439-16E1C635441A> /System/Library/PrivateFrameworks/AppSupport.framework/Versions/A/AppSupport\ndyld[33989]: <816EC446-7C41-3A2F-A582-7CB856797C09> /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation\ndyld[33989]: <38C8FBEC-DE88-33FE-B742-A192F22CC754> /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics\ndyld[33989]: /System/Library/PrivateFrameworks/DuetActivityScheduler.framework/Versions/A/DuetActivityScheduler\ndyld[33989]: <0E78989C-854F-3664-AD92-6B7B6D04191C> /System/Library/PrivateFrameworks/FTServices.framework/Versions/A/FTServices\ndyld[33989]: <277D18EF-39E4-3F72-99E8-8D3DF65ED1D0> /System/Library/Frameworks/GSS.framework/Versions/A/GSS\ndyld[33989]: <5ACC6C0E-51E9-3B5A-B24F-89B22D070878> /System/Library/PrivateFrameworks/InternationalSupport.framework/Versions/A/InternationalSupport\ndyld[33989]: /usr/lib/libMemoryResourceException.dylib\ndyld[33989]: <798012E0-3FFC-3B8D-AC74-E7B7DAEA7E66> /System/Library/PrivateFrameworks/NetworkScore.framework/Versions/A/NetworkScore\ndyld[33989]: <2C93123F-99C8-3B8D-AAE6-3A817BE0A2BF> /System/Library/PrivateFrameworks/NetworkServiceProxy.framework/Versions/A/NetworkServiceProxy\ndyld[33989]: /System/Library/PrivateFrameworks/StreamingExtractor.framework/Versions/A/StreamingExtractor\ndyld[33989]: <1F2EDC7B-8F28-3721-8A60-F6E1BCFC29A3> /System/Library/PrivateFrameworks/StreamingZip.framework/Versions/A/StreamingZip\ndyld[33989]: <5DA62AF9-3D46-3D17-A3EB-7026A2F006DF> /System/Library/PrivateFrameworks/SymptomReporter.framework/Versions/A/SymptomReporter\ndyld[33989]: /usr/lib/liblzma.5.dylib\ndyld[33989]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents\ndyld[33989]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore\ndyld[33989]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata\ndyld[33989]: <61677289-93B7-382F-86CA-B856361D293F> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices\ndyld[33989]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit\ndyld[33989]: <435D6243-695B-3543-A722-10106F5696BD> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE\ndyld[33989]: <01579E0C-9D85-3521-8916-4DDC990CD064> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices\ndyld[33989]: <6A26D479-5926-330B-9FB8-9B7A6BE8E239> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices\ndyld[33989]: <297AC970-E432-3BBD-986C-36782634062E> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList\ndyld[33989]: <6508C698-D587-3B5A-B95B-A3A3F78CE122> /usr/lib/libCheckFix.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC\ndyld[33989]: /System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP\ndyld[33989]: <29AA0F7F-26F4-35B3-96DF-8A67B00A58AB> /System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities\ndyld[33989]: <9171DD7D-3994-3963-9A28-BC163BF97DE6> /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate\ndyld[33989]: /usr/lib/libmecab.dylib\ndyld[33989]: <1CA9048E-57DD-30F4-A3E6-FE6E97D5BF82> /usr/lib/libCRFSuite.dylib\ndyld[33989]: <74E55DD6-720D-39E4-897E-EB4328E1946D> /usr/lib/libgermantok.dylib\ndyld[33989]: <92FAD15C-EEA5-34E9-B309-75A1CD1B620B> /usr/lib/libThaiTokenizer.dylib\ndyld[33989]: <2B16DF37-A596-3D8A-AE47-33E580EB1354> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage\ndyld[33989]: <8203944D-B53E-3D7E-A481-3C676CAE1B6A> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib\ndyld[33989]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib\ndyld[33989]: <08508E7B-096D-31AB-9C66-191C877ED62F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/Libraries/libCGInterfaces.dylib\ndyld[33989]: <8945E7B7-12AE-3FF4-AA3B-D4DF9A06FEE7> /System/Library/PrivateFrameworks/AccelerateGPU.framework/Versions/A/AccelerateGPU\ndyld[33989]: <23402175-D2CF-3B08-88D0-AFBBCF775FEF> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib\ndyld[33989]: <086CBEED-2F64-3E75-AB99-8C8C0E0A2F1C> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices\ndyld[33989]: <0616AF41-149E-3F4A-906E-56E2642457BE> /System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo\ndyld[33989]: <873404F1-CC9D-30F9-AE06-8EA58D292005> /System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync\ndyld[33989]: /System/Library/Frameworks/CoreText.framework/Versions/A/CoreText\ndyld[33989]: /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO\ndyld[33989]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS\ndyld[33989]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices\ndyld[33989]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore\ndyld[33989]: <59BBF27B-1D89-3D35-9210-8386EFA15A8D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD\ndyld[33989]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy\ndyld[33989]: <9CDA611B-254A-3779-9356-369485134C2D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis\ndyld[33989]: <0C8F41C6-6D93-3DB3-B522-CA8CFF5C3B33> /System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight\ndyld[33989]: <9E126CE0-FBB2-3B15-953F-CCDC758E34FB> /System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib\ndyld[33989]: <07CF779F-8F51-3764-B486-23D76868FF91> /System/Library/PrivateFrameworks/BiomeLibrary.framework/Versions/A/BiomeLibrary\ndyld[33989]: <959C748F-8851-3A25-BFFA-5FEA80296965> /System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard\ndyld[33989]: /System/Library/PrivateFrameworks/BoardServices.framework/Versions/A/BoardServices\ndyld[33989]: /System/Library/PrivateFrameworks/BackBoardServices.framework/Versions/A/BackBoardServices\ndyld[33989]: <7F763DF9-EA7F-3938-B599-DCCF4605E610> /System/Library/PrivateFrameworks/BackBoardHIDEventFoundation.framework/Versions/A/BackBoardHIDEventFoundation\ndyld[33989]: /System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay\ndyld[33989]: /System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox\ndyld[33989]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders\ndyld[33989]: /System/Library/PrivateFrameworks/ProDisplayLibrary.framework/Versions/A/ProDisplayLibrary\ndyld[33989]: <1E529C1A-B09C-3EB7-A286-CE00E292D561> /System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator\ndyld[33989]: <493E76D9-74D4-333B-A3B2-E5F9BC86429D> /System/Library/Frameworks/Metal.framework/Versions/A/Metal\ndyld[33989]: /System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator\ndyld[33989]: /System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia\ndyld[33989]: /System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient\ndyld[33989]: <98CB7012-30E5-3BDD-8C84-CDBDA9DB3017> /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore\ndyld[33989]: <57F7BB9C-649D-3360-AA86-A502815D77FA> /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport\ndyld[33989]: <625F222D-6394-39B9-A1F2-12B9EA56DD85> /usr/lib/swift/libswiftAccelerate.dylib\ndyld[33989]: /usr/lib/swift/libswiftCoreAudio.dylib\ndyld[33989]: /usr/lib/swift/libswiftCoreMedia.dylib\ndyld[33989]: <7235A6A9-49B2-3B94-9DD6-C987019CDBF2> /usr/lib/swift/libswiftMetal.dylib\ndyld[33989]: <9670AE5C-271A-3DCB-9A0A-8E3A7CCC2726> /usr/lib/swift/libswiftOSLog.dylib\ndyld[33989]: <63444A8C-9E8C-3778-820D-1E0C88CA2DF7> /usr/lib/swift/libswiftQuartzCore.dylib\ndyld[33989]: /usr/lib/swift/libswiftUniformTypeIdentifiers.dylib\ndyld[33989]: <9247A5B6-A883-3A07-BEE7-A223840317A4> /usr/lib/swift/libswiftVideoToolbox.dylib\ndyld[33989]: /usr/lib/swift/libswiftsimd.dylib\ndyld[33989]: <2110407D-EFB4-373E-B963-9C92E26594B2> /System/Library/PrivateFrameworks/BiomeStreams.framework/Versions/A/BiomeStreams\ndyld[33989]: /System/Library/PrivateFrameworks/BiomeStorage.framework/Versions/A/BiomeStorage\ndyld[33989]: <455A5553-E683-30B4-A906-1F14E75F6E61> /System/Library/PrivateFrameworks/BiomeFoundation.framework/Versions/A/BiomeFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/IntelligencePlatformLibrary.framework/Versions/A/IntelligencePlatformLibrary\ndyld[33989]: <42CDC0E6-51BA-3804-BD3E-EDF87FC74034> /System/Library/PrivateFrameworks/PoirotSchematizer.framework/Versions/A/PoirotSchematizer\ndyld[33989]: <2362E209-EC61-3FFC-9486-1244BB29BE82> /System/Library/PrivateFrameworks/BiomeSync.framework/Versions/A/BiomeSync\ndyld[33989]: /System/Library/PrivateFrameworks/BiomeDSL.framework/Versions/A/BiomeDSL\ndyld[33989]: <0F03104F-FC8B-3ADD-8850-4B7029E2B56E> /System/Library/PrivateFrameworks/BiomePubSub.framework/Versions/A/BiomePubSub\ndyld[33989]: <73EE1A0A-0D29-3104-98CB-BEFEDA53F7C0> /System/Library/PrivateFrameworks/ProactiveSupport.framework/Versions/A/ProactiveSupport\ndyld[33989]: /System/Library/PrivateFrameworks/FeatureFlags.framework/Versions/A/FeatureFlags\ndyld[33989]: /System/Library/PrivateFrameworks/PoirotUDFs.framework/Versions/A/PoirotUDFs\ndyld[33989]: /usr/lib/swift/libswift_DarwinFoundation2.dylib\ndyld[33989]: <8D2C31B5-FB10-3BF6-8566-F0DCD56C8582> /usr/lib/swift/libswift_DarwinFoundation3.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime\ndyld[33989]: <858910C5-1D4A-37B7-BF0E-EE02E24A2ACD> /System/Library/PrivateFrameworks/InternationalTextSearch.framework/Versions/A/InternationalTextSearch\ndyld[33989]: <13271AA6-33EA-369B-B2D1-6EC528C820E7> /System/Library/PrivateFrameworks/SoftwareUpdateCoreSupport.framework/Versions/A/SoftwareUpdateCoreSupport\ndyld[33989]: <2CA857AF-D999-34DC-94A1-3AC0E5B80416> /System/Library/PrivateFrameworks/SoftwareUpdateCoreConnect.framework/Versions/A/SoftwareUpdateCoreConnect\ndyld[33989]: /usr/lib/libbootpolicy.dylib\ndyld[33989]: /usr/lib/libpartition2_dynamic.dylib\ndyld[33989]: <9A8926C8-36A6-3DB4-A485-059C1F630984> /usr/lib/libAppleArchive.dylib\ndyld[33989]: <5FFE1FFA-6BD0-32AF-A815-7543731CA763> /usr/lib/libbz2.1.0.dylib\ndyld[33989]: <06728C4D-5750-308F-8290-EAF7BE91F4BB> /System/Library/PrivateFrameworks/OSAnalytics.framework/Versions/A/OSAnalytics\ndyld[33989]: <2FA711C7-F764-363A-BF03-295E0DA88B79> /System/Library/PrivateFrameworks/DeviceRecovery.framework/Versions/A/DeviceRecovery\ndyld[33989]: <59136324-34E6-3367-92BB-659346907A04> /System/Library/PrivateFrameworks/CoreSymbolication.framework/Versions/A/CoreSymbolication\ndyld[33989]: <724D42FC-F4FD-39C7-A1BF-D0AD086231F4> /System/Library/PrivateFrameworks/Symbolication.framework/Versions/A/Symbolication\ndyld[33989]: <7C923545-F3BB-3215-9720-85196358D9F1> /System/Library/PrivateFrameworks/DebugSymbols.framework/Versions/A/DebugSymbols\ndyld[33989]: <566F2D7D-0F3B-3290-A739-7A40A151F0BE> /System/Library/PrivateFrameworks/MallocStackLogging.framework/Versions/A/MallocStackLogging\ndyld[33989]: <7B63C2BF-8C7C-3ECA-ACD9-F1B75DBE018C> /usr/lib/swift/libswift_RegexParser.dylib\ndyld[33989]: <4646F780-1D5E-3EE7-B00A-64619293CC18> /usr/lib/libiconv.2.dylib\ndyld[33989]: <1940124C-0D73-35D2-9D94-A75F116088A0> /usr/lib/libcharset.1.dylib\ndyld[33989]: <24779350-BC29-3465-AAB3-F7CD0DA5844A> /System/Library/PrivateFrameworks/PoirotSQLite.framework/Versions/A/PoirotSQLite\ndyld[33989]: <2091B02D-8D55-3DC4-8097-60C193D03C85> /System/Library/PrivateFrameworks/CascadeSets.framework/Versions/A/CascadeSets\ndyld[33989]: <7F00413A-4D40-3DBF-8FD5-859B23E6DC03> /System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG\ndyld[33989]: /usr/lib/libexpat.1.dylib\ndyld[33989]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib\ndyld[33989]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib\ndyld[33989]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib\ndyld[33989]: <7304F8B3-8E0F-3813-BFAF-9A565CEA0A11> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib\ndyld[33989]: <01AAD3B4-D6BA-36D9-BA6F-D494D2AC161D> /usr/lib/libate.dylib\ndyld[33989]: <8EA6CA42-AA01-3C0F-9672-4917481BAAAE> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib\ndyld[33989]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib\ndyld[33989]: <526C249F-FF2E-3DC4-A639-B41A032E8CCE> /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libllvm-flatbuffers.dylib\ndyld[33989]: <1FDD3B19-C04A-3EE7-B7DF-E1F89954A696> /System/Library/PrivateFrameworks/FramePacing.framework/Versions/A/FramePacing\ndyld[33989]: <2C410B78-B9A5-30DC-8D83-FFEC1277F34C> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libGPUCompilerUtils.dylib\ndyld[33989]: <90CFC86E-833E-3E9F-BAAC-2B61BD750DA6> /System/Library/PrivateFrameworks/CoreDuetContext.framework/Versions/A/CoreDuetContext\ndyld[33989]: <838F99F9-D3FA-335B-9767-B5D04A3FACA6> /System/Library/PrivateFrameworks/CoreDuet.framework/Versions/A/CoreDuet\ndyld[33989]: <712AD9C1-44D2-36F4-BA8E-15038521462B> /System/Library/Frameworks/CoreData.framework/Versions/A/CoreData\ndyld[33989]: <9805BB7B-12C9-39F5-9070-C5B8BFCAE2AF> /System/Library/Frameworks/CoreLocation.framework/Versions/A/CoreLocation\ndyld[33989]: /System/Library/Frameworks/Intents.framework/Versions/A/Intents\ndyld[33989]: /System/Library/Frameworks/_LocationEssentials.framework/Versions/A/_LocationEssentials\ndyld[33989]: <1DAFDDDA-BB7B-320E-BCFC-B7C22886D486> /System/Library/PrivateFrameworks/GeoServices.framework/Versions/A/GeoServices\ndyld[33989]: /System/Library/PrivateFrameworks/LocationSupport.framework/Versions/A/LocationSupport\ndyld[33989]: <515FDCCC-535A-398B-BBD3-3D35565F5423> /System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth\ndyld[33989]: <38EE3C42-06D6-3A46-A420-DF701A4EA911> /System/Library/PrivateFrameworks/GeoServicesCore.framework/Versions/A/GeoServicesCore\ndyld[33989]: <8D0ECDD1-24B6-3B8D-9CF9-CDC55FC64490> /System/Library/PrivateFrameworks/PhoneNumbers.framework/Versions/A/PhoneNumbers\ndyld[33989]: <3D533C35-3A2A-3672-92EF-5FEE9EE739AC> /System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation\ndyld[33989]: <2B5FB7B0-844C-3D84-9EFD-020B285B0F8D> /System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport\ndyld[33989]: <62740FDD-2B16-3319-B5C9-022D45C6B03A> /System/Library/PrivateFrameworks/OSEligibility.framework/Versions/A/OSEligibility\ndyld[33989]: <10C63D59-07BC-3518-87A0-83CAC48D8A70> /System/Library/PrivateFrameworks/IconServices.framework/Versions/A/IconServices\ndyld[33989]: <8EF56F82-8CCE-3811-AD16-6D0939187B45> /System/Library/Frameworks/LightweightCodeRequirements.framework/Versions/A/LightweightCodeRequirements\ndyld[33989]: /System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit\ndyld[33989]: <1946F8FE-0ABC-3F8F-9116-5451ECABD14C> /System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices\ndyld[33989]: /System/Library/PrivateFrameworks/IconFoundation.framework/Versions/A/IconFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/AssistantServices.framework/Versions/A/AssistantServices\ndyld[33989]: <6A34A62A-16D4-34F0-B34B-2D96B53C20AD> /System/Library/PrivateFrameworks/IconRendering.framework/Versions/A/IconRendering\ndyld[33989]: /System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI\ndyld[33989]: <0943679D-FF88-3F18-BE4B-D8B4827AB0B5> /System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage\ndyld[33989]: <968B5A5F-9749-3527-AF2A-66B599785308> /System/Library/PrivateFrameworks/SFSymbols.framework/Versions/A/SFSymbols\ndyld[33989]: /System/Library/Frameworks/DeveloperToolsSupport.framework/Versions/A/DeveloperToolsSupport\ndyld[33989]: <92090A92-DFAF-3EBC-886C-655EC158A53F> /System/Library/PrivateFrameworks/RenderBox.framework/Versions/A/RenderBox\ndyld[33989]: <986D57A7-BFF1-3DAA-8EB1-17CCAA76C731> /System/Library/PrivateFrameworks/CoreSVG.framework/Versions/A/CoreSVG\ndyld[33989]: /System/Library/PrivateFrameworks/TextureIO.framework/Versions/A/TextureIO\ndyld[33989]: /usr/lib/swift/libswiftCoreImage.dylib\ndyld[33989]: <77D85BA0-FE1C-3B5A-92DB-70A30202C990> /System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer\ndyld[33989]: /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL\ndyld[33989]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib\ndyld[33989]: <6CEF3932-AAC9-3F8E-905D-A826F2884C9A> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib\ndyld[33989]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib\ndyld[33989]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib\ndyld[33989]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib\ndyld[33989]: <07CB5D41-C2F3-3C33-951F-67B2C8B8B662> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices\ndyld[33989]: <5D3E7FFF-AC8E-3D6F-8E99-B199E593D270> /System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG\ndyld[33989]: <49E7449E-1385-3B53-94CC-36EFC31E98FE> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib\ndyld[33989]: <23C577A8-DB0B-3A0A-9058-1289483C262A> /usr/lib/libhvf.dylib\ndyld[33989]: <11E757EC-72FB-3C53-8ED7-641428AB6169> /System/Library/PrivateFrameworks/ParsingInternal.framework/Versions/A/ParsingInternal\ndyld[33989]: /System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib\ndyld[33989]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore\ndyld[33989]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage\ndyld[33989]: <199F6401-91D0-36E9-9EA9-D4B44ED1CE3A> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork\ndyld[33989]: <4D134FE3-50EE-39D5-9699-04B4B673DD35> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix\ndyld[33989]: <2E7E2722-3821-3DBF-B25A-6EA45D1A8FD4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector\ndyld[33989]: <3E1FE9EA-34A2-3545-B639-48B1FE1FD3D4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray\ndyld[33989]: <3103E210-FF5C-3677-BDD3-59FF17A6ACEC> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions\ndyld[33989]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSBenchmarkLoop.framework/Versions/A/MPSBenchmarkLoop\ndyld[33989]: <31F90368-23A5-39BB-822B-C8470C4479AE> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSHost.framework/Versions/A/MPSHost\ndyld[33989]: <9C416BB2-0882-315C-AF23-F476E34983BC> /System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools\ndyld[33989]: /System/Library/PrivateFrameworks/IOAccelMemoryInfo.framework/Versions/A/IOAccelMemoryInfo\ndyld[33989]: /System/Library/PrivateFrameworks/kperf.framework/Versions/A/kperf\ndyld[33989]: <03470B3A-A004-39A0-B6A4-F2A4AFFFCDD3> /System/Library/PrivateFrameworks/GPURawCounter.framework/Versions/A/GPURawCounter\ndyld[33989]: <4D8F39C6-B221-3AF1-BB40-CAEB0A174D61> /System/Library/PrivateFrameworks/ASEProcessing.framework/Versions/A/ASEProcessing\ndyld[33989]: /System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing\ndyld[33989]: <1B4C0154-843C-3CEE-9628-22978082DD2D> /System/Library/PrivateFrameworks/SILManager.framework/Versions/A/SILManager\ndyld[33989]: /System/Library/PrivateFrameworks/Tightbeam.framework/Versions/A/Tightbeam\ndyld[33989]: <856ACB2A-3334-3BA6-AAC8-8F344E7CDB83> /usr/lib/swift/libswiftCompression.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/AFKUser.framework/Versions/A/AFKUser\ndyld[33989]: <2186F196-EE17-3A59-B9DA-D6823BEDD35B> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI\ndyld[33989]: <086BB8AD-E317-3FC4-9E44-0D7C6036E8D7> /System/Library/PrivateFrameworks/SAObjects.framework/Versions/A/SAObjects\ndyld[33989]: /System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox\ndyld[33989]: /System/Library/PrivateFrameworks/MediaRemote.framework/Versions/A/MediaRemote\ndyld[33989]: <7F1A25E4-ED0A-3502-AABA-26EDB4A0D2A7> /System/Library/Frameworks/UserNotifications.framework/Versions/A/UserNotifications\ndyld[33989]: <8A5E0FF6-3116-3082-A0AD-20DCD6C5E1B4> /System/Library/PrivateFrameworks/SiriInstrumentation.framework/Versions/A/SiriInstrumentation\ndyld[33989]: <600E036E-9B18-35BE-B40B-E8D2D53AC90D> /System/Library/PrivateFrameworks/SiriAnalytics.framework/Versions/A/SiriAnalytics\ndyld[33989]: <619E6770-766A-3629-9AF8-F32C009375E9> /System/Library/PrivateFrameworks/SiriTTSService.framework/Versions/A/SiriTTSService\ndyld[33989]: <71CAE70A-72AD-3F74-834D-3519B545C08D> /System/Library/PrivateFrameworks/SiriCrossDeviceArbitration.framework/Versions/A/SiriCrossDeviceArbitration\ndyld[33989]: /System/Library/PrivateFrameworks/FeedbackLogger.framework/Versions/A/FeedbackLogger\ndyld[33989]: <55FBCBE4-1032-3017-BA49-D734B82405DF> /System/Library/PrivateFrameworks/FaceTimeNameUtility.framework/Versions/A/FaceTimeNameUtility\ndyld[33989]: /System/Library/PrivateFrameworks/SiriCrossDeviceArbitrationFeedback.framework/Versions/A/SiriCrossDeviceArbitrationFeedback\ndyld[33989]: <368BC882-02B9-38AB-89B4-F62430F2B8EB> /usr/lib/swift/libswiftCoreLocation.dylib\ndyld[33989]: /usr/lib/swift/libswiftAVFoundation.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/UIKitServices.framework/Versions/A/UIKitServices\ndyld[33989]: <54A2CBB8-623D-3629-904A-D0399ED13547> /System/Library/PrivateFrameworks/UnifiedAssetFramework.framework/Versions/A/UnifiedAssetFramework\ndyld[33989]: <8AF1606D-5C93-3B80-BC81-60C5688628E2> /System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore\ndyld[33989]: /System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession\ndyld[33989]: <52BD9E26-B356-3EAA-9AD7-7FF700C61A91> /System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI\ndyld[33989]: <3FF99846-E48C-3C9A-814C-35B45E5F60EC> /usr/lib/libAudioStatistics.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk\ndyld[33989]: /System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio\ndyld[33989]: <75F77FEC-BE14-3C97-93DA-403C3B529D3B> /usr/lib/libAudioToolboxUtility.dylib\ndyld[33989]: <7AE04E20-83FD-3B1B-8846-E9869AD98DB5> /usr/lib/swift/libswiftCoreMIDI.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata\ndyld[33989]: /System/Library/PrivateFrameworks/AudioDSPGraph.framework/Versions/A/AudioDSPGraph\ndyld[33989]: <6108A12D-286B-3CF2-B848-B0E7A0189DCC> /System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy\ndyld[33989]: <655F6374-6CE8-3D0E-994E-4D7C37F78E89> /usr/lib/libSMC.dylib\ndyld[33989]: <912BFF10-FB8F-3D52-9941-ACDCE1CAE36A> /usr/lib/libperfcheck.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/AudioAnalytics.framework/Versions/A/AudioAnalytics\ndyld[33989]: <869F0693-0E82-38C1-8920-C782E71735CA> /System/Library/Frameworks/OSLog.framework/Versions/A/OSLog\ndyld[33989]: <173A632F-20F3-30C1-BC55-EFFE977BBB8E> /usr/lib/libmis.dylib\ndyld[33989]: <52A7AD42-9DE0-393B-A6FB-A7CB6FF8F3A5> /System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience\ndyld[33989]: <1A63E9E1-2D64-3AF4-9CCD-6EF042397F84> /System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore\ndyld[33989]: <04DC06C1-2BFA-3FEE-9429-A33E41721A3E> /usr/lib/libspindump.dylib\ndyld[33989]: <7CC1BC36-42C1-39A3-AA4F-F9C8ABCE0CD5> /System/Library/PrivateFrameworks/AudioAccessoryServices.framework/Versions/A/AudioAccessoryServices\ndyld[33989]: /System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils\ndyld[33989]: /System/Library/PrivateFrameworks/HID.framework/Versions/A/HID\ndyld[33989]: <920D8AA6-CDCD-3E0F-AD66-F0673ACABD3F> /System/Library/PrivateFrameworks/Sharing.framework/Versions/A/Sharing\ndyld[33989]: <3DD8C4CA-23E9-35CF-AD67-549DD72D3344> /System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras\ndyld[33989]: <236517AD-8D16-3E62-8603-EBE7B65ACACA> /System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211\ndyld[33989]: <42F76533-D8DD-3A24-A08A-103C51428797> /System/Library/PrivateFrameworks/IDSFoundation.framework/Versions/A/IDSFoundation\ndyld[33989]: <116D159B-163E-3F91-9591-30FF8B6EB537> /System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Apple80211\ndyld[33989]: /System/Library/Frameworks/CoreWLAN.framework/Versions/A/CoreWLAN\ndyld[33989]: <1ABB6C50-A5DE-3744-8F07-8C3B5617B0B9> /System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth\ndyld[33989]: <82D79BDA-26A0-3A44-AAC8-911411801FDE> /usr/lib/swift/libswiftRegexBuilder.dylib\ndyld[33989]: <41E45E0C-2E88-3605-B213-F7CD760A9FF4> /System/Library/PrivateFrameworks/IMFoundation.framework/Versions/A/IMFoundation\ndyld[33989]: <40F60A3F-90A9-3F07-A99D-005E3158C6F6> /System/Library/PrivateFrameworks/Marco.framework/Versions/A/Marco\ndyld[33989]: <59FFD032-1427-39F6-BC16-A6877582A243> /System/Library/PrivateFrameworks/CommonUtilities.framework/Versions/A/CommonUtilities\ndyld[33989]: <6C3E51F8-D809-3AA1-8695-B75714C2D39A> /System/Library/PrivateFrameworks/Engram.framework/Versions/A/Engram\ndyld[33989]: /System/Library/PrivateFrameworks/XPCDistributed.framework/Versions/A/XPCDistributed\ndyld[33989]: <12066854-2BE4-35DF-BA9F-B38221C980FD> /usr/lib/libtidy.A.dylib\ndyld[33989]: <63F598E2-AF8A-3F29-BE11-3F14DB377A5B> /System/Library/PrivateFrameworks/Bom.framework/Versions/A/Bom\ndyld[33989]: /usr/lib/libParallelCompression.dylib\ndyld[33989]: <9E06CB59-0638-3C9F-B202-264E739433AC> /usr/lib/libIOReport.dylib\ndyld[33989]: <15EEE715-2670-3288-AFA8-504BAD951B4F> /System/Library/PrivateFrameworks/WiFiPeerToPeer.framework/Versions/A/WiFiPeerToPeer\ndyld[33989]: <3854272C-7B14-3A3C-9BB1-F0FBA394708A> /System/Library/PrivateFrameworks/Centauri.framework/Versions/A/Centauri\ndyld[33989]: <946B1484-B180-3451-A452-B54BF5A6D392> /System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon\ndyld[33989]: <2B49C295-4EA2-3DE3-90B4-DC03A96F2657> /usr/lib/libmrc.dylib\ndyld[33989]: <6661265C-7B78-3158-9011-4BFDFFEF7807> /System/Library/PrivateFrameworks/IPConfiguration.framework/Versions/A/IPConfiguration\ndyld[33989]: /System/Library/PrivateFrameworks/Netrb.framework/Versions/A/Netrb\ndyld[33989]: /System/Library/PrivateFrameworks/FrontBoardServices.framework/Versions/A/FrontBoardServices\ndyld[33989]: /System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData\ndyld[33989]: <757FEDFF-841C-3D62-B703-CDE79E929363> /System/Library/PrivateFrameworks/GraphicsServices.framework/Versions/A/GraphicsServices\ndyld[33989]: <093EF25B-5305-3611-B068-E65071858F52> /System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit\ndyld[33989]: /System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory\ndyld[33989]: <3B7FD4C1-D1D4-3DA9-B2F8-3D4094679D76> /System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory\ndyld[33989]: <016C5057-625C-30B1-AD32-7BC9D082F05B> /System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio\ndyld[33989]: /System/Library/Frameworks/AVRouting.framework/Versions/A/AVRouting\ndyld[33989]: <5240B3A0-D035-345E-A636-BC3A92C847C4> /usr/lib/libAccessibility.dylib\ndyld[33989]: <1FB2BCFD-D9FC-385A-A0EA-E2B5052E27F5> /System/Library/PrivateFrameworks/MediaServices.framework/Versions/A/MediaServices\ndyld[33989]: /System/Library/PrivateFrameworks/IDS.framework/Versions/A/IDS\ndyld[33989]: /System/Library/PrivateFrameworks/IsolatedCoreAudioClient.framework/Versions/A/IsolatedCoreAudioClient\ndyld[33989]: <7CC0621B-3B88-3533-A3FB-52E6214486EE> /System/Library/PrivateFrameworks/CoreAudioOrchestration.framework/Versions/A/CoreAudioOrchestration\ndyld[33989]: /System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox\ndyld[33989]: /System/Library/PrivateFrameworks/CoreAVCHD.framework/Versions/A/CoreAVCHD\ndyld[33989]: <74D313A5-4D99-35D1-A4C9-B76AB6457EF0> /System/Library/Frameworks/MediaAccessibility.framework/Versions/A/MediaAccessibility\ndyld[33989]: <87F549F4-73CC-302B-ABDB-D3CCFADABFA9> /System/Library/PrivateFrameworks/Mangrove.framework/Versions/A/Mangrove\ndyld[33989]: <214294AE-C7B7-3C9A-A4F8-201C989F9779> /System/Library/PrivateFrameworks/CMPhoto.framework/Versions/A/CMPhoto\ndyld[33989]: <5F090F48-E481-3737-8E75-362E5D274879> /System/Library/Frameworks/CoreTelephony.framework/Versions/A/CoreTelephony\ndyld[33989]: <9ACFCA55-82CB-33DB-AD00-443576099FDB> /System/Library/PrivateFrameworks/CoreAUC.framework/Versions/A/CoreAUC\ndyld[33989]: <71A0C0AD-67F3-36F9-BF73-6DD5D7424AF7> /System/Library/PrivateFrameworks/AppleJPEGXL.framework/Versions/A/AppleJPEGXL\ndyld[33989]: <825E8416-E246-338E-A5CF-AA81A1B01DD9> /System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport\ndyld[33989]: <7CF84496-675C-3241-B0EF-E83C95F188FA> /System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA\ndyld[33989]: <63A6BBA0-CD50-30F8-9CD2-81B59264EA13> /usr/lib/libTelephonyUtilDynamic.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler\ndyld[33989]: /System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment\ndyld[33989]: /System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay\ndyld[33989]: /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit\ndyld[33989]: /System/Library/PrivateFrameworks/CryptoKitCBridging.framework/Versions/A/CryptoKitCBridging\ndyld[33989]: <714063A8-D81E-3B22-9B36-88948A979E7F> /System/Library/Frameworks/CryptoTokenKit.framework/Versions/A/CryptoTokenKit\ndyld[33989]: <86858734-8B4D-38E6-AAA7-B7A046A7CB2A> /System/Library/Frameworks/LocalAuthentication.framework/Versions/A/LocalAuthentication\ndyld[33989]: /System/Library/PrivateFrameworks/LocalAuthenticationCore.framework/Versions/A/LocalAuthenticationCore\ndyld[33989]: /System/Library/PrivateFrameworks/LocalAuthenticationCredentialServices.framework/Versions/A/LocalAuthenticationCredentialServices\ndyld[33989]: /System/Library/Frameworks/LocalAuthentication.framework/Support/SharedUtils.framework/Versions/A/SharedUtils\ndyld[33989]: <80C3E2D4-B6B8-3C62-B257-27DEEBAD4935> /usr/lib/libcsfde.dylib\ndyld[33989]: <650E155C-1FE3-36ED-8D84-157D380F7F95> /usr/lib/libCoreStorage.dylib\ndyld[33989]: <46DD93AF-BACD-309B-AD51-9CC47C78CA2C> /System/Library/PrivateFrameworks/MediaKit.framework/Versions/A/MediaKit\ndyld[33989]: /System/Library/Frameworks/DiscRecording.framework/Versions/A/DiscRecording\ndyld[33989]: <1F8700BE-BD91-3B94-AC32-A5F10CEFEE35> /System/Library/PrivateFrameworks/ProtectedCloudStorage.framework/Versions/A/ProtectedCloudStorage\ndyld[33989]: /System/Library/PrivateFrameworks/EFILogin.framework/Versions/A/EFILogin\ndyld[33989]: /System/Library/PrivateFrameworks/PersistentConnection.framework/Versions/A/PersistentConnection\ndyld[33989]: <6B0D099C-AC56-35DB-90F1-88A09E587FCB> /System/Library/PrivateFrameworks/SonicFoundation.framework/Versions/A/SonicFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/AsyncAlgorithmsInternal.framework/Versions/A/AsyncAlgorithmsInternal\ndyld[33989]: <2CA62C12-37B5-345A-BF79-5D05F43F6BFB> /System/Library/PrivateFrameworks/FTAWD.framework/Versions/A/FTAWD\ndyld[33989]: <0A1C4D11-C108-35E9-A921-86ED86CF7446> /System/Library/PrivateFrameworks/Dendrite.framework/Versions/A/Dendrite\ndyld[33989]: /usr/lib/libtailspin.dylib\ndyld[33989]: <5FEA8C08-1577-3296-BC9C-7F3203E8EBFB> /System/Library/PrivateFrameworks/Osprey.framework/Versions/A/Osprey\ndyld[33989]: /System/Library/PrivateFrameworks/SiriTTS.framework/Versions/A/SiriTTS\ndyld[33989]: <0C005C4D-CA12-389C-9CCE-C4ED05B187E8> /System/Library/Frameworks/NaturalLanguage.framework/Versions/A/NaturalLanguage\ndyld[33989]: <0E502870-00F4-35D4-AF82-E7059244798E> /System/Library/PrivateFrameworks/GenerativeModels.framework/Versions/A/GenerativeModels\ndyld[33989]: <68474F39-798D-325B-B52F-3DE214F279AE> /System/Library/PrivateFrameworks/SiriPowerInstrumentation.framework/Versions/A/SiriPowerInstrumentation\ndyld[33989]: <5E36265A-7670-3D39-A2B8-71DA0AA131CF> /usr/lib/swift/libswiftNaturalLanguage.dylib\ndyld[33989]: <6794652C-86F0-37EB-838D-483177685E26> /System/Library/PrivateFrameworks/TailspinSymbolication.framework/Versions/A/TailspinSymbolication\ndyld[33989]: <089C1A34-2F4E-3649-94AA-B28A7ECB008B> /System/Library/PrivateFrameworks/Darwinup.framework/Versions/A/Darwinup\ndyld[33989]: /System/Library/PrivateFrameworks/SignpostSupport.framework/Versions/A/SignpostSupport\ndyld[33989]: <8C10B437-C282-37F5-834F-E7179C700373> /System/Library/PrivateFrameworks/FeatureFlagsSupport.framework/Versions/A/FeatureFlagsSupport\ndyld[33989]: /System/Library/PrivateFrameworks/ktrace.framework/Versions/A/ktrace\ndyld[33989]: /System/Library/PrivateFrameworks/SampleAnalysis.framework/Versions/A/SampleAnalysis\ndyld[33989]: <400B0E96-4869-37BE-9832-1A14C386148B> /System/Library/PrivateFrameworks/kperfdata.framework/Versions/A/kperfdata\ndyld[33989]: <7E3E0CF7-905A-3244-A0C9-0ADCC2E16415> /usr/lib/libdscsym.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity\ndyld[33989]: <6FBA9099-E428-3571-B940-0D210B6D0861> /System/Library/PrivateFrameworks/BulkSymbolication.framework/Versions/A/BulkSymbolication\ndyld[33989]: <90E600A3-0A27-348A-AA57-D1DF4FB305E8> /usr/lib/libTLE.dylib\ndyld[33989]: <2D1B971F-6A7F-32D0-8B0F-F8FA3A13E8F1> /System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper\ndyld[33989]: <8F2949A6-43A0-30A2-B5D2-945949C5AA01> /System/Library/PrivateFrameworks/Espresso.framework/Versions/A/Espresso\ndyld[33989]: /System/Library/Frameworks/CoreML.framework/Versions/A/CoreML\ndyld[33989]: /usr/lib/libedit.3.dylib\ndyld[33989]: <465A74BC-F20D-3C05-9441-7E08EAA49FAF> /System/Library/PrivateFrameworks/ANECompiler.framework/Versions/A/ANECompiler\ndyld[33989]: /System/Library/PrivateFrameworks/AppleNeuralEngine.framework/Versions/A/AppleNeuralEngine\ndyld[33989]: <97C5C585-F5EE-323A-B949-69EAE9080871> /System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL\ndyld[33989]: <7401E849-7B2E-39A9-99D3-5CB0A6BBDFFE> /System/Library/Frameworks/MetalPerformanceShadersGraph.framework/Versions/A/MetalPerformanceShadersGraph\ndyld[33989]: /System/Library/PrivateFrameworks/MLCompilerServices.framework/Versions/A/MLCompilerServices\ndyld[33989]: /System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices\ndyld[33989]: <9EB04E94-EE2D-38A5-A214-00AF73DBE4E9> /usr/lib/libncurses.5.4.dylib\ndyld[33989]: /usr/lib/libsandbox.1.dylib\ndyld[33989]: <2F2EF0D7-2FE4-3A5A-8E4C-E1571C8D0C10> /usr/lib/libMatch.1.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/ODIE.framework/Versions/A/ODIE\ndyld[33989]: /System/Library/PrivateFrameworks/MLModelAsset.framework/Versions/A/MLModelAsset\ndyld[33989]: <22B4CD07-5C72-3CA4-9CD1-2C87686CDDE5> /System/Library/PrivateFrameworks/MLCompilerRuntime.framework/Versions/A/MLCompilerRuntime\ndyld[33989]: /System/Library/Frameworks/MLCompute.framework/Versions/A/MLCompute\ndyld[33989]: <6028DD46-8E5A-33F0-93B2-41FA480366CC> /System/Library/PrivateFrameworks/MLAssetIO.framework/Versions/A/MLAssetIO\ndyld[33989]: /usr/lib/swift/libswiftMLCompute.dylib\ndyld[33989]: <067E2603-4FEA-3CA5-8926-45F60681EDDB> /System/Library/PrivateFrameworks/AVFCore.framework/Versions/A/AVFCore\ndyld[33989]: /System/Library/PrivateFrameworks/AVFCapture.framework/Versions/A/AVFCapture\ndyld[33989]: <3B782AC2-00C4-3534-91D2-5C7242B32440> /System/Library/PrivateFrameworks/CMImaging.framework/Versions/A/CMImaging\ndyld[33989]: <1772C40D-6EF4-3F81-BA00-6EE8B05039A6> /System/Library/PrivateFrameworks/Quagga.framework/Versions/A/Quagga\ndyld[33989]: <57A10B70-C3C9-34C6-8D22-F5118B63E2F0> /System/Library/PrivateFrameworks/CMCapture.framework/Versions/A/CMCapture\ndyld[33989]: <1035C1AB-5058-3AFA-8D77-514901516251> /System/Library/Frameworks/CoreMediaIO.framework/Versions/A/CoreMediaIO\ndyld[33989]: /System/Library/PrivateFrameworks/CMCaptureDevice.framework/Versions/A/CMCaptureDevice\ndyld[33989]: <1F873909-B3B8-3D55-9673-9AFA86BB085B> /System/Library/PrivateFrameworks/CoreBrightness.framework/Versions/A/CoreBrightness\ndyld[33989]: /System/Library/PrivateFrameworks/CinematicFraming.framework/Versions/A/CinematicFraming\ndyld[33989]: <882BC08E-B1E1-3E52-AE8A-AC22A1BF2BE8> /System/Library/PrivateFrameworks/ModelManagerServices.framework/Versions/A/ModelManagerServices\ndyld[33989]: <3E83115F-D04B-3C8D-8646-35204AA2DB84> /System/Library/PrivateFrameworks/CPMS.framework/Versions/A/CPMS\ndyld[33989]: /System/Library/PrivateFrameworks/SystemStatus.framework/Versions/A/SystemStatus\ndyld[33989]: <2E109991-45C6-3783-8A36-B6A8070AAD67> /System/Library/Frameworks/CoreMotion.framework/Versions/A/CoreMotion\ndyld[33989]: /System/Library/PrivateFrameworks/TimeSync.framework/Versions/A/TimeSync\ndyld[33989]: <9B3D4CA3-7BCF-36C9-AA99-27BDFE7854CD> /System/Library/PrivateFrameworks/DistributedSensing.framework/Versions/A/DistributedSensing\ndyld[33989]: /System/Library/PrivateFrameworks/MobileBluetooth.framework/Versions/A/MobileBluetooth\ndyld[33989]: <0BAB3589-8D81-3C60-9F05-9D207E89F4B6> /System/Library/PrivateFrameworks/IOKitten.framework/Versions/A/IOKitten\ndyld[33989]: <8A2C8C17-E138-3B34-8643-ED4FB1C9049E> /System/Library/PrivateFrameworks/LocationLogEncryption.framework/Versions/A/LocationLogEncryption\ndyld[33989]: <05EC9C98-7211-39C9-B376-796F37327801> /System/Library/PrivateFrameworks/AppleIntelligenceReporting.framework/Versions/A/AppleIntelligenceReporting\ndyld[33989]: <47AAECAD-C28C-352E-BB86-7F292E0BFBC6> /System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji\ndyld[33989]: <327536E3-A27C-38C2-A67F-D6488D04CCEE> /System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling\ndyld[33989]: <95BA357E-906A-3183-A402-A41D486B5AB3> /System/Library/PrivateFrameworks/Montreal.framework/Versions/A/Montreal\ndyld[33989]: /usr/lib/libcmph.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/GenerativeModelsFoundation.framework/Versions/A/GenerativeModelsFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/TokenGeneration.framework/Versions/A/TokenGeneration\ndyld[33989]: <418985BB-52A3-34D4-8379-40DC4C63AA32> /System/Library/PrivateFrameworks/GenerativeFunctions.framework/Versions/A/GenerativeFunctions\ndyld[33989]: <63FD423F-836C-3034-BA48-100AE09A9140> /System/Library/PrivateFrameworks/GenerativeFunctionsFoundation.framework/Versions/A/GenerativeFunctionsFoundation\ndyld[33989]: /System/Library/PrivateFrameworks/ModelCatalog.framework/Versions/A/ModelCatalog\ndyld[33989]: <67C3B698-8279-30F1-9167-4730E6F41F5A> /System/Library/PrivateFrameworks/SensitiveContentAnalysisML.framework/Versions/A/SensitiveContentAnalysisML\ndyld[33989]: /System/Library/PrivateFrameworks/GenerativeFunctionsInstrumentation.framework/Versions/A/GenerativeFunctionsInstrumentation\ndyld[33989]: /System/Library/PrivateFrameworks/PromptKit.framework/Versions/A/PromptKit\ndyld[33989]: <12245228-2B9A-3B24-8C5E-10111D68BE65> /System/Library/PrivateFrameworks/ProactiveDaemonSupport.framework/Versions/A/ProactiveDaemonSupport\ndyld[33989]: <3166486F-3F65-31DB-8018-779FFA32DC71> /System/Library/PrivateFrameworks/TokenGenerationCore.framework/Versions/A/TokenGenerationCore\ndyld[33989]: <53B3126E-7B01-30DD-961A-510E9CFC3CF1> /System/Library/PrivateFrameworks/Trial.framework/Versions/A/Trial\ndyld[33989]: /System/Library/PrivateFrameworks/TrialProto.framework/Versions/A/TrialProto\ndyld[33989]: /System/Library/PrivateFrameworks/AppleFlatBuffers.framework/Versions/A/AppleFlatBuffers\ndyld[33989]: <642E3357-AB6D-3039-A818-EDB5D6A189C2> /System/Library/PrivateFrameworks/SentencePieceInternal.framework/Versions/A/SentencePieceInternal\ndyld[33989]: <10F83439-3A9F-316B-992E-451A72876715> /System/Library/Frameworks/Vision.framework/Versions/A/Vision\ndyld[33989]: /System/Library/PrivateFrameworks/CoreSceneUnderstanding.framework/Versions/A/CoreSceneUnderstanding\ndyld[33989]: <4E70B4ED-C8E0-3636-80E4-0939FE56BB63> /System/Library/PrivateFrameworks/VisionCore.framework/Versions/A/VisionCore\ndyld[33989]: /System/Library/PrivateFrameworks/DataDetectorsCore.framework/Versions/A/DataDetectorsCore\ndyld[33989]: /System/Library/Frameworks/Vision.framework/libfaceCore.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/Futhark.framework/Versions/A/Futhark\ndyld[33989]: /System/Library/PrivateFrameworks/InertiaCam.framework/Versions/A/InertiaCam\ndyld[33989]: /System/Library/PrivateFrameworks/TextRecognition.framework/Versions/A/TextRecognition\ndyld[33989]: <73F6F860-69AF-3162-86E0-A683642287D6> /System/Library/Frameworks/DataDetection.framework/Versions/A/DataDetection\ndyld[33989]: <1D5DF9CA-41FC-3B7B-B19F-2C435F3A66F2> /System/Library/PrivateFrameworks/TextInput.framework/Versions/A/TextInput\ndyld[33989]: /System/Library/PrivateFrameworks/CVNLP.framework/Versions/A/CVNLP\ndyld[33989]: <58AC6CAB-5B91-367F-932F-BD0939BBD125> /System/Library/PrivateFrameworks/IntentsFoundation.framework/Versions/A/IntentsFoundation\ndyld[33989]: <27479D70-8BF6-3D3C-B528-1BB9B1B98391> /System/Library/PrivateFrameworks/ApplePushService.framework/Versions/A/ApplePushService\ndyld[33989]: <676D50CC-8455-3267-B8E8-CA31B8EF8F91> /System/Library/Frameworks/CloudKit.framework/Versions/A/CloudKit\ndyld[33989]: /System/Library/PrivateFrameworks/CoreDuetDaemonProtocol.framework/Versions/A/CoreDuetDaemonProtocol\ndyld[33989]: <962EA390-008E-3DDC-B2A5-7B2DAF6E8786> /System/Library/PrivateFrameworks/DeviceIdentity.framework/Versions/A/DeviceIdentity\ndyld[33989]: /System/Library/Frameworks/SharedWithYouCore.framework/Versions/A/SharedWithYouCore\ndyld[33989]: <121798D0-5254-3547-8F96-0F2AF8D84250> /System/Library/PrivateFrameworks/CloudTelemetry.framework/Versions/A/CloudTelemetry\ndyld[33989]: <768DFB9E-7FB3-3998-A3AF-BEF0C6C740A7> /System/Library/PrivateFrameworks/AppleAccount.framework/Versions/A/AppleAccount\ndyld[33989]: /System/Library/PrivateFrameworks/CacheDelete.framework/Versions/A/CacheDelete\ndyld[33989]: /System/Library/PrivateFrameworks/C2.framework/Versions/A/C2\ndyld[33989]: <23871A43-55FD-3D0C-B29F-B143A64D1D8D> /System/Library/PrivateFrameworks/CloudCoreInternal.framework/Versions/A/CloudCoreInternal\ndyld[33989]: /System/Library/PrivateFrameworks/CloudAsset.framework/Versions/A/CloudAsset\ndyld[33989]: <36607924-B1B2-39ED-B6D1-29683EFB67A0> /System/Library/Frameworks/PushKit.framework/Versions/A/PushKit\ndyld[33989]: <26865685-385E-3120-9886-2082EEC20B20> /System/Library/Frameworks/CoreTransferable.framework/Versions/A/CoreTransferable\ndyld[33989]: <5EA68C5E-69B0-3011-9D66-AEF49D82B29D> /System/Library/Frameworks/FileProvider.framework/Versions/A/FileProvider\ndyld[33989]: /System/Library/PrivateFrameworks/GenerationalStorage.framework/Versions/A/GenerationalStorage\ndyld[33989]: /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/DesktopServicesPriv\ndyld[33989]: <024DBF34-DF66-3164-825E-F77F85462E66> /System/Library/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth\ndyld[33989]: <87907862-52FF-3F24-AC29-7C1678BCD277> /System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport\ndyld[33989]: /System/Library/PrivateFrameworks/CloudTelemetryTools.framework/Versions/A/CloudTelemetryTools\ndyld[33989]: /System/Library/PrivateFrameworks/CloudTelemetryShared.dylib\ndyld[33989]: <7BEBC9F1-212D-37F4-B601-A7AAD12F7225> /System/Library/PrivateFrameworks/RTCReporting.framework/Versions/A/RTCReporting\ndyld[33989]: <7A222E30-8DD4-3B1D-B820-4EA33B797525> /System/Library/PrivateFrameworks/AAAFoundationSwift.framework/Versions/A/AAAFoundationSwift\ndyld[33989]: <9D724BE7-0B01-39F3-82BE-BCEDC6EBAC8A> /System/Library/PrivateFrameworks/AppleIDSSOAuthentication.framework/Versions/A/AppleIDSSOAuthentication\ndyld[33989]: <659AFBBD-E22E-3474-BCFE-298DA57B1464> /System/Library/PrivateFrameworks/UIFoundation.framework/Versions/A/UIFoundation\ndyld[33989]: <0368EA7D-01B2-3AA9-A6D6-A2A0850AC800> /System/Library/PrivateFrameworks/HIDDisplay.framework/Versions/A/HIDDisplay\ndyld[33989]: <6A5A8E21-A9E6-32A2-9BDB-8013F002AEF8> /usr/lib/libcups.2.dylib\ndyld[33989]: /System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos\ndyld[33989]: <4AB71911-9300-30D4-88CF-D20EFD75ACE6> /usr/lib/libresolv.9.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal\ndyld[33989]: <0CB2E7E3-E96F-343B-A4E7-545E74AF0255> /System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib\ndyld[33989]: <097F7235-CA53-3644-BB95-F6F912B4F2C7> /System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth\ndyld[33989]: /System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities\ndyld[33989]: /System/Library/PrivateFrameworks/AttributeGraph.framework/Versions/A/AttributeGraph\ndyld[33989]: /usr/lib/libAXSafeCategoryBundle.dylib\ndyld[33989]: /System/Library/Frameworks/TabularData.framework/Versions/A/TabularData\ndyld[33989]: <841D5662-2CB9-3A27-ADA7-E33AC5E45199> /System/Library/PrivateFrameworks/ArgumentParserInternal.framework/Versions/A/ArgumentParserInternal\ndyld[33989]: <4C851329-A9F4-3E9E-9E48-07FF4120DCF9> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib\ndyld[33989]: <5015CD96-C046-364D-AAE3-1F439044468B> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib\ndyld[33989]: <407BCF3E-A91F-3A7F-8B8C-DBB8E807990F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib\ndyld[33989]: <669ABE12-838F-3F14-8456-D60DE5DF8EB8> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib\ndyld[33989]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib\ndyld[33989]: <54A103BA-7D04-32DB-B204-179E2E0290CA> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib\ndyld[33989]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib\ndyld[33989]: <1ACDAA8A-EB43-37C7-B661-39B1C0E05290> /System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary\ndyld[33989]: <12479A32-B72F-3A09-BB03-BA56C37853B5> /System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore\ndyld[33989]: /usr/lib/libapp_launch_measurement.dylib\ndyld[33989]: <4F3BEA3B-A363-3D04-B903-9B613C993CA1> /System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices\ndyld[33989]: <6C426EA5-7F1E-333E-BB5D-74465EFED12B> /usr/lib/libxslt.1.dylib\ndyld[33989]: <627D64D5-2D3C-3EC6-B4AB-FEF4DEE40871> /System/Library/PrivateFrameworks/FindMyDevice.framework/Versions/A/FindMyDevice\ndyld[33989]: <930F9F83-A947-3788-9FBA-49872FC3AF8D> /System/Library/PrivateFrameworks/FMCoreLite.framework/Versions/A/FMCoreLite\ndyld[33989]: <5F356BA6-47B5-382B-B54A-1550BB138A62> /System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement\ndyld[33989]: <38DAF669-429F-384F-87D6-8550842EEB5E> /System/Library/PrivateFrameworks/CryptoKitPrivate.framework/Versions/A/CryptoKitPrivate\ndyld[33989]: <5B73C216-2ACE-3F8C-A2B3-7D35D5D0395A> /System/Library/PrivateFrameworks/CaptiveNetwork.framework/Versions/A/CaptiveNetwork\ndyld[33989]: /System/Library/PrivateFrameworks/EAP8021X.framework/Versions/A/EAP8021X\ndyld[33989]: <36F215D1-A2C0-32CA-ADD8-6D85AB48A772> /System/Library/Frameworks/QuickLookThumbnailing.framework/Versions/A/QuickLookThumbnailing\ndyld[33989]: /System/Library/PrivateFrameworks/DiskImages.framework/Versions/A/DiskImages\ndyld[33989]: <49121861-2603-3B0A-B664-BAD9E729BE5D> /System/Library/Frameworks/NetFS.framework/Versions/A/NetFS\ndyld[33989]: <2E99AD96-DC1C-3643-9988-273AB6844EFC> /usr/lib/libcurl.4.dylib\ndyld[33989]: <46D13DA8-E7BD-37DC-91DD-D5E6CE00C2B8> /usr/lib/libcrypto.46.dylib\ndyld[33989]: <07D5F4C6-1A13-344C-882B-0B0A08048DE5> /usr/lib/libssl.48.dylib\ndyld[33989]: <8CABDD64-E6C6-3B77-B839-2E2B875CE0FE> /System/Library/Frameworks/LDAP.framework/Versions/A/LDAP\ndyld[33989]: <96C0BAAA-7FE6-3277-AFBC-31926F5935EE> /System/Library/PrivateFrameworks/TrustEvaluationAgent.framework/Versions/A/TrustEvaluationAgent\ndyld[33989]: <7CF2A32E-72DD-34F7-B179-A17ED3D7DD75> /usr/lib/libsasl2.2.dylib\ndyld[33989]: move loaded to delayed: libcmark-gfm.dylib\ndyld[33989]: move loaded to delayed: BackgroundSystemTasks\ndyld[33989]: move loaded to delayed: CoreWiFi\ndyld[33989]: move loaded to delayed: Rapport\ndyld[33989]: move loaded to delayed: SymptomAnalytics\ndyld[33989]: move loaded to delayed: libcupolicy.dylib\ndyld[33989]: move loaded to delayed: libnetworkextension.dylib\ndyld[33989]: move loaded to delayed: NetworkExtension\ndyld[33989]: move loaded to delayed: libnwswifttls.dylib\ndyld[33989]: move loaded to delayed: libpcap.A.dylib\ndyld[33989]: move loaded to delayed: XPCSupport\ndyld[33989]: move loaded to delayed: CloudServices\ndyld[33989]: move loaded to delayed: OctagonTrust\ndyld[33989]: move loaded to delayed: AppleIDAuthSupport\ndyld[33989]: move loaded to delayed: KeychainCircle\ndyld[33989]: move loaded to delayed: AuthKit\ndyld[33989]: move loaded to delayed: AAAFoundation\ndyld[33989]: move loaded to delayed: MultiverseSupport\ndyld[33989]: move loaded to delayed: DiskManagement\ndyld[33989]: move loaded to delayed: Accounts\ndyld[33989]: move loaded to delayed: URLFormatting\ndyld[33989]: move loaded to delayed: AOSKit\ndyld[33989]: move loaded to delayed: AppSSOCore\ndyld[33989]: move loaded to delayed: AVFoundation\ndyld[33989]: move loaded to delayed: DuetActivityScheduler\ndyld[33989]: move loaded to delayed: FTServices\ndyld[33989]: move loaded to delayed: InternationalSupport\ndyld[33989]: move loaded to delayed: libMemoryResourceException.dylib\ndyld[33989]: move loaded to delayed: NetworkScore\ndyld[33989]: move loaded to delayed: NetworkServiceProxy\ndyld[33989]: move loaded to delayed: StreamingExtractor\ndyld[33989]: move loaded to delayed: SymptomReporter\ndyld[33989]: move loaded to delayed: libCGInterfaces.dylib\ndyld[33989]: move loaded to delayed: AccelerateGPU\ndyld[33989]: move loaded to delayed: ApplicationServices\ndyld[33989]: move loaded to delayed: ATS\ndyld[33989]: move loaded to delayed: HIServices\ndyld[33989]: move loaded to delayed: PrintCore\ndyld[33989]: move loaded to delayed: QD\ndyld[33989]: move loaded to delayed: ColorSyncLegacy\ndyld[33989]: move loaded to delayed: SpeechSynthesis\ndyld[33989]: move loaded to delayed: CoreDuetContext\ndyld[33989]: move loaded to delayed: CoreDuet\ndyld[33989]: move loaded to delayed: CoreLocation\ndyld[33989]: move loaded to delayed: Intents\ndyld[33989]: move loaded to delayed: _LocationEssentials\ndyld[33989]: move loaded to delayed: GeoServices\ndyld[33989]: move loaded to delayed: LocationSupport\ndyld[33989]: move loaded to delayed: CoreBluetooth\ndyld[33989]: move loaded to delayed: GeoServicesCore\ndyld[33989]: move loaded to delayed: PhoneNumbers\ndyld[33989]: move loaded to delayed: IconServices\ndyld[33989]: move loaded to delayed: IconFoundation\ndyld[33989]: move loaded to delayed: AssistantServices\ndyld[33989]: move loaded to delayed: IconRendering\ndyld[33989]: move loaded to delayed: CoreUI\ndyld[33989]: move loaded to delayed: SFSymbols\ndyld[33989]: move loaded to delayed: DeveloperToolsSupport\ndyld[33989]: move loaded to delayed: RenderBox\ndyld[33989]: move loaded to delayed: CoreSVG\ndyld[33989]: move loaded to delayed: TextureIO\ndyld[33989]: move loaded to delayed: libswiftCoreImage.dylib\ndyld[33989]: move loaded to delayed: ATSUI\ndyld[33989]: move loaded to delayed: SAObjects\ndyld[33989]: move loaded to delayed: MediaRemote\ndyld[33989]: move loaded to delayed: UserNotifications\ndyld[33989]: move loaded to delayed: SiriInstrumentation\ndyld[33989]: move loaded to delayed: SiriAnalytics\ndyld[33989]: move loaded to delayed: SiriTTSService\ndyld[33989]: move loaded to delayed: SiriCrossDeviceArbitration\ndyld[33989]: move loaded to delayed: FeedbackLogger\ndyld[33989]: move loaded to delayed: FaceTimeNameUtility\ndyld[33989]: move loaded to delayed: SiriCrossDeviceArbitrationFeedback\ndyld[33989]: move loaded to delayed: libswiftCoreLocation.dylib\ndyld[33989]: move loaded to delayed: libswiftAVFoundation.dylib\ndyld[33989]: move loaded to delayed: UIKitServices\ndyld[33989]: move loaded to delayed: UnifiedAssetFramework\ndyld[33989]: move loaded to delayed: AudioDSPGraph\ndyld[33989]: move loaded to delayed: AudioAccessoryServices\ndyld[33989]: move loaded to delayed: CoreUtils\ndyld[33989]: move loaded to delayed: Sharing\ndyld[33989]: move loaded to delayed: CoreUtilsExtras\ndyld[33989]: move loaded to delayed: IO80211\ndyld[33989]: move loaded to delayed: IDSFoundation\ndyld[33989]: move loaded to delayed: Apple80211\ndyld[33989]: move loaded to delayed: CoreWLAN\ndyld[33989]: move loaded to delayed: IOBluetooth\ndyld[33989]: move loaded to delayed: libswiftRegexBuilder.dylib\ndyld[33989]: move loaded to delayed: IMFoundation\ndyld[33989]: move loaded to delayed: Marco\ndyld[33989]: move loaded to delayed: CommonUtilities\ndyld[33989]: move loaded to delayed: Engram\ndyld[33989]: move loaded to delayed: XPCDistributed\ndyld[33989]: move loaded to delayed: libtidy.A.dylib\ndyld[33989]: move loaded to delayed: Bom\ndyld[33989]: move loaded to delayed: libParallelCompression.dylib\ndyld[33989]: move loaded to delayed: libIOReport.dylib\ndyld[33989]: move loaded to delayed: WiFiPeerToPeer\ndyld[33989]: move loaded to delayed: Centauri\ndyld[33989]: move loaded to delayed: libmrc.dylib\ndyld[33989]: move loaded to delayed: IPConfiguration\ndyld[33989]: move loaded to delayed: Netrb\ndyld[33989]: move loaded to delayed: FrontBoardServices\ndyld[33989]: move loaded to delayed: AudioUnit\ndyld[33989]: move loaded to delayed: AVFAudio\ndyld[33989]: move loaded to delayed: AVRouting\ndyld[33989]: move loaded to delayed: libAccessibility.dylib\ndyld[33989]: move loaded to delayed: MediaServices\ndyld[33989]: move loaded to delayed: IDS\ndyld[33989]: move loaded to delayed: IsolatedCoreAudioClient\ndyld[33989]: move loaded to delayed: CoreAudioOrchestration\ndyld[33989]: move loaded to delayed: MediaToolbox\ndyld[33989]: move loaded to delayed: CoreAVCHD\ndyld[33989]: move loaded to delayed: MediaAccessibility\ndyld[33989]: move loaded to delayed: Mangrove\ndyld[33989]: move loaded to delayed: CMPhoto\ndyld[33989]: move loaded to delayed: CoreTelephony\ndyld[33989]: move loaded to delayed: CoreAUC\ndyld[33989]: move loaded to delayed: AppleJPEGXL\ndyld[33989]: move loaded to delayed: libTelephonyUtilDynamic.dylib\ndyld[33989]: move loaded to delayed: CryptoKit\ndyld[33989]: move loaded to delayed: CryptoKitCBridging\ndyld[33989]: move loaded to delayed: CryptoTokenKit\ndyld[33989]: move loaded to delayed: LocalAuthentication\ndyld[33989]: move loaded to delayed: LocalAuthenticationCore\ndyld[33989]: move loaded to delayed: LocalAuthenticationCredentialServices\ndyld[33989]: move loaded to delayed: SharedUtils\ndyld[33989]: move loaded to delayed: libcsfde.dylib\ndyld[33989]: move loaded to delayed: libCoreStorage.dylib\ndyld[33989]: move loaded to delayed: ProtectedCloudStorage\ndyld[33989]: move loaded to delayed: EFILogin\ndyld[33989]: move loaded to delayed: PersistentConnection\ndyld[33989]: move loaded to delayed: SonicFoundation\ndyld[33989]: move loaded to delayed: AsyncAlgorithmsInternal\ndyld[33989]: move loaded to delayed: FTAWD\ndyld[33989]: move loaded to delayed: Dendrite\ndyld[33989]: move loaded to delayed: libtailspin.dylib\ndyld[33989]: move loaded to delayed: Osprey\ndyld[33989]: move loaded to delayed: SiriTTS\ndyld[33989]: move loaded to delayed: NaturalLanguage\ndyld[33989]: move loaded to delayed: GenerativeModels\ndyld[33989]: move loaded to delayed: SiriPowerInstrumentation\ndyld[33989]: move loaded to delayed: libswiftNaturalLanguage.dylib\ndyld[33989]: move loaded to delayed: TailspinSymbolication\ndyld[33989]: move loaded to delayed: Darwinup\ndyld[33989]: move loaded to delayed: SignpostSupport\ndyld[33989]: move loaded to delayed: FeatureFlagsSupport\ndyld[33989]: move loaded to delayed: ktrace\ndyld[33989]: move loaded to delayed: SampleAnalysis\ndyld[33989]: move loaded to delayed: kperfdata\ndyld[33989]: move loaded to delayed: libdscsym.dylib\ndyld[33989]: move loaded to delayed: BulkSymbolication\ndyld[33989]: move loaded to delayed: Espresso\ndyld[33989]: move loaded to delayed: CoreML\ndyld[33989]: move loaded to delayed: libedit.3.dylib\ndyld[33989]: move loaded to delayed: ANECompiler\ndyld[33989]: move loaded to delayed: AppleNeuralEngine\ndyld[33989]: move loaded to delayed: MetalPerformanceShadersGraph\ndyld[33989]: move loaded to delayed: MLCompilerServices\ndyld[33989]: move loaded to delayed: ANEServices\ndyld[33989]: move loaded to delayed: libncurses.5.4.dylib\ndyld[33989]: move loaded to delayed: libsandbox.1.dylib\ndyld[33989]: move loaded to delayed: libMatch.1.dylib\ndyld[33989]: move loaded to delayed: ODIE\ndyld[33989]: move loaded to delayed: MLModelAsset\ndyld[33989]: move loaded to delayed: MLCompilerRuntime\ndyld[33989]: move loaded to delayed: MLCompute\ndyld[33989]: move loaded to delayed: MLAssetIO\ndyld[33989]: move loaded to delayed: libswiftMLCompute.dylib\ndyld[33989]: move loaded to delayed: AVFCore\ndyld[33989]: move loaded to delayed: AVFCapture\ndyld[33989]: move loaded to delayed: CMImaging\ndyld[33989]: move loaded to delayed: Quagga\ndyld[33989]: move loaded to delayed: CMCapture\ndyld[33989]: move loaded to delayed: CoreMediaIO\ndyld[33989]: move loaded to delayed: CMCaptureDevice\ndyld[33989]: move loaded to delayed: CoreBrightness\ndyld[33989]: move loaded to delayed: CinematicFraming\ndyld[33989]: move loaded to delayed: ModelManagerServices\ndyld[33989]: move loaded to delayed: CPMS\ndyld[33989]: move loaded to delayed: SystemStatus\ndyld[33989]: move loaded to delayed: CoreMotion\ndyld[33989]: move loaded to delayed: TimeSync\ndyld[33989]: move loaded to delayed: DistributedSensing\ndyld[33989]: move loaded to delayed: MobileBluetooth\ndyld[33989]: move loaded to delayed: IOKitten\ndyld[33989]: move loaded to delayed: LocationLogEncryption\ndyld[33989]: move loaded to delayed: AppleIntelligenceReporting\ndyld[33989]: move loaded to delayed: CoreEmoji\ndyld[33989]: move loaded to delayed: LanguageModeling\ndyld[33989]: move loaded to delayed: Montreal\ndyld[33989]: move loaded to delayed: libcmph.dylib\ndyld[33989]: move loaded to delayed: GenerativeModelsFoundation\ndyld[33989]: move loaded to delayed: TokenGeneration\ndyld[33989]: move loaded to delayed: GenerativeFunctions\ndyld[33989]: move loaded to delayed: GenerativeFunctionsFoundation\ndyld[33989]: move loaded to delayed: ModelCatalog\ndyld[33989]: move loaded to delayed: SensitiveContentAnalysisML\ndyld[33989]: move loaded to delayed: GenerativeFunctionsInstrumentation\ndyld[33989]: move loaded to delayed: PromptKit\ndyld[33989]: move loaded to delayed: ProactiveDaemonSupport\ndyld[33989]: move loaded to delayed: TokenGenerationCore\ndyld[33989]: move loaded to delayed: Trial\ndyld[33989]: move loaded to delayed: TrialProto\ndyld[33989]: move loaded to delayed: AppleFlatBuffers\ndyld[33989]: move loaded to delayed: SentencePieceInternal\ndyld[33989]: move loaded to delayed: Vision\ndyld[33989]: move loaded to delayed: CoreSceneUnderstanding\ndyld[33989]: move loaded to delayed: VisionCore\ndyld[33989]: move loaded to delayed: DataDetectorsCore\ndyld[33989]: move loaded to delayed: libfaceCore.dylib\ndyld[33989]: move loaded to delayed: Futhark\ndyld[33989]: move loaded to delayed: InertiaCam\ndyld[33989]: move loaded to delayed: TextRecognition\ndyld[33989]: move loaded to delayed: DataDetection\ndyld[33989]: move loaded to delayed: TextInput\ndyld[33989]: move loaded to delayed: CVNLP\ndyld[33989]: move loaded to delayed: IntentsFoundation\ndyld[33989]: move loaded to delayed: ApplePushService\ndyld[33989]: move loaded to delayed: CloudKit\ndyld[33989]: move loaded to delayed: CoreDuetDaemonProtocol\ndyld[33989]: move loaded to delayed: DeviceIdentity\ndyld[33989]: move loaded to delayed: SharedWithYouCore\ndyld[33989]: move loaded to delayed: CloudTelemetry\ndyld[33989]: move loaded to delayed: AppleAccount\ndyld[33989]: move loaded to delayed: CacheDelete\ndyld[33989]: move loaded to delayed: C2\ndyld[33989]: move loaded to delayed: CloudCoreInternal\ndyld[33989]: move loaded to delayed: CloudAsset\ndyld[33989]: move loaded to delayed: PushKit\ndyld[33989]: move loaded to delayed: CoreTransferable\ndyld[33989]: move loaded to delayed: FileProvider\ndyld[33989]: move loaded to delayed: GenerationalStorage\ndyld[33989]: move loaded to delayed: DesktopServicesPriv\ndyld[33989]: move loaded to delayed: CloudTelemetryTools\ndyld[33989]: move loaded to delayed: CloudTelemetryShared.dylib\ndyld[33989]: move loaded to delayed: RTCReporting\ndyld[33989]: move loaded to delayed: AAAFoundationSwift\ndyld[33989]: move loaded to delayed: AppleIDSSOAuthentication\ndyld[33989]: move loaded to delayed: UIFoundation\ndyld[33989]: move loaded to delayed: libcups.2.dylib\ndyld[33989]: move loaded to delayed: AXCoreUtilities\ndyld[33989]: move loaded to delayed: AttributeGraph\ndyld[33989]: move loaded to delayed: libAXSafeCategoryBundle.dylib\ndyld[33989]: move loaded to delayed: TabularData\ndyld[33989]: move loaded to delayed: ArgumentParserInternal\ndyld[33989]: move loaded to delayed: FindMyDevice\ndyld[33989]: move loaded to delayed: FMCoreLite\ndyld[33989]: move loaded to delayed: ServiceManagement\ndyld[33989]: move loaded to delayed: CryptoKitPrivate\ndyld[33989]: move loaded to delayed: CaptiveNetwork\ndyld[33989]: move loaded to delayed: EAP8021X\ndyld[33989]: move loaded to delayed: QuickLookThumbnailing\ndyld[33989]: /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/lib/python3.14/lib-dynload/_ctypes.cpython-314-darwin.so\ndyld[33989]: /usr/lib/libffi.dylib\ndyld[33989]: <85E37FC4-F653-335B-AD84-D8291D8EB9C3> /usr/lib/libffi-trampolines.dylib\ndyld[33989]: <9DE830B2-E5A8-3D1A-B8CA-5C7BAB606430> /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/lib/python3.14/lib-dynload/_struct.cpython-314-darwin.so\ndyld[33989]: <592C467A-7B2E-30BC-8E42-4235C999F1D1> /private/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/native-enabled/libwebscene_native_engine.dylib\ndyld[33989]: <4C4C44AF-5555-3144-A120-3E4412E00745> /private/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/native-enabled/libEGL.dylib\ndyld[33989]: <4C4C4498-5555-3144-A17E-05A77F85C5AE> /private/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/native-enabled/libGLESv2.dylib\ndyld[33989]: /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa\ndyld[33989]: /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit\ndyld[33989]: <4840B78C-D96B-35B9-85C7-E5889C44A7C4> /System/Library/PrivateFrameworks/CollectionViewCore.framework/Versions/A/CollectionViewCore\ndyld[33989]: /System/Library/PrivateFrameworks/XCTTargetBootstrap.framework/Versions/A/XCTTargetBootstrap\ndyld[33989]: <0D6F3043-5372-3B15-96BC-6F45AD86F410> /System/Library/PrivateFrameworks/UserActivity.framework/Versions/A/UserActivity\ndyld[33989]: <7CDC68D4-0845-3053-AB80-C5A1F354060F> /System/Library/PrivateFrameworks/FrontBoard.framework/Versions/A/FrontBoard\ndyld[33989]: /System/Library/PrivateFrameworks/UIIntelligenceSupport.framework/Versions/A/UIIntelligenceSupport\ndyld[33989]: <9EB0840F-B045-3529-9467-1470A3C6CA02> /System/Library/Frameworks/SwiftUICore.framework/Versions/A/SwiftUICore\ndyld[33989]: <84FB5635-42EE-3BB5-B7CD-8A354AD7DB0A> /System/Library/PrivateFrameworks/WritingTools.framework/Versions/A/WritingTools\ndyld[33989]: <6ECD36F7-0A2E-3631-A57F-FD0152173454> /System/Library/PrivateFrameworks/WindowManagement.framework/Versions/A/WindowManagement\ndyld[33989]: /System/Library/PrivateFrameworks/FocusEngine.framework/Versions/A/FocusEngine\ndyld[33989]: <2BC041DF-695A-32EE-B164-1C35C2AFFC53> /System/Library/PrivateFrameworks/DesignLibrary.framework/Versions/A/DesignLibrary\ndyld[33989]: /System/Library/PrivateFrameworks/DFRFoundation.framework/Versions/A/DFRFoundation\ndyld[33989]: <365E81B9-B9BA-3F4F-83FD-86A35CFBC8AC> /System/Library/PrivateFrameworks/UpdateCycle.framework/Versions/A/UpdateCycle\ndyld[33989]: <38408482-CE3B-359E-9465-7FEB4BB79B54> /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox\ndyld[33989]: /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/SpeechRecognition.framework/Versions/A/SpeechRecognition\ndyld[33989]: <74C54353-A613-35A2-857A-737BB48906F9> /System/Library/PrivateFrameworks/PerformanceAnalysis.framework/Versions/A/PerformanceAnalysis\ndyld[33989]: <22008BA9-C61B-3FAD-A1A0-F6A1CD220343> /System/Library/Frameworks/Accessibility.framework/Versions/A/Accessibility\ndyld[33989]: <028E944B-66C4-39E2-A436-FB93FB6CED4E> /System/Library/Frameworks/Symbols.framework/Versions/A/Symbols\ndyld[33989]: /System/Library/PrivateFrameworks/Gestures.framework/Versions/A/Gestures\ndyld[33989]: <713AEF7A-43B6-3735-8AB5-361F5EE06BBA> /usr/lib/swift/libswiftSpatial.dylib\ndyld[33989]: /usr/lib/swift/libswiftCoreGraphics.dylib\ndyld[33989]: <14A11A94-6A52-3D24-9267-42EDAEEC5FDD> /usr/lib/swift/libswiftFoundation.dylib\ndyld[33989]: <76A7FE10-AD26-3505-A8CC-D37AC1C24D32> /usr/lib/swift/libswiftSwiftOnoneSupport.dylib\ndyld[33989]: <4B5C0268-23EB-3E20-8F57-CDECFB6E3205> /usr/lib/swift/libswiftsys_time.dylib\ndyld[33989]: /System/Library/PrivateFrameworks/CoreMaterial.framework/Versions/A/CoreMaterial\ndyld[33989]: <68B7C15F-537C-3FEF-838B-DC548772A45F> /usr/lib/libSpatial.dylib\ndyld[33989]: <183FD4D6-D766-34FC-B8E1-7C4D17435AC3> /System/Library/SubFrameworks/UIUtilities.framework/Versions/A/UIUtilities\ndyld[33989]: move delayed to loaded: CoreWiFi\ndyld[33989]: move delayed to loaded: Rapport\ndyld[33989]: move delayed to loaded: Accounts\ndyld[33989]: move delayed to loaded: AVFoundation\ndyld[33989]: move delayed to loaded: InternationalSupport\ndyld[33989]: move delayed to loaded: ApplicationServices\ndyld[33989]: move delayed to loaded: ATS\ndyld[33989]: move delayed to loaded: HIServices\ndyld[33989]: move delayed to loaded: PrintCore\ndyld[33989]: move delayed to loaded: QD\ndyld[33989]: move delayed to loaded: ColorSyncLegacy\ndyld[33989]: move delayed to loaded: SpeechSynthesis\ndyld[33989]: move delayed to loaded: _LocationEssentials\ndyld[33989]: move delayed to loaded: GeoServices\ndyld[33989]: move delayed to loaded: LocationSupport\ndyld[33989]: move delayed to loaded: CoreBluetooth\ndyld[33989]: move delayed to loaded: GeoServicesCore\ndyld[33989]: move delayed to loaded: PhoneNumbers\ndyld[33989]: move delayed to loaded: IconServices\ndyld[33989]: move delayed to loaded: IconFoundation\ndyld[33989]: move delayed to loaded: IconRendering\ndyld[33989]: move delayed to loaded: CoreUI\ndyld[33989]: move delayed to loaded: SFSymbols\ndyld[33989]: move delayed to loaded: DeveloperToolsSupport\ndyld[33989]: move delayed to loaded: RenderBox\ndyld[33989]: move delayed to loaded: CoreSVG\ndyld[33989]: move delayed to loaded: TextureIO\ndyld[33989]: move delayed to loaded: libswiftCoreImage.dylib\ndyld[33989]: move delayed to loaded: ATSUI\ndyld[33989]: move delayed to loaded: UserNotifications\ndyld[33989]: move delayed to loaded: SiriInstrumentation\ndyld[33989]: move delayed to loaded: SiriAnalytics\ndyld[33989]: move delayed to loaded: FeedbackLogger\ndyld[33989]: move delayed to loaded: libswiftAVFoundation.dylib\ndyld[33989]: move delayed to loaded: UnifiedAssetFramework\ndyld[33989]: move delayed to loaded: CoreUtils\ndyld[33989]: move delayed to loaded: CoreUtilsExtras\ndyld[33989]: move delayed to loaded: IO80211\ndyld[33989]: move delayed to loaded: IOBluetooth\ndyld[33989]: move delayed to loaded: libswiftRegexBuilder.dylib\ndyld[33989]: move delayed to loaded: libIOReport.dylib\ndyld[33989]: move delayed to loaded: WiFiPeerToPeer\ndyld[33989]: move delayed to loaded: Centauri\ndyld[33989]: move delayed to loaded: libmrc.dylib\ndyld[33989]: move delayed to loaded: IPConfiguration\ndyld[33989]: move delayed to loaded: Netrb\ndyld[33989]: move delayed to loaded: FrontBoardServices\ndyld[33989]: move delayed to loaded: AudioUnit\ndyld[33989]: move delayed to loaded: AVFAudio\ndyld[33989]: move delayed to loaded: AVRouting\ndyld[33989]: move delayed to loaded: libAccessibility.dylib\ndyld[33989]: move delayed to loaded: IsolatedCoreAudioClient\ndyld[33989]: move delayed to loaded: CoreAudioOrchestration\ndyld[33989]: move delayed to loaded: MediaToolbox\ndyld[33989]: move delayed to loaded: CoreAVCHD\ndyld[33989]: move delayed to loaded: MediaAccessibility\ndyld[33989]: move delayed to loaded: Mangrove\ndyld[33989]: move delayed to loaded: CMPhoto\ndyld[33989]: move delayed to loaded: CoreTelephony\ndyld[33989]: move delayed to loaded: CoreAUC\ndyld[33989]: move delayed to loaded: AppleJPEGXL\ndyld[33989]: move delayed to loaded: libTelephonyUtilDynamic.dylib\ndyld[33989]: move delayed to loaded: CryptoKit\ndyld[33989]: move delayed to loaded: CryptoKitCBridging\ndyld[33989]: move delayed to loaded: CryptoTokenKit\ndyld[33989]: move delayed to loaded: Dendrite\ndyld[33989]: move delayed to loaded: NaturalLanguage\ndyld[33989]: move delayed to loaded: GenerativeModels\ndyld[33989]: move delayed to loaded: libswiftNaturalLanguage.dylib\ndyld[33989]: move delayed to loaded: Espresso\ndyld[33989]: move delayed to loaded: CoreML\ndyld[33989]: move delayed to loaded: libedit.3.dylib\ndyld[33989]: move delayed to loaded: ANECompiler\ndyld[33989]: move delayed to loaded: AppleNeuralEngine\ndyld[33989]: move delayed to loaded: MetalPerformanceShadersGraph\ndyld[33989]: move delayed to loaded: MLCompilerServices\ndyld[33989]: move delayed to loaded: ANEServices\ndyld[33989]: move delayed to loaded: libncurses.5.4.dylib\ndyld[33989]: move delayed to loaded: libsandbox.1.dylib\ndyld[33989]: move delayed to loaded: libMatch.1.dylib\ndyld[33989]: move delayed to loaded: ODIE\ndyld[33989]: move delayed to loaded: MLModelAsset\ndyld[33989]: move delayed to loaded: MLCompilerRuntime\ndyld[33989]: move delayed to loaded: MLCompute\ndyld[33989]: move delayed to loaded: MLAssetIO\ndyld[33989]: move delayed to loaded: libswiftMLCompute.dylib\ndyld[33989]: move delayed to loaded: AVFCore\ndyld[33989]: move delayed to loaded: AVFCapture\ndyld[33989]: move delayed to loaded: CMImaging\ndyld[33989]: move delayed to loaded: Quagga\ndyld[33989]: move delayed to loaded: CMCapture\ndyld[33989]: move delayed to loaded: CoreMediaIO\ndyld[33989]: move delayed to loaded: CMCaptureDevice\ndyld[33989]: move delayed to loaded: CoreBrightness\ndyld[33989]: move delayed to loaded: CinematicFraming\ndyld[33989]: move delayed to loaded: ModelManagerServices\ndyld[33989]: move delayed to loaded: CPMS\ndyld[33989]: move delayed to loaded: SystemStatus\ndyld[33989]: move delayed to loaded: CoreMotion\ndyld[33989]: move delayed to loaded: TimeSync\ndyld[33989]: move delayed to loaded: DistributedSensing\ndyld[33989]: move delayed to loaded: MobileBluetooth\ndyld[33989]: move delayed to loaded: IOKitten\ndyld[33989]: move delayed to loaded: LocationLogEncryption\ndyld[33989]: move delayed to loaded: AppleIntelligenceReporting\ndyld[33989]: move delayed to loaded: CoreEmoji\ndyld[33989]: move delayed to loaded: LanguageModeling\ndyld[33989]: move delayed to loaded: Montreal\ndyld[33989]: move delayed to loaded: libcmph.dylib\ndyld[33989]: move delayed to loaded: GenerativeModelsFoundation\ndyld[33989]: move delayed to loaded: TokenGeneration\ndyld[33989]: move delayed to loaded: GenerativeFunctions\ndyld[33989]: move delayed to loaded: GenerativeFunctionsFoundation\ndyld[33989]: move delayed to loaded: ModelCatalog\ndyld[33989]: move delayed to loaded: SensitiveContentAnalysisML\ndyld[33989]: move delayed to loaded: GenerativeFunctionsInstrumentation\ndyld[33989]: move delayed to loaded: PromptKit\ndyld[33989]: move delayed to loaded: ProactiveDaemonSupport\ndyld[33989]: move delayed to loaded: TokenGenerationCore\ndyld[33989]: move delayed to loaded: Trial\ndyld[33989]: move delayed to loaded: TrialProto\ndyld[33989]: move delayed to loaded: AppleFlatBuffers\ndyld[33989]: move delayed to loaded: SentencePieceInternal\ndyld[33989]: move delayed to loaded: Vision\ndyld[33989]: move delayed to loaded: CoreSceneUnderstanding\ndyld[33989]: move delayed to loaded: VisionCore\ndyld[33989]: move delayed to loaded: DataDetectorsCore\ndyld[33989]: move delayed to loaded: libfaceCore.dylib\ndyld[33989]: move delayed to loaded: Futhark\ndyld[33989]: move delayed to loaded: InertiaCam\ndyld[33989]: move delayed to loaded: TextRecognition\ndyld[33989]: move delayed to loaded: DataDetection\ndyld[33989]: move delayed to loaded: TextInput\ndyld[33989]: move delayed to loaded: CVNLP\ndyld[33989]: move delayed to loaded: CoreTransferable\ndyld[33989]: move delayed to loaded: UIFoundation\ndyld[33989]: move delayed to loaded: libcups.2.dylib\ndyld[33989]: move delayed to loaded: AXCoreUtilities\ndyld[33989]: move delayed to loaded: AttributeGraph\ndyld[33989]: move delayed to loaded: libAXSafeCategoryBundle.dylib\ndyld[33989]: move delayed to loaded: TabularData\ndyld[33989]: move delayed to loaded: ArgumentParserInternal\n" + }, + { + "command": [ + "/opt/homebrew/opt/python@3.14/bin/python3.14", + "-c", + "import ctypes; library=ctypes.CDLL('/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/native-disabled/libwebscene_native_engine.dylib'); print(library.webscene_engine_get_abi_version())" + ], + "passed": true, + "exitCode": 0, + "stdout": "3\n", + "loaderTrace": "dyld[33990]: <8D7882C5-027F-3692-BFE3-C897D8F59412> /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/bin/python3.14\ndyld[33990]: <1DEC725C-63A6-3B9C-A038-DC35832D65CB> /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python\ndyld[33990]: <9B672762-7B1F-30BC-96DE-F176B372D66D> /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation\ndyld[33990]: <03BD9E32-CF0A-37B0-898A-3CE8DE06D842> /usr/lib/libobjc.A.dylib\ndyld[33990]: <7D56DA94-31EB-35F0-B886-4010C075E035> /System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal\ndyld[33990]: <91DACE39-FA28-3191-818D-1FCC6A0E615A> /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation\ndyld[33990]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/liboah.dylib\ndyld[33990]: <820D290D-51A0-3064-A1F2-4F0AAF7E6BF4> /usr/lib/libfakelink.dylib\ndyld[33990]: <53A3E31E-06A8-325E-B5A8-316B88AA3C92> /usr/lib/libicucore.A.dylib\ndyld[33990]: <4FED5EE2-5D3E-35B1-A170-9859C4B683BB> /usr/lib/libSystem.B.dylib\ndyld[33990]: <4109E8DD-0A81-310C-B1B3-23B87186D0D8> /System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking\ndyld[33990]: <83794FB3-DE9B-3D23-AB5E-2C1D5D30F134> /usr/lib/swift/libswiftCore.dylib\ndyld[33990]: /usr/lib/libc++abi.dylib\ndyld[33990]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/libRosetta.dylib\ndyld[33990]: /usr/lib/libc++.1.dylib\ndyld[33990]: <4FD234EA-2C18-3C25-8BD0-B1F4805C6675> /usr/lib/swift/libswiftObjectiveC.dylib\ndyld[33990]: <9E3C7597-446F-3C50-9930-2425D9252C0C> /usr/lib/libswiftPrespecialized.dylib\ndyld[33990]: <1479C415-3678-3968-AC77-06373490860E> /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration\ndyld[33990]: <13EDE3A5-A7D9-3FB8-B0C2-2FB7F7272B34> /usr/lib/libz.1.dylib\ndyld[33990]: <54AD73AF-852E-3CD6-8B7D-E73BE79857D3> /System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout\ndyld[33990]: <1A2A9A41-5269-3B0C-BCEE-B446966CE366> /usr/lib/libcmark-gfm.dylib\ndyld[33990]: /usr/lib/libcompression.dylib\ndyld[33990]: <4A3B95C5-AA2E-338C-9398-56895AF82D97> /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork\ndyld[33990]: <332C4B80-5B3C-34E7-AD1F-F6131E607F95> /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration\ndyld[33990]: <0048DB96-1737-3FC5-AF0C-AF784FA24A03> /usr/lib/libarchive.2.dylib\ndyld[33990]: <6CD959AA-4825-306A-864A-BD69EC5F2DC0> /usr/lib/libDiagnosticMessagesClient.dylib\ndyld[33990]: <1E8A4F9E-3954-3458-B3BB-BE97F961C105> /usr/lib/libxml2.2.dylib\ndyld[33990]: <56AE2857-29E0-34E9-B2C3-EE8E951EEFC5> /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices\ndyld[33990]: /usr/lib/liblangid.dylib\ndyld[33990]: <12372585-DF92-33EF-B632-714FAA13260A> /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit\ndyld[33990]: /System/Library/Frameworks/Combine.framework/Versions/A/Combine\ndyld[33990]: <6098453F-4D7E-38B4-8ADC-02C9FF51E14A> /System/Library/PrivateFrameworks/CollectionsInternal.framework/Versions/A/CollectionsInternal\ndyld[33990]: <9A1279D4-575A-3E48-A460-A631A3F82D18> /System/Library/PrivateFrameworks/ReflectionInternal.framework/Versions/A/ReflectionInternal\ndyld[33990]: <6D89CD71-A86D-3D78-A64B-96AB79550F79> /System/Library/PrivateFrameworks/RuntimeInternal.framework/Versions/A/RuntimeInternal\ndyld[33990]: <4975D13C-2AC5-3473-85C0-98054A81D7C6> /usr/lib/swift/libswiftCoreFoundation.dylib\ndyld[33990]: <1DB56DA9-CF6B-3023-ABDF-5A37CB79223C> /usr/lib/swift/libswiftDarwin.dylib\ndyld[33990]: /usr/lib/swift/libswiftDispatch.dylib\ndyld[33990]: <06A92787-4440-3757-AF32-F2B331C753A2> /usr/lib/swift/libswiftIOKit.dylib\ndyld[33990]: <7CD9BDE7-F36B-3471-9295-38E181D6D9E5> /usr/lib/swift/libswiftSystem.dylib\ndyld[33990]: <24AEDAC1-C1EE-30F4-8818-72EBF8969D0C> /usr/lib/swift/libswiftXPC.dylib\ndyld[33990]: <52F59382-A6A6-3F55-8A85-D9FB822D370F> /usr/lib/swift/libswift_Builtin_float.dylib\ndyld[33990]: <8E168857-47F4-349F-A718-A18DB144FCB0> /usr/lib/swift/libswift_Concurrency.dylib\ndyld[33990]: <85246B9A-A757-3F67-B792-3A2F7BB2BB25> /usr/lib/swift/libswift_DarwinFoundation1.dylib\ndyld[33990]: <8DF0116D-DFC9-3906-9DF6-F1DBC47E324B> /usr/lib/swift/libswift_StringProcessing.dylib\ndyld[33990]: /usr/lib/swift/libswiftos.dylib\ndyld[33990]: <1C7E652B-6B94-3180-93A6-EF8DBA3A5448> /System/Library/Frameworks/Network.framework/Versions/A/Network\ndyld[33990]: <4C6139EE-BF87-37A6-B226-830A6FDC36F8> /System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo\ndyld[33990]: <9D0387FC-E8F6-3004-9C95-CA68EA715C8B> /System/Library/Frameworks/Security.framework/Versions/A/Security\ndyld[33990]: <633BCB5F-F063-3D5A-B52A-F72AE236824B> /usr/lib/libbsm.0.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer\ndyld[33990]: <10A4E63B-A1EB-31CC-B3E1-DB4FE115FC84> /System/Library/PrivateFrameworks/BackgroundSystemTasks.framework/Versions/A/BackgroundSystemTasks\ndyld[33990]: /System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics\ndyld[33990]: <7C50137B-2ABD-3819-B033-AE65B05A6085> /System/Library/PrivateFrameworks/CoreWiFi.framework/Versions/A/CoreWiFi\ndyld[33990]: /System/Library/PrivateFrameworks/LoggingSupport.framework/Versions/A/LoggingSupport\ndyld[33990]: <91A461DE-C8E8-3868-B393-BA6E5A17DF2A> /System/Library/PrivateFrameworks/MobileAsset.framework/Versions/A/MobileAsset\ndyld[33990]: /System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog\ndyld[33990]: /System/Library/PrivateFrameworks/Rapport.framework/Versions/A/Rapport\ndyld[33990]: /System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices\ndyld[33990]: <9F52706C-75BD-34AF-A29E-C26608124ACC> /System/Library/Frameworks/SwiftData.framework/Versions/A/SwiftData\ndyld[33990]: <259877CE-4E2C-34A9-A07F-FEE2999D7B2F> /System/Library/PrivateFrameworks/Symptoms.framework/Versions/A/Frameworks/SymptomAnalytics.framework/Versions/A/SymptomAnalytics\ndyld[33990]: /System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers\ndyld[33990]: <4A78C569-FF0D-398B-9C25-33453F0CEC40> /System/Library/PrivateFrameworks/UserManagement.framework/Versions/A/UserManagement\ndyld[33990]: <5BF55637-F306-3D79-B5A1-DB8A871DAD4B> /usr/lib/libboringssl.dylib\ndyld[33990]: <831C79C1-8DBE-31A3-AA4E-8E2B041488D6> /usr/lib/libcupolicy.dylib\ndyld[33990]: <88925A0C-4960-3F6D-AF3A-B1983F7B3D18> /usr/lib/libdns_services.dylib\ndyld[33990]: /usr/lib/libnetworkextension.dylib\ndyld[33990]: <9753F471-40DD-3B9E-9D64-8D07C1B06BC9> /System/Library/Frameworks/NetworkExtension.framework/Versions/A/NetworkExtension\ndyld[33990]: /usr/lib/libnwswifttls.dylib\ndyld[33990]: <6F59933A-6618-33F1-BE52-E7FC3BF7A1EF> /usr/lib/libpcap.A.dylib\ndyld[33990]: <5E89267F-C684-348D-8356-F9DAD8B4CB13> /usr/lib/libquic.dylib\ndyld[33990]: /usr/lib/libusrtcp.dylib\ndyld[33990]: /usr/lib/libMobileGestalt.dylib\ndyld[33990]: /usr/lib/libapple_nghttp2.dylib\ndyld[33990]: <6937D729-7EF4-3972-9E12-694C17C1C1AB> /usr/lib/libcoretls_cfhelpers.dylib\ndyld[33990]: /usr/lib/libsqlite3.dylib\ndyld[33990]: <1617DBB1-2BFF-3619-903C-2FBB31348FB6> /System/Library/PrivateFrameworks/AtomicsInternal.framework/Versions/A/AtomicsInternal\ndyld[33990]: <41F66F01-A342-3091-A832-0B2B645C922B> /System/Library/PrivateFrameworks/InternalSwiftProtobuf.framework/Versions/A/InternalSwiftProtobuf\ndyld[33990]: <2EDB2E62-942F-3AB5-82AF-8E1328544E17> /usr/lib/swift/libswiftDistributed.dylib\ndyld[33990]: /usr/lib/swift/libswiftObservation.dylib\ndyld[33990]: /usr/lib/swift/libswiftSynchronization.dylib\ndyld[33990]: <9CD7B1E1-3E47-339C-A193-2392E3E0ED23> /usr/lib/system/libcache.dylib\ndyld[33990]: <3B110564-5278-3CB0-85F1-2CE8431FF935> /usr/lib/system/libcommonCrypto.dylib\ndyld[33990]: <6FB345CA-7F5C-3263-A23F-143F7539FD8A> /usr/lib/system/libcompiler_rt.dylib\ndyld[33990]: /usr/lib/system/libcopyfile.dylib\ndyld[33990]: <0642DDAD-4771-3C82-805C-E7C6701C1461> /usr/lib/system/libcorecrypto.dylib\ndyld[33990]: /usr/lib/system/libdispatch.dylib\ndyld[33990]: <957F93B3-8805-39C7-9C51-EDD1715F550E> /usr/lib/system/libdyld.dylib\ndyld[33990]: <7E863FCA-F3FF-32C7-8A8C-F983E946AFC3> /usr/lib/system/libkeymgr.dylib\ndyld[33990]: <949131E5-BDA2-39BA-AA50-62651BB51802> /usr/lib/system/libmacho.dylib\ndyld[33990]: /usr/lib/system/libquarantine.dylib\ndyld[33990]: <7460B5AE-469A-36A0-A7EC-6C7D69628E86> /usr/lib/system/libremovefile.dylib\ndyld[33990]: <54439739-33EE-3273-839F-CBA67D7F5CB1> /usr/lib/system/libsystem_asl.dylib\ndyld[33990]: /usr/lib/system/libsystem_blocks.dylib\ndyld[33990]: /usr/lib/system/libsystem_c.dylib\ndyld[33990]: /usr/lib/system/libsystem_collections.dylib\ndyld[33990]: /usr/lib/system/libsystem_configuration.dylib\ndyld[33990]: <14B2A47F-19C8-392F-8FDB-FE8AE375DD41> /usr/lib/system/libsystem_containermanager.dylib\ndyld[33990]: /usr/lib/system/libsystem_coreservices.dylib\ndyld[33990]: <8E07D22E-CE5A-38A0-B091-5B0338C326F5> /usr/lib/system/libsystem_darwin.dylib\ndyld[33990]: <971A4F65-493D-39F3-846D-0D33FA2769FD> /usr/lib/system/libsystem_darwindirectory.dylib\ndyld[33990]: <305F4398-E688-3384-B351-02D865EC8A04> /usr/lib/system/libsystem_dnssd.dylib\ndyld[33990]: <750CA446-92EA-3A56-9A7B-CC0841686C50> /usr/lib/system/libsystem_eligibility.dylib\ndyld[33990]: /usr/lib/system/libsystem_featureflags.dylib\ndyld[33990]: <9B5FB84B-31AD-3EA7-8F89-8C700D369DC8> /usr/lib/system/libsystem_info.dylib\ndyld[33990]: /usr/lib/system/libsystem_m.dylib\ndyld[33990]: /usr/lib/system/libsystem_malloc.dylib\ndyld[33990]: <9C7B1EEB-47BE-3791-93A9-CFC693CB9417> /usr/lib/system/libsystem_networkextension.dylib\ndyld[33990]: <15799128-6CBD-30D6-A2BB-B9D02B4470C0> /usr/lib/system/libsystem_notify.dylib\ndyld[33990]: <54688162-B50D-3D31-A1E8-7B9766D3530D> /usr/lib/system/libsystem_sandbox.dylib\ndyld[33990]: /usr/lib/system/libsystem_sanitizers.dylib\ndyld[33990]: /usr/lib/system/libsystem_secinit.dylib\ndyld[33990]: /usr/lib/system/libsystem_kernel.dylib\ndyld[33990]: /usr/lib/system/libsystem_platform.dylib\ndyld[33990]: /usr/lib/system/libsystem_pthread.dylib\ndyld[33990]: <229122B9-B8B1-3F2F-870E-8650AE3C4FB5> /usr/lib/system/libsystem_symptoms.dylib\ndyld[33990]: <93F1DD8C-6CD9-32B9-B222-D23DA5D161B4> /usr/lib/system/libsystem_trace.dylib\ndyld[33990]: <7194FF5B-A6C5-3D67-B00A-90209F10D603> /usr/lib/system/libsystem_trial.dylib\ndyld[33990]: <05FD0014-55B1-3B8A-A6BA-6C7A389C4123> /usr/lib/system/libunwind.dylib\ndyld[33990]: <33E44C2D-D65E-37A6-B85F-1A4CF524A050> /usr/lib/system/libxpc.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/XPCSupport.framework/Versions/A/XPCSupport\ndyld[33990]: /System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement\ndyld[33990]: /System/Library/PrivateFrameworks/AppleKeyStore.framework/Versions/A/AppleKeyStore\ndyld[33990]: /usr/lib/libCoreEntitlements.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity\ndyld[33990]: <81F4A8BA-C80F-3B53-82E7-57F6928609C5> /System/Library/PrivateFrameworks/CloudServices.framework/Versions/A/CloudServices\ndyld[33990]: <737479F2-7B20-3DB6-B9F4-0DAA1B73E9D0> /System/Library/PrivateFrameworks/SymptomDiagnosticReporter.framework/Versions/A/SymptomDiagnosticReporter\ndyld[33990]: /System/Library/PrivateFrameworks/CrashReporterSupport.framework/Versions/A/CrashReporterSupport\ndyld[33990]: /System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression\ndyld[33990]: <0EAB1F4A-9275-3FED-8EA6-E962ACDDEE5D> /usr/lib/libcoretls.dylib\ndyld[33990]: <7E84FD3B-E90E-317E-AC19-17B70AC809E5> /usr/lib/libpam.2.dylib\ndyld[33990]: /usr/lib/libxar.1.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS\ndyld[33990]: /System/Library/PrivateFrameworks/SwiftASN1Internal.framework/Versions/A/SwiftASN1Internal\ndyld[33990]: /usr/lib/libutil.dylib\ndyld[33990]: <8E04C57D-3651-386E-83D5-4728B732F214> /usr/lib/libenergytrace.dylib\ndyld[33990]: /usr/lib/system/libkxld.dylib\ndyld[33990]: <2BC48182-F354-3AB0-8F18-0C60CAAFE398> /System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer\ndyld[33990]: <5556FD64-9D47-3547-961E-3A27681F3C51> /System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface\ndyld[33990]: <6A4A85F4-3D12-3C4C-85EC-D53D61379F28> /usr/lib/libheimdal-asn1.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce\ndyld[33990]: /System/Library/PrivateFrameworks/OctagonTrust.framework/Versions/A/OctagonTrust\ndyld[33990]: /System/Library/PrivateFrameworks/AppleIDAuthSupport.framework/Versions/A/AppleIDAuthSupport\ndyld[33990]: <9A86DB3F-CC62-3E89-B872-35D04CFFBE42> /System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/KeychainCircle.framework/Versions/A/KeychainCircle\ndyld[33990]: <336E2CAC-84D2-34DC-8AE3-7FE688C609EA> /System/Library/PrivateFrameworks/AuthKit.framework/Versions/A/AuthKit\ndyld[33990]: /System/Library/PrivateFrameworks/AAAFoundation.framework/Versions/A/AAAFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag\ndyld[33990]: <79000980-1797-3115-B74B-60FA1E9C3C73> /System/Library/PrivateFrameworks/CorePhoneNumbers.framework/Versions/A/CorePhoneNumbers\ndyld[33990]: <21723046-939E-302F-883C-9DB417452E3A> /System/Library/PrivateFrameworks/MultiverseSupport.framework/Versions/A/MultiverseSupport\ndyld[33990]: <823F3D1A-65F1-3CC5-96B1-750263B8DB36> /System/Library/PrivateFrameworks/RemoteServiceDiscovery.framework/Versions/A/RemoteServiceDiscovery\ndyld[33990]: /System/Library/PrivateFrameworks/DiskManagement.framework/Versions/A/DiskManagement\ndyld[33990]: <5F6B668E-00B2-3BEC-959F-26BD6B50D42B> /System/Library/Frameworks/Accounts.framework/Versions/A/Accounts\ndyld[33990]: /System/Library/PrivateFrameworks/URLFormatting.framework/Versions/A/URLFormatting\ndyld[33990]: <91BDD1F8-831B-3B01-86BA-6BBCB43373C4> /System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary\ndyld[33990]: <885F9C72-1018-368B-AD36-E8A42E87FD91> /System/Library/PrivateFrameworks/RemoteXPC.framework/Versions/A/RemoteXPC\ndyld[33990]: /usr/lib/libFDR.dylib\ndyld[33990]: <24D28E7F-A1AE-3031-8679-A0D6C6D68A86> /usr/lib/libamsupport.dylib\ndyld[33990]: <29367004-5D60-38DB-831F-9E5EE9364B21> /usr/lib/libReverseProxyDevice.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/MSUDataAccessor.framework/Versions/A/MSUDataAccessor\ndyld[33990]: <9594FBFB-D49D-3DF6-8820-564633EAEC2B> /System/Library/PrivateFrameworks/AppleDeviceQuerySupport.framework/Versions/A/AppleDeviceQuerySupport\ndyld[33990]: <44CD8313-2D5B-3A34-BACA-EF8800803B4A> /System/Library/PrivateFrameworks/AOSKit.framework/Versions/A/AOSKit\ndyld[33990]: <5198BFE1-41D2-33D5-A9E0-C63F81A512D3> /System/Library/PrivateFrameworks/AppSSOCore.framework/Versions/A/AppSSOCore\ndyld[33990]: <61B2B917-D14A-38AD-A439-16E1C635441A> /System/Library/PrivateFrameworks/AppSupport.framework/Versions/A/AppSupport\ndyld[33990]: <816EC446-7C41-3A2F-A582-7CB856797C09> /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation\ndyld[33990]: <38C8FBEC-DE88-33FE-B742-A192F22CC754> /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics\ndyld[33990]: /System/Library/PrivateFrameworks/DuetActivityScheduler.framework/Versions/A/DuetActivityScheduler\ndyld[33990]: <0E78989C-854F-3664-AD92-6B7B6D04191C> /System/Library/PrivateFrameworks/FTServices.framework/Versions/A/FTServices\ndyld[33990]: <277D18EF-39E4-3F72-99E8-8D3DF65ED1D0> /System/Library/Frameworks/GSS.framework/Versions/A/GSS\ndyld[33990]: <5ACC6C0E-51E9-3B5A-B24F-89B22D070878> /System/Library/PrivateFrameworks/InternationalSupport.framework/Versions/A/InternationalSupport\ndyld[33990]: /usr/lib/libMemoryResourceException.dylib\ndyld[33990]: <798012E0-3FFC-3B8D-AC74-E7B7DAEA7E66> /System/Library/PrivateFrameworks/NetworkScore.framework/Versions/A/NetworkScore\ndyld[33990]: <2C93123F-99C8-3B8D-AAE6-3A817BE0A2BF> /System/Library/PrivateFrameworks/NetworkServiceProxy.framework/Versions/A/NetworkServiceProxy\ndyld[33990]: /System/Library/PrivateFrameworks/StreamingExtractor.framework/Versions/A/StreamingExtractor\ndyld[33990]: <1F2EDC7B-8F28-3721-8A60-F6E1BCFC29A3> /System/Library/PrivateFrameworks/StreamingZip.framework/Versions/A/StreamingZip\ndyld[33990]: <5DA62AF9-3D46-3D17-A3EB-7026A2F006DF> /System/Library/PrivateFrameworks/SymptomReporter.framework/Versions/A/SymptomReporter\ndyld[33990]: /usr/lib/liblzma.5.dylib\ndyld[33990]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents\ndyld[33990]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore\ndyld[33990]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata\ndyld[33990]: <61677289-93B7-382F-86CA-B856361D293F> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices\ndyld[33990]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit\ndyld[33990]: <435D6243-695B-3543-A722-10106F5696BD> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE\ndyld[33990]: <01579E0C-9D85-3521-8916-4DDC990CD064> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices\ndyld[33990]: <6A26D479-5926-330B-9FB8-9B7A6BE8E239> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices\ndyld[33990]: <297AC970-E432-3BBD-986C-36782634062E> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList\ndyld[33990]: <6508C698-D587-3B5A-B95B-A3A3F78CE122> /usr/lib/libCheckFix.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC\ndyld[33990]: /System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP\ndyld[33990]: <29AA0F7F-26F4-35B3-96DF-8A67B00A58AB> /System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities\ndyld[33990]: <9171DD7D-3994-3963-9A28-BC163BF97DE6> /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate\ndyld[33990]: /usr/lib/libmecab.dylib\ndyld[33990]: <1CA9048E-57DD-30F4-A3E6-FE6E97D5BF82> /usr/lib/libCRFSuite.dylib\ndyld[33990]: <74E55DD6-720D-39E4-897E-EB4328E1946D> /usr/lib/libgermantok.dylib\ndyld[33990]: <92FAD15C-EEA5-34E9-B309-75A1CD1B620B> /usr/lib/libThaiTokenizer.dylib\ndyld[33990]: <2B16DF37-A596-3D8A-AE47-33E580EB1354> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage\ndyld[33990]: <8203944D-B53E-3D7E-A481-3C676CAE1B6A> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib\ndyld[33990]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib\ndyld[33990]: <08508E7B-096D-31AB-9C66-191C877ED62F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/Libraries/libCGInterfaces.dylib\ndyld[33990]: <8945E7B7-12AE-3FF4-AA3B-D4DF9A06FEE7> /System/Library/PrivateFrameworks/AccelerateGPU.framework/Versions/A/AccelerateGPU\ndyld[33990]: <23402175-D2CF-3B08-88D0-AFBBCF775FEF> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib\ndyld[33990]: <086CBEED-2F64-3E75-AB99-8C8C0E0A2F1C> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices\ndyld[33990]: <0616AF41-149E-3F4A-906E-56E2642457BE> /System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo\ndyld[33990]: <873404F1-CC9D-30F9-AE06-8EA58D292005> /System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync\ndyld[33990]: /System/Library/Frameworks/CoreText.framework/Versions/A/CoreText\ndyld[33990]: /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO\ndyld[33990]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS\ndyld[33990]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices\ndyld[33990]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore\ndyld[33990]: <59BBF27B-1D89-3D35-9210-8386EFA15A8D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD\ndyld[33990]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy\ndyld[33990]: <9CDA611B-254A-3779-9356-369485134C2D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis\ndyld[33990]: <0C8F41C6-6D93-3DB3-B522-CA8CFF5C3B33> /System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight\ndyld[33990]: <9E126CE0-FBB2-3B15-953F-CCDC758E34FB> /System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib\ndyld[33990]: <07CF779F-8F51-3764-B486-23D76868FF91> /System/Library/PrivateFrameworks/BiomeLibrary.framework/Versions/A/BiomeLibrary\ndyld[33990]: <959C748F-8851-3A25-BFFA-5FEA80296965> /System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard\ndyld[33990]: /System/Library/PrivateFrameworks/BoardServices.framework/Versions/A/BoardServices\ndyld[33990]: /System/Library/PrivateFrameworks/BackBoardServices.framework/Versions/A/BackBoardServices\ndyld[33990]: <7F763DF9-EA7F-3938-B599-DCCF4605E610> /System/Library/PrivateFrameworks/BackBoardHIDEventFoundation.framework/Versions/A/BackBoardHIDEventFoundation\ndyld[33990]: /System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay\ndyld[33990]: /System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox\ndyld[33990]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders\ndyld[33990]: /System/Library/PrivateFrameworks/ProDisplayLibrary.framework/Versions/A/ProDisplayLibrary\ndyld[33990]: <1E529C1A-B09C-3EB7-A286-CE00E292D561> /System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator\ndyld[33990]: <493E76D9-74D4-333B-A3B2-E5F9BC86429D> /System/Library/Frameworks/Metal.framework/Versions/A/Metal\ndyld[33990]: /System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator\ndyld[33990]: /System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia\ndyld[33990]: /System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient\ndyld[33990]: <98CB7012-30E5-3BDD-8C84-CDBDA9DB3017> /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore\ndyld[33990]: <57F7BB9C-649D-3360-AA86-A502815D77FA> /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport\ndyld[33990]: <625F222D-6394-39B9-A1F2-12B9EA56DD85> /usr/lib/swift/libswiftAccelerate.dylib\ndyld[33990]: /usr/lib/swift/libswiftCoreAudio.dylib\ndyld[33990]: /usr/lib/swift/libswiftCoreMedia.dylib\ndyld[33990]: <7235A6A9-49B2-3B94-9DD6-C987019CDBF2> /usr/lib/swift/libswiftMetal.dylib\ndyld[33990]: <9670AE5C-271A-3DCB-9A0A-8E3A7CCC2726> /usr/lib/swift/libswiftOSLog.dylib\ndyld[33990]: <63444A8C-9E8C-3778-820D-1E0C88CA2DF7> /usr/lib/swift/libswiftQuartzCore.dylib\ndyld[33990]: /usr/lib/swift/libswiftUniformTypeIdentifiers.dylib\ndyld[33990]: <9247A5B6-A883-3A07-BEE7-A223840317A4> /usr/lib/swift/libswiftVideoToolbox.dylib\ndyld[33990]: /usr/lib/swift/libswiftsimd.dylib\ndyld[33990]: <2110407D-EFB4-373E-B963-9C92E26594B2> /System/Library/PrivateFrameworks/BiomeStreams.framework/Versions/A/BiomeStreams\ndyld[33990]: /System/Library/PrivateFrameworks/BiomeStorage.framework/Versions/A/BiomeStorage\ndyld[33990]: <455A5553-E683-30B4-A906-1F14E75F6E61> /System/Library/PrivateFrameworks/BiomeFoundation.framework/Versions/A/BiomeFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/IntelligencePlatformLibrary.framework/Versions/A/IntelligencePlatformLibrary\ndyld[33990]: <42CDC0E6-51BA-3804-BD3E-EDF87FC74034> /System/Library/PrivateFrameworks/PoirotSchematizer.framework/Versions/A/PoirotSchematizer\ndyld[33990]: <2362E209-EC61-3FFC-9486-1244BB29BE82> /System/Library/PrivateFrameworks/BiomeSync.framework/Versions/A/BiomeSync\ndyld[33990]: /System/Library/PrivateFrameworks/BiomeDSL.framework/Versions/A/BiomeDSL\ndyld[33990]: <0F03104F-FC8B-3ADD-8850-4B7029E2B56E> /System/Library/PrivateFrameworks/BiomePubSub.framework/Versions/A/BiomePubSub\ndyld[33990]: <73EE1A0A-0D29-3104-98CB-BEFEDA53F7C0> /System/Library/PrivateFrameworks/ProactiveSupport.framework/Versions/A/ProactiveSupport\ndyld[33990]: /System/Library/PrivateFrameworks/FeatureFlags.framework/Versions/A/FeatureFlags\ndyld[33990]: /System/Library/PrivateFrameworks/PoirotUDFs.framework/Versions/A/PoirotUDFs\ndyld[33990]: /usr/lib/swift/libswift_DarwinFoundation2.dylib\ndyld[33990]: <8D2C31B5-FB10-3BF6-8566-F0DCD56C8582> /usr/lib/swift/libswift_DarwinFoundation3.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime\ndyld[33990]: <858910C5-1D4A-37B7-BF0E-EE02E24A2ACD> /System/Library/PrivateFrameworks/InternationalTextSearch.framework/Versions/A/InternationalTextSearch\ndyld[33990]: <13271AA6-33EA-369B-B2D1-6EC528C820E7> /System/Library/PrivateFrameworks/SoftwareUpdateCoreSupport.framework/Versions/A/SoftwareUpdateCoreSupport\ndyld[33990]: <2CA857AF-D999-34DC-94A1-3AC0E5B80416> /System/Library/PrivateFrameworks/SoftwareUpdateCoreConnect.framework/Versions/A/SoftwareUpdateCoreConnect\ndyld[33990]: /usr/lib/libbootpolicy.dylib\ndyld[33990]: /usr/lib/libpartition2_dynamic.dylib\ndyld[33990]: <9A8926C8-36A6-3DB4-A485-059C1F630984> /usr/lib/libAppleArchive.dylib\ndyld[33990]: <5FFE1FFA-6BD0-32AF-A815-7543731CA763> /usr/lib/libbz2.1.0.dylib\ndyld[33990]: <06728C4D-5750-308F-8290-EAF7BE91F4BB> /System/Library/PrivateFrameworks/OSAnalytics.framework/Versions/A/OSAnalytics\ndyld[33990]: <2FA711C7-F764-363A-BF03-295E0DA88B79> /System/Library/PrivateFrameworks/DeviceRecovery.framework/Versions/A/DeviceRecovery\ndyld[33990]: <59136324-34E6-3367-92BB-659346907A04> /System/Library/PrivateFrameworks/CoreSymbolication.framework/Versions/A/CoreSymbolication\ndyld[33990]: <724D42FC-F4FD-39C7-A1BF-D0AD086231F4> /System/Library/PrivateFrameworks/Symbolication.framework/Versions/A/Symbolication\ndyld[33990]: <7C923545-F3BB-3215-9720-85196358D9F1> /System/Library/PrivateFrameworks/DebugSymbols.framework/Versions/A/DebugSymbols\ndyld[33990]: <566F2D7D-0F3B-3290-A739-7A40A151F0BE> /System/Library/PrivateFrameworks/MallocStackLogging.framework/Versions/A/MallocStackLogging\ndyld[33990]: <7B63C2BF-8C7C-3ECA-ACD9-F1B75DBE018C> /usr/lib/swift/libswift_RegexParser.dylib\ndyld[33990]: <4646F780-1D5E-3EE7-B00A-64619293CC18> /usr/lib/libiconv.2.dylib\ndyld[33990]: <1940124C-0D73-35D2-9D94-A75F116088A0> /usr/lib/libcharset.1.dylib\ndyld[33990]: <24779350-BC29-3465-AAB3-F7CD0DA5844A> /System/Library/PrivateFrameworks/PoirotSQLite.framework/Versions/A/PoirotSQLite\ndyld[33990]: <2091B02D-8D55-3DC4-8097-60C193D03C85> /System/Library/PrivateFrameworks/CascadeSets.framework/Versions/A/CascadeSets\ndyld[33990]: <7F00413A-4D40-3DBF-8FD5-859B23E6DC03> /System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG\ndyld[33990]: /usr/lib/libexpat.1.dylib\ndyld[33990]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib\ndyld[33990]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib\ndyld[33990]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib\ndyld[33990]: <7304F8B3-8E0F-3813-BFAF-9A565CEA0A11> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib\ndyld[33990]: <01AAD3B4-D6BA-36D9-BA6F-D494D2AC161D> /usr/lib/libate.dylib\ndyld[33990]: <8EA6CA42-AA01-3C0F-9672-4917481BAAAE> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib\ndyld[33990]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib\ndyld[33990]: <526C249F-FF2E-3DC4-A639-B41A032E8CCE> /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libllvm-flatbuffers.dylib\ndyld[33990]: <1FDD3B19-C04A-3EE7-B7DF-E1F89954A696> /System/Library/PrivateFrameworks/FramePacing.framework/Versions/A/FramePacing\ndyld[33990]: <2C410B78-B9A5-30DC-8D83-FFEC1277F34C> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libGPUCompilerUtils.dylib\ndyld[33990]: <90CFC86E-833E-3E9F-BAAC-2B61BD750DA6> /System/Library/PrivateFrameworks/CoreDuetContext.framework/Versions/A/CoreDuetContext\ndyld[33990]: <838F99F9-D3FA-335B-9767-B5D04A3FACA6> /System/Library/PrivateFrameworks/CoreDuet.framework/Versions/A/CoreDuet\ndyld[33990]: <712AD9C1-44D2-36F4-BA8E-15038521462B> /System/Library/Frameworks/CoreData.framework/Versions/A/CoreData\ndyld[33990]: <9805BB7B-12C9-39F5-9070-C5B8BFCAE2AF> /System/Library/Frameworks/CoreLocation.framework/Versions/A/CoreLocation\ndyld[33990]: /System/Library/Frameworks/Intents.framework/Versions/A/Intents\ndyld[33990]: /System/Library/Frameworks/_LocationEssentials.framework/Versions/A/_LocationEssentials\ndyld[33990]: <1DAFDDDA-BB7B-320E-BCFC-B7C22886D486> /System/Library/PrivateFrameworks/GeoServices.framework/Versions/A/GeoServices\ndyld[33990]: /System/Library/PrivateFrameworks/LocationSupport.framework/Versions/A/LocationSupport\ndyld[33990]: <515FDCCC-535A-398B-BBD3-3D35565F5423> /System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth\ndyld[33990]: <38EE3C42-06D6-3A46-A420-DF701A4EA911> /System/Library/PrivateFrameworks/GeoServicesCore.framework/Versions/A/GeoServicesCore\ndyld[33990]: <8D0ECDD1-24B6-3B8D-9CF9-CDC55FC64490> /System/Library/PrivateFrameworks/PhoneNumbers.framework/Versions/A/PhoneNumbers\ndyld[33990]: <3D533C35-3A2A-3672-92EF-5FEE9EE739AC> /System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation\ndyld[33990]: <2B5FB7B0-844C-3D84-9EFD-020B285B0F8D> /System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport\ndyld[33990]: <62740FDD-2B16-3319-B5C9-022D45C6B03A> /System/Library/PrivateFrameworks/OSEligibility.framework/Versions/A/OSEligibility\ndyld[33990]: <10C63D59-07BC-3518-87A0-83CAC48D8A70> /System/Library/PrivateFrameworks/IconServices.framework/Versions/A/IconServices\ndyld[33990]: <8EF56F82-8CCE-3811-AD16-6D0939187B45> /System/Library/Frameworks/LightweightCodeRequirements.framework/Versions/A/LightweightCodeRequirements\ndyld[33990]: /System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit\ndyld[33990]: <1946F8FE-0ABC-3F8F-9116-5451ECABD14C> /System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices\ndyld[33990]: /System/Library/PrivateFrameworks/IconFoundation.framework/Versions/A/IconFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/AssistantServices.framework/Versions/A/AssistantServices\ndyld[33990]: <6A34A62A-16D4-34F0-B34B-2D96B53C20AD> /System/Library/PrivateFrameworks/IconRendering.framework/Versions/A/IconRendering\ndyld[33990]: /System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI\ndyld[33990]: <0943679D-FF88-3F18-BE4B-D8B4827AB0B5> /System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage\ndyld[33990]: <968B5A5F-9749-3527-AF2A-66B599785308> /System/Library/PrivateFrameworks/SFSymbols.framework/Versions/A/SFSymbols\ndyld[33990]: /System/Library/Frameworks/DeveloperToolsSupport.framework/Versions/A/DeveloperToolsSupport\ndyld[33990]: <92090A92-DFAF-3EBC-886C-655EC158A53F> /System/Library/PrivateFrameworks/RenderBox.framework/Versions/A/RenderBox\ndyld[33990]: <986D57A7-BFF1-3DAA-8EB1-17CCAA76C731> /System/Library/PrivateFrameworks/CoreSVG.framework/Versions/A/CoreSVG\ndyld[33990]: /System/Library/PrivateFrameworks/TextureIO.framework/Versions/A/TextureIO\ndyld[33990]: /usr/lib/swift/libswiftCoreImage.dylib\ndyld[33990]: <77D85BA0-FE1C-3B5A-92DB-70A30202C990> /System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer\ndyld[33990]: /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL\ndyld[33990]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib\ndyld[33990]: <6CEF3932-AAC9-3F8E-905D-A826F2884C9A> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib\ndyld[33990]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib\ndyld[33990]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib\ndyld[33990]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib\ndyld[33990]: <07CB5D41-C2F3-3C33-951F-67B2C8B8B662> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices\ndyld[33990]: <5D3E7FFF-AC8E-3D6F-8E99-B199E593D270> /System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG\ndyld[33990]: <49E7449E-1385-3B53-94CC-36EFC31E98FE> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib\ndyld[33990]: <23C577A8-DB0B-3A0A-9058-1289483C262A> /usr/lib/libhvf.dylib\ndyld[33990]: <11E757EC-72FB-3C53-8ED7-641428AB6169> /System/Library/PrivateFrameworks/ParsingInternal.framework/Versions/A/ParsingInternal\ndyld[33990]: /System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib\ndyld[33990]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore\ndyld[33990]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage\ndyld[33990]: <199F6401-91D0-36E9-9EA9-D4B44ED1CE3A> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork\ndyld[33990]: <4D134FE3-50EE-39D5-9699-04B4B673DD35> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix\ndyld[33990]: <2E7E2722-3821-3DBF-B25A-6EA45D1A8FD4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector\ndyld[33990]: <3E1FE9EA-34A2-3545-B639-48B1FE1FD3D4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray\ndyld[33990]: <3103E210-FF5C-3677-BDD3-59FF17A6ACEC> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions\ndyld[33990]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSBenchmarkLoop.framework/Versions/A/MPSBenchmarkLoop\ndyld[33990]: <31F90368-23A5-39BB-822B-C8470C4479AE> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSHost.framework/Versions/A/MPSHost\ndyld[33990]: <9C416BB2-0882-315C-AF23-F476E34983BC> /System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools\ndyld[33990]: /System/Library/PrivateFrameworks/IOAccelMemoryInfo.framework/Versions/A/IOAccelMemoryInfo\ndyld[33990]: /System/Library/PrivateFrameworks/kperf.framework/Versions/A/kperf\ndyld[33990]: <03470B3A-A004-39A0-B6A4-F2A4AFFFCDD3> /System/Library/PrivateFrameworks/GPURawCounter.framework/Versions/A/GPURawCounter\ndyld[33990]: <4D8F39C6-B221-3AF1-BB40-CAEB0A174D61> /System/Library/PrivateFrameworks/ASEProcessing.framework/Versions/A/ASEProcessing\ndyld[33990]: /System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing\ndyld[33990]: <1B4C0154-843C-3CEE-9628-22978082DD2D> /System/Library/PrivateFrameworks/SILManager.framework/Versions/A/SILManager\ndyld[33990]: /System/Library/PrivateFrameworks/Tightbeam.framework/Versions/A/Tightbeam\ndyld[33990]: <856ACB2A-3334-3BA6-AAC8-8F344E7CDB83> /usr/lib/swift/libswiftCompression.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/AFKUser.framework/Versions/A/AFKUser\ndyld[33990]: <2186F196-EE17-3A59-B9DA-D6823BEDD35B> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI\ndyld[33990]: <086BB8AD-E317-3FC4-9E44-0D7C6036E8D7> /System/Library/PrivateFrameworks/SAObjects.framework/Versions/A/SAObjects\ndyld[33990]: /System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox\ndyld[33990]: /System/Library/PrivateFrameworks/MediaRemote.framework/Versions/A/MediaRemote\ndyld[33990]: <7F1A25E4-ED0A-3502-AABA-26EDB4A0D2A7> /System/Library/Frameworks/UserNotifications.framework/Versions/A/UserNotifications\ndyld[33990]: <8A5E0FF6-3116-3082-A0AD-20DCD6C5E1B4> /System/Library/PrivateFrameworks/SiriInstrumentation.framework/Versions/A/SiriInstrumentation\ndyld[33990]: <600E036E-9B18-35BE-B40B-E8D2D53AC90D> /System/Library/PrivateFrameworks/SiriAnalytics.framework/Versions/A/SiriAnalytics\ndyld[33990]: <619E6770-766A-3629-9AF8-F32C009375E9> /System/Library/PrivateFrameworks/SiriTTSService.framework/Versions/A/SiriTTSService\ndyld[33990]: <71CAE70A-72AD-3F74-834D-3519B545C08D> /System/Library/PrivateFrameworks/SiriCrossDeviceArbitration.framework/Versions/A/SiriCrossDeviceArbitration\ndyld[33990]: /System/Library/PrivateFrameworks/FeedbackLogger.framework/Versions/A/FeedbackLogger\ndyld[33990]: <55FBCBE4-1032-3017-BA49-D734B82405DF> /System/Library/PrivateFrameworks/FaceTimeNameUtility.framework/Versions/A/FaceTimeNameUtility\ndyld[33990]: /System/Library/PrivateFrameworks/SiriCrossDeviceArbitrationFeedback.framework/Versions/A/SiriCrossDeviceArbitrationFeedback\ndyld[33990]: <368BC882-02B9-38AB-89B4-F62430F2B8EB> /usr/lib/swift/libswiftCoreLocation.dylib\ndyld[33990]: /usr/lib/swift/libswiftAVFoundation.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/UIKitServices.framework/Versions/A/UIKitServices\ndyld[33990]: <54A2CBB8-623D-3629-904A-D0399ED13547> /System/Library/PrivateFrameworks/UnifiedAssetFramework.framework/Versions/A/UnifiedAssetFramework\ndyld[33990]: <8AF1606D-5C93-3B80-BC81-60C5688628E2> /System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore\ndyld[33990]: /System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession\ndyld[33990]: <52BD9E26-B356-3EAA-9AD7-7FF700C61A91> /System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI\ndyld[33990]: <3FF99846-E48C-3C9A-814C-35B45E5F60EC> /usr/lib/libAudioStatistics.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk\ndyld[33990]: /System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio\ndyld[33990]: <75F77FEC-BE14-3C97-93DA-403C3B529D3B> /usr/lib/libAudioToolboxUtility.dylib\ndyld[33990]: <7AE04E20-83FD-3B1B-8846-E9869AD98DB5> /usr/lib/swift/libswiftCoreMIDI.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata\ndyld[33990]: /System/Library/PrivateFrameworks/AudioDSPGraph.framework/Versions/A/AudioDSPGraph\ndyld[33990]: <6108A12D-286B-3CF2-B848-B0E7A0189DCC> /System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy\ndyld[33990]: <655F6374-6CE8-3D0E-994E-4D7C37F78E89> /usr/lib/libSMC.dylib\ndyld[33990]: <912BFF10-FB8F-3D52-9941-ACDCE1CAE36A> /usr/lib/libperfcheck.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/AudioAnalytics.framework/Versions/A/AudioAnalytics\ndyld[33990]: <869F0693-0E82-38C1-8920-C782E71735CA> /System/Library/Frameworks/OSLog.framework/Versions/A/OSLog\ndyld[33990]: <173A632F-20F3-30C1-BC55-EFFE977BBB8E> /usr/lib/libmis.dylib\ndyld[33990]: <52A7AD42-9DE0-393B-A6FB-A7CB6FF8F3A5> /System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience\ndyld[33990]: <1A63E9E1-2D64-3AF4-9CCD-6EF042397F84> /System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore\ndyld[33990]: <04DC06C1-2BFA-3FEE-9429-A33E41721A3E> /usr/lib/libspindump.dylib\ndyld[33990]: <7CC1BC36-42C1-39A3-AA4F-F9C8ABCE0CD5> /System/Library/PrivateFrameworks/AudioAccessoryServices.framework/Versions/A/AudioAccessoryServices\ndyld[33990]: /System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils\ndyld[33990]: /System/Library/PrivateFrameworks/HID.framework/Versions/A/HID\ndyld[33990]: <920D8AA6-CDCD-3E0F-AD66-F0673ACABD3F> /System/Library/PrivateFrameworks/Sharing.framework/Versions/A/Sharing\ndyld[33990]: <3DD8C4CA-23E9-35CF-AD67-549DD72D3344> /System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras\ndyld[33990]: <236517AD-8D16-3E62-8603-EBE7B65ACACA> /System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211\ndyld[33990]: <42F76533-D8DD-3A24-A08A-103C51428797> /System/Library/PrivateFrameworks/IDSFoundation.framework/Versions/A/IDSFoundation\ndyld[33990]: <116D159B-163E-3F91-9591-30FF8B6EB537> /System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Apple80211\ndyld[33990]: /System/Library/Frameworks/CoreWLAN.framework/Versions/A/CoreWLAN\ndyld[33990]: <1ABB6C50-A5DE-3744-8F07-8C3B5617B0B9> /System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth\ndyld[33990]: <82D79BDA-26A0-3A44-AAC8-911411801FDE> /usr/lib/swift/libswiftRegexBuilder.dylib\ndyld[33990]: <41E45E0C-2E88-3605-B213-F7CD760A9FF4> /System/Library/PrivateFrameworks/IMFoundation.framework/Versions/A/IMFoundation\ndyld[33990]: <40F60A3F-90A9-3F07-A99D-005E3158C6F6> /System/Library/PrivateFrameworks/Marco.framework/Versions/A/Marco\ndyld[33990]: <59FFD032-1427-39F6-BC16-A6877582A243> /System/Library/PrivateFrameworks/CommonUtilities.framework/Versions/A/CommonUtilities\ndyld[33990]: <6C3E51F8-D809-3AA1-8695-B75714C2D39A> /System/Library/PrivateFrameworks/Engram.framework/Versions/A/Engram\ndyld[33990]: /System/Library/PrivateFrameworks/XPCDistributed.framework/Versions/A/XPCDistributed\ndyld[33990]: <12066854-2BE4-35DF-BA9F-B38221C980FD> /usr/lib/libtidy.A.dylib\ndyld[33990]: <63F598E2-AF8A-3F29-BE11-3F14DB377A5B> /System/Library/PrivateFrameworks/Bom.framework/Versions/A/Bom\ndyld[33990]: /usr/lib/libParallelCompression.dylib\ndyld[33990]: <9E06CB59-0638-3C9F-B202-264E739433AC> /usr/lib/libIOReport.dylib\ndyld[33990]: <15EEE715-2670-3288-AFA8-504BAD951B4F> /System/Library/PrivateFrameworks/WiFiPeerToPeer.framework/Versions/A/WiFiPeerToPeer\ndyld[33990]: <3854272C-7B14-3A3C-9BB1-F0FBA394708A> /System/Library/PrivateFrameworks/Centauri.framework/Versions/A/Centauri\ndyld[33990]: <946B1484-B180-3451-A452-B54BF5A6D392> /System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon\ndyld[33990]: <2B49C295-4EA2-3DE3-90B4-DC03A96F2657> /usr/lib/libmrc.dylib\ndyld[33990]: <6661265C-7B78-3158-9011-4BFDFFEF7807> /System/Library/PrivateFrameworks/IPConfiguration.framework/Versions/A/IPConfiguration\ndyld[33990]: /System/Library/PrivateFrameworks/Netrb.framework/Versions/A/Netrb\ndyld[33990]: /System/Library/PrivateFrameworks/FrontBoardServices.framework/Versions/A/FrontBoardServices\ndyld[33990]: /System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData\ndyld[33990]: <757FEDFF-841C-3D62-B703-CDE79E929363> /System/Library/PrivateFrameworks/GraphicsServices.framework/Versions/A/GraphicsServices\ndyld[33990]: <093EF25B-5305-3611-B068-E65071858F52> /System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit\ndyld[33990]: /System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory\ndyld[33990]: <3B7FD4C1-D1D4-3DA9-B2F8-3D4094679D76> /System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory\ndyld[33990]: <016C5057-625C-30B1-AD32-7BC9D082F05B> /System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio\ndyld[33990]: /System/Library/Frameworks/AVRouting.framework/Versions/A/AVRouting\ndyld[33990]: <5240B3A0-D035-345E-A636-BC3A92C847C4> /usr/lib/libAccessibility.dylib\ndyld[33990]: <1FB2BCFD-D9FC-385A-A0EA-E2B5052E27F5> /System/Library/PrivateFrameworks/MediaServices.framework/Versions/A/MediaServices\ndyld[33990]: /System/Library/PrivateFrameworks/IDS.framework/Versions/A/IDS\ndyld[33990]: /System/Library/PrivateFrameworks/IsolatedCoreAudioClient.framework/Versions/A/IsolatedCoreAudioClient\ndyld[33990]: <7CC0621B-3B88-3533-A3FB-52E6214486EE> /System/Library/PrivateFrameworks/CoreAudioOrchestration.framework/Versions/A/CoreAudioOrchestration\ndyld[33990]: /System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox\ndyld[33990]: /System/Library/PrivateFrameworks/CoreAVCHD.framework/Versions/A/CoreAVCHD\ndyld[33990]: <74D313A5-4D99-35D1-A4C9-B76AB6457EF0> /System/Library/Frameworks/MediaAccessibility.framework/Versions/A/MediaAccessibility\ndyld[33990]: <87F549F4-73CC-302B-ABDB-D3CCFADABFA9> /System/Library/PrivateFrameworks/Mangrove.framework/Versions/A/Mangrove\ndyld[33990]: <214294AE-C7B7-3C9A-A4F8-201C989F9779> /System/Library/PrivateFrameworks/CMPhoto.framework/Versions/A/CMPhoto\ndyld[33990]: <5F090F48-E481-3737-8E75-362E5D274879> /System/Library/Frameworks/CoreTelephony.framework/Versions/A/CoreTelephony\ndyld[33990]: <9ACFCA55-82CB-33DB-AD00-443576099FDB> /System/Library/PrivateFrameworks/CoreAUC.framework/Versions/A/CoreAUC\ndyld[33990]: <71A0C0AD-67F3-36F9-BF73-6DD5D7424AF7> /System/Library/PrivateFrameworks/AppleJPEGXL.framework/Versions/A/AppleJPEGXL\ndyld[33990]: <825E8416-E246-338E-A5CF-AA81A1B01DD9> /System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport\ndyld[33990]: <7CF84496-675C-3241-B0EF-E83C95F188FA> /System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA\ndyld[33990]: <63A6BBA0-CD50-30F8-9CD2-81B59264EA13> /usr/lib/libTelephonyUtilDynamic.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler\ndyld[33990]: /System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment\ndyld[33990]: /System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay\ndyld[33990]: /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit\ndyld[33990]: /System/Library/PrivateFrameworks/CryptoKitCBridging.framework/Versions/A/CryptoKitCBridging\ndyld[33990]: <714063A8-D81E-3B22-9B36-88948A979E7F> /System/Library/Frameworks/CryptoTokenKit.framework/Versions/A/CryptoTokenKit\ndyld[33990]: <86858734-8B4D-38E6-AAA7-B7A046A7CB2A> /System/Library/Frameworks/LocalAuthentication.framework/Versions/A/LocalAuthentication\ndyld[33990]: /System/Library/PrivateFrameworks/LocalAuthenticationCore.framework/Versions/A/LocalAuthenticationCore\ndyld[33990]: /System/Library/PrivateFrameworks/LocalAuthenticationCredentialServices.framework/Versions/A/LocalAuthenticationCredentialServices\ndyld[33990]: /System/Library/Frameworks/LocalAuthentication.framework/Support/SharedUtils.framework/Versions/A/SharedUtils\ndyld[33990]: <80C3E2D4-B6B8-3C62-B257-27DEEBAD4935> /usr/lib/libcsfde.dylib\ndyld[33990]: <650E155C-1FE3-36ED-8D84-157D380F7F95> /usr/lib/libCoreStorage.dylib\ndyld[33990]: <46DD93AF-BACD-309B-AD51-9CC47C78CA2C> /System/Library/PrivateFrameworks/MediaKit.framework/Versions/A/MediaKit\ndyld[33990]: /System/Library/Frameworks/DiscRecording.framework/Versions/A/DiscRecording\ndyld[33990]: <1F8700BE-BD91-3B94-AC32-A5F10CEFEE35> /System/Library/PrivateFrameworks/ProtectedCloudStorage.framework/Versions/A/ProtectedCloudStorage\ndyld[33990]: /System/Library/PrivateFrameworks/EFILogin.framework/Versions/A/EFILogin\ndyld[33990]: /System/Library/PrivateFrameworks/PersistentConnection.framework/Versions/A/PersistentConnection\ndyld[33990]: <6B0D099C-AC56-35DB-90F1-88A09E587FCB> /System/Library/PrivateFrameworks/SonicFoundation.framework/Versions/A/SonicFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/AsyncAlgorithmsInternal.framework/Versions/A/AsyncAlgorithmsInternal\ndyld[33990]: <2CA62C12-37B5-345A-BF79-5D05F43F6BFB> /System/Library/PrivateFrameworks/FTAWD.framework/Versions/A/FTAWD\ndyld[33990]: <0A1C4D11-C108-35E9-A921-86ED86CF7446> /System/Library/PrivateFrameworks/Dendrite.framework/Versions/A/Dendrite\ndyld[33990]: /usr/lib/libtailspin.dylib\ndyld[33990]: <5FEA8C08-1577-3296-BC9C-7F3203E8EBFB> /System/Library/PrivateFrameworks/Osprey.framework/Versions/A/Osprey\ndyld[33990]: /System/Library/PrivateFrameworks/SiriTTS.framework/Versions/A/SiriTTS\ndyld[33990]: <0C005C4D-CA12-389C-9CCE-C4ED05B187E8> /System/Library/Frameworks/NaturalLanguage.framework/Versions/A/NaturalLanguage\ndyld[33990]: <0E502870-00F4-35D4-AF82-E7059244798E> /System/Library/PrivateFrameworks/GenerativeModels.framework/Versions/A/GenerativeModels\ndyld[33990]: <68474F39-798D-325B-B52F-3DE214F279AE> /System/Library/PrivateFrameworks/SiriPowerInstrumentation.framework/Versions/A/SiriPowerInstrumentation\ndyld[33990]: <5E36265A-7670-3D39-A2B8-71DA0AA131CF> /usr/lib/swift/libswiftNaturalLanguage.dylib\ndyld[33990]: <6794652C-86F0-37EB-838D-483177685E26> /System/Library/PrivateFrameworks/TailspinSymbolication.framework/Versions/A/TailspinSymbolication\ndyld[33990]: <089C1A34-2F4E-3649-94AA-B28A7ECB008B> /System/Library/PrivateFrameworks/Darwinup.framework/Versions/A/Darwinup\ndyld[33990]: /System/Library/PrivateFrameworks/SignpostSupport.framework/Versions/A/SignpostSupport\ndyld[33990]: <8C10B437-C282-37F5-834F-E7179C700373> /System/Library/PrivateFrameworks/FeatureFlagsSupport.framework/Versions/A/FeatureFlagsSupport\ndyld[33990]: /System/Library/PrivateFrameworks/ktrace.framework/Versions/A/ktrace\ndyld[33990]: /System/Library/PrivateFrameworks/SampleAnalysis.framework/Versions/A/SampleAnalysis\ndyld[33990]: <400B0E96-4869-37BE-9832-1A14C386148B> /System/Library/PrivateFrameworks/kperfdata.framework/Versions/A/kperfdata\ndyld[33990]: <7E3E0CF7-905A-3244-A0C9-0ADCC2E16415> /usr/lib/libdscsym.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity\ndyld[33990]: <6FBA9099-E428-3571-B940-0D210B6D0861> /System/Library/PrivateFrameworks/BulkSymbolication.framework/Versions/A/BulkSymbolication\ndyld[33990]: <90E600A3-0A27-348A-AA57-D1DF4FB305E8> /usr/lib/libTLE.dylib\ndyld[33990]: <2D1B971F-6A7F-32D0-8B0F-F8FA3A13E8F1> /System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper\ndyld[33990]: <8F2949A6-43A0-30A2-B5D2-945949C5AA01> /System/Library/PrivateFrameworks/Espresso.framework/Versions/A/Espresso\ndyld[33990]: /System/Library/Frameworks/CoreML.framework/Versions/A/CoreML\ndyld[33990]: /usr/lib/libedit.3.dylib\ndyld[33990]: <465A74BC-F20D-3C05-9441-7E08EAA49FAF> /System/Library/PrivateFrameworks/ANECompiler.framework/Versions/A/ANECompiler\ndyld[33990]: /System/Library/PrivateFrameworks/AppleNeuralEngine.framework/Versions/A/AppleNeuralEngine\ndyld[33990]: <97C5C585-F5EE-323A-B949-69EAE9080871> /System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL\ndyld[33990]: <7401E849-7B2E-39A9-99D3-5CB0A6BBDFFE> /System/Library/Frameworks/MetalPerformanceShadersGraph.framework/Versions/A/MetalPerformanceShadersGraph\ndyld[33990]: /System/Library/PrivateFrameworks/MLCompilerServices.framework/Versions/A/MLCompilerServices\ndyld[33990]: /System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices\ndyld[33990]: <9EB04E94-EE2D-38A5-A214-00AF73DBE4E9> /usr/lib/libncurses.5.4.dylib\ndyld[33990]: /usr/lib/libsandbox.1.dylib\ndyld[33990]: <2F2EF0D7-2FE4-3A5A-8E4C-E1571C8D0C10> /usr/lib/libMatch.1.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/ODIE.framework/Versions/A/ODIE\ndyld[33990]: /System/Library/PrivateFrameworks/MLModelAsset.framework/Versions/A/MLModelAsset\ndyld[33990]: <22B4CD07-5C72-3CA4-9CD1-2C87686CDDE5> /System/Library/PrivateFrameworks/MLCompilerRuntime.framework/Versions/A/MLCompilerRuntime\ndyld[33990]: /System/Library/Frameworks/MLCompute.framework/Versions/A/MLCompute\ndyld[33990]: <6028DD46-8E5A-33F0-93B2-41FA480366CC> /System/Library/PrivateFrameworks/MLAssetIO.framework/Versions/A/MLAssetIO\ndyld[33990]: /usr/lib/swift/libswiftMLCompute.dylib\ndyld[33990]: <067E2603-4FEA-3CA5-8926-45F60681EDDB> /System/Library/PrivateFrameworks/AVFCore.framework/Versions/A/AVFCore\ndyld[33990]: /System/Library/PrivateFrameworks/AVFCapture.framework/Versions/A/AVFCapture\ndyld[33990]: <3B782AC2-00C4-3534-91D2-5C7242B32440> /System/Library/PrivateFrameworks/CMImaging.framework/Versions/A/CMImaging\ndyld[33990]: <1772C40D-6EF4-3F81-BA00-6EE8B05039A6> /System/Library/PrivateFrameworks/Quagga.framework/Versions/A/Quagga\ndyld[33990]: <57A10B70-C3C9-34C6-8D22-F5118B63E2F0> /System/Library/PrivateFrameworks/CMCapture.framework/Versions/A/CMCapture\ndyld[33990]: <1035C1AB-5058-3AFA-8D77-514901516251> /System/Library/Frameworks/CoreMediaIO.framework/Versions/A/CoreMediaIO\ndyld[33990]: /System/Library/PrivateFrameworks/CMCaptureDevice.framework/Versions/A/CMCaptureDevice\ndyld[33990]: <1F873909-B3B8-3D55-9673-9AFA86BB085B> /System/Library/PrivateFrameworks/CoreBrightness.framework/Versions/A/CoreBrightness\ndyld[33990]: /System/Library/PrivateFrameworks/CinematicFraming.framework/Versions/A/CinematicFraming\ndyld[33990]: <882BC08E-B1E1-3E52-AE8A-AC22A1BF2BE8> /System/Library/PrivateFrameworks/ModelManagerServices.framework/Versions/A/ModelManagerServices\ndyld[33990]: <3E83115F-D04B-3C8D-8646-35204AA2DB84> /System/Library/PrivateFrameworks/CPMS.framework/Versions/A/CPMS\ndyld[33990]: /System/Library/PrivateFrameworks/SystemStatus.framework/Versions/A/SystemStatus\ndyld[33990]: <2E109991-45C6-3783-8A36-B6A8070AAD67> /System/Library/Frameworks/CoreMotion.framework/Versions/A/CoreMotion\ndyld[33990]: /System/Library/PrivateFrameworks/TimeSync.framework/Versions/A/TimeSync\ndyld[33990]: <9B3D4CA3-7BCF-36C9-AA99-27BDFE7854CD> /System/Library/PrivateFrameworks/DistributedSensing.framework/Versions/A/DistributedSensing\ndyld[33990]: /System/Library/PrivateFrameworks/MobileBluetooth.framework/Versions/A/MobileBluetooth\ndyld[33990]: <0BAB3589-8D81-3C60-9F05-9D207E89F4B6> /System/Library/PrivateFrameworks/IOKitten.framework/Versions/A/IOKitten\ndyld[33990]: <8A2C8C17-E138-3B34-8643-ED4FB1C9049E> /System/Library/PrivateFrameworks/LocationLogEncryption.framework/Versions/A/LocationLogEncryption\ndyld[33990]: <05EC9C98-7211-39C9-B376-796F37327801> /System/Library/PrivateFrameworks/AppleIntelligenceReporting.framework/Versions/A/AppleIntelligenceReporting\ndyld[33990]: <47AAECAD-C28C-352E-BB86-7F292E0BFBC6> /System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji\ndyld[33990]: <327536E3-A27C-38C2-A67F-D6488D04CCEE> /System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling\ndyld[33990]: <95BA357E-906A-3183-A402-A41D486B5AB3> /System/Library/PrivateFrameworks/Montreal.framework/Versions/A/Montreal\ndyld[33990]: /usr/lib/libcmph.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/GenerativeModelsFoundation.framework/Versions/A/GenerativeModelsFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/TokenGeneration.framework/Versions/A/TokenGeneration\ndyld[33990]: <418985BB-52A3-34D4-8379-40DC4C63AA32> /System/Library/PrivateFrameworks/GenerativeFunctions.framework/Versions/A/GenerativeFunctions\ndyld[33990]: <63FD423F-836C-3034-BA48-100AE09A9140> /System/Library/PrivateFrameworks/GenerativeFunctionsFoundation.framework/Versions/A/GenerativeFunctionsFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/ModelCatalog.framework/Versions/A/ModelCatalog\ndyld[33990]: <67C3B698-8279-30F1-9167-4730E6F41F5A> /System/Library/PrivateFrameworks/SensitiveContentAnalysisML.framework/Versions/A/SensitiveContentAnalysisML\ndyld[33990]: /System/Library/PrivateFrameworks/GenerativeFunctionsInstrumentation.framework/Versions/A/GenerativeFunctionsInstrumentation\ndyld[33990]: /System/Library/PrivateFrameworks/PromptKit.framework/Versions/A/PromptKit\ndyld[33990]: <12245228-2B9A-3B24-8C5E-10111D68BE65> /System/Library/PrivateFrameworks/ProactiveDaemonSupport.framework/Versions/A/ProactiveDaemonSupport\ndyld[33990]: <3166486F-3F65-31DB-8018-779FFA32DC71> /System/Library/PrivateFrameworks/TokenGenerationCore.framework/Versions/A/TokenGenerationCore\ndyld[33990]: <53B3126E-7B01-30DD-961A-510E9CFC3CF1> /System/Library/PrivateFrameworks/Trial.framework/Versions/A/Trial\ndyld[33990]: /System/Library/PrivateFrameworks/TrialProto.framework/Versions/A/TrialProto\ndyld[33990]: /System/Library/PrivateFrameworks/AppleFlatBuffers.framework/Versions/A/AppleFlatBuffers\ndyld[33990]: <642E3357-AB6D-3039-A818-EDB5D6A189C2> /System/Library/PrivateFrameworks/SentencePieceInternal.framework/Versions/A/SentencePieceInternal\ndyld[33990]: <10F83439-3A9F-316B-992E-451A72876715> /System/Library/Frameworks/Vision.framework/Versions/A/Vision\ndyld[33990]: /System/Library/PrivateFrameworks/CoreSceneUnderstanding.framework/Versions/A/CoreSceneUnderstanding\ndyld[33990]: <4E70B4ED-C8E0-3636-80E4-0939FE56BB63> /System/Library/PrivateFrameworks/VisionCore.framework/Versions/A/VisionCore\ndyld[33990]: /System/Library/PrivateFrameworks/DataDetectorsCore.framework/Versions/A/DataDetectorsCore\ndyld[33990]: /System/Library/Frameworks/Vision.framework/libfaceCore.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/Futhark.framework/Versions/A/Futhark\ndyld[33990]: /System/Library/PrivateFrameworks/InertiaCam.framework/Versions/A/InertiaCam\ndyld[33990]: /System/Library/PrivateFrameworks/TextRecognition.framework/Versions/A/TextRecognition\ndyld[33990]: <73F6F860-69AF-3162-86E0-A683642287D6> /System/Library/Frameworks/DataDetection.framework/Versions/A/DataDetection\ndyld[33990]: <1D5DF9CA-41FC-3B7B-B19F-2C435F3A66F2> /System/Library/PrivateFrameworks/TextInput.framework/Versions/A/TextInput\ndyld[33990]: /System/Library/PrivateFrameworks/CVNLP.framework/Versions/A/CVNLP\ndyld[33990]: <58AC6CAB-5B91-367F-932F-BD0939BBD125> /System/Library/PrivateFrameworks/IntentsFoundation.framework/Versions/A/IntentsFoundation\ndyld[33990]: <27479D70-8BF6-3D3C-B528-1BB9B1B98391> /System/Library/PrivateFrameworks/ApplePushService.framework/Versions/A/ApplePushService\ndyld[33990]: <676D50CC-8455-3267-B8E8-CA31B8EF8F91> /System/Library/Frameworks/CloudKit.framework/Versions/A/CloudKit\ndyld[33990]: /System/Library/PrivateFrameworks/CoreDuetDaemonProtocol.framework/Versions/A/CoreDuetDaemonProtocol\ndyld[33990]: <962EA390-008E-3DDC-B2A5-7B2DAF6E8786> /System/Library/PrivateFrameworks/DeviceIdentity.framework/Versions/A/DeviceIdentity\ndyld[33990]: /System/Library/Frameworks/SharedWithYouCore.framework/Versions/A/SharedWithYouCore\ndyld[33990]: <121798D0-5254-3547-8F96-0F2AF8D84250> /System/Library/PrivateFrameworks/CloudTelemetry.framework/Versions/A/CloudTelemetry\ndyld[33990]: <768DFB9E-7FB3-3998-A3AF-BEF0C6C740A7> /System/Library/PrivateFrameworks/AppleAccount.framework/Versions/A/AppleAccount\ndyld[33990]: /System/Library/PrivateFrameworks/CacheDelete.framework/Versions/A/CacheDelete\ndyld[33990]: /System/Library/PrivateFrameworks/C2.framework/Versions/A/C2\ndyld[33990]: <23871A43-55FD-3D0C-B29F-B143A64D1D8D> /System/Library/PrivateFrameworks/CloudCoreInternal.framework/Versions/A/CloudCoreInternal\ndyld[33990]: /System/Library/PrivateFrameworks/CloudAsset.framework/Versions/A/CloudAsset\ndyld[33990]: <36607924-B1B2-39ED-B6D1-29683EFB67A0> /System/Library/Frameworks/PushKit.framework/Versions/A/PushKit\ndyld[33990]: <26865685-385E-3120-9886-2082EEC20B20> /System/Library/Frameworks/CoreTransferable.framework/Versions/A/CoreTransferable\ndyld[33990]: <5EA68C5E-69B0-3011-9D66-AEF49D82B29D> /System/Library/Frameworks/FileProvider.framework/Versions/A/FileProvider\ndyld[33990]: /System/Library/PrivateFrameworks/GenerationalStorage.framework/Versions/A/GenerationalStorage\ndyld[33990]: /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/DesktopServicesPriv\ndyld[33990]: <024DBF34-DF66-3164-825E-F77F85462E66> /System/Library/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth\ndyld[33990]: <87907862-52FF-3F24-AC29-7C1678BCD277> /System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport\ndyld[33990]: /System/Library/PrivateFrameworks/CloudTelemetryTools.framework/Versions/A/CloudTelemetryTools\ndyld[33990]: /System/Library/PrivateFrameworks/CloudTelemetryShared.dylib\ndyld[33990]: <7BEBC9F1-212D-37F4-B601-A7AAD12F7225> /System/Library/PrivateFrameworks/RTCReporting.framework/Versions/A/RTCReporting\ndyld[33990]: <7A222E30-8DD4-3B1D-B820-4EA33B797525> /System/Library/PrivateFrameworks/AAAFoundationSwift.framework/Versions/A/AAAFoundationSwift\ndyld[33990]: <9D724BE7-0B01-39F3-82BE-BCEDC6EBAC8A> /System/Library/PrivateFrameworks/AppleIDSSOAuthentication.framework/Versions/A/AppleIDSSOAuthentication\ndyld[33990]: <659AFBBD-E22E-3474-BCFE-298DA57B1464> /System/Library/PrivateFrameworks/UIFoundation.framework/Versions/A/UIFoundation\ndyld[33990]: <0368EA7D-01B2-3AA9-A6D6-A2A0850AC800> /System/Library/PrivateFrameworks/HIDDisplay.framework/Versions/A/HIDDisplay\ndyld[33990]: <6A5A8E21-A9E6-32A2-9BDB-8013F002AEF8> /usr/lib/libcups.2.dylib\ndyld[33990]: /System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos\ndyld[33990]: <4AB71911-9300-30D4-88CF-D20EFD75ACE6> /usr/lib/libresolv.9.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal\ndyld[33990]: <0CB2E7E3-E96F-343B-A4E7-545E74AF0255> /System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib\ndyld[33990]: <097F7235-CA53-3644-BB95-F6F912B4F2C7> /System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth\ndyld[33990]: /System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities\ndyld[33990]: /System/Library/PrivateFrameworks/AttributeGraph.framework/Versions/A/AttributeGraph\ndyld[33990]: /usr/lib/libAXSafeCategoryBundle.dylib\ndyld[33990]: /System/Library/Frameworks/TabularData.framework/Versions/A/TabularData\ndyld[33990]: <841D5662-2CB9-3A27-ADA7-E33AC5E45199> /System/Library/PrivateFrameworks/ArgumentParserInternal.framework/Versions/A/ArgumentParserInternal\ndyld[33990]: <4C851329-A9F4-3E9E-9E48-07FF4120DCF9> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib\ndyld[33990]: <5015CD96-C046-364D-AAE3-1F439044468B> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib\ndyld[33990]: <407BCF3E-A91F-3A7F-8B8C-DBB8E807990F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib\ndyld[33990]: <669ABE12-838F-3F14-8456-D60DE5DF8EB8> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib\ndyld[33990]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib\ndyld[33990]: <54A103BA-7D04-32DB-B204-179E2E0290CA> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib\ndyld[33990]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib\ndyld[33990]: <1ACDAA8A-EB43-37C7-B661-39B1C0E05290> /System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary\ndyld[33990]: <12479A32-B72F-3A09-BB03-BA56C37853B5> /System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore\ndyld[33990]: /usr/lib/libapp_launch_measurement.dylib\ndyld[33990]: <4F3BEA3B-A363-3D04-B903-9B613C993CA1> /System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices\ndyld[33990]: <6C426EA5-7F1E-333E-BB5D-74465EFED12B> /usr/lib/libxslt.1.dylib\ndyld[33990]: <627D64D5-2D3C-3EC6-B4AB-FEF4DEE40871> /System/Library/PrivateFrameworks/FindMyDevice.framework/Versions/A/FindMyDevice\ndyld[33990]: <930F9F83-A947-3788-9FBA-49872FC3AF8D> /System/Library/PrivateFrameworks/FMCoreLite.framework/Versions/A/FMCoreLite\ndyld[33990]: <5F356BA6-47B5-382B-B54A-1550BB138A62> /System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement\ndyld[33990]: <38DAF669-429F-384F-87D6-8550842EEB5E> /System/Library/PrivateFrameworks/CryptoKitPrivate.framework/Versions/A/CryptoKitPrivate\ndyld[33990]: <5B73C216-2ACE-3F8C-A2B3-7D35D5D0395A> /System/Library/PrivateFrameworks/CaptiveNetwork.framework/Versions/A/CaptiveNetwork\ndyld[33990]: /System/Library/PrivateFrameworks/EAP8021X.framework/Versions/A/EAP8021X\ndyld[33990]: <36F215D1-A2C0-32CA-ADD8-6D85AB48A772> /System/Library/Frameworks/QuickLookThumbnailing.framework/Versions/A/QuickLookThumbnailing\ndyld[33990]: /System/Library/PrivateFrameworks/DiskImages.framework/Versions/A/DiskImages\ndyld[33990]: <49121861-2603-3B0A-B664-BAD9E729BE5D> /System/Library/Frameworks/NetFS.framework/Versions/A/NetFS\ndyld[33990]: <2E99AD96-DC1C-3643-9988-273AB6844EFC> /usr/lib/libcurl.4.dylib\ndyld[33990]: <46D13DA8-E7BD-37DC-91DD-D5E6CE00C2B8> /usr/lib/libcrypto.46.dylib\ndyld[33990]: <07D5F4C6-1A13-344C-882B-0B0A08048DE5> /usr/lib/libssl.48.dylib\ndyld[33990]: <8CABDD64-E6C6-3B77-B839-2E2B875CE0FE> /System/Library/Frameworks/LDAP.framework/Versions/A/LDAP\ndyld[33990]: <96C0BAAA-7FE6-3277-AFBC-31926F5935EE> /System/Library/PrivateFrameworks/TrustEvaluationAgent.framework/Versions/A/TrustEvaluationAgent\ndyld[33990]: <7CF2A32E-72DD-34F7-B179-A17ED3D7DD75> /usr/lib/libsasl2.2.dylib\ndyld[33990]: move loaded to delayed: libcmark-gfm.dylib\ndyld[33990]: move loaded to delayed: BackgroundSystemTasks\ndyld[33990]: move loaded to delayed: CoreWiFi\ndyld[33990]: move loaded to delayed: Rapport\ndyld[33990]: move loaded to delayed: SymptomAnalytics\ndyld[33990]: move loaded to delayed: libcupolicy.dylib\ndyld[33990]: move loaded to delayed: libnetworkextension.dylib\ndyld[33990]: move loaded to delayed: NetworkExtension\ndyld[33990]: move loaded to delayed: libnwswifttls.dylib\ndyld[33990]: move loaded to delayed: libpcap.A.dylib\ndyld[33990]: move loaded to delayed: XPCSupport\ndyld[33990]: move loaded to delayed: CloudServices\ndyld[33990]: move loaded to delayed: OctagonTrust\ndyld[33990]: move loaded to delayed: AppleIDAuthSupport\ndyld[33990]: move loaded to delayed: KeychainCircle\ndyld[33990]: move loaded to delayed: AuthKit\ndyld[33990]: move loaded to delayed: AAAFoundation\ndyld[33990]: move loaded to delayed: MultiverseSupport\ndyld[33990]: move loaded to delayed: DiskManagement\ndyld[33990]: move loaded to delayed: Accounts\ndyld[33990]: move loaded to delayed: URLFormatting\ndyld[33990]: move loaded to delayed: AOSKit\ndyld[33990]: move loaded to delayed: AppSSOCore\ndyld[33990]: move loaded to delayed: AVFoundation\ndyld[33990]: move loaded to delayed: DuetActivityScheduler\ndyld[33990]: move loaded to delayed: FTServices\ndyld[33990]: move loaded to delayed: InternationalSupport\ndyld[33990]: move loaded to delayed: libMemoryResourceException.dylib\ndyld[33990]: move loaded to delayed: NetworkScore\ndyld[33990]: move loaded to delayed: NetworkServiceProxy\ndyld[33990]: move loaded to delayed: StreamingExtractor\ndyld[33990]: move loaded to delayed: SymptomReporter\ndyld[33990]: move loaded to delayed: libCGInterfaces.dylib\ndyld[33990]: move loaded to delayed: AccelerateGPU\ndyld[33990]: move loaded to delayed: ApplicationServices\ndyld[33990]: move loaded to delayed: ATS\ndyld[33990]: move loaded to delayed: HIServices\ndyld[33990]: move loaded to delayed: PrintCore\ndyld[33990]: move loaded to delayed: QD\ndyld[33990]: move loaded to delayed: ColorSyncLegacy\ndyld[33990]: move loaded to delayed: SpeechSynthesis\ndyld[33990]: move loaded to delayed: CoreDuetContext\ndyld[33990]: move loaded to delayed: CoreDuet\ndyld[33990]: move loaded to delayed: CoreLocation\ndyld[33990]: move loaded to delayed: Intents\ndyld[33990]: move loaded to delayed: _LocationEssentials\ndyld[33990]: move loaded to delayed: GeoServices\ndyld[33990]: move loaded to delayed: LocationSupport\ndyld[33990]: move loaded to delayed: CoreBluetooth\ndyld[33990]: move loaded to delayed: GeoServicesCore\ndyld[33990]: move loaded to delayed: PhoneNumbers\ndyld[33990]: move loaded to delayed: IconServices\ndyld[33990]: move loaded to delayed: IconFoundation\ndyld[33990]: move loaded to delayed: AssistantServices\ndyld[33990]: move loaded to delayed: IconRendering\ndyld[33990]: move loaded to delayed: CoreUI\ndyld[33990]: move loaded to delayed: SFSymbols\ndyld[33990]: move loaded to delayed: DeveloperToolsSupport\ndyld[33990]: move loaded to delayed: RenderBox\ndyld[33990]: move loaded to delayed: CoreSVG\ndyld[33990]: move loaded to delayed: TextureIO\ndyld[33990]: move loaded to delayed: libswiftCoreImage.dylib\ndyld[33990]: move loaded to delayed: ATSUI\ndyld[33990]: move loaded to delayed: SAObjects\ndyld[33990]: move loaded to delayed: MediaRemote\ndyld[33990]: move loaded to delayed: UserNotifications\ndyld[33990]: move loaded to delayed: SiriInstrumentation\ndyld[33990]: move loaded to delayed: SiriAnalytics\ndyld[33990]: move loaded to delayed: SiriTTSService\ndyld[33990]: move loaded to delayed: SiriCrossDeviceArbitration\ndyld[33990]: move loaded to delayed: FeedbackLogger\ndyld[33990]: move loaded to delayed: FaceTimeNameUtility\ndyld[33990]: move loaded to delayed: SiriCrossDeviceArbitrationFeedback\ndyld[33990]: move loaded to delayed: libswiftCoreLocation.dylib\ndyld[33990]: move loaded to delayed: libswiftAVFoundation.dylib\ndyld[33990]: move loaded to delayed: UIKitServices\ndyld[33990]: move loaded to delayed: UnifiedAssetFramework\ndyld[33990]: move loaded to delayed: AudioDSPGraph\ndyld[33990]: move loaded to delayed: AudioAccessoryServices\ndyld[33990]: move loaded to delayed: CoreUtils\ndyld[33990]: move loaded to delayed: Sharing\ndyld[33990]: move loaded to delayed: CoreUtilsExtras\ndyld[33990]: move loaded to delayed: IO80211\ndyld[33990]: move loaded to delayed: IDSFoundation\ndyld[33990]: move loaded to delayed: Apple80211\ndyld[33990]: move loaded to delayed: CoreWLAN\ndyld[33990]: move loaded to delayed: IOBluetooth\ndyld[33990]: move loaded to delayed: libswiftRegexBuilder.dylib\ndyld[33990]: move loaded to delayed: IMFoundation\ndyld[33990]: move loaded to delayed: Marco\ndyld[33990]: move loaded to delayed: CommonUtilities\ndyld[33990]: move loaded to delayed: Engram\ndyld[33990]: move loaded to delayed: XPCDistributed\ndyld[33990]: move loaded to delayed: libtidy.A.dylib\ndyld[33990]: move loaded to delayed: Bom\ndyld[33990]: move loaded to delayed: libParallelCompression.dylib\ndyld[33990]: move loaded to delayed: libIOReport.dylib\ndyld[33990]: move loaded to delayed: WiFiPeerToPeer\ndyld[33990]: move loaded to delayed: Centauri\ndyld[33990]: move loaded to delayed: libmrc.dylib\ndyld[33990]: move loaded to delayed: IPConfiguration\ndyld[33990]: move loaded to delayed: Netrb\ndyld[33990]: move loaded to delayed: FrontBoardServices\ndyld[33990]: move loaded to delayed: AudioUnit\ndyld[33990]: move loaded to delayed: AVFAudio\ndyld[33990]: move loaded to delayed: AVRouting\ndyld[33990]: move loaded to delayed: libAccessibility.dylib\ndyld[33990]: move loaded to delayed: MediaServices\ndyld[33990]: move loaded to delayed: IDS\ndyld[33990]: move loaded to delayed: IsolatedCoreAudioClient\ndyld[33990]: move loaded to delayed: CoreAudioOrchestration\ndyld[33990]: move loaded to delayed: MediaToolbox\ndyld[33990]: move loaded to delayed: CoreAVCHD\ndyld[33990]: move loaded to delayed: MediaAccessibility\ndyld[33990]: move loaded to delayed: Mangrove\ndyld[33990]: move loaded to delayed: CMPhoto\ndyld[33990]: move loaded to delayed: CoreTelephony\ndyld[33990]: move loaded to delayed: CoreAUC\ndyld[33990]: move loaded to delayed: AppleJPEGXL\ndyld[33990]: move loaded to delayed: libTelephonyUtilDynamic.dylib\ndyld[33990]: move loaded to delayed: CryptoKit\ndyld[33990]: move loaded to delayed: CryptoKitCBridging\ndyld[33990]: move loaded to delayed: CryptoTokenKit\ndyld[33990]: move loaded to delayed: LocalAuthentication\ndyld[33990]: move loaded to delayed: LocalAuthenticationCore\ndyld[33990]: move loaded to delayed: LocalAuthenticationCredentialServices\ndyld[33990]: move loaded to delayed: SharedUtils\ndyld[33990]: move loaded to delayed: libcsfde.dylib\ndyld[33990]: move loaded to delayed: libCoreStorage.dylib\ndyld[33990]: move loaded to delayed: ProtectedCloudStorage\ndyld[33990]: move loaded to delayed: EFILogin\ndyld[33990]: move loaded to delayed: PersistentConnection\ndyld[33990]: move loaded to delayed: SonicFoundation\ndyld[33990]: move loaded to delayed: AsyncAlgorithmsInternal\ndyld[33990]: move loaded to delayed: FTAWD\ndyld[33990]: move loaded to delayed: Dendrite\ndyld[33990]: move loaded to delayed: libtailspin.dylib\ndyld[33990]: move loaded to delayed: Osprey\ndyld[33990]: move loaded to delayed: SiriTTS\ndyld[33990]: move loaded to delayed: NaturalLanguage\ndyld[33990]: move loaded to delayed: GenerativeModels\ndyld[33990]: move loaded to delayed: SiriPowerInstrumentation\ndyld[33990]: move loaded to delayed: libswiftNaturalLanguage.dylib\ndyld[33990]: move loaded to delayed: TailspinSymbolication\ndyld[33990]: move loaded to delayed: Darwinup\ndyld[33990]: move loaded to delayed: SignpostSupport\ndyld[33990]: move loaded to delayed: FeatureFlagsSupport\ndyld[33990]: move loaded to delayed: ktrace\ndyld[33990]: move loaded to delayed: SampleAnalysis\ndyld[33990]: move loaded to delayed: kperfdata\ndyld[33990]: move loaded to delayed: libdscsym.dylib\ndyld[33990]: move loaded to delayed: BulkSymbolication\ndyld[33990]: move loaded to delayed: Espresso\ndyld[33990]: move loaded to delayed: CoreML\ndyld[33990]: move loaded to delayed: libedit.3.dylib\ndyld[33990]: move loaded to delayed: ANECompiler\ndyld[33990]: move loaded to delayed: AppleNeuralEngine\ndyld[33990]: move loaded to delayed: MetalPerformanceShadersGraph\ndyld[33990]: move loaded to delayed: MLCompilerServices\ndyld[33990]: move loaded to delayed: ANEServices\ndyld[33990]: move loaded to delayed: libncurses.5.4.dylib\ndyld[33990]: move loaded to delayed: libsandbox.1.dylib\ndyld[33990]: move loaded to delayed: libMatch.1.dylib\ndyld[33990]: move loaded to delayed: ODIE\ndyld[33990]: move loaded to delayed: MLModelAsset\ndyld[33990]: move loaded to delayed: MLCompilerRuntime\ndyld[33990]: move loaded to delayed: MLCompute\ndyld[33990]: move loaded to delayed: MLAssetIO\ndyld[33990]: move loaded to delayed: libswiftMLCompute.dylib\ndyld[33990]: move loaded to delayed: AVFCore\ndyld[33990]: move loaded to delayed: AVFCapture\ndyld[33990]: move loaded to delayed: CMImaging\ndyld[33990]: move loaded to delayed: Quagga\ndyld[33990]: move loaded to delayed: CMCapture\ndyld[33990]: move loaded to delayed: CoreMediaIO\ndyld[33990]: move loaded to delayed: CMCaptureDevice\ndyld[33990]: move loaded to delayed: CoreBrightness\ndyld[33990]: move loaded to delayed: CinematicFraming\ndyld[33990]: move loaded to delayed: ModelManagerServices\ndyld[33990]: move loaded to delayed: CPMS\ndyld[33990]: move loaded to delayed: SystemStatus\ndyld[33990]: move loaded to delayed: CoreMotion\ndyld[33990]: move loaded to delayed: TimeSync\ndyld[33990]: move loaded to delayed: DistributedSensing\ndyld[33990]: move loaded to delayed: MobileBluetooth\ndyld[33990]: move loaded to delayed: IOKitten\ndyld[33990]: move loaded to delayed: LocationLogEncryption\ndyld[33990]: move loaded to delayed: AppleIntelligenceReporting\ndyld[33990]: move loaded to delayed: CoreEmoji\ndyld[33990]: move loaded to delayed: LanguageModeling\ndyld[33990]: move loaded to delayed: Montreal\ndyld[33990]: move loaded to delayed: libcmph.dylib\ndyld[33990]: move loaded to delayed: GenerativeModelsFoundation\ndyld[33990]: move loaded to delayed: TokenGeneration\ndyld[33990]: move loaded to delayed: GenerativeFunctions\ndyld[33990]: move loaded to delayed: GenerativeFunctionsFoundation\ndyld[33990]: move loaded to delayed: ModelCatalog\ndyld[33990]: move loaded to delayed: SensitiveContentAnalysisML\ndyld[33990]: move loaded to delayed: GenerativeFunctionsInstrumentation\ndyld[33990]: move loaded to delayed: PromptKit\ndyld[33990]: move loaded to delayed: ProactiveDaemonSupport\ndyld[33990]: move loaded to delayed: TokenGenerationCore\ndyld[33990]: move loaded to delayed: Trial\ndyld[33990]: move loaded to delayed: TrialProto\ndyld[33990]: move loaded to delayed: AppleFlatBuffers\ndyld[33990]: move loaded to delayed: SentencePieceInternal\ndyld[33990]: move loaded to delayed: Vision\ndyld[33990]: move loaded to delayed: CoreSceneUnderstanding\ndyld[33990]: move loaded to delayed: VisionCore\ndyld[33990]: move loaded to delayed: DataDetectorsCore\ndyld[33990]: move loaded to delayed: libfaceCore.dylib\ndyld[33990]: move loaded to delayed: Futhark\ndyld[33990]: move loaded to delayed: InertiaCam\ndyld[33990]: move loaded to delayed: TextRecognition\ndyld[33990]: move loaded to delayed: DataDetection\ndyld[33990]: move loaded to delayed: TextInput\ndyld[33990]: move loaded to delayed: CVNLP\ndyld[33990]: move loaded to delayed: IntentsFoundation\ndyld[33990]: move loaded to delayed: ApplePushService\ndyld[33990]: move loaded to delayed: CloudKit\ndyld[33990]: move loaded to delayed: CoreDuetDaemonProtocol\ndyld[33990]: move loaded to delayed: DeviceIdentity\ndyld[33990]: move loaded to delayed: SharedWithYouCore\ndyld[33990]: move loaded to delayed: CloudTelemetry\ndyld[33990]: move loaded to delayed: AppleAccount\ndyld[33990]: move loaded to delayed: CacheDelete\ndyld[33990]: move loaded to delayed: C2\ndyld[33990]: move loaded to delayed: CloudCoreInternal\ndyld[33990]: move loaded to delayed: CloudAsset\ndyld[33990]: move loaded to delayed: PushKit\ndyld[33990]: move loaded to delayed: CoreTransferable\ndyld[33990]: move loaded to delayed: FileProvider\ndyld[33990]: move loaded to delayed: GenerationalStorage\ndyld[33990]: move loaded to delayed: DesktopServicesPriv\ndyld[33990]: move loaded to delayed: CloudTelemetryTools\ndyld[33990]: move loaded to delayed: CloudTelemetryShared.dylib\ndyld[33990]: move loaded to delayed: RTCReporting\ndyld[33990]: move loaded to delayed: AAAFoundationSwift\ndyld[33990]: move loaded to delayed: AppleIDSSOAuthentication\ndyld[33990]: move loaded to delayed: UIFoundation\ndyld[33990]: move loaded to delayed: libcups.2.dylib\ndyld[33990]: move loaded to delayed: AXCoreUtilities\ndyld[33990]: move loaded to delayed: AttributeGraph\ndyld[33990]: move loaded to delayed: libAXSafeCategoryBundle.dylib\ndyld[33990]: move loaded to delayed: TabularData\ndyld[33990]: move loaded to delayed: ArgumentParserInternal\ndyld[33990]: move loaded to delayed: FindMyDevice\ndyld[33990]: move loaded to delayed: FMCoreLite\ndyld[33990]: move loaded to delayed: ServiceManagement\ndyld[33990]: move loaded to delayed: CryptoKitPrivate\ndyld[33990]: move loaded to delayed: CaptiveNetwork\ndyld[33990]: move loaded to delayed: EAP8021X\ndyld[33990]: move loaded to delayed: QuickLookThumbnailing\ndyld[33990]: <8A5F0D29-A245-3FB7-8531-4BFD4394BE26> /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python\ndyld[33990]: <1DEC725C-63A6-3B9C-A038-DC35832D65CB> /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/Python\ndyld[33990]: <9B672762-7B1F-30BC-96DE-F176B372D66D> /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation\ndyld[33990]: <03BD9E32-CF0A-37B0-898A-3CE8DE06D842> /usr/lib/libobjc.A.dylib\ndyld[33990]: <7D56DA94-31EB-35F0-B886-4010C075E035> /System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal\ndyld[33990]: <91DACE39-FA28-3191-818D-1FCC6A0E615A> /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation\ndyld[33990]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/liboah.dylib\ndyld[33990]: <820D290D-51A0-3064-A1F2-4F0AAF7E6BF4> /usr/lib/libfakelink.dylib\ndyld[33990]: <53A3E31E-06A8-325E-B5A8-316B88AA3C92> /usr/lib/libicucore.A.dylib\ndyld[33990]: <4FED5EE2-5D3E-35B1-A170-9859C4B683BB> /usr/lib/libSystem.B.dylib\ndyld[33990]: <4109E8DD-0A81-310C-B1B3-23B87186D0D8> /System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking\ndyld[33990]: <83794FB3-DE9B-3D23-AB5E-2C1D5D30F134> /usr/lib/swift/libswiftCore.dylib\ndyld[33990]: /usr/lib/libc++abi.dylib\ndyld[33990]: <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/libRosetta.dylib\ndyld[33990]: /usr/lib/libc++.1.dylib\ndyld[33990]: <4FD234EA-2C18-3C25-8BD0-B1F4805C6675> /usr/lib/swift/libswiftObjectiveC.dylib\ndyld[33990]: <9E3C7597-446F-3C50-9930-2425D9252C0C> /usr/lib/libswiftPrespecialized.dylib\ndyld[33990]: <1479C415-3678-3968-AC77-06373490860E> /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration\ndyld[33990]: <13EDE3A5-A7D9-3FB8-B0C2-2FB7F7272B34> /usr/lib/libz.1.dylib\ndyld[33990]: <54AD73AF-852E-3CD6-8B7D-E73BE79857D3> /System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout\ndyld[33990]: <1A2A9A41-5269-3B0C-BCEE-B446966CE366> /usr/lib/libcmark-gfm.dylib\ndyld[33990]: /usr/lib/libcompression.dylib\ndyld[33990]: <4A3B95C5-AA2E-338C-9398-56895AF82D97> /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork\ndyld[33990]: <332C4B80-5B3C-34E7-AD1F-F6131E607F95> /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration\ndyld[33990]: <0048DB96-1737-3FC5-AF0C-AF784FA24A03> /usr/lib/libarchive.2.dylib\ndyld[33990]: <6CD959AA-4825-306A-864A-BD69EC5F2DC0> /usr/lib/libDiagnosticMessagesClient.dylib\ndyld[33990]: <1E8A4F9E-3954-3458-B3BB-BE97F961C105> /usr/lib/libxml2.2.dylib\ndyld[33990]: <56AE2857-29E0-34E9-B2C3-EE8E951EEFC5> /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices\ndyld[33990]: /usr/lib/liblangid.dylib\ndyld[33990]: <12372585-DF92-33EF-B632-714FAA13260A> /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit\ndyld[33990]: /System/Library/Frameworks/Combine.framework/Versions/A/Combine\ndyld[33990]: <6098453F-4D7E-38B4-8ADC-02C9FF51E14A> /System/Library/PrivateFrameworks/CollectionsInternal.framework/Versions/A/CollectionsInternal\ndyld[33990]: <9A1279D4-575A-3E48-A460-A631A3F82D18> /System/Library/PrivateFrameworks/ReflectionInternal.framework/Versions/A/ReflectionInternal\ndyld[33990]: <6D89CD71-A86D-3D78-A64B-96AB79550F79> /System/Library/PrivateFrameworks/RuntimeInternal.framework/Versions/A/RuntimeInternal\ndyld[33990]: <4975D13C-2AC5-3473-85C0-98054A81D7C6> /usr/lib/swift/libswiftCoreFoundation.dylib\ndyld[33990]: <1DB56DA9-CF6B-3023-ABDF-5A37CB79223C> /usr/lib/swift/libswiftDarwin.dylib\ndyld[33990]: /usr/lib/swift/libswiftDispatch.dylib\ndyld[33990]: <06A92787-4440-3757-AF32-F2B331C753A2> /usr/lib/swift/libswiftIOKit.dylib\ndyld[33990]: <7CD9BDE7-F36B-3471-9295-38E181D6D9E5> /usr/lib/swift/libswiftSystem.dylib\ndyld[33990]: <24AEDAC1-C1EE-30F4-8818-72EBF8969D0C> /usr/lib/swift/libswiftXPC.dylib\ndyld[33990]: <52F59382-A6A6-3F55-8A85-D9FB822D370F> /usr/lib/swift/libswift_Builtin_float.dylib\ndyld[33990]: <8E168857-47F4-349F-A718-A18DB144FCB0> /usr/lib/swift/libswift_Concurrency.dylib\ndyld[33990]: <85246B9A-A757-3F67-B792-3A2F7BB2BB25> /usr/lib/swift/libswift_DarwinFoundation1.dylib\ndyld[33990]: <8DF0116D-DFC9-3906-9DF6-F1DBC47E324B> /usr/lib/swift/libswift_StringProcessing.dylib\ndyld[33990]: /usr/lib/swift/libswiftos.dylib\ndyld[33990]: <1C7E652B-6B94-3180-93A6-EF8DBA3A5448> /System/Library/Frameworks/Network.framework/Versions/A/Network\ndyld[33990]: <4C6139EE-BF87-37A6-B226-830A6FDC36F8> /System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo\ndyld[33990]: <9D0387FC-E8F6-3004-9C95-CA68EA715C8B> /System/Library/Frameworks/Security.framework/Versions/A/Security\ndyld[33990]: <633BCB5F-F063-3D5A-B52A-F72AE236824B> /usr/lib/libbsm.0.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer\ndyld[33990]: <10A4E63B-A1EB-31CC-B3E1-DB4FE115FC84> /System/Library/PrivateFrameworks/BackgroundSystemTasks.framework/Versions/A/BackgroundSystemTasks\ndyld[33990]: /System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics\ndyld[33990]: <7C50137B-2ABD-3819-B033-AE65B05A6085> /System/Library/PrivateFrameworks/CoreWiFi.framework/Versions/A/CoreWiFi\ndyld[33990]: /System/Library/PrivateFrameworks/LoggingSupport.framework/Versions/A/LoggingSupport\ndyld[33990]: <91A461DE-C8E8-3868-B393-BA6E5A17DF2A> /System/Library/PrivateFrameworks/MobileAsset.framework/Versions/A/MobileAsset\ndyld[33990]: /System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog\ndyld[33990]: /System/Library/PrivateFrameworks/Rapport.framework/Versions/A/Rapport\ndyld[33990]: /System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices\ndyld[33990]: <9F52706C-75BD-34AF-A29E-C26608124ACC> /System/Library/Frameworks/SwiftData.framework/Versions/A/SwiftData\ndyld[33990]: <259877CE-4E2C-34A9-A07F-FEE2999D7B2F> /System/Library/PrivateFrameworks/Symptoms.framework/Versions/A/Frameworks/SymptomAnalytics.framework/Versions/A/SymptomAnalytics\ndyld[33990]: /System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers\ndyld[33990]: <4A78C569-FF0D-398B-9C25-33453F0CEC40> /System/Library/PrivateFrameworks/UserManagement.framework/Versions/A/UserManagement\ndyld[33990]: <5BF55637-F306-3D79-B5A1-DB8A871DAD4B> /usr/lib/libboringssl.dylib\ndyld[33990]: <831C79C1-8DBE-31A3-AA4E-8E2B041488D6> /usr/lib/libcupolicy.dylib\ndyld[33990]: <88925A0C-4960-3F6D-AF3A-B1983F7B3D18> /usr/lib/libdns_services.dylib\ndyld[33990]: /usr/lib/libnetworkextension.dylib\ndyld[33990]: <9753F471-40DD-3B9E-9D64-8D07C1B06BC9> /System/Library/Frameworks/NetworkExtension.framework/Versions/A/NetworkExtension\ndyld[33990]: /usr/lib/libnwswifttls.dylib\ndyld[33990]: <6F59933A-6618-33F1-BE52-E7FC3BF7A1EF> /usr/lib/libpcap.A.dylib\ndyld[33990]: <5E89267F-C684-348D-8356-F9DAD8B4CB13> /usr/lib/libquic.dylib\ndyld[33990]: /usr/lib/libusrtcp.dylib\ndyld[33990]: /usr/lib/libMobileGestalt.dylib\ndyld[33990]: /usr/lib/libapple_nghttp2.dylib\ndyld[33990]: <6937D729-7EF4-3972-9E12-694C17C1C1AB> /usr/lib/libcoretls_cfhelpers.dylib\ndyld[33990]: /usr/lib/libsqlite3.dylib\ndyld[33990]: <1617DBB1-2BFF-3619-903C-2FBB31348FB6> /System/Library/PrivateFrameworks/AtomicsInternal.framework/Versions/A/AtomicsInternal\ndyld[33990]: <41F66F01-A342-3091-A832-0B2B645C922B> /System/Library/PrivateFrameworks/InternalSwiftProtobuf.framework/Versions/A/InternalSwiftProtobuf\ndyld[33990]: <2EDB2E62-942F-3AB5-82AF-8E1328544E17> /usr/lib/swift/libswiftDistributed.dylib\ndyld[33990]: /usr/lib/swift/libswiftObservation.dylib\ndyld[33990]: /usr/lib/swift/libswiftSynchronization.dylib\ndyld[33990]: <9CD7B1E1-3E47-339C-A193-2392E3E0ED23> /usr/lib/system/libcache.dylib\ndyld[33990]: <3B110564-5278-3CB0-85F1-2CE8431FF935> /usr/lib/system/libcommonCrypto.dylib\ndyld[33990]: <6FB345CA-7F5C-3263-A23F-143F7539FD8A> /usr/lib/system/libcompiler_rt.dylib\ndyld[33990]: /usr/lib/system/libcopyfile.dylib\ndyld[33990]: <0642DDAD-4771-3C82-805C-E7C6701C1461> /usr/lib/system/libcorecrypto.dylib\ndyld[33990]: /usr/lib/system/libdispatch.dylib\ndyld[33990]: <957F93B3-8805-39C7-9C51-EDD1715F550E> /usr/lib/system/libdyld.dylib\ndyld[33990]: <7E863FCA-F3FF-32C7-8A8C-F983E946AFC3> /usr/lib/system/libkeymgr.dylib\ndyld[33990]: <949131E5-BDA2-39BA-AA50-62651BB51802> /usr/lib/system/libmacho.dylib\ndyld[33990]: /usr/lib/system/libquarantine.dylib\ndyld[33990]: <7460B5AE-469A-36A0-A7EC-6C7D69628E86> /usr/lib/system/libremovefile.dylib\ndyld[33990]: <54439739-33EE-3273-839F-CBA67D7F5CB1> /usr/lib/system/libsystem_asl.dylib\ndyld[33990]: /usr/lib/system/libsystem_blocks.dylib\ndyld[33990]: /usr/lib/system/libsystem_c.dylib\ndyld[33990]: /usr/lib/system/libsystem_collections.dylib\ndyld[33990]: /usr/lib/system/libsystem_configuration.dylib\ndyld[33990]: <14B2A47F-19C8-392F-8FDB-FE8AE375DD41> /usr/lib/system/libsystem_containermanager.dylib\ndyld[33990]: /usr/lib/system/libsystem_coreservices.dylib\ndyld[33990]: <8E07D22E-CE5A-38A0-B091-5B0338C326F5> /usr/lib/system/libsystem_darwin.dylib\ndyld[33990]: <971A4F65-493D-39F3-846D-0D33FA2769FD> /usr/lib/system/libsystem_darwindirectory.dylib\ndyld[33990]: <305F4398-E688-3384-B351-02D865EC8A04> /usr/lib/system/libsystem_dnssd.dylib\ndyld[33990]: <750CA446-92EA-3A56-9A7B-CC0841686C50> /usr/lib/system/libsystem_eligibility.dylib\ndyld[33990]: /usr/lib/system/libsystem_featureflags.dylib\ndyld[33990]: <9B5FB84B-31AD-3EA7-8F89-8C700D369DC8> /usr/lib/system/libsystem_info.dylib\ndyld[33990]: /usr/lib/system/libsystem_m.dylib\ndyld[33990]: /usr/lib/system/libsystem_malloc.dylib\ndyld[33990]: <9C7B1EEB-47BE-3791-93A9-CFC693CB9417> /usr/lib/system/libsystem_networkextension.dylib\ndyld[33990]: <15799128-6CBD-30D6-A2BB-B9D02B4470C0> /usr/lib/system/libsystem_notify.dylib\ndyld[33990]: <54688162-B50D-3D31-A1E8-7B9766D3530D> /usr/lib/system/libsystem_sandbox.dylib\ndyld[33990]: /usr/lib/system/libsystem_sanitizers.dylib\ndyld[33990]: /usr/lib/system/libsystem_secinit.dylib\ndyld[33990]: /usr/lib/system/libsystem_kernel.dylib\ndyld[33990]: /usr/lib/system/libsystem_platform.dylib\ndyld[33990]: /usr/lib/system/libsystem_pthread.dylib\ndyld[33990]: <229122B9-B8B1-3F2F-870E-8650AE3C4FB5> /usr/lib/system/libsystem_symptoms.dylib\ndyld[33990]: <93F1DD8C-6CD9-32B9-B222-D23DA5D161B4> /usr/lib/system/libsystem_trace.dylib\ndyld[33990]: <7194FF5B-A6C5-3D67-B00A-90209F10D603> /usr/lib/system/libsystem_trial.dylib\ndyld[33990]: <05FD0014-55B1-3B8A-A6BA-6C7A389C4123> /usr/lib/system/libunwind.dylib\ndyld[33990]: <33E44C2D-D65E-37A6-B85F-1A4CF524A050> /usr/lib/system/libxpc.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/XPCSupport.framework/Versions/A/XPCSupport\ndyld[33990]: /System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement\ndyld[33990]: /System/Library/PrivateFrameworks/AppleKeyStore.framework/Versions/A/AppleKeyStore\ndyld[33990]: /usr/lib/libCoreEntitlements.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity\ndyld[33990]: <81F4A8BA-C80F-3B53-82E7-57F6928609C5> /System/Library/PrivateFrameworks/CloudServices.framework/Versions/A/CloudServices\ndyld[33990]: <737479F2-7B20-3DB6-B9F4-0DAA1B73E9D0> /System/Library/PrivateFrameworks/SymptomDiagnosticReporter.framework/Versions/A/SymptomDiagnosticReporter\ndyld[33990]: /System/Library/PrivateFrameworks/CrashReporterSupport.framework/Versions/A/CrashReporterSupport\ndyld[33990]: /System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression\ndyld[33990]: <0EAB1F4A-9275-3FED-8EA6-E962ACDDEE5D> /usr/lib/libcoretls.dylib\ndyld[33990]: <7E84FD3B-E90E-317E-AC19-17B70AC809E5> /usr/lib/libpam.2.dylib\ndyld[33990]: /usr/lib/libxar.1.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS\ndyld[33990]: /System/Library/PrivateFrameworks/SwiftASN1Internal.framework/Versions/A/SwiftASN1Internal\ndyld[33990]: /usr/lib/libutil.dylib\ndyld[33990]: <8E04C57D-3651-386E-83D5-4728B732F214> /usr/lib/libenergytrace.dylib\ndyld[33990]: /usr/lib/system/libkxld.dylib\ndyld[33990]: <2BC48182-F354-3AB0-8F18-0C60CAAFE398> /System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer\ndyld[33990]: <5556FD64-9D47-3547-961E-3A27681F3C51> /System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface\ndyld[33990]: <6A4A85F4-3D12-3C4C-85EC-D53D61379F28> /usr/lib/libheimdal-asn1.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce\ndyld[33990]: /System/Library/PrivateFrameworks/OctagonTrust.framework/Versions/A/OctagonTrust\ndyld[33990]: /System/Library/PrivateFrameworks/AppleIDAuthSupport.framework/Versions/A/AppleIDAuthSupport\ndyld[33990]: <9A86DB3F-CC62-3E89-B872-35D04CFFBE42> /System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/KeychainCircle.framework/Versions/A/KeychainCircle\ndyld[33990]: <336E2CAC-84D2-34DC-8AE3-7FE688C609EA> /System/Library/PrivateFrameworks/AuthKit.framework/Versions/A/AuthKit\ndyld[33990]: /System/Library/PrivateFrameworks/AAAFoundation.framework/Versions/A/AAAFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag\ndyld[33990]: <79000980-1797-3115-B74B-60FA1E9C3C73> /System/Library/PrivateFrameworks/CorePhoneNumbers.framework/Versions/A/CorePhoneNumbers\ndyld[33990]: <21723046-939E-302F-883C-9DB417452E3A> /System/Library/PrivateFrameworks/MultiverseSupport.framework/Versions/A/MultiverseSupport\ndyld[33990]: <823F3D1A-65F1-3CC5-96B1-750263B8DB36> /System/Library/PrivateFrameworks/RemoteServiceDiscovery.framework/Versions/A/RemoteServiceDiscovery\ndyld[33990]: /System/Library/PrivateFrameworks/DiskManagement.framework/Versions/A/DiskManagement\ndyld[33990]: <5F6B668E-00B2-3BEC-959F-26BD6B50D42B> /System/Library/Frameworks/Accounts.framework/Versions/A/Accounts\ndyld[33990]: /System/Library/PrivateFrameworks/URLFormatting.framework/Versions/A/URLFormatting\ndyld[33990]: <91BDD1F8-831B-3B01-86BA-6BBCB43373C4> /System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary\ndyld[33990]: <885F9C72-1018-368B-AD36-E8A42E87FD91> /System/Library/PrivateFrameworks/RemoteXPC.framework/Versions/A/RemoteXPC\ndyld[33990]: /usr/lib/libFDR.dylib\ndyld[33990]: <24D28E7F-A1AE-3031-8679-A0D6C6D68A86> /usr/lib/libamsupport.dylib\ndyld[33990]: <29367004-5D60-38DB-831F-9E5EE9364B21> /usr/lib/libReverseProxyDevice.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/MSUDataAccessor.framework/Versions/A/MSUDataAccessor\ndyld[33990]: <9594FBFB-D49D-3DF6-8820-564633EAEC2B> /System/Library/PrivateFrameworks/AppleDeviceQuerySupport.framework/Versions/A/AppleDeviceQuerySupport\ndyld[33990]: <44CD8313-2D5B-3A34-BACA-EF8800803B4A> /System/Library/PrivateFrameworks/AOSKit.framework/Versions/A/AOSKit\ndyld[33990]: <5198BFE1-41D2-33D5-A9E0-C63F81A512D3> /System/Library/PrivateFrameworks/AppSSOCore.framework/Versions/A/AppSSOCore\ndyld[33990]: <61B2B917-D14A-38AD-A439-16E1C635441A> /System/Library/PrivateFrameworks/AppSupport.framework/Versions/A/AppSupport\ndyld[33990]: <816EC446-7C41-3A2F-A582-7CB856797C09> /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation\ndyld[33990]: <38C8FBEC-DE88-33FE-B742-A192F22CC754> /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics\ndyld[33990]: /System/Library/PrivateFrameworks/DuetActivityScheduler.framework/Versions/A/DuetActivityScheduler\ndyld[33990]: <0E78989C-854F-3664-AD92-6B7B6D04191C> /System/Library/PrivateFrameworks/FTServices.framework/Versions/A/FTServices\ndyld[33990]: <277D18EF-39E4-3F72-99E8-8D3DF65ED1D0> /System/Library/Frameworks/GSS.framework/Versions/A/GSS\ndyld[33990]: <5ACC6C0E-51E9-3B5A-B24F-89B22D070878> /System/Library/PrivateFrameworks/InternationalSupport.framework/Versions/A/InternationalSupport\ndyld[33990]: /usr/lib/libMemoryResourceException.dylib\ndyld[33990]: <798012E0-3FFC-3B8D-AC74-E7B7DAEA7E66> /System/Library/PrivateFrameworks/NetworkScore.framework/Versions/A/NetworkScore\ndyld[33990]: <2C93123F-99C8-3B8D-AAE6-3A817BE0A2BF> /System/Library/PrivateFrameworks/NetworkServiceProxy.framework/Versions/A/NetworkServiceProxy\ndyld[33990]: /System/Library/PrivateFrameworks/StreamingExtractor.framework/Versions/A/StreamingExtractor\ndyld[33990]: <1F2EDC7B-8F28-3721-8A60-F6E1BCFC29A3> /System/Library/PrivateFrameworks/StreamingZip.framework/Versions/A/StreamingZip\ndyld[33990]: <5DA62AF9-3D46-3D17-A3EB-7026A2F006DF> /System/Library/PrivateFrameworks/SymptomReporter.framework/Versions/A/SymptomReporter\ndyld[33990]: /usr/lib/liblzma.5.dylib\ndyld[33990]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents\ndyld[33990]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore\ndyld[33990]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata\ndyld[33990]: <61677289-93B7-382F-86CA-B856361D293F> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices\ndyld[33990]: /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit\ndyld[33990]: <435D6243-695B-3543-A722-10106F5696BD> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE\ndyld[33990]: <01579E0C-9D85-3521-8916-4DDC990CD064> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices\ndyld[33990]: <6A26D479-5926-330B-9FB8-9B7A6BE8E239> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices\ndyld[33990]: <297AC970-E432-3BBD-986C-36782634062E> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList\ndyld[33990]: <6508C698-D587-3B5A-B95B-A3A3F78CE122> /usr/lib/libCheckFix.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC\ndyld[33990]: /System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP\ndyld[33990]: <29AA0F7F-26F4-35B3-96DF-8A67B00A58AB> /System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities\ndyld[33990]: <9171DD7D-3994-3963-9A28-BC163BF97DE6> /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate\ndyld[33990]: /usr/lib/libmecab.dylib\ndyld[33990]: <1CA9048E-57DD-30F4-A3E6-FE6E97D5BF82> /usr/lib/libCRFSuite.dylib\ndyld[33990]: <74E55DD6-720D-39E4-897E-EB4328E1946D> /usr/lib/libgermantok.dylib\ndyld[33990]: <92FAD15C-EEA5-34E9-B309-75A1CD1B620B> /usr/lib/libThaiTokenizer.dylib\ndyld[33990]: <2B16DF37-A596-3D8A-AE47-33E580EB1354> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage\ndyld[33990]: <8203944D-B53E-3D7E-A481-3C676CAE1B6A> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib\ndyld[33990]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib\ndyld[33990]: <08508E7B-096D-31AB-9C66-191C877ED62F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/Libraries/libCGInterfaces.dylib\ndyld[33990]: <8945E7B7-12AE-3FF4-AA3B-D4DF9A06FEE7> /System/Library/PrivateFrameworks/AccelerateGPU.framework/Versions/A/AccelerateGPU\ndyld[33990]: <23402175-D2CF-3B08-88D0-AFBBCF775FEF> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib\ndyld[33990]: <086CBEED-2F64-3E75-AB99-8C8C0E0A2F1C> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices\ndyld[33990]: <0616AF41-149E-3F4A-906E-56E2642457BE> /System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo\ndyld[33990]: <873404F1-CC9D-30F9-AE06-8EA58D292005> /System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync\ndyld[33990]: /System/Library/Frameworks/CoreText.framework/Versions/A/CoreText\ndyld[33990]: /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO\ndyld[33990]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS\ndyld[33990]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices\ndyld[33990]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore\ndyld[33990]: <59BBF27B-1D89-3D35-9210-8386EFA15A8D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD\ndyld[33990]: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy\ndyld[33990]: <9CDA611B-254A-3779-9356-369485134C2D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis\ndyld[33990]: <0C8F41C6-6D93-3DB3-B522-CA8CFF5C3B33> /System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight\ndyld[33990]: <9E126CE0-FBB2-3B15-953F-CCDC758E34FB> /System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib\ndyld[33990]: <07CF779F-8F51-3764-B486-23D76868FF91> /System/Library/PrivateFrameworks/BiomeLibrary.framework/Versions/A/BiomeLibrary\ndyld[33990]: <959C748F-8851-3A25-BFFA-5FEA80296965> /System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard\ndyld[33990]: /System/Library/PrivateFrameworks/BoardServices.framework/Versions/A/BoardServices\ndyld[33990]: /System/Library/PrivateFrameworks/BackBoardServices.framework/Versions/A/BackBoardServices\ndyld[33990]: <7F763DF9-EA7F-3938-B599-DCCF4605E610> /System/Library/PrivateFrameworks/BackBoardHIDEventFoundation.framework/Versions/A/BackBoardHIDEventFoundation\ndyld[33990]: /System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay\ndyld[33990]: /System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox\ndyld[33990]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders\ndyld[33990]: /System/Library/PrivateFrameworks/ProDisplayLibrary.framework/Versions/A/ProDisplayLibrary\ndyld[33990]: <1E529C1A-B09C-3EB7-A286-CE00E292D561> /System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator\ndyld[33990]: <493E76D9-74D4-333B-A3B2-E5F9BC86429D> /System/Library/Frameworks/Metal.framework/Versions/A/Metal\ndyld[33990]: /System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator\ndyld[33990]: /System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia\ndyld[33990]: /System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient\ndyld[33990]: <98CB7012-30E5-3BDD-8C84-CDBDA9DB3017> /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore\ndyld[33990]: <57F7BB9C-649D-3360-AA86-A502815D77FA> /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport\ndyld[33990]: <625F222D-6394-39B9-A1F2-12B9EA56DD85> /usr/lib/swift/libswiftAccelerate.dylib\ndyld[33990]: /usr/lib/swift/libswiftCoreAudio.dylib\ndyld[33990]: /usr/lib/swift/libswiftCoreMedia.dylib\ndyld[33990]: <7235A6A9-49B2-3B94-9DD6-C987019CDBF2> /usr/lib/swift/libswiftMetal.dylib\ndyld[33990]: <9670AE5C-271A-3DCB-9A0A-8E3A7CCC2726> /usr/lib/swift/libswiftOSLog.dylib\ndyld[33990]: <63444A8C-9E8C-3778-820D-1E0C88CA2DF7> /usr/lib/swift/libswiftQuartzCore.dylib\ndyld[33990]: /usr/lib/swift/libswiftUniformTypeIdentifiers.dylib\ndyld[33990]: <9247A5B6-A883-3A07-BEE7-A223840317A4> /usr/lib/swift/libswiftVideoToolbox.dylib\ndyld[33990]: /usr/lib/swift/libswiftsimd.dylib\ndyld[33990]: <2110407D-EFB4-373E-B963-9C92E26594B2> /System/Library/PrivateFrameworks/BiomeStreams.framework/Versions/A/BiomeStreams\ndyld[33990]: /System/Library/PrivateFrameworks/BiomeStorage.framework/Versions/A/BiomeStorage\ndyld[33990]: <455A5553-E683-30B4-A906-1F14E75F6E61> /System/Library/PrivateFrameworks/BiomeFoundation.framework/Versions/A/BiomeFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/IntelligencePlatformLibrary.framework/Versions/A/IntelligencePlatformLibrary\ndyld[33990]: <42CDC0E6-51BA-3804-BD3E-EDF87FC74034> /System/Library/PrivateFrameworks/PoirotSchematizer.framework/Versions/A/PoirotSchematizer\ndyld[33990]: <2362E209-EC61-3FFC-9486-1244BB29BE82> /System/Library/PrivateFrameworks/BiomeSync.framework/Versions/A/BiomeSync\ndyld[33990]: /System/Library/PrivateFrameworks/BiomeDSL.framework/Versions/A/BiomeDSL\ndyld[33990]: <0F03104F-FC8B-3ADD-8850-4B7029E2B56E> /System/Library/PrivateFrameworks/BiomePubSub.framework/Versions/A/BiomePubSub\ndyld[33990]: <73EE1A0A-0D29-3104-98CB-BEFEDA53F7C0> /System/Library/PrivateFrameworks/ProactiveSupport.framework/Versions/A/ProactiveSupport\ndyld[33990]: /System/Library/PrivateFrameworks/FeatureFlags.framework/Versions/A/FeatureFlags\ndyld[33990]: /System/Library/PrivateFrameworks/PoirotUDFs.framework/Versions/A/PoirotUDFs\ndyld[33990]: /usr/lib/swift/libswift_DarwinFoundation2.dylib\ndyld[33990]: <8D2C31B5-FB10-3BF6-8566-F0DCD56C8582> /usr/lib/swift/libswift_DarwinFoundation3.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime\ndyld[33990]: <858910C5-1D4A-37B7-BF0E-EE02E24A2ACD> /System/Library/PrivateFrameworks/InternationalTextSearch.framework/Versions/A/InternationalTextSearch\ndyld[33990]: <13271AA6-33EA-369B-B2D1-6EC528C820E7> /System/Library/PrivateFrameworks/SoftwareUpdateCoreSupport.framework/Versions/A/SoftwareUpdateCoreSupport\ndyld[33990]: <2CA857AF-D999-34DC-94A1-3AC0E5B80416> /System/Library/PrivateFrameworks/SoftwareUpdateCoreConnect.framework/Versions/A/SoftwareUpdateCoreConnect\ndyld[33990]: /usr/lib/libbootpolicy.dylib\ndyld[33990]: /usr/lib/libpartition2_dynamic.dylib\ndyld[33990]: <9A8926C8-36A6-3DB4-A485-059C1F630984> /usr/lib/libAppleArchive.dylib\ndyld[33990]: <5FFE1FFA-6BD0-32AF-A815-7543731CA763> /usr/lib/libbz2.1.0.dylib\ndyld[33990]: <06728C4D-5750-308F-8290-EAF7BE91F4BB> /System/Library/PrivateFrameworks/OSAnalytics.framework/Versions/A/OSAnalytics\ndyld[33990]: <2FA711C7-F764-363A-BF03-295E0DA88B79> /System/Library/PrivateFrameworks/DeviceRecovery.framework/Versions/A/DeviceRecovery\ndyld[33990]: <59136324-34E6-3367-92BB-659346907A04> /System/Library/PrivateFrameworks/CoreSymbolication.framework/Versions/A/CoreSymbolication\ndyld[33990]: <724D42FC-F4FD-39C7-A1BF-D0AD086231F4> /System/Library/PrivateFrameworks/Symbolication.framework/Versions/A/Symbolication\ndyld[33990]: <7C923545-F3BB-3215-9720-85196358D9F1> /System/Library/PrivateFrameworks/DebugSymbols.framework/Versions/A/DebugSymbols\ndyld[33990]: <566F2D7D-0F3B-3290-A739-7A40A151F0BE> /System/Library/PrivateFrameworks/MallocStackLogging.framework/Versions/A/MallocStackLogging\ndyld[33990]: <7B63C2BF-8C7C-3ECA-ACD9-F1B75DBE018C> /usr/lib/swift/libswift_RegexParser.dylib\ndyld[33990]: <4646F780-1D5E-3EE7-B00A-64619293CC18> /usr/lib/libiconv.2.dylib\ndyld[33990]: <1940124C-0D73-35D2-9D94-A75F116088A0> /usr/lib/libcharset.1.dylib\ndyld[33990]: <24779350-BC29-3465-AAB3-F7CD0DA5844A> /System/Library/PrivateFrameworks/PoirotSQLite.framework/Versions/A/PoirotSQLite\ndyld[33990]: <2091B02D-8D55-3DC4-8097-60C193D03C85> /System/Library/PrivateFrameworks/CascadeSets.framework/Versions/A/CascadeSets\ndyld[33990]: <7F00413A-4D40-3DBF-8FD5-859B23E6DC03> /System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG\ndyld[33990]: /usr/lib/libexpat.1.dylib\ndyld[33990]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib\ndyld[33990]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib\ndyld[33990]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib\ndyld[33990]: <7304F8B3-8E0F-3813-BFAF-9A565CEA0A11> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib\ndyld[33990]: <01AAD3B4-D6BA-36D9-BA6F-D494D2AC161D> /usr/lib/libate.dylib\ndyld[33990]: <8EA6CA42-AA01-3C0F-9672-4917481BAAAE> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib\ndyld[33990]: /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib\ndyld[33990]: <526C249F-FF2E-3DC4-A639-B41A032E8CCE> /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libllvm-flatbuffers.dylib\ndyld[33990]: <1FDD3B19-C04A-3EE7-B7DF-E1F89954A696> /System/Library/PrivateFrameworks/FramePacing.framework/Versions/A/FramePacing\ndyld[33990]: <2C410B78-B9A5-30DC-8D83-FFEC1277F34C> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libGPUCompilerUtils.dylib\ndyld[33990]: <90CFC86E-833E-3E9F-BAAC-2B61BD750DA6> /System/Library/PrivateFrameworks/CoreDuetContext.framework/Versions/A/CoreDuetContext\ndyld[33990]: <838F99F9-D3FA-335B-9767-B5D04A3FACA6> /System/Library/PrivateFrameworks/CoreDuet.framework/Versions/A/CoreDuet\ndyld[33990]: <712AD9C1-44D2-36F4-BA8E-15038521462B> /System/Library/Frameworks/CoreData.framework/Versions/A/CoreData\ndyld[33990]: <9805BB7B-12C9-39F5-9070-C5B8BFCAE2AF> /System/Library/Frameworks/CoreLocation.framework/Versions/A/CoreLocation\ndyld[33990]: /System/Library/Frameworks/Intents.framework/Versions/A/Intents\ndyld[33990]: /System/Library/Frameworks/_LocationEssentials.framework/Versions/A/_LocationEssentials\ndyld[33990]: <1DAFDDDA-BB7B-320E-BCFC-B7C22886D486> /System/Library/PrivateFrameworks/GeoServices.framework/Versions/A/GeoServices\ndyld[33990]: /System/Library/PrivateFrameworks/LocationSupport.framework/Versions/A/LocationSupport\ndyld[33990]: <515FDCCC-535A-398B-BBD3-3D35565F5423> /System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth\ndyld[33990]: <38EE3C42-06D6-3A46-A420-DF701A4EA911> /System/Library/PrivateFrameworks/GeoServicesCore.framework/Versions/A/GeoServicesCore\ndyld[33990]: <8D0ECDD1-24B6-3B8D-9CF9-CDC55FC64490> /System/Library/PrivateFrameworks/PhoneNumbers.framework/Versions/A/PhoneNumbers\ndyld[33990]: <3D533C35-3A2A-3672-92EF-5FEE9EE739AC> /System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation\ndyld[33990]: <2B5FB7B0-844C-3D84-9EFD-020B285B0F8D> /System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport\ndyld[33990]: <62740FDD-2B16-3319-B5C9-022D45C6B03A> /System/Library/PrivateFrameworks/OSEligibility.framework/Versions/A/OSEligibility\ndyld[33990]: <10C63D59-07BC-3518-87A0-83CAC48D8A70> /System/Library/PrivateFrameworks/IconServices.framework/Versions/A/IconServices\ndyld[33990]: <8EF56F82-8CCE-3811-AD16-6D0939187B45> /System/Library/Frameworks/LightweightCodeRequirements.framework/Versions/A/LightweightCodeRequirements\ndyld[33990]: /System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit\ndyld[33990]: <1946F8FE-0ABC-3F8F-9116-5451ECABD14C> /System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices\ndyld[33990]: /System/Library/PrivateFrameworks/IconFoundation.framework/Versions/A/IconFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/AssistantServices.framework/Versions/A/AssistantServices\ndyld[33990]: <6A34A62A-16D4-34F0-B34B-2D96B53C20AD> /System/Library/PrivateFrameworks/IconRendering.framework/Versions/A/IconRendering\ndyld[33990]: /System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI\ndyld[33990]: <0943679D-FF88-3F18-BE4B-D8B4827AB0B5> /System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage\ndyld[33990]: <968B5A5F-9749-3527-AF2A-66B599785308> /System/Library/PrivateFrameworks/SFSymbols.framework/Versions/A/SFSymbols\ndyld[33990]: /System/Library/Frameworks/DeveloperToolsSupport.framework/Versions/A/DeveloperToolsSupport\ndyld[33990]: <92090A92-DFAF-3EBC-886C-655EC158A53F> /System/Library/PrivateFrameworks/RenderBox.framework/Versions/A/RenderBox\ndyld[33990]: <986D57A7-BFF1-3DAA-8EB1-17CCAA76C731> /System/Library/PrivateFrameworks/CoreSVG.framework/Versions/A/CoreSVG\ndyld[33990]: /System/Library/PrivateFrameworks/TextureIO.framework/Versions/A/TextureIO\ndyld[33990]: /usr/lib/swift/libswiftCoreImage.dylib\ndyld[33990]: <77D85BA0-FE1C-3B5A-92DB-70A30202C990> /System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer\ndyld[33990]: /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL\ndyld[33990]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib\ndyld[33990]: <6CEF3932-AAC9-3F8E-905D-A826F2884C9A> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib\ndyld[33990]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib\ndyld[33990]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib\ndyld[33990]: /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib\ndyld[33990]: <07CB5D41-C2F3-3C33-951F-67B2C8B8B662> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices\ndyld[33990]: <5D3E7FFF-AC8E-3D6F-8E99-B199E593D270> /System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG\ndyld[33990]: <49E7449E-1385-3B53-94CC-36EFC31E98FE> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib\ndyld[33990]: <23C577A8-DB0B-3A0A-9058-1289483C262A> /usr/lib/libhvf.dylib\ndyld[33990]: <11E757EC-72FB-3C53-8ED7-641428AB6169> /System/Library/PrivateFrameworks/ParsingInternal.framework/Versions/A/ParsingInternal\ndyld[33990]: /System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib\ndyld[33990]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore\ndyld[33990]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage\ndyld[33990]: <199F6401-91D0-36E9-9EA9-D4B44ED1CE3A> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork\ndyld[33990]: <4D134FE3-50EE-39D5-9699-04B4B673DD35> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix\ndyld[33990]: <2E7E2722-3821-3DBF-B25A-6EA45D1A8FD4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector\ndyld[33990]: <3E1FE9EA-34A2-3545-B639-48B1FE1FD3D4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray\ndyld[33990]: <3103E210-FF5C-3677-BDD3-59FF17A6ACEC> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions\ndyld[33990]: /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSBenchmarkLoop.framework/Versions/A/MPSBenchmarkLoop\ndyld[33990]: <31F90368-23A5-39BB-822B-C8470C4479AE> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSHost.framework/Versions/A/MPSHost\ndyld[33990]: <9C416BB2-0882-315C-AF23-F476E34983BC> /System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools\ndyld[33990]: /System/Library/PrivateFrameworks/IOAccelMemoryInfo.framework/Versions/A/IOAccelMemoryInfo\ndyld[33990]: /System/Library/PrivateFrameworks/kperf.framework/Versions/A/kperf\ndyld[33990]: <03470B3A-A004-39A0-B6A4-F2A4AFFFCDD3> /System/Library/PrivateFrameworks/GPURawCounter.framework/Versions/A/GPURawCounter\ndyld[33990]: <4D8F39C6-B221-3AF1-BB40-CAEB0A174D61> /System/Library/PrivateFrameworks/ASEProcessing.framework/Versions/A/ASEProcessing\ndyld[33990]: /System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing\ndyld[33990]: <1B4C0154-843C-3CEE-9628-22978082DD2D> /System/Library/PrivateFrameworks/SILManager.framework/Versions/A/SILManager\ndyld[33990]: /System/Library/PrivateFrameworks/Tightbeam.framework/Versions/A/Tightbeam\ndyld[33990]: <856ACB2A-3334-3BA6-AAC8-8F344E7CDB83> /usr/lib/swift/libswiftCompression.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/AFKUser.framework/Versions/A/AFKUser\ndyld[33990]: <2186F196-EE17-3A59-B9DA-D6823BEDD35B> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI\ndyld[33990]: <086BB8AD-E317-3FC4-9E44-0D7C6036E8D7> /System/Library/PrivateFrameworks/SAObjects.framework/Versions/A/SAObjects\ndyld[33990]: /System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox\ndyld[33990]: /System/Library/PrivateFrameworks/MediaRemote.framework/Versions/A/MediaRemote\ndyld[33990]: <7F1A25E4-ED0A-3502-AABA-26EDB4A0D2A7> /System/Library/Frameworks/UserNotifications.framework/Versions/A/UserNotifications\ndyld[33990]: <8A5E0FF6-3116-3082-A0AD-20DCD6C5E1B4> /System/Library/PrivateFrameworks/SiriInstrumentation.framework/Versions/A/SiriInstrumentation\ndyld[33990]: <600E036E-9B18-35BE-B40B-E8D2D53AC90D> /System/Library/PrivateFrameworks/SiriAnalytics.framework/Versions/A/SiriAnalytics\ndyld[33990]: <619E6770-766A-3629-9AF8-F32C009375E9> /System/Library/PrivateFrameworks/SiriTTSService.framework/Versions/A/SiriTTSService\ndyld[33990]: <71CAE70A-72AD-3F74-834D-3519B545C08D> /System/Library/PrivateFrameworks/SiriCrossDeviceArbitration.framework/Versions/A/SiriCrossDeviceArbitration\ndyld[33990]: /System/Library/PrivateFrameworks/FeedbackLogger.framework/Versions/A/FeedbackLogger\ndyld[33990]: <55FBCBE4-1032-3017-BA49-D734B82405DF> /System/Library/PrivateFrameworks/FaceTimeNameUtility.framework/Versions/A/FaceTimeNameUtility\ndyld[33990]: /System/Library/PrivateFrameworks/SiriCrossDeviceArbitrationFeedback.framework/Versions/A/SiriCrossDeviceArbitrationFeedback\ndyld[33990]: <368BC882-02B9-38AB-89B4-F62430F2B8EB> /usr/lib/swift/libswiftCoreLocation.dylib\ndyld[33990]: /usr/lib/swift/libswiftAVFoundation.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/UIKitServices.framework/Versions/A/UIKitServices\ndyld[33990]: <54A2CBB8-623D-3629-904A-D0399ED13547> /System/Library/PrivateFrameworks/UnifiedAssetFramework.framework/Versions/A/UnifiedAssetFramework\ndyld[33990]: <8AF1606D-5C93-3B80-BC81-60C5688628E2> /System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore\ndyld[33990]: /System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession\ndyld[33990]: <52BD9E26-B356-3EAA-9AD7-7FF700C61A91> /System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI\ndyld[33990]: <3FF99846-E48C-3C9A-814C-35B45E5F60EC> /usr/lib/libAudioStatistics.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk\ndyld[33990]: /System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio\ndyld[33990]: <75F77FEC-BE14-3C97-93DA-403C3B529D3B> /usr/lib/libAudioToolboxUtility.dylib\ndyld[33990]: <7AE04E20-83FD-3B1B-8846-E9869AD98DB5> /usr/lib/swift/libswiftCoreMIDI.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata\ndyld[33990]: /System/Library/PrivateFrameworks/AudioDSPGraph.framework/Versions/A/AudioDSPGraph\ndyld[33990]: <6108A12D-286B-3CF2-B848-B0E7A0189DCC> /System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy\ndyld[33990]: <655F6374-6CE8-3D0E-994E-4D7C37F78E89> /usr/lib/libSMC.dylib\ndyld[33990]: <912BFF10-FB8F-3D52-9941-ACDCE1CAE36A> /usr/lib/libperfcheck.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/AudioAnalytics.framework/Versions/A/AudioAnalytics\ndyld[33990]: <869F0693-0E82-38C1-8920-C782E71735CA> /System/Library/Frameworks/OSLog.framework/Versions/A/OSLog\ndyld[33990]: <173A632F-20F3-30C1-BC55-EFFE977BBB8E> /usr/lib/libmis.dylib\ndyld[33990]: <52A7AD42-9DE0-393B-A6FB-A7CB6FF8F3A5> /System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience\ndyld[33990]: <1A63E9E1-2D64-3AF4-9CCD-6EF042397F84> /System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore\ndyld[33990]: <04DC06C1-2BFA-3FEE-9429-A33E41721A3E> /usr/lib/libspindump.dylib\ndyld[33990]: <7CC1BC36-42C1-39A3-AA4F-F9C8ABCE0CD5> /System/Library/PrivateFrameworks/AudioAccessoryServices.framework/Versions/A/AudioAccessoryServices\ndyld[33990]: /System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils\ndyld[33990]: /System/Library/PrivateFrameworks/HID.framework/Versions/A/HID\ndyld[33990]: <920D8AA6-CDCD-3E0F-AD66-F0673ACABD3F> /System/Library/PrivateFrameworks/Sharing.framework/Versions/A/Sharing\ndyld[33990]: <3DD8C4CA-23E9-35CF-AD67-549DD72D3344> /System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras\ndyld[33990]: <236517AD-8D16-3E62-8603-EBE7B65ACACA> /System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211\ndyld[33990]: <42F76533-D8DD-3A24-A08A-103C51428797> /System/Library/PrivateFrameworks/IDSFoundation.framework/Versions/A/IDSFoundation\ndyld[33990]: <116D159B-163E-3F91-9591-30FF8B6EB537> /System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Apple80211\ndyld[33990]: /System/Library/Frameworks/CoreWLAN.framework/Versions/A/CoreWLAN\ndyld[33990]: <1ABB6C50-A5DE-3744-8F07-8C3B5617B0B9> /System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth\ndyld[33990]: <82D79BDA-26A0-3A44-AAC8-911411801FDE> /usr/lib/swift/libswiftRegexBuilder.dylib\ndyld[33990]: <41E45E0C-2E88-3605-B213-F7CD760A9FF4> /System/Library/PrivateFrameworks/IMFoundation.framework/Versions/A/IMFoundation\ndyld[33990]: <40F60A3F-90A9-3F07-A99D-005E3158C6F6> /System/Library/PrivateFrameworks/Marco.framework/Versions/A/Marco\ndyld[33990]: <59FFD032-1427-39F6-BC16-A6877582A243> /System/Library/PrivateFrameworks/CommonUtilities.framework/Versions/A/CommonUtilities\ndyld[33990]: <6C3E51F8-D809-3AA1-8695-B75714C2D39A> /System/Library/PrivateFrameworks/Engram.framework/Versions/A/Engram\ndyld[33990]: /System/Library/PrivateFrameworks/XPCDistributed.framework/Versions/A/XPCDistributed\ndyld[33990]: <12066854-2BE4-35DF-BA9F-B38221C980FD> /usr/lib/libtidy.A.dylib\ndyld[33990]: <63F598E2-AF8A-3F29-BE11-3F14DB377A5B> /System/Library/PrivateFrameworks/Bom.framework/Versions/A/Bom\ndyld[33990]: /usr/lib/libParallelCompression.dylib\ndyld[33990]: <9E06CB59-0638-3C9F-B202-264E739433AC> /usr/lib/libIOReport.dylib\ndyld[33990]: <15EEE715-2670-3288-AFA8-504BAD951B4F> /System/Library/PrivateFrameworks/WiFiPeerToPeer.framework/Versions/A/WiFiPeerToPeer\ndyld[33990]: <3854272C-7B14-3A3C-9BB1-F0FBA394708A> /System/Library/PrivateFrameworks/Centauri.framework/Versions/A/Centauri\ndyld[33990]: <946B1484-B180-3451-A452-B54BF5A6D392> /System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon\ndyld[33990]: <2B49C295-4EA2-3DE3-90B4-DC03A96F2657> /usr/lib/libmrc.dylib\ndyld[33990]: <6661265C-7B78-3158-9011-4BFDFFEF7807> /System/Library/PrivateFrameworks/IPConfiguration.framework/Versions/A/IPConfiguration\ndyld[33990]: /System/Library/PrivateFrameworks/Netrb.framework/Versions/A/Netrb\ndyld[33990]: /System/Library/PrivateFrameworks/FrontBoardServices.framework/Versions/A/FrontBoardServices\ndyld[33990]: /System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData\ndyld[33990]: <757FEDFF-841C-3D62-B703-CDE79E929363> /System/Library/PrivateFrameworks/GraphicsServices.framework/Versions/A/GraphicsServices\ndyld[33990]: <093EF25B-5305-3611-B068-E65071858F52> /System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit\ndyld[33990]: /System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory\ndyld[33990]: <3B7FD4C1-D1D4-3DA9-B2F8-3D4094679D76> /System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory\ndyld[33990]: <016C5057-625C-30B1-AD32-7BC9D082F05B> /System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio\ndyld[33990]: /System/Library/Frameworks/AVRouting.framework/Versions/A/AVRouting\ndyld[33990]: <5240B3A0-D035-345E-A636-BC3A92C847C4> /usr/lib/libAccessibility.dylib\ndyld[33990]: <1FB2BCFD-D9FC-385A-A0EA-E2B5052E27F5> /System/Library/PrivateFrameworks/MediaServices.framework/Versions/A/MediaServices\ndyld[33990]: /System/Library/PrivateFrameworks/IDS.framework/Versions/A/IDS\ndyld[33990]: /System/Library/PrivateFrameworks/IsolatedCoreAudioClient.framework/Versions/A/IsolatedCoreAudioClient\ndyld[33990]: <7CC0621B-3B88-3533-A3FB-52E6214486EE> /System/Library/PrivateFrameworks/CoreAudioOrchestration.framework/Versions/A/CoreAudioOrchestration\ndyld[33990]: /System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox\ndyld[33990]: /System/Library/PrivateFrameworks/CoreAVCHD.framework/Versions/A/CoreAVCHD\ndyld[33990]: <74D313A5-4D99-35D1-A4C9-B76AB6457EF0> /System/Library/Frameworks/MediaAccessibility.framework/Versions/A/MediaAccessibility\ndyld[33990]: <87F549F4-73CC-302B-ABDB-D3CCFADABFA9> /System/Library/PrivateFrameworks/Mangrove.framework/Versions/A/Mangrove\ndyld[33990]: <214294AE-C7B7-3C9A-A4F8-201C989F9779> /System/Library/PrivateFrameworks/CMPhoto.framework/Versions/A/CMPhoto\ndyld[33990]: <5F090F48-E481-3737-8E75-362E5D274879> /System/Library/Frameworks/CoreTelephony.framework/Versions/A/CoreTelephony\ndyld[33990]: <9ACFCA55-82CB-33DB-AD00-443576099FDB> /System/Library/PrivateFrameworks/CoreAUC.framework/Versions/A/CoreAUC\ndyld[33990]: <71A0C0AD-67F3-36F9-BF73-6DD5D7424AF7> /System/Library/PrivateFrameworks/AppleJPEGXL.framework/Versions/A/AppleJPEGXL\ndyld[33990]: <825E8416-E246-338E-A5CF-AA81A1B01DD9> /System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport\ndyld[33990]: <7CF84496-675C-3241-B0EF-E83C95F188FA> /System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA\ndyld[33990]: <63A6BBA0-CD50-30F8-9CD2-81B59264EA13> /usr/lib/libTelephonyUtilDynamic.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler\ndyld[33990]: /System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment\ndyld[33990]: /System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay\ndyld[33990]: /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit\ndyld[33990]: /System/Library/PrivateFrameworks/CryptoKitCBridging.framework/Versions/A/CryptoKitCBridging\ndyld[33990]: <714063A8-D81E-3B22-9B36-88948A979E7F> /System/Library/Frameworks/CryptoTokenKit.framework/Versions/A/CryptoTokenKit\ndyld[33990]: <86858734-8B4D-38E6-AAA7-B7A046A7CB2A> /System/Library/Frameworks/LocalAuthentication.framework/Versions/A/LocalAuthentication\ndyld[33990]: /System/Library/PrivateFrameworks/LocalAuthenticationCore.framework/Versions/A/LocalAuthenticationCore\ndyld[33990]: /System/Library/PrivateFrameworks/LocalAuthenticationCredentialServices.framework/Versions/A/LocalAuthenticationCredentialServices\ndyld[33990]: /System/Library/Frameworks/LocalAuthentication.framework/Support/SharedUtils.framework/Versions/A/SharedUtils\ndyld[33990]: <80C3E2D4-B6B8-3C62-B257-27DEEBAD4935> /usr/lib/libcsfde.dylib\ndyld[33990]: <650E155C-1FE3-36ED-8D84-157D380F7F95> /usr/lib/libCoreStorage.dylib\ndyld[33990]: <46DD93AF-BACD-309B-AD51-9CC47C78CA2C> /System/Library/PrivateFrameworks/MediaKit.framework/Versions/A/MediaKit\ndyld[33990]: /System/Library/Frameworks/DiscRecording.framework/Versions/A/DiscRecording\ndyld[33990]: <1F8700BE-BD91-3B94-AC32-A5F10CEFEE35> /System/Library/PrivateFrameworks/ProtectedCloudStorage.framework/Versions/A/ProtectedCloudStorage\ndyld[33990]: /System/Library/PrivateFrameworks/EFILogin.framework/Versions/A/EFILogin\ndyld[33990]: /System/Library/PrivateFrameworks/PersistentConnection.framework/Versions/A/PersistentConnection\ndyld[33990]: <6B0D099C-AC56-35DB-90F1-88A09E587FCB> /System/Library/PrivateFrameworks/SonicFoundation.framework/Versions/A/SonicFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/AsyncAlgorithmsInternal.framework/Versions/A/AsyncAlgorithmsInternal\ndyld[33990]: <2CA62C12-37B5-345A-BF79-5D05F43F6BFB> /System/Library/PrivateFrameworks/FTAWD.framework/Versions/A/FTAWD\ndyld[33990]: <0A1C4D11-C108-35E9-A921-86ED86CF7446> /System/Library/PrivateFrameworks/Dendrite.framework/Versions/A/Dendrite\ndyld[33990]: /usr/lib/libtailspin.dylib\ndyld[33990]: <5FEA8C08-1577-3296-BC9C-7F3203E8EBFB> /System/Library/PrivateFrameworks/Osprey.framework/Versions/A/Osprey\ndyld[33990]: /System/Library/PrivateFrameworks/SiriTTS.framework/Versions/A/SiriTTS\ndyld[33990]: <0C005C4D-CA12-389C-9CCE-C4ED05B187E8> /System/Library/Frameworks/NaturalLanguage.framework/Versions/A/NaturalLanguage\ndyld[33990]: <0E502870-00F4-35D4-AF82-E7059244798E> /System/Library/PrivateFrameworks/GenerativeModels.framework/Versions/A/GenerativeModels\ndyld[33990]: <68474F39-798D-325B-B52F-3DE214F279AE> /System/Library/PrivateFrameworks/SiriPowerInstrumentation.framework/Versions/A/SiriPowerInstrumentation\ndyld[33990]: <5E36265A-7670-3D39-A2B8-71DA0AA131CF> /usr/lib/swift/libswiftNaturalLanguage.dylib\ndyld[33990]: <6794652C-86F0-37EB-838D-483177685E26> /System/Library/PrivateFrameworks/TailspinSymbolication.framework/Versions/A/TailspinSymbolication\ndyld[33990]: <089C1A34-2F4E-3649-94AA-B28A7ECB008B> /System/Library/PrivateFrameworks/Darwinup.framework/Versions/A/Darwinup\ndyld[33990]: /System/Library/PrivateFrameworks/SignpostSupport.framework/Versions/A/SignpostSupport\ndyld[33990]: <8C10B437-C282-37F5-834F-E7179C700373> /System/Library/PrivateFrameworks/FeatureFlagsSupport.framework/Versions/A/FeatureFlagsSupport\ndyld[33990]: /System/Library/PrivateFrameworks/ktrace.framework/Versions/A/ktrace\ndyld[33990]: /System/Library/PrivateFrameworks/SampleAnalysis.framework/Versions/A/SampleAnalysis\ndyld[33990]: <400B0E96-4869-37BE-9832-1A14C386148B> /System/Library/PrivateFrameworks/kperfdata.framework/Versions/A/kperfdata\ndyld[33990]: <7E3E0CF7-905A-3244-A0C9-0ADCC2E16415> /usr/lib/libdscsym.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity\ndyld[33990]: <6FBA9099-E428-3571-B940-0D210B6D0861> /System/Library/PrivateFrameworks/BulkSymbolication.framework/Versions/A/BulkSymbolication\ndyld[33990]: <90E600A3-0A27-348A-AA57-D1DF4FB305E8> /usr/lib/libTLE.dylib\ndyld[33990]: <2D1B971F-6A7F-32D0-8B0F-F8FA3A13E8F1> /System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper\ndyld[33990]: <8F2949A6-43A0-30A2-B5D2-945949C5AA01> /System/Library/PrivateFrameworks/Espresso.framework/Versions/A/Espresso\ndyld[33990]: /System/Library/Frameworks/CoreML.framework/Versions/A/CoreML\ndyld[33990]: /usr/lib/libedit.3.dylib\ndyld[33990]: <465A74BC-F20D-3C05-9441-7E08EAA49FAF> /System/Library/PrivateFrameworks/ANECompiler.framework/Versions/A/ANECompiler\ndyld[33990]: /System/Library/PrivateFrameworks/AppleNeuralEngine.framework/Versions/A/AppleNeuralEngine\ndyld[33990]: <97C5C585-F5EE-323A-B949-69EAE9080871> /System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL\ndyld[33990]: <7401E849-7B2E-39A9-99D3-5CB0A6BBDFFE> /System/Library/Frameworks/MetalPerformanceShadersGraph.framework/Versions/A/MetalPerformanceShadersGraph\ndyld[33990]: /System/Library/PrivateFrameworks/MLCompilerServices.framework/Versions/A/MLCompilerServices\ndyld[33990]: /System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices\ndyld[33990]: <9EB04E94-EE2D-38A5-A214-00AF73DBE4E9> /usr/lib/libncurses.5.4.dylib\ndyld[33990]: /usr/lib/libsandbox.1.dylib\ndyld[33990]: <2F2EF0D7-2FE4-3A5A-8E4C-E1571C8D0C10> /usr/lib/libMatch.1.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/ODIE.framework/Versions/A/ODIE\ndyld[33990]: /System/Library/PrivateFrameworks/MLModelAsset.framework/Versions/A/MLModelAsset\ndyld[33990]: <22B4CD07-5C72-3CA4-9CD1-2C87686CDDE5> /System/Library/PrivateFrameworks/MLCompilerRuntime.framework/Versions/A/MLCompilerRuntime\ndyld[33990]: /System/Library/Frameworks/MLCompute.framework/Versions/A/MLCompute\ndyld[33990]: <6028DD46-8E5A-33F0-93B2-41FA480366CC> /System/Library/PrivateFrameworks/MLAssetIO.framework/Versions/A/MLAssetIO\ndyld[33990]: /usr/lib/swift/libswiftMLCompute.dylib\ndyld[33990]: <067E2603-4FEA-3CA5-8926-45F60681EDDB> /System/Library/PrivateFrameworks/AVFCore.framework/Versions/A/AVFCore\ndyld[33990]: /System/Library/PrivateFrameworks/AVFCapture.framework/Versions/A/AVFCapture\ndyld[33990]: <3B782AC2-00C4-3534-91D2-5C7242B32440> /System/Library/PrivateFrameworks/CMImaging.framework/Versions/A/CMImaging\ndyld[33990]: <1772C40D-6EF4-3F81-BA00-6EE8B05039A6> /System/Library/PrivateFrameworks/Quagga.framework/Versions/A/Quagga\ndyld[33990]: <57A10B70-C3C9-34C6-8D22-F5118B63E2F0> /System/Library/PrivateFrameworks/CMCapture.framework/Versions/A/CMCapture\ndyld[33990]: <1035C1AB-5058-3AFA-8D77-514901516251> /System/Library/Frameworks/CoreMediaIO.framework/Versions/A/CoreMediaIO\ndyld[33990]: /System/Library/PrivateFrameworks/CMCaptureDevice.framework/Versions/A/CMCaptureDevice\ndyld[33990]: <1F873909-B3B8-3D55-9673-9AFA86BB085B> /System/Library/PrivateFrameworks/CoreBrightness.framework/Versions/A/CoreBrightness\ndyld[33990]: /System/Library/PrivateFrameworks/CinematicFraming.framework/Versions/A/CinematicFraming\ndyld[33990]: <882BC08E-B1E1-3E52-AE8A-AC22A1BF2BE8> /System/Library/PrivateFrameworks/ModelManagerServices.framework/Versions/A/ModelManagerServices\ndyld[33990]: <3E83115F-D04B-3C8D-8646-35204AA2DB84> /System/Library/PrivateFrameworks/CPMS.framework/Versions/A/CPMS\ndyld[33990]: /System/Library/PrivateFrameworks/SystemStatus.framework/Versions/A/SystemStatus\ndyld[33990]: <2E109991-45C6-3783-8A36-B6A8070AAD67> /System/Library/Frameworks/CoreMotion.framework/Versions/A/CoreMotion\ndyld[33990]: /System/Library/PrivateFrameworks/TimeSync.framework/Versions/A/TimeSync\ndyld[33990]: <9B3D4CA3-7BCF-36C9-AA99-27BDFE7854CD> /System/Library/PrivateFrameworks/DistributedSensing.framework/Versions/A/DistributedSensing\ndyld[33990]: /System/Library/PrivateFrameworks/MobileBluetooth.framework/Versions/A/MobileBluetooth\ndyld[33990]: <0BAB3589-8D81-3C60-9F05-9D207E89F4B6> /System/Library/PrivateFrameworks/IOKitten.framework/Versions/A/IOKitten\ndyld[33990]: <8A2C8C17-E138-3B34-8643-ED4FB1C9049E> /System/Library/PrivateFrameworks/LocationLogEncryption.framework/Versions/A/LocationLogEncryption\ndyld[33990]: <05EC9C98-7211-39C9-B376-796F37327801> /System/Library/PrivateFrameworks/AppleIntelligenceReporting.framework/Versions/A/AppleIntelligenceReporting\ndyld[33990]: <47AAECAD-C28C-352E-BB86-7F292E0BFBC6> /System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji\ndyld[33990]: <327536E3-A27C-38C2-A67F-D6488D04CCEE> /System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling\ndyld[33990]: <95BA357E-906A-3183-A402-A41D486B5AB3> /System/Library/PrivateFrameworks/Montreal.framework/Versions/A/Montreal\ndyld[33990]: /usr/lib/libcmph.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/GenerativeModelsFoundation.framework/Versions/A/GenerativeModelsFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/TokenGeneration.framework/Versions/A/TokenGeneration\ndyld[33990]: <418985BB-52A3-34D4-8379-40DC4C63AA32> /System/Library/PrivateFrameworks/GenerativeFunctions.framework/Versions/A/GenerativeFunctions\ndyld[33990]: <63FD423F-836C-3034-BA48-100AE09A9140> /System/Library/PrivateFrameworks/GenerativeFunctionsFoundation.framework/Versions/A/GenerativeFunctionsFoundation\ndyld[33990]: /System/Library/PrivateFrameworks/ModelCatalog.framework/Versions/A/ModelCatalog\ndyld[33990]: <67C3B698-8279-30F1-9167-4730E6F41F5A> /System/Library/PrivateFrameworks/SensitiveContentAnalysisML.framework/Versions/A/SensitiveContentAnalysisML\ndyld[33990]: /System/Library/PrivateFrameworks/GenerativeFunctionsInstrumentation.framework/Versions/A/GenerativeFunctionsInstrumentation\ndyld[33990]: /System/Library/PrivateFrameworks/PromptKit.framework/Versions/A/PromptKit\ndyld[33990]: <12245228-2B9A-3B24-8C5E-10111D68BE65> /System/Library/PrivateFrameworks/ProactiveDaemonSupport.framework/Versions/A/ProactiveDaemonSupport\ndyld[33990]: <3166486F-3F65-31DB-8018-779FFA32DC71> /System/Library/PrivateFrameworks/TokenGenerationCore.framework/Versions/A/TokenGenerationCore\ndyld[33990]: <53B3126E-7B01-30DD-961A-510E9CFC3CF1> /System/Library/PrivateFrameworks/Trial.framework/Versions/A/Trial\ndyld[33990]: /System/Library/PrivateFrameworks/TrialProto.framework/Versions/A/TrialProto\ndyld[33990]: /System/Library/PrivateFrameworks/AppleFlatBuffers.framework/Versions/A/AppleFlatBuffers\ndyld[33990]: <642E3357-AB6D-3039-A818-EDB5D6A189C2> /System/Library/PrivateFrameworks/SentencePieceInternal.framework/Versions/A/SentencePieceInternal\ndyld[33990]: <10F83439-3A9F-316B-992E-451A72876715> /System/Library/Frameworks/Vision.framework/Versions/A/Vision\ndyld[33990]: /System/Library/PrivateFrameworks/CoreSceneUnderstanding.framework/Versions/A/CoreSceneUnderstanding\ndyld[33990]: <4E70B4ED-C8E0-3636-80E4-0939FE56BB63> /System/Library/PrivateFrameworks/VisionCore.framework/Versions/A/VisionCore\ndyld[33990]: /System/Library/PrivateFrameworks/DataDetectorsCore.framework/Versions/A/DataDetectorsCore\ndyld[33990]: /System/Library/Frameworks/Vision.framework/libfaceCore.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/Futhark.framework/Versions/A/Futhark\ndyld[33990]: /System/Library/PrivateFrameworks/InertiaCam.framework/Versions/A/InertiaCam\ndyld[33990]: /System/Library/PrivateFrameworks/TextRecognition.framework/Versions/A/TextRecognition\ndyld[33990]: <73F6F860-69AF-3162-86E0-A683642287D6> /System/Library/Frameworks/DataDetection.framework/Versions/A/DataDetection\ndyld[33990]: <1D5DF9CA-41FC-3B7B-B19F-2C435F3A66F2> /System/Library/PrivateFrameworks/TextInput.framework/Versions/A/TextInput\ndyld[33990]: /System/Library/PrivateFrameworks/CVNLP.framework/Versions/A/CVNLP\ndyld[33990]: <58AC6CAB-5B91-367F-932F-BD0939BBD125> /System/Library/PrivateFrameworks/IntentsFoundation.framework/Versions/A/IntentsFoundation\ndyld[33990]: <27479D70-8BF6-3D3C-B528-1BB9B1B98391> /System/Library/PrivateFrameworks/ApplePushService.framework/Versions/A/ApplePushService\ndyld[33990]: <676D50CC-8455-3267-B8E8-CA31B8EF8F91> /System/Library/Frameworks/CloudKit.framework/Versions/A/CloudKit\ndyld[33990]: /System/Library/PrivateFrameworks/CoreDuetDaemonProtocol.framework/Versions/A/CoreDuetDaemonProtocol\ndyld[33990]: <962EA390-008E-3DDC-B2A5-7B2DAF6E8786> /System/Library/PrivateFrameworks/DeviceIdentity.framework/Versions/A/DeviceIdentity\ndyld[33990]: /System/Library/Frameworks/SharedWithYouCore.framework/Versions/A/SharedWithYouCore\ndyld[33990]: <121798D0-5254-3547-8F96-0F2AF8D84250> /System/Library/PrivateFrameworks/CloudTelemetry.framework/Versions/A/CloudTelemetry\ndyld[33990]: <768DFB9E-7FB3-3998-A3AF-BEF0C6C740A7> /System/Library/PrivateFrameworks/AppleAccount.framework/Versions/A/AppleAccount\ndyld[33990]: /System/Library/PrivateFrameworks/CacheDelete.framework/Versions/A/CacheDelete\ndyld[33990]: /System/Library/PrivateFrameworks/C2.framework/Versions/A/C2\ndyld[33990]: <23871A43-55FD-3D0C-B29F-B143A64D1D8D> /System/Library/PrivateFrameworks/CloudCoreInternal.framework/Versions/A/CloudCoreInternal\ndyld[33990]: /System/Library/PrivateFrameworks/CloudAsset.framework/Versions/A/CloudAsset\ndyld[33990]: <36607924-B1B2-39ED-B6D1-29683EFB67A0> /System/Library/Frameworks/PushKit.framework/Versions/A/PushKit\ndyld[33990]: <26865685-385E-3120-9886-2082EEC20B20> /System/Library/Frameworks/CoreTransferable.framework/Versions/A/CoreTransferable\ndyld[33990]: <5EA68C5E-69B0-3011-9D66-AEF49D82B29D> /System/Library/Frameworks/FileProvider.framework/Versions/A/FileProvider\ndyld[33990]: /System/Library/PrivateFrameworks/GenerationalStorage.framework/Versions/A/GenerationalStorage\ndyld[33990]: /System/Library/PrivateFrameworks/DesktopServicesPriv.framework/Versions/A/DesktopServicesPriv\ndyld[33990]: <024DBF34-DF66-3164-825E-F77F85462E66> /System/Library/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth\ndyld[33990]: <87907862-52FF-3F24-AC29-7C1678BCD277> /System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport\ndyld[33990]: /System/Library/PrivateFrameworks/CloudTelemetryTools.framework/Versions/A/CloudTelemetryTools\ndyld[33990]: /System/Library/PrivateFrameworks/CloudTelemetryShared.dylib\ndyld[33990]: <7BEBC9F1-212D-37F4-B601-A7AAD12F7225> /System/Library/PrivateFrameworks/RTCReporting.framework/Versions/A/RTCReporting\ndyld[33990]: <7A222E30-8DD4-3B1D-B820-4EA33B797525> /System/Library/PrivateFrameworks/AAAFoundationSwift.framework/Versions/A/AAAFoundationSwift\ndyld[33990]: <9D724BE7-0B01-39F3-82BE-BCEDC6EBAC8A> /System/Library/PrivateFrameworks/AppleIDSSOAuthentication.framework/Versions/A/AppleIDSSOAuthentication\ndyld[33990]: <659AFBBD-E22E-3474-BCFE-298DA57B1464> /System/Library/PrivateFrameworks/UIFoundation.framework/Versions/A/UIFoundation\ndyld[33990]: <0368EA7D-01B2-3AA9-A6D6-A2A0850AC800> /System/Library/PrivateFrameworks/HIDDisplay.framework/Versions/A/HIDDisplay\ndyld[33990]: <6A5A8E21-A9E6-32A2-9BDB-8013F002AEF8> /usr/lib/libcups.2.dylib\ndyld[33990]: /System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos\ndyld[33990]: <4AB71911-9300-30D4-88CF-D20EFD75ACE6> /usr/lib/libresolv.9.dylib\ndyld[33990]: /System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal\ndyld[33990]: <0CB2E7E3-E96F-343B-A4E7-545E74AF0255> /System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib\ndyld[33990]: <097F7235-CA53-3644-BB95-F6F912B4F2C7> /System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth\ndyld[33990]: /System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities\ndyld[33990]: /System/Library/PrivateFrameworks/AttributeGraph.framework/Versions/A/AttributeGraph\ndyld[33990]: /usr/lib/libAXSafeCategoryBundle.dylib\ndyld[33990]: /System/Library/Frameworks/TabularData.framework/Versions/A/TabularData\ndyld[33990]: <841D5662-2CB9-3A27-ADA7-E33AC5E45199> /System/Library/PrivateFrameworks/ArgumentParserInternal.framework/Versions/A/ArgumentParserInternal\ndyld[33990]: <4C851329-A9F4-3E9E-9E48-07FF4120DCF9> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib\ndyld[33990]: <5015CD96-C046-364D-AAE3-1F439044468B> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib\ndyld[33990]: <407BCF3E-A91F-3A7F-8B8C-DBB8E807990F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib\ndyld[33990]: <669ABE12-838F-3F14-8456-D60DE5DF8EB8> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib\ndyld[33990]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib\ndyld[33990]: <54A103BA-7D04-32DB-B204-179E2E0290CA> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib\ndyld[33990]: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib\ndyld[33990]: <1ACDAA8A-EB43-37C7-B661-39B1C0E05290> /System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary\ndyld[33990]: <12479A32-B72F-3A09-BB03-BA56C37853B5> /System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore\ndyld[33990]: /usr/lib/libapp_launch_measurement.dylib\ndyld[33990]: <4F3BEA3B-A363-3D04-B903-9B613C993CA1> /System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices\ndyld[33990]: <6C426EA5-7F1E-333E-BB5D-74465EFED12B> /usr/lib/libxslt.1.dylib\ndyld[33990]: <627D64D5-2D3C-3EC6-B4AB-FEF4DEE40871> /System/Library/PrivateFrameworks/FindMyDevice.framework/Versions/A/FindMyDevice\ndyld[33990]: <930F9F83-A947-3788-9FBA-49872FC3AF8D> /System/Library/PrivateFrameworks/FMCoreLite.framework/Versions/A/FMCoreLite\ndyld[33990]: <5F356BA6-47B5-382B-B54A-1550BB138A62> /System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement\ndyld[33990]: <38DAF669-429F-384F-87D6-8550842EEB5E> /System/Library/PrivateFrameworks/CryptoKitPrivate.framework/Versions/A/CryptoKitPrivate\ndyld[33990]: <5B73C216-2ACE-3F8C-A2B3-7D35D5D0395A> /System/Library/PrivateFrameworks/CaptiveNetwork.framework/Versions/A/CaptiveNetwork\ndyld[33990]: /System/Library/PrivateFrameworks/EAP8021X.framework/Versions/A/EAP8021X\ndyld[33990]: <36F215D1-A2C0-32CA-ADD8-6D85AB48A772> /System/Library/Frameworks/QuickLookThumbnailing.framework/Versions/A/QuickLookThumbnailing\ndyld[33990]: /System/Library/PrivateFrameworks/DiskImages.framework/Versions/A/DiskImages\ndyld[33990]: <49121861-2603-3B0A-B664-BAD9E729BE5D> /System/Library/Frameworks/NetFS.framework/Versions/A/NetFS\ndyld[33990]: <2E99AD96-DC1C-3643-9988-273AB6844EFC> /usr/lib/libcurl.4.dylib\ndyld[33990]: <46D13DA8-E7BD-37DC-91DD-D5E6CE00C2B8> /usr/lib/libcrypto.46.dylib\ndyld[33990]: <07D5F4C6-1A13-344C-882B-0B0A08048DE5> /usr/lib/libssl.48.dylib\ndyld[33990]: <8CABDD64-E6C6-3B77-B839-2E2B875CE0FE> /System/Library/Frameworks/LDAP.framework/Versions/A/LDAP\ndyld[33990]: <96C0BAAA-7FE6-3277-AFBC-31926F5935EE> /System/Library/PrivateFrameworks/TrustEvaluationAgent.framework/Versions/A/TrustEvaluationAgent\ndyld[33990]: <7CF2A32E-72DD-34F7-B179-A17ED3D7DD75> /usr/lib/libsasl2.2.dylib\ndyld[33990]: move loaded to delayed: libcmark-gfm.dylib\ndyld[33990]: move loaded to delayed: BackgroundSystemTasks\ndyld[33990]: move loaded to delayed: CoreWiFi\ndyld[33990]: move loaded to delayed: Rapport\ndyld[33990]: move loaded to delayed: SymptomAnalytics\ndyld[33990]: move loaded to delayed: libcupolicy.dylib\ndyld[33990]: move loaded to delayed: libnetworkextension.dylib\ndyld[33990]: move loaded to delayed: NetworkExtension\ndyld[33990]: move loaded to delayed: libnwswifttls.dylib\ndyld[33990]: move loaded to delayed: libpcap.A.dylib\ndyld[33990]: move loaded to delayed: XPCSupport\ndyld[33990]: move loaded to delayed: CloudServices\ndyld[33990]: move loaded to delayed: OctagonTrust\ndyld[33990]: move loaded to delayed: AppleIDAuthSupport\ndyld[33990]: move loaded to delayed: KeychainCircle\ndyld[33990]: move loaded to delayed: AuthKit\ndyld[33990]: move loaded to delayed: AAAFoundation\ndyld[33990]: move loaded to delayed: MultiverseSupport\ndyld[33990]: move loaded to delayed: DiskManagement\ndyld[33990]: move loaded to delayed: Accounts\ndyld[33990]: move loaded to delayed: URLFormatting\ndyld[33990]: move loaded to delayed: AOSKit\ndyld[33990]: move loaded to delayed: AppSSOCore\ndyld[33990]: move loaded to delayed: AVFoundation\ndyld[33990]: move loaded to delayed: DuetActivityScheduler\ndyld[33990]: move loaded to delayed: FTServices\ndyld[33990]: move loaded to delayed: InternationalSupport\ndyld[33990]: move loaded to delayed: libMemoryResourceException.dylib\ndyld[33990]: move loaded to delayed: NetworkScore\ndyld[33990]: move loaded to delayed: NetworkServiceProxy\ndyld[33990]: move loaded to delayed: StreamingExtractor\ndyld[33990]: move loaded to delayed: SymptomReporter\ndyld[33990]: move loaded to delayed: libCGInterfaces.dylib\ndyld[33990]: move loaded to delayed: AccelerateGPU\ndyld[33990]: move loaded to delayed: ApplicationServices\ndyld[33990]: move loaded to delayed: ATS\ndyld[33990]: move loaded to delayed: HIServices\ndyld[33990]: move loaded to delayed: PrintCore\ndyld[33990]: move loaded to delayed: QD\ndyld[33990]: move loaded to delayed: ColorSyncLegacy\ndyld[33990]: move loaded to delayed: SpeechSynthesis\ndyld[33990]: move loaded to delayed: CoreDuetContext\ndyld[33990]: move loaded to delayed: CoreDuet\ndyld[33990]: move loaded to delayed: CoreLocation\ndyld[33990]: move loaded to delayed: Intents\ndyld[33990]: move loaded to delayed: _LocationEssentials\ndyld[33990]: move loaded to delayed: GeoServices\ndyld[33990]: move loaded to delayed: LocationSupport\ndyld[33990]: move loaded to delayed: CoreBluetooth\ndyld[33990]: move loaded to delayed: GeoServicesCore\ndyld[33990]: move loaded to delayed: PhoneNumbers\ndyld[33990]: move loaded to delayed: IconServices\ndyld[33990]: move loaded to delayed: IconFoundation\ndyld[33990]: move loaded to delayed: AssistantServices\ndyld[33990]: move loaded to delayed: IconRendering\ndyld[33990]: move loaded to delayed: CoreUI\ndyld[33990]: move loaded to delayed: SFSymbols\ndyld[33990]: move loaded to delayed: DeveloperToolsSupport\ndyld[33990]: move loaded to delayed: RenderBox\ndyld[33990]: move loaded to delayed: CoreSVG\ndyld[33990]: move loaded to delayed: TextureIO\ndyld[33990]: move loaded to delayed: libswiftCoreImage.dylib\ndyld[33990]: move loaded to delayed: ATSUI\ndyld[33990]: move loaded to delayed: SAObjects\ndyld[33990]: move loaded to delayed: MediaRemote\ndyld[33990]: move loaded to delayed: UserNotifications\ndyld[33990]: move loaded to delayed: SiriInstrumentation\ndyld[33990]: move loaded to delayed: SiriAnalytics\ndyld[33990]: move loaded to delayed: SiriTTSService\ndyld[33990]: move loaded to delayed: SiriCrossDeviceArbitration\ndyld[33990]: move loaded to delayed: FeedbackLogger\ndyld[33990]: move loaded to delayed: FaceTimeNameUtility\ndyld[33990]: move loaded to delayed: SiriCrossDeviceArbitrationFeedback\ndyld[33990]: move loaded to delayed: libswiftCoreLocation.dylib\ndyld[33990]: move loaded to delayed: libswiftAVFoundation.dylib\ndyld[33990]: move loaded to delayed: UIKitServices\ndyld[33990]: move loaded to delayed: UnifiedAssetFramework\ndyld[33990]: move loaded to delayed: AudioDSPGraph\ndyld[33990]: move loaded to delayed: AudioAccessoryServices\ndyld[33990]: move loaded to delayed: CoreUtils\ndyld[33990]: move loaded to delayed: Sharing\ndyld[33990]: move loaded to delayed: CoreUtilsExtras\ndyld[33990]: move loaded to delayed: IO80211\ndyld[33990]: move loaded to delayed: IDSFoundation\ndyld[33990]: move loaded to delayed: Apple80211\ndyld[33990]: move loaded to delayed: CoreWLAN\ndyld[33990]: move loaded to delayed: IOBluetooth\ndyld[33990]: move loaded to delayed: libswiftRegexBuilder.dylib\ndyld[33990]: move loaded to delayed: IMFoundation\ndyld[33990]: move loaded to delayed: Marco\ndyld[33990]: move loaded to delayed: CommonUtilities\ndyld[33990]: move loaded to delayed: Engram\ndyld[33990]: move loaded to delayed: XPCDistributed\ndyld[33990]: move loaded to delayed: libtidy.A.dylib\ndyld[33990]: move loaded to delayed: Bom\ndyld[33990]: move loaded to delayed: libParallelCompression.dylib\ndyld[33990]: move loaded to delayed: libIOReport.dylib\ndyld[33990]: move loaded to delayed: WiFiPeerToPeer\ndyld[33990]: move loaded to delayed: Centauri\ndyld[33990]: move loaded to delayed: libmrc.dylib\ndyld[33990]: move loaded to delayed: IPConfiguration\ndyld[33990]: move loaded to delayed: Netrb\ndyld[33990]: move loaded to delayed: FrontBoardServices\ndyld[33990]: move loaded to delayed: AudioUnit\ndyld[33990]: move loaded to delayed: AVFAudio\ndyld[33990]: move loaded to delayed: AVRouting\ndyld[33990]: move loaded to delayed: libAccessibility.dylib\ndyld[33990]: move loaded to delayed: MediaServices\ndyld[33990]: move loaded to delayed: IDS\ndyld[33990]: move loaded to delayed: IsolatedCoreAudioClient\ndyld[33990]: move loaded to delayed: CoreAudioOrchestration\ndyld[33990]: move loaded to delayed: MediaToolbox\ndyld[33990]: move loaded to delayed: CoreAVCHD\ndyld[33990]: move loaded to delayed: MediaAccessibility\ndyld[33990]: move loaded to delayed: Mangrove\ndyld[33990]: move loaded to delayed: CMPhoto\ndyld[33990]: move loaded to delayed: CoreTelephony\ndyld[33990]: move loaded to delayed: CoreAUC\ndyld[33990]: move loaded to delayed: AppleJPEGXL\ndyld[33990]: move loaded to delayed: libTelephonyUtilDynamic.dylib\ndyld[33990]: move loaded to delayed: CryptoKit\ndyld[33990]: move loaded to delayed: CryptoKitCBridging\ndyld[33990]: move loaded to delayed: CryptoTokenKit\ndyld[33990]: move loaded to delayed: LocalAuthentication\ndyld[33990]: move loaded to delayed: LocalAuthenticationCore\ndyld[33990]: move loaded to delayed: LocalAuthenticationCredentialServices\ndyld[33990]: move loaded to delayed: SharedUtils\ndyld[33990]: move loaded to delayed: libcsfde.dylib\ndyld[33990]: move loaded to delayed: libCoreStorage.dylib\ndyld[33990]: move loaded to delayed: ProtectedCloudStorage\ndyld[33990]: move loaded to delayed: EFILogin\ndyld[33990]: move loaded to delayed: PersistentConnection\ndyld[33990]: move loaded to delayed: SonicFoundation\ndyld[33990]: move loaded to delayed: AsyncAlgorithmsInternal\ndyld[33990]: move loaded to delayed: FTAWD\ndyld[33990]: move loaded to delayed: Dendrite\ndyld[33990]: move loaded to delayed: libtailspin.dylib\ndyld[33990]: move loaded to delayed: Osprey\ndyld[33990]: move loaded to delayed: SiriTTS\ndyld[33990]: move loaded to delayed: NaturalLanguage\ndyld[33990]: move loaded to delayed: GenerativeModels\ndyld[33990]: move loaded to delayed: SiriPowerInstrumentation\ndyld[33990]: move loaded to delayed: libswiftNaturalLanguage.dylib\ndyld[33990]: move loaded to delayed: TailspinSymbolication\ndyld[33990]: move loaded to delayed: Darwinup\ndyld[33990]: move loaded to delayed: SignpostSupport\ndyld[33990]: move loaded to delayed: FeatureFlagsSupport\ndyld[33990]: move loaded to delayed: ktrace\ndyld[33990]: move loaded to delayed: SampleAnalysis\ndyld[33990]: move loaded to delayed: kperfdata\ndyld[33990]: move loaded to delayed: libdscsym.dylib\ndyld[33990]: move loaded to delayed: BulkSymbolication\ndyld[33990]: move loaded to delayed: Espresso\ndyld[33990]: move loaded to delayed: CoreML\ndyld[33990]: move loaded to delayed: libedit.3.dylib\ndyld[33990]: move loaded to delayed: ANECompiler\ndyld[33990]: move loaded to delayed: AppleNeuralEngine\ndyld[33990]: move loaded to delayed: MetalPerformanceShadersGraph\ndyld[33990]: move loaded to delayed: MLCompilerServices\ndyld[33990]: move loaded to delayed: ANEServices\ndyld[33990]: move loaded to delayed: libncurses.5.4.dylib\ndyld[33990]: move loaded to delayed: libsandbox.1.dylib\ndyld[33990]: move loaded to delayed: libMatch.1.dylib\ndyld[33990]: move loaded to delayed: ODIE\ndyld[33990]: move loaded to delayed: MLModelAsset\ndyld[33990]: move loaded to delayed: MLCompilerRuntime\ndyld[33990]: move loaded to delayed: MLCompute\ndyld[33990]: move loaded to delayed: MLAssetIO\ndyld[33990]: move loaded to delayed: libswiftMLCompute.dylib\ndyld[33990]: move loaded to delayed: AVFCore\ndyld[33990]: move loaded to delayed: AVFCapture\ndyld[33990]: move loaded to delayed: CMImaging\ndyld[33990]: move loaded to delayed: Quagga\ndyld[33990]: move loaded to delayed: CMCapture\ndyld[33990]: move loaded to delayed: CoreMediaIO\ndyld[33990]: move loaded to delayed: CMCaptureDevice\ndyld[33990]: move loaded to delayed: CoreBrightness\ndyld[33990]: move loaded to delayed: CinematicFraming\ndyld[33990]: move loaded to delayed: ModelManagerServices\ndyld[33990]: move loaded to delayed: CPMS\ndyld[33990]: move loaded to delayed: SystemStatus\ndyld[33990]: move loaded to delayed: CoreMotion\ndyld[33990]: move loaded to delayed: TimeSync\ndyld[33990]: move loaded to delayed: DistributedSensing\ndyld[33990]: move loaded to delayed: MobileBluetooth\ndyld[33990]: move loaded to delayed: IOKitten\ndyld[33990]: move loaded to delayed: LocationLogEncryption\ndyld[33990]: move loaded to delayed: AppleIntelligenceReporting\ndyld[33990]: move loaded to delayed: CoreEmoji\ndyld[33990]: move loaded to delayed: LanguageModeling\ndyld[33990]: move loaded to delayed: Montreal\ndyld[33990]: move loaded to delayed: libcmph.dylib\ndyld[33990]: move loaded to delayed: GenerativeModelsFoundation\ndyld[33990]: move loaded to delayed: TokenGeneration\ndyld[33990]: move loaded to delayed: GenerativeFunctions\ndyld[33990]: move loaded to delayed: GenerativeFunctionsFoundation\ndyld[33990]: move loaded to delayed: ModelCatalog\ndyld[33990]: move loaded to delayed: SensitiveContentAnalysisML\ndyld[33990]: move loaded to delayed: GenerativeFunctionsInstrumentation\ndyld[33990]: move loaded to delayed: PromptKit\ndyld[33990]: move loaded to delayed: ProactiveDaemonSupport\ndyld[33990]: move loaded to delayed: TokenGenerationCore\ndyld[33990]: move loaded to delayed: Trial\ndyld[33990]: move loaded to delayed: TrialProto\ndyld[33990]: move loaded to delayed: AppleFlatBuffers\ndyld[33990]: move loaded to delayed: SentencePieceInternal\ndyld[33990]: move loaded to delayed: Vision\ndyld[33990]: move loaded to delayed: CoreSceneUnderstanding\ndyld[33990]: move loaded to delayed: VisionCore\ndyld[33990]: move loaded to delayed: DataDetectorsCore\ndyld[33990]: move loaded to delayed: libfaceCore.dylib\ndyld[33990]: move loaded to delayed: Futhark\ndyld[33990]: move loaded to delayed: InertiaCam\ndyld[33990]: move loaded to delayed: TextRecognition\ndyld[33990]: move loaded to delayed: DataDetection\ndyld[33990]: move loaded to delayed: TextInput\ndyld[33990]: move loaded to delayed: CVNLP\ndyld[33990]: move loaded to delayed: IntentsFoundation\ndyld[33990]: move loaded to delayed: ApplePushService\ndyld[33990]: move loaded to delayed: CloudKit\ndyld[33990]: move loaded to delayed: CoreDuetDaemonProtocol\ndyld[33990]: move loaded to delayed: DeviceIdentity\ndyld[33990]: move loaded to delayed: SharedWithYouCore\ndyld[33990]: move loaded to delayed: CloudTelemetry\ndyld[33990]: move loaded to delayed: AppleAccount\ndyld[33990]: move loaded to delayed: CacheDelete\ndyld[33990]: move loaded to delayed: C2\ndyld[33990]: move loaded to delayed: CloudCoreInternal\ndyld[33990]: move loaded to delayed: CloudAsset\ndyld[33990]: move loaded to delayed: PushKit\ndyld[33990]: move loaded to delayed: CoreTransferable\ndyld[33990]: move loaded to delayed: FileProvider\ndyld[33990]: move loaded to delayed: GenerationalStorage\ndyld[33990]: move loaded to delayed: DesktopServicesPriv\ndyld[33990]: move loaded to delayed: CloudTelemetryTools\ndyld[33990]: move loaded to delayed: CloudTelemetryShared.dylib\ndyld[33990]: move loaded to delayed: RTCReporting\ndyld[33990]: move loaded to delayed: AAAFoundationSwift\ndyld[33990]: move loaded to delayed: AppleIDSSOAuthentication\ndyld[33990]: move loaded to delayed: UIFoundation\ndyld[33990]: move loaded to delayed: libcups.2.dylib\ndyld[33990]: move loaded to delayed: AXCoreUtilities\ndyld[33990]: move loaded to delayed: AttributeGraph\ndyld[33990]: move loaded to delayed: libAXSafeCategoryBundle.dylib\ndyld[33990]: move loaded to delayed: TabularData\ndyld[33990]: move loaded to delayed: ArgumentParserInternal\ndyld[33990]: move loaded to delayed: FindMyDevice\ndyld[33990]: move loaded to delayed: FMCoreLite\ndyld[33990]: move loaded to delayed: ServiceManagement\ndyld[33990]: move loaded to delayed: CryptoKitPrivate\ndyld[33990]: move loaded to delayed: CaptiveNetwork\ndyld[33990]: move loaded to delayed: EAP8021X\ndyld[33990]: move loaded to delayed: QuickLookThumbnailing\ndyld[33990]: /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/lib/python3.14/lib-dynload/_ctypes.cpython-314-darwin.so\ndyld[33990]: /usr/lib/libffi.dylib\ndyld[33990]: <85E37FC4-F653-335B-AD84-D8291D8EB9C3> /usr/lib/libffi-trampolines.dylib\ndyld[33990]: <9DE830B2-E5A8-3D1A-B8CA-5C7BAB606430> /opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/lib/python3.14/lib-dynload/_struct.cpython-314-darwin.so\ndyld[33990]: <56ABFF5C-436A-3F19-AA8B-732CACE57338> /private/var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-relocation-qwa2j7uc/native-disabled/libwebscene_native_engine.dylib\n" + } + ] +} diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/sdk-integrity.json b/docs/graphics/evidence/2026-09-07-osx-arm64/sdk-integrity.json new file mode 100644 index 000000000..053c4da21 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/sdk-integrity.json @@ -0,0 +1,86 @@ +{ + "scope": "SDK input integrity only; not graphics execution", + "status": "passed", + "checks": [ + { + "test": "relocated SDK integrity", + "passed": true, + "expectedSuccess": true, + "exitCode": 0, + "stdout": "Verified dawn 2ca8cbfe0f8275aa0f739e7b6b4345a16e2f0378 for osx-arm64: 77 files\n", + "stderr": "" + }, + { + "test": "relocated CMake import", + "passed": true, + "expectedSuccess": true, + "exitCode": 0, + "stdout": "-- The C compiler identification is AppleClang 21.0.0.21000101\n-- The CXX compiler identification is AppleClang 21.0.0.21000101\n-- Detecting C compiler ABI info\n-- Detecting C compiler ABI info - done\n-- Check for working C compiler: /usr/bin/cc - skipped\n-- Detecting C compile features\n-- Detecting C compile features - done\n-- Detecting CXX compiler ABI info\n-- Detecting CXX compiler ABI info - done\n-- Check for working CXX compiler: /usr/bin/c++ - skipped\n-- Detecting CXX compile features\n-- Detecting CXX compile features - done\n-- Found Python3: /opt/homebrew/Frameworks/Python.framework/Versions/3.14/bin/python3.14 (found version \"3.14.4\") found components: Interpreter\n-- Performing Test CMAKE_HAVE_LIBC_PTHREAD\n-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success\n-- Found Threads: TRUE\n-- Verified dawn 2ca8cbfe0f8275aa0f739e7b6b4345a16e2f0378 for osx-arm64: 77 files\n\n-- Configuring done (0.9s)\n-- Generating done (0.0s)\n-- Build files have been written to: /var/folders/75/zbbvxqqn0nq344djj53fzz0h0000gn/T/webscene-sdk-integrity-v9_sf4kv/build\n", + "stderr": "" + }, + { + "test": "foreign cached Dawn_DIR rejection", + "passed": true, + "expectedSuccess": false, + "exitCode": 1, + "stdout": "-- Verified dawn 2ca8cbfe0f8275aa0f739e7b6b4345a16e2f0378 for osx-arm64: 77 files\n\n-- Configuring incomplete, errors occurred!\n", + "stderr": "CMake Error at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/eng/graphics/GraphicsDependencies.cmake:45 (message):\n Dawn_DIR points outside the verified SDK; clear the stale cache\nCall Stack (most recent call first):\n CMakeLists.txt:10 (include)\n\n\n" + }, + { + "test": "revision mismatch rejection", + "passed": true, + "expectedSuccess": false, + "exitCode": 1, + "stdout": "", + "stderr": "Graphics SDK verification failed: dawn: revision mismatch; rebuild using the current dependency lock\n" + }, + { + "test": "rid mismatch rejection", + "passed": true, + "expectedSuccess": false, + "exitCode": 1, + "stdout": "", + "stderr": "Graphics SDK verification failed: dawn: rid mismatch; rebuild using the current dependency lock\n" + }, + { + "test": "lockSha256 mismatch rejection", + "passed": true, + "expectedSuccess": false, + "exitCode": 1, + "stdout": "", + "stderr": "Graphics SDK verification failed: dawn: lockSha256 mismatch; rebuild using the current dependency lock\n" + }, + { + "test": "wrong build settings rejection", + "passed": true, + "expectedSuccess": false, + "exitCode": 1, + "stdout": "", + "stderr": "Graphics SDK verification failed: dawn: build settings mismatch\n" + }, + { + "test": "mismatched generated WebGPU header rejection", + "passed": true, + "expectedSuccess": false, + "exitCode": 1, + "stdout": "", + "stderr": "Graphics SDK verification failed: dawn: checksum mismatch: include/dawn/webgpu.h\n" + }, + { + "test": "unexpected installed header rejection", + "passed": true, + "expectedSuccess": false, + "exitCode": 1, + "stdout": "", + "stderr": "Graphics SDK verification failed: dawn: installed file inventory mismatch\n" + }, + { + "test": "mismatched native library rejection", + "passed": true, + "expectedSuccess": false, + "exitCode": 1, + "stdout": "", + "stderr": "Graphics SDK verification failed: dawn: checksum mismatch: lib/libwebgpu_dawn.a\n" + } + ] +} diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/webgl-cts-acquisition.json b/docs/graphics/evidence/2026-09-07-osx-arm64/webgl-cts-acquisition.json new file mode 100644 index 000000000..06c1cd2b0 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/webgl-cts-acquisition.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": 1, + "suite": "webgl-cts", + "repository": "https://github.com/KhronosGroup/WebGL.git", + "revision": "3b7a7538e880ab7ee7383b73fbfefb6a35179e7c", + "trackedFiles": 16006, + "status": "acquired", + "conformanceStatus": "not-run" +} diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/webgpu-cts-acquisition.json b/docs/graphics/evidence/2026-09-07-osx-arm64/webgpu-cts-acquisition.json new file mode 100644 index 000000000..7a9fe04b0 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/webgpu-cts-acquisition.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": 1, + "suite": "webgpu-cts", + "repository": "https://github.com/gpuweb/cts.git", + "revision": "de0f06a6e1ab4b4399359007e5b67fe07eecf163", + "trackedFiles": 1327, + "status": "acquired", + "conformanceStatus": "not-run" +} diff --git a/docs/graphics/evidence/2026-09-07-osx-arm64/wpt-acquisition.json b/docs/graphics/evidence/2026-09-07-osx-arm64/wpt-acquisition.json new file mode 100644 index 000000000..487876a1b --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-osx-arm64/wpt-acquisition.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": 1, + "suite": "wpt", + "repository": "https://github.com/web-platform-tests/wpt.git", + "revision": "f2666d25c2aa2e1a33cf553c0386c65482dbcdc9", + "trackedFiles": 162979, + "status": "acquired", + "conformanceStatus": "not-run" +} diff --git a/docs/graphics/evidence/2026-09-07-portability/README.md b/docs/graphics/evidence/2026-09-07-portability/README.md new file mode 100644 index 000000000..2064e7121 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-portability/README.md @@ -0,0 +1,11 @@ +# G01 hosted build portability findings + +Hosted SDK run [34115613271](https://github.com/wieslawsoltes/WebScene/actions/runs/34115613271), commit `8ba8211`, failed Windows Dawn/ANGLE source license checks and Linux Dawn configuration. Logs are retained here; no hardware qualification was attempted. + +Windows inherited native CRLF behavior for upstream `text=auto` attributes despite `core.autocrlf=false`. Commit `16eb3ab` also sets `core.eol=lf` before checkout. Its regression test creates a real Git repository under a CRLF global configuration and verifies exact license bytes after checkout. All ten Python tests pass. + +Linux GCC passed Dawn's C++ module language feature probe but lacked CMake module dependency discovery. The optional module wrapper is now disabled in the dependency lock; WebScene uses generated C/C++ headers. Local macOS rebuilds against the updated lock succeeded, and all three hardware probes still pass with the adjacent library hashes verified. Logs and full probe/SDK manifests are compressed here. + +These changes require a new Windows/Linux hosted build before being considered verified on those platforms. Linux ANGLE's original job was still running when this evidence was recorded and was deliberately left uninterrupted. Hardware, driver and graphics package qualification remain separate incomplete gates for #23. + +The Linux ANGLE job subsequently completed compilation but failed SDK staging because `include/vulkan` already existed (ANGLE supplies `vulkan_fuchsia_ext.h`). The copier now merges the pinned Vulkan headers into that directory. A local staging reproduction verified that both ANGLE's extension header and upstream `vulkan.h` retain their original bytes. The hosted failure log is retained here; a new full Linux job is still required to verify the corrected stage and probe linkage. diff --git a/docs/graphics/evidence/2026-09-07-portability/angle-final-portability-build.log.gz b/docs/graphics/evidence/2026-09-07-portability/angle-final-portability-build.log.gz new file mode 100644 index 000000000..19b381580 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-portability/angle-final-portability-build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-portability/checkout-fix-tests.log.gz b/docs/graphics/evidence/2026-09-07-portability/checkout-fix-tests.log.gz new file mode 100644 index 000000000..ba78ec52e Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-portability/checkout-fix-tests.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-portability/dawn-final-portability-build.log.gz b/docs/graphics/evidence/2026-09-07-portability/dawn-final-portability-build.log.gz new file mode 100644 index 000000000..f6204383b Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-portability/dawn-final-portability-build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-portability/hosted-angle-linux.log.gz b/docs/graphics/evidence/2026-09-07-portability/hosted-angle-linux.log.gz new file mode 100644 index 000000000..9028c73e2 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-portability/hosted-angle-linux.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-portability/hosted-angle-windows-02.log.gz b/docs/graphics/evidence/2026-09-07-portability/hosted-angle-windows-02.log.gz new file mode 100644 index 000000000..ac840c7aa Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-portability/hosted-angle-windows-02.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-portability/hosted-angle-windows-03.log.gz b/docs/graphics/evidence/2026-09-07-portability/hosted-angle-windows-03.log.gz new file mode 100644 index 000000000..2ebd7125a Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-portability/hosted-angle-windows-03.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-portability/hosted-angle-windows.log.gz b/docs/graphics/evidence/2026-09-07-portability/hosted-angle-windows.log.gz new file mode 100644 index 000000000..a75b95e61 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-portability/hosted-angle-windows.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-portability/hosted-dawn-linux.log.gz b/docs/graphics/evidence/2026-09-07-portability/hosted-dawn-linux.log.gz new file mode 100644 index 000000000..acb672dfc Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-portability/hosted-dawn-linux.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-portability/hosted-dawn-windows-02.log.gz b/docs/graphics/evidence/2026-09-07-portability/hosted-dawn-windows-02.log.gz new file mode 100644 index 000000000..aa1794de2 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-portability/hosted-dawn-windows-02.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-portability/hosted-dawn-windows.log.gz b/docs/graphics/evidence/2026-09-07-portability/hosted-dawn-windows.log.gz new file mode 100644 index 000000000..3d5b39e59 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-portability/hosted-dawn-windows.log.gz differ diff --git a/docs/graphics/evidence/2026-09-07-portability/macos-probes.json.gz b/docs/graphics/evidence/2026-09-07-portability/macos-probes.json.gz new file mode 100644 index 000000000..89f4eadd1 Binary files /dev/null and b/docs/graphics/evidence/2026-09-07-portability/macos-probes.json.gz differ diff --git a/docs/graphics/evidence/2026-09-07-portability/windows-followup.md b/docs/graphics/evidence/2026-09-07-portability/windows-followup.md new file mode 100644 index 000000000..477e17fd9 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-portability/windows-followup.md @@ -0,0 +1,9 @@ +# Windows build follow-up + +Run [34116313032](https://github.com/wieslawsoltes/WebScene/actions/runs/34116313032), revision 1ef6394, failed both Windows component jobs. Full logs are retained alongside this note. + +Dawn reached shared-library linkage but Abseil objects referenced dynamic CRT imports while the surrounding build selected the static CRT. The pinned Abseil CMakeLists.txt explicitly overrides CMAKE_MSVC_RUNTIME_LIBRARY unless ABSL_MSVC_STATIC_RUNTIME is enabled. Both builder and SDK verifier now require that option on Windows. + +ANGLE failed before dependency synchronization completed: git_cache.Mirror invokes git.bat, which did not exist. DEPOT_TOOLS_UPDATE=0 skips the normal Windows bootstrap as well as self-update. The builder now explicitly invokes the pinned bootstrap/win_tools.bat and requires its generated Git wrapper before gclient sync. The depot_tools source revision remains fixed. + +All 10 local Python regression tests pass. These Windows corrections require the successor hosted build; they are not yet verified Windows successes. Linux Dawn in this run successfully built its SDK and linked its probe, without GPU hardware execution. diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/README.md b/docs/graphics/evidence/2026-09-07-v8-integration/README.md new file mode 100644 index 000000000..8538e1976 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/README.md @@ -0,0 +1,39 @@ +# G01 V8 integration attempt: failed qualification + +Both graphics-enabled and graphics-disabled Release builds linked successfully with an existing local V8 15.3.10 SDK, generated bootstrap snapshots, and passed the three native parser suites. The native engine test crashed in both configurations: SIGSEGV with graphics enabled and SIGBUS with graphics disabled. These results do not qualify either runtime configuration. Graphics linkage is not necessary to trigger the failure; its root cause has not been established. + +`result.json` records the repository/V8 revisions, input hashes and complete WebScene CMake settings for each build. The existing SDK was consumed read-only from a neighboring checkout. Its inspector header/source patches were already present. This is not evidence of a clean V8 SDK build, and should not be substituted for one. Its `args.gn` is retained alongside configure/build/test logs. + +The macOS crash report for the enabled run identifies an invalid address in `mfm_free` on a native test thread. VS Code DebugMCP tools were unavailable. LLDB could print its version, but attempts to launch a debug session exited 137 without output, so no debugger root-cause finding is claimed. Do not suppress the native test or count the successful build as a runtime pass. + +Reproduction uses `experiments/WebScene.NativeEngine.Probe` with the settings in `result.json`, separate new build directories, and `WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS` ON/OFF respectively. Substitute a local V8 SDK matching the recorded inputs and a verified graphics SDK; no author-specific path is required by the implementation. After configure: + +```sh +cmake --build --parallel 6 +cmake -E copy_if_different /icudtl.dat /icudtl.dat +ctest --test-dir --output-on-failure +``` + +Next verification must establish the V8 SDK/build compatibility and investigate the failing runtime test in both configurations. The earlier hosted CI package successes use their own SDK builds and do not establish that this reused local SDK is valid. Issue #23 remains incomplete. + +## Follow-up: inspector ABI mismatch identified + +Symbolicating a graphics-disabled RelWithDebInfo reproduction located the failure in `shutdown_inspector()` at the call to `allAsyncTasksCanceled()`, dispatching into `V8StackTraceId` construction. The reused archive predates the patched inspector header: the header inserts the virtual `consoleAPICalled` method, but the archive does not define `V8InspectorImpl::consoleAPICalled`. This mismatches the vtable layout. `symbolized-control-crash.txt` preserves the call path. + +Native CMake now checks both the header declaration and the archive's defined implementation symbol when Inspector is enabled. It prefers the SDK's LLVM symbol reader for matching ThinLTO support, otherwise using the platform symbol tool. The actual stale archive fails configure with the explicit ABI diagnostic in `stale-sdk-rejection.log`. A newer local V8 15.3.10 archive containing the implementation passes the same check and links successfully. Matching-SDK runtime verification is still in progress; absence of the earlier immediate crash alone is not a suite pass. The original failed results above remain historical evidence. + +The matching-SDK graphics-enabled test subsequently reported `binary invocation did not complete`. A live process sample located the main thread in the component-catalog test failure path, waiting during process shutdown. The test process was explicitly terminated after sampling (117.81 seconds elapsed); its CTest result is a failure, not a timeout pass. `v8-matching-sdk-tests.log` and `v8-matching-sdk-sample.txt` preserve this separate remaining failure. The immediate inspector ABI crash is addressed by rejecting the invalid SDK; runtime qualification still requires resolving this later failure. + +## Upstream-aligned control + +At merge commit `2de32f0` (upstream `96088f6`), the matching-SDK graphics-disabled build passes all four CTest suites, including the native runtime suite (12.52 seconds). The graphics-enabled configuration still reports an incomplete binary invocation and reaches the explicit 60-second CTest timeout. Both logs are retained. This comparison narrows the remaining failure to the graphics-enabled configuration; the earlier stale-SDK and old-runtime failures must not be conflated with it. + +Both Dawn and V8 archives define Abseil spin-lock symbols without an inline version namespace. Their implementations differ. A shared-Dawn experiment is in progress to test symbol isolation; this is a hypothesis, not a confirmed fix or a qualified dependency change. + +## Isolated shared-Dawn experiment + +The disposable build was relinked against a shared Dawn monolith exporting only `_wgpu*`, retaining ANGLE and the same native sources/V8 SDK. All four native suites passed; the native runtime suite took 11.82 seconds. The preceding static-Dawn build timed out and the no-graphics control passed. This intervention supports a dependency symbol collision: Dawn and V8 bundle different unversioned Abseil implementations, and linking them into one image permits one library's implementation to satisfy the other's references. + +This is an experiment, not the committed SDK configuration. Dawn's existing build directory was reconfigured with `DAWN_BUILD_MONOLITHIC_LIBRARY=SHARED` and `CMAKE_SHARED_LINKER_FLAGS=-Wl,-exported_symbols_list,` where the file contains `_wgpu*`. In the disposable native `build.ninja`, only the installed Dawn archive path was replaced by that shared candidate path, and the candidate library was copied alongside the native engine. After the run the original Ninja file was restored. The installed, manifest-verified static SDK was not changed. The native binary in that disposable directory remains the experimental shared build until rebuilt. + +The selected fix must now be integrated into the SDK builder and verified for exported-symbol isolation, native linkage, hardware probes and relocation. Windows/Linux qualification and production package changes remain outstanding. No CPU pixel presentation path is involved in this change. diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/dawn-shared-isolation-build.log b/docs/graphics/evidence/2026-09-07-v8-integration/dawn-shared-isolation-build.log new file mode 100644 index 000000000..642f240a8 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/dawn-shared-isolation-build.log @@ -0,0 +1 @@ +[1/1] Linking CXX shared library src/dawn/native/libwebgpu_dawn.dylib diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/native-shared-dawn-link.log b/docs/graphics/evidence/2026-09-07-v8-integration/native-shared-dawn-link.log new file mode 100644 index 000000000..eb3679554 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/native-shared-dawn-link.log @@ -0,0 +1,3 @@ +ninja: Entering directory `artifacts/graphics-build/native-v8-enabled' +[1/2] Linking CXX shared library libwebscene_native_engine.dylib +[2/2] Linking CXX executable webscene_native_engine_tests diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/native-shared-dawn-tests.log b/docs/graphics/evidence/2026-09-07-v8-integration/native-shared-dawn-tests.log new file mode 100644 index 000000000..ec5b65d90 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/native-shared-dawn-tests.log @@ -0,0 +1,13 @@ +Test project /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled + Start 1: webscene_html_parser_tests +1/4 Test #1: webscene_html_parser_tests ....... Passed 0.01 sec + Start 2: webscene_css_parser_tests +2/4 Test #2: webscene_css_parser_tests ........ Passed 0.00 sec + Start 3: webscene_selector_parser_tests +3/4 Test #3: webscene_selector_parser_tests ... Passed 0.00 sec + Start 4: webscene_native_engine_tests +4/4 Test #4: webscene_native_engine_tests ..... Passed 11.82 sec + +100% tests passed, 0 tests failed out of 4 + +Total Test time (real) = 11.84 sec diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-disabled-build.log b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-disabled-build.log new file mode 100644 index 000000000..e282b031e --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-disabled-build.log @@ -0,0 +1,302 @@ +[1/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXCancellationRequest.cpp.o +[2/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXConnectionState.cpp.o +[3/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXBench.cpp.o +[4/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXExponentialBackoff.cpp.o +[5/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXDNSLookup.cpp.o +[6/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXGzipCodec.cpp.o +[7/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXGetFreePort.cpp.o +[8/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXNetSystem.cpp.o +[9/63] Building CXX object CMakeFiles/webscene_v8_snapshot_builder.dir/tools/v8_snapshot_builder.cpp.o +[10/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSelectInterrupt.cpp.o +[11/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXHttp.cpp.o +[12/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSelectInterruptFactory.cpp.o +[13/63] Linking CXX executable webscene_v8_snapshot_builder +[14/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXHttpClient.cpp.o +[15/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSelectInterruptPipe.cpp.o +[16/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSelectInterruptEvent.cpp.o +[17/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXHttpServer.cpp.o +[18/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSetThreadName.cpp.o +[19/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocket.cpp.o +[20/63] Creating the WebScene V8 bootstrap snapshot +snapshot_bytes=437848 v8=15.3.10-WebScene +[21/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketConnect.cpp.o +[22/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketFactory.cpp.o +[23/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketServer.cpp.o +[24/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXStrCaseCompare.cpp.o +[25/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXUdpSocket.cpp.o +[26/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketTLSOptions.cpp.o +[27/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXUrlParser.cpp.o +[28/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketCloseConstants.cpp.o +[29/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXUserAgent.cpp.o +[30/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXUuid.cpp.o +[31/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketPerMessageDeflate.cpp.o +[32/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketHttpHeaders.cpp.o +[33/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp.o +[34/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocket.cpp.o +[35/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketHandshake.cpp.o +[36/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp.o +[37/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketProxyServer.cpp.o +[38/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketServer.cpp.o +[39/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketAppleSSL.cpp.o +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:176:22: warning: 'SSLHandshake' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 176 | status = SSLHandshake(_sslContext); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:1641:1: note: 'SSLHandshake' has been explicitly marked deprecated here + 1641 | SSLHandshake (SSLContextRef context) + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:202:65: warning: 'kSSLClientSide' is deprecated: first deprecated in macOS 10.15 [-Wdeprecated-declarations] + 202 | _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:218:5: note: 'kSSLClientSide' has been explicitly marked deprecated here + 218 | kSSLClientSide CF_ENUM_DEPRECATED(10_2, 10_15, 2_0, 13_0) + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:202:81: warning: 'kSSLStreamType' is deprecated: first deprecated in macOS 10.15 [-Wdeprecated-declarations] + 202 | _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:223:5: note: 'kSSLStreamType' has been explicitly marked deprecated here + 223 | kSSLStreamType CF_ENUM_DEPRECATED(10_2, 10_15, 2_0, 13_0), + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:202:27: warning: 'SSLCreateContext' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 202 | _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:316:1: note: 'SSLCreateContext' has been explicitly marked deprecated here + 316 | SSLCreateContext(CFAllocatorRef __nullable alloc, SSLProtocolSide protocolSide, SSLConnectionType connectionType) + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:204:13: warning: 'SSLSetIOFuncs' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 204 | SSLSetIOFuncs( + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:418:1: note: 'SSLSetIOFuncs' has been explicitly marked deprecated here + 418 | SSLSetIOFuncs (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:206:13: warning: 'SSLSetConnection' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 206 | SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:657:1: note: 'SSLSetConnection' has been explicitly marked deprecated here + 657 | SSLSetConnection (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:207:51: warning: 'kTLSProtocol12' is deprecated: first deprecated in macOS 10.15 [-Wdeprecated-declarations] + 207 | SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecProtocolTypes.h:162:5: note: 'kTLSProtocol12' has been explicitly marked deprecated here + 162 | kTLSProtocol12 CF_ENUM_DEPRECATED(10_2, 10_15, 5_0, 13_0) = 8, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:207:13: warning: 'SSLSetProtocolVersionMin' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 207 | SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:458:1: note: 'SSLSetProtocolVersionMin' has been explicitly marked deprecated here + 458 | SSLSetProtocolVersionMin (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:210:17: warning: 'SSLSetPeerDomainName' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 210 | SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:686:1: note: 'SSLSetPeerDomainName' has been explicitly marked deprecated here + 686 | SSLSetPeerDomainName (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:215:50: warning: 'kSSLSessionOptionBreakOnServerAuth' is deprecated: first deprecated in macOS 10.15 [-Wdeprecated-declarations] + 215 | SSLSetSessionOption(_sslContext, kSSLSessionOptionBreakOnServerAuth, option); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:99:2: note: 'kSSLSessionOptionBreakOnServerAuth' has been explicitly marked deprecated here + 99 | kSSLSessionOptionBreakOnServerAuth CF_ENUM_DEPRECATED(10_2, 10_15, 2_0, 13_0) = 0, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:215:17: warning: 'SSLSetSessionOption' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 215 | SSLSetSessionOption(_sslContext, kSSLSessionOptionBreakOnServerAuth, option); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:384:1: note: 'SSLSetSessionOption' has been explicitly marked deprecated here + 384 | SSLSetSessionOption (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:247:9: warning: 'SSLClose' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 247 | SSLClose(_sslContext); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:1731:1: note: 'SSLClose' has been explicitly marked deprecated here + 1731 | SSLClose (SSLContextRef context) + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:261:22: warning: 'SSLWrite' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 261 | status = SSLWrite(_sslContext, buf, nbyte, &processed); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:1670:1: note: 'SSLWrite' has been explicitly marked deprecated here + 1670 | SSLWrite (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:291:22: warning: 'SSLRead' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 291 | status = SSLRead(_sslContext, buf, nbyte, &processed); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:1689:1: note: 'SSLRead' has been explicitly marked deprecated here + 1689 | SSLRead (SSLContextRef context, + | ^ +14 warnings generated. +[40/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketTransport.cpp.o +[41/63] Linking CXX static library _deps/webscene_ixwebsocket-build/libixwebsocket.a +[42/63] Building the pinned html5ever WebScene parser + Compiling proc-macro2 v1.0.107 + Compiling quote v1.0.47 + Compiling unicode-ident v1.0.24 + Compiling siphasher v1.0.3 + Compiling fastrand v2.5.0 + Compiling libc v0.2.189 + Compiling smallvec v1.15.2 + Compiling parking_lot_core v0.9.12 + Compiling scopeguard v1.2.0 + Compiling phf_shared v0.13.1 + Compiling cfg-if v1.0.4 + Compiling new_debug_unreachable v1.0.6 + Compiling lock_api v0.4.14 + Compiling precomputed-hash v0.1.1 + Compiling dtoa v1.0.11 + Compiling log v0.4.33 + Compiling dtoa-short v0.3.5 + Compiling tendril v0.5.1 + Compiling itoa v1.0.18 + Compiling stable_deref_trait v1.2.1 + Compiling phf_generator v0.13.1 + Compiling servo_arc v0.4.3 + Compiling bitflags v2.13.1 + Compiling phf_codegen v0.13.1 + Compiling rustc-hash v2.1.3 + Compiling selectors v0.39.0 + Compiling syn v2.0.119 + Compiling string_cache_codegen v0.6.1 + Compiling web_atoms v0.2.5 + Compiling parking_lot v0.12.5 + Compiling string_cache v0.9.0 + Compiling phf_macros v0.13.1 + Compiling derive_more-impl v2.1.1 + Compiling cssparser-macros v0.7.0 + Compiling phf v0.13.1 + Compiling derive_more v2.1.1 + Compiling cssparser v0.37.0 + Compiling markup5ever v0.39.0 + Compiling html5ever v0.39.0 + Compiling webscene-html-parser v0.1.0 (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/html_parser) + Finished `release` profile [optimized] target(s) in 10.63s +[43/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_css_parser.cpp.o +[44/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_selector_parser.cpp.o +[45/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_html_parser.cpp.o +[46/63] Building CXX object CMakeFiles/webscene_html_parser_tests.dir/tests/html_parser_tests.cpp.o +[47/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_native_websocket.cpp.o +[48/63] Building CXX object CMakeFiles/webscene_html_parser_tests.dir/native/webscene_html_parser.cpp.o +[49/63] Building CXX object CMakeFiles/webscene_css_parser_tests.dir/tests/css_parser_tests.cpp.o +[50/63] Building CXX object CMakeFiles/webscene_css_parser_tests.dir/native/webscene_css_parser.cpp.o +[51/63] Building CXX object CMakeFiles/webscene_selector_parser_tests.dir/tests/selector_parser_tests.cpp.o +[52/63] Building CXX object CMakeFiles/webscene_selector_parser_tests.dir/native/webscene_selector_parser.cpp.o +[53/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_native_engine.cpp.o +[54/63] Linking CXX executable webscene_css_parser_tests +[55/63] Linking CXX executable webscene_selector_parser_tests +[56/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_native_dom.cpp.o +In file included from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.cpp:1082: +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:167:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 167 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:208:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 208 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:585:32: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 585 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:801:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 801 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:813:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 813 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1108:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1108 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1120:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1120 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1167:33: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1167 | arrow_color, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1172:33: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1172 | arrow_color, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1226:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1226 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1350:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1350 | commands.push_back(webscene_scene_command{13U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1353:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1353 | commands.push_back(webscene_scene_command{20U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1356:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1356 | commands.push_back(webscene_scene_command{16U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1361:67: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1361 | node.layout.width, node.layout.height, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1495:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1495 | commands.push_back(webscene_scene_command{13U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1612:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1612 | commands.push_back(webscene_scene_command{20U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1615:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1615 | commands.push_back(webscene_scene_command{16U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1620:63: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1620 | node.layout.width, node.layout.height, 0U, node.id}); + | ^ +18 warnings generated. +[57/63] Building CXX object CMakeFiles/webscene_html_parser_tests.dir/native/webscene_native_dom.cpp.o +In file included from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.cpp:1082: +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:167:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 167 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:208:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 208 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:585:32: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 585 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:801:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 801 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:813:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 813 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1108:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1108 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1120:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1120 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1167:33: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1167 | arrow_color, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1172:33: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1172 | arrow_color, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1226:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1226 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1350:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1350 | commands.push_back(webscene_scene_command{13U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1353:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1353 | commands.push_back(webscene_scene_command{20U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1356:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1356 | commands.push_back(webscene_scene_command{16U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1361:67: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1361 | node.layout.width, node.layout.height, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1495:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1495 | commands.push_back(webscene_scene_command{13U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1612:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1612 | commands.push_back(webscene_scene_command{20U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1615:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1615 | commands.push_back(webscene_scene_command{16U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1620:63: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1620 | node.layout.width, node.layout.height, 0U, node.id}); + | ^ +18 warnings generated. +[58/63] Linking CXX executable webscene_html_parser_tests +[59/63] Building CXX object CMakeFiles/webscene_native_engine_tests.dir/native/webscene_native_dom.cpp.o +[60/63] Building CXX object CMakeFiles/webscene_native_engine_tests.dir/tests/native_v8_runtime_tests.cpp.o +[61/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_v8_runtime.cpp.o +[62/63] Linking CXX shared library libwebscene_native_engine.dylib +[63/63] Linking CXX executable webscene_native_engine_tests diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-disabled-configure.log b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-disabled-configure.log new file mode 100644 index 000000000..ce493207c --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-disabled-configure.log @@ -0,0 +1,26 @@ +-- The C compiler identification is AppleClang 21.0.0.21000101 +-- The CXX compiler identification is AppleClang 21.0.0.21000101 +-- Detecting C compiler ABI info +-- Detecting C compiler ABI info - done +-- Check for working C compiler: /usr/bin/cc - skipped +-- Detecting C compile features +-- Detecting C compile features - done +-- Detecting CXX compiler ABI info +-- Detecting CXX compiler ABI info - done +-- Check for working CXX compiler: /usr/bin/c++ - skipped +-- Detecting CXX compile features +-- Detecting CXX compile features - done +-- WebScene native engine: generated WebIDL DOM bindings selected +-- WebScene native engine: cssparser syntax parser selected +-- WebScene native engine: Servo selector parser selected +-- WebScene native engine: html5ever parser selected +-- WebScene native engine: certification telemetry excluded +-- WebScene native engine: V8 Inspector support enabled +-- TLS configured to use secure transport +-- Found ZLIB: /Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib/libz.tbd (found version "1.2.12") +-- Found Python3: /opt/homebrew/Frameworks/Python.framework/Versions/3.14/bin/python3.14 (found version "3.14.4") found components: Interpreter +-- WebScene native engine: bootstrap snapshot selected +-- WebScene native engine: V8 enabled (/Volumes/SSD/repos/HtmlML/artifacts/native-engine-v8-15.3/osx-arm64/v8/out/arm64/Release/obj/libv8_monolith.a) +-- Configuring done (1.8s) +-- Generating done (0.0s) +-- Build files have been written to: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-disabled-tests.log b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-disabled-tests.log new file mode 100644 index 000000000..6777b9086 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-disabled-tests.log @@ -0,0 +1,18 @@ +Test project /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled + Start 1: webscene_html_parser_tests +1/4 Test #1: webscene_html_parser_tests ....... Passed 0.31 sec + Start 2: webscene_css_parser_tests +2/4 Test #2: webscene_css_parser_tests ........ Passed 0.25 sec + Start 3: webscene_selector_parser_tests +3/4 Test #3: webscene_selector_parser_tests ... Passed 0.25 sec + Start 4: webscene_native_engine_tests +4/4 Test #4: webscene_native_engine_tests .....Bus error***Exception: 2.30 sec + + +75% tests passed, 1 tests failed out of 4 + +Total Test time (real) = 3.12 sec + +The following tests FAILED: + 4 - webscene_native_engine_tests (Bus error) +Errors while running CTest diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-enabled-build.log b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-enabled-build.log new file mode 100644 index 000000000..53dcfb6e9 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-enabled-build.log @@ -0,0 +1,302 @@ +[1/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXCancellationRequest.cpp.o +[2/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXConnectionState.cpp.o +[3/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXBench.cpp.o +[4/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXExponentialBackoff.cpp.o +[5/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXDNSLookup.cpp.o +[6/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXGzipCodec.cpp.o +[7/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXGetFreePort.cpp.o +[8/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXNetSystem.cpp.o +[9/63] Building CXX object CMakeFiles/webscene_v8_snapshot_builder.dir/tools/v8_snapshot_builder.cpp.o +[10/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSelectInterrupt.cpp.o +[11/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXHttp.cpp.o +[12/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSelectInterruptFactory.cpp.o +[13/63] Linking CXX executable webscene_v8_snapshot_builder +[14/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXHttpClient.cpp.o +[15/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSelectInterruptPipe.cpp.o +[16/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSetThreadName.cpp.o +[17/63] Creating the WebScene V8 bootstrap snapshot +snapshot_bytes=437848 v8=15.3.10-WebScene +[18/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXHttpServer.cpp.o +[19/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSelectInterruptEvent.cpp.o +[20/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocket.cpp.o +[21/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketConnect.cpp.o +[22/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketTLSOptions.cpp.o +[23/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketFactory.cpp.o +[24/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXStrCaseCompare.cpp.o +[25/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketServer.cpp.o +[26/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXUdpSocket.cpp.o +[27/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXUrlParser.cpp.o +[28/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXUuid.cpp.o +[29/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXUserAgent.cpp.o +[30/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketCloseConstants.cpp.o +[31/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketPerMessageDeflate.cpp.o +[32/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocket.cpp.o +[33/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketHttpHeaders.cpp.o +[34/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketPerMessageDeflateCodec.cpp.o +[35/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketHandshake.cpp.o +[36/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp.o +[37/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketProxyServer.cpp.o +[38/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketServer.cpp.o +[39/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketAppleSSL.cpp.o +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:176:22: warning: 'SSLHandshake' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 176 | status = SSLHandshake(_sslContext); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:1641:1: note: 'SSLHandshake' has been explicitly marked deprecated here + 1641 | SSLHandshake (SSLContextRef context) + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:202:65: warning: 'kSSLClientSide' is deprecated: first deprecated in macOS 10.15 [-Wdeprecated-declarations] + 202 | _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:218:5: note: 'kSSLClientSide' has been explicitly marked deprecated here + 218 | kSSLClientSide CF_ENUM_DEPRECATED(10_2, 10_15, 2_0, 13_0) + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:202:81: warning: 'kSSLStreamType' is deprecated: first deprecated in macOS 10.15 [-Wdeprecated-declarations] + 202 | _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:223:5: note: 'kSSLStreamType' has been explicitly marked deprecated here + 223 | kSSLStreamType CF_ENUM_DEPRECATED(10_2, 10_15, 2_0, 13_0), + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:202:27: warning: 'SSLCreateContext' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 202 | _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:316:1: note: 'SSLCreateContext' has been explicitly marked deprecated here + 316 | SSLCreateContext(CFAllocatorRef __nullable alloc, SSLProtocolSide protocolSide, SSLConnectionType connectionType) + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:204:13: warning: 'SSLSetIOFuncs' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 204 | SSLSetIOFuncs( + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:418:1: note: 'SSLSetIOFuncs' has been explicitly marked deprecated here + 418 | SSLSetIOFuncs (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:206:13: warning: 'SSLSetConnection' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 206 | SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:657:1: note: 'SSLSetConnection' has been explicitly marked deprecated here + 657 | SSLSetConnection (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:207:51: warning: 'kTLSProtocol12' is deprecated: first deprecated in macOS 10.15 [-Wdeprecated-declarations] + 207 | SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecProtocolTypes.h:162:5: note: 'kTLSProtocol12' has been explicitly marked deprecated here + 162 | kTLSProtocol12 CF_ENUM_DEPRECATED(10_2, 10_15, 5_0, 13_0) = 8, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:207:13: warning: 'SSLSetProtocolVersionMin' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 207 | SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:458:1: note: 'SSLSetProtocolVersionMin' has been explicitly marked deprecated here + 458 | SSLSetProtocolVersionMin (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:210:17: warning: 'SSLSetPeerDomainName' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 210 | SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:686:1: note: 'SSLSetPeerDomainName' has been explicitly marked deprecated here + 686 | SSLSetPeerDomainName (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:215:50: warning: 'kSSLSessionOptionBreakOnServerAuth' is deprecated: first deprecated in macOS 10.15 [-Wdeprecated-declarations] + 215 | SSLSetSessionOption(_sslContext, kSSLSessionOptionBreakOnServerAuth, option); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:99:2: note: 'kSSLSessionOptionBreakOnServerAuth' has been explicitly marked deprecated here + 99 | kSSLSessionOptionBreakOnServerAuth CF_ENUM_DEPRECATED(10_2, 10_15, 2_0, 13_0) = 0, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:215:17: warning: 'SSLSetSessionOption' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 215 | SSLSetSessionOption(_sslContext, kSSLSessionOptionBreakOnServerAuth, option); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:384:1: note: 'SSLSetSessionOption' has been explicitly marked deprecated here + 384 | SSLSetSessionOption (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:247:9: warning: 'SSLClose' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 247 | SSLClose(_sslContext); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:1731:1: note: 'SSLClose' has been explicitly marked deprecated here + 1731 | SSLClose (SSLContextRef context) + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:261:22: warning: 'SSLWrite' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 261 | status = SSLWrite(_sslContext, buf, nbyte, &processed); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:1670:1: note: 'SSLWrite' has been explicitly marked deprecated here + 1670 | SSLWrite (SSLContextRef context, + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/_deps/webscene_ixwebsocket-src/ixwebsocket/IXSocketAppleSSL.cpp:291:22: warning: 'SSLRead' is deprecated: first deprecated in macOS 10.15 - No longer supported. Use Network.framework. [-Wdeprecated-declarations] + 291 | status = SSLRead(_sslContext, buf, nbyte, &processed); + | ^ +/Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Security.framework/Headers/SecureTransport.h:1689:1: note: 'SSLRead' has been explicitly marked deprecated here + 1689 | SSLRead (SSLContextRef context, + | ^ +14 warnings generated. +[40/63] Building CXX object _deps/webscene_ixwebsocket-build/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketTransport.cpp.o +[41/63] Linking CXX static library _deps/webscene_ixwebsocket-build/libixwebsocket.a +[42/63] Building the pinned html5ever WebScene parser + Compiling proc-macro2 v1.0.107 + Compiling quote v1.0.47 + Compiling unicode-ident v1.0.24 + Compiling siphasher v1.0.3 + Compiling fastrand v2.5.0 + Compiling libc v0.2.189 + Compiling smallvec v1.15.2 + Compiling parking_lot_core v0.9.12 + Compiling new_debug_unreachable v1.0.6 + Compiling cfg-if v1.0.4 + Compiling scopeguard v1.2.0 + Compiling phf_shared v0.13.1 + Compiling precomputed-hash v0.1.1 + Compiling lock_api v0.4.14 + Compiling log v0.4.33 + Compiling dtoa v1.0.11 + Compiling tendril v0.5.1 + Compiling dtoa-short v0.3.5 + Compiling stable_deref_trait v1.2.1 + Compiling itoa v1.0.18 + Compiling servo_arc v0.4.3 + Compiling phf_generator v0.13.1 + Compiling bitflags v2.13.1 + Compiling phf_codegen v0.13.1 + Compiling rustc-hash v2.1.3 + Compiling selectors v0.39.0 + Compiling syn v2.0.119 + Compiling string_cache_codegen v0.6.1 + Compiling web_atoms v0.2.5 + Compiling parking_lot v0.12.5 + Compiling string_cache v0.9.0 + Compiling phf_macros v0.13.1 + Compiling cssparser-macros v0.7.0 + Compiling derive_more-impl v2.1.1 + Compiling phf v0.13.1 + Compiling derive_more v2.1.1 + Compiling cssparser v0.37.0 + Compiling markup5ever v0.39.0 + Compiling html5ever v0.39.0 + Compiling webscene-html-parser v0.1.0 (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/html_parser) + Finished `release` profile [optimized] target(s) in 12.32s +[43/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_css_parser.cpp.o +[44/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_selector_parser.cpp.o +[45/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_html_parser.cpp.o +[46/63] Building CXX object CMakeFiles/webscene_html_parser_tests.dir/tests/html_parser_tests.cpp.o +[47/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_native_websocket.cpp.o +[48/63] Building CXX object CMakeFiles/webscene_html_parser_tests.dir/native/webscene_html_parser.cpp.o +[49/63] Building CXX object CMakeFiles/webscene_css_parser_tests.dir/tests/css_parser_tests.cpp.o +[50/63] Building CXX object CMakeFiles/webscene_css_parser_tests.dir/native/webscene_css_parser.cpp.o +[51/63] Building CXX object CMakeFiles/webscene_selector_parser_tests.dir/tests/selector_parser_tests.cpp.o +[52/63] Building CXX object CMakeFiles/webscene_selector_parser_tests.dir/native/webscene_selector_parser.cpp.o +[53/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_native_engine.cpp.o +[54/63] Linking CXX executable webscene_css_parser_tests +[55/63] Linking CXX executable webscene_selector_parser_tests +[56/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_native_dom.cpp.o +In file included from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.cpp:1082: +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:167:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 167 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:208:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 208 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:585:32: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 585 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:801:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 801 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:813:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 813 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1108:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1108 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1120:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1120 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1167:33: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1167 | arrow_color, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1172:33: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1172 | arrow_color, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1226:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1226 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1350:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1350 | commands.push_back(webscene_scene_command{13U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1353:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1353 | commands.push_back(webscene_scene_command{20U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1356:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1356 | commands.push_back(webscene_scene_command{16U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1361:67: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1361 | node.layout.width, node.layout.height, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1495:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1495 | commands.push_back(webscene_scene_command{13U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1612:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1612 | commands.push_back(webscene_scene_command{20U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1615:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1615 | commands.push_back(webscene_scene_command{16U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1620:63: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1620 | node.layout.width, node.layout.height, 0U, node.id}); + | ^ +18 warnings generated. +[57/63] Building CXX object CMakeFiles/webscene_html_parser_tests.dir/native/webscene_native_dom.cpp.o +In file included from /Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.cpp:1082: +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:167:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 167 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:208:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 208 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:585:32: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 585 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:801:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 801 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:813:20: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 813 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1108:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1108 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1120:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1120 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1167:33: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1167 | arrow_color, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1172:33: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1172 | arrow_color, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1226:24: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1226 | node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1350:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1350 | commands.push_back(webscene_scene_command{13U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1353:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1353 | commands.push_back(webscene_scene_command{20U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1356:87: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1356 | commands.push_back(webscene_scene_command{16U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1361:67: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1361 | node.layout.width, node.layout.height, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1495:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1495 | commands.push_back(webscene_scene_command{13U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1612:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1612 | commands.push_back(webscene_scene_command{20U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1615:83: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1615 | commands.push_back(webscene_scene_command{16U, 0U, 0, 0, 0, 0, 0U, node.id}); + | ^ +/Volumes/SSD/repos/worktrees/aa5a/HtmlML/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc:1620:63: warning: missing field 'radius_top_left' initializer [-Wmissing-field-initializers] + 1620 | node.layout.width, node.layout.height, 0U, node.id}); + | ^ +18 warnings generated. +[58/63] Linking CXX executable webscene_html_parser_tests +[59/63] Building CXX object CMakeFiles/webscene_native_engine_tests.dir/native/webscene_native_dom.cpp.o +[60/63] Building CXX object CMakeFiles/webscene_native_engine_tests.dir/tests/native_v8_runtime_tests.cpp.o +[61/63] Building CXX object CMakeFiles/webscene_native_engine.dir/native/webscene_v8_runtime.cpp.o +[62/63] Linking CXX shared library libwebscene_native_engine.dylib +[63/63] Linking CXX executable webscene_native_engine_tests diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-enabled-configure.log b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-enabled-configure.log new file mode 100644 index 000000000..87d795857 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-enabled-configure.log @@ -0,0 +1,33 @@ +-- The C compiler identification is AppleClang 21.0.0.21000101 +-- The CXX compiler identification is AppleClang 21.0.0.21000101 +-- Detecting C compiler ABI info +-- Detecting C compiler ABI info - done +-- Check for working C compiler: /usr/bin/cc - skipped +-- Detecting C compile features +-- Detecting C compile features - done +-- Detecting CXX compiler ABI info +-- Detecting CXX compiler ABI info - done +-- Check for working CXX compiler: /usr/bin/c++ - skipped +-- Detecting CXX compile features +-- Detecting CXX compile features - done +-- Found Python3: /opt/homebrew/Frameworks/Python.framework/Versions/3.14/bin/python3.14 (found version "3.14.4") found components: Interpreter +-- Performing Test CMAKE_HAVE_LIBC_PTHREAD +-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success +-- Found Threads: TRUE +-- Verified dawn 2ca8cbfe0f8275aa0f739e7b6b4345a16e2f0378 for osx-arm64: 77 files + +-- Verified angle 082d85ba19efba24d3c25108dc1f0cad9cf149f9 for osx-arm64: 1039 files + +-- WebScene native engine: generated WebIDL DOM bindings selected +-- WebScene native engine: cssparser syntax parser selected +-- WebScene native engine: Servo selector parser selected +-- WebScene native engine: html5ever parser selected +-- WebScene native engine: certification telemetry excluded +-- WebScene native engine: V8 Inspector support enabled +-- TLS configured to use secure transport +-- Found ZLIB: /Volumes/SSD/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib/libz.tbd (found version "1.2.12") +-- WebScene native engine: bootstrap snapshot selected +-- WebScene native engine: V8 enabled (/Volumes/SSD/repos/HtmlML/artifacts/native-engine-v8-15.3/osx-arm64/v8/out/arm64/Release/obj/libv8_monolith.a) +-- Configuring done (3.4s) +-- Generating done (0.0s) +-- Build files have been written to: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-enabled-tests.log b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-enabled-tests.log new file mode 100644 index 000000000..090b19842 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/native-v8-enabled-tests.log @@ -0,0 +1,18 @@ +Test project /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled + Start 1: webscene_html_parser_tests +1/4 Test #1: webscene_html_parser_tests ....... Passed 0.29 sec + Start 2: webscene_css_parser_tests +2/4 Test #2: webscene_css_parser_tests ........ Passed 0.25 sec + Start 3: webscene_selector_parser_tests +3/4 Test #3: webscene_selector_parser_tests ... Passed 0.25 sec + Start 4: webscene_native_engine_tests +4/4 Test #4: webscene_native_engine_tests .....***Exception: SegFault 1.66 sec + + +75% tests passed, 1 tests failed out of 4 + +Total Test time (real) = 2.46 sec + +The following tests FAILED: + 4 - webscene_native_engine_tests (SEGFAULT) +Errors while running CTest diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/result.json b/docs/graphics/evidence/2026-09-07-v8-integration/result.json new file mode 100644 index 000000000..7ebb95fe3 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/result.json @@ -0,0 +1,106 @@ +{ + "status": "failed", + "repositoryCommit": "f9dd6104888b61470bfebf0ec21ca5ddd4bd81a1", + "v8Revision": "f671abbcff8361085f9d0f82d24f2526955b62c4", + "sdkOrigin": "Existing local V8 15.3.10 SDK reused read-only; not a clean SDK build", + "inputs": { + "include/v8-version.h": { + "sha256": "44b5fb050d6402890206ff674c12fa23094fad350423c4bac4d86866cc886818", + "bytes": 771 + }, + "include/v8-inspector.h": { + "sha256": "5e52907a091f99fbba67a0d83c3ffca5ce0617b8e6ff99b6d7e29502c26e4efb", + "bytes": 18523 + }, + "out/arm64/Release/args.gn": { + "sha256": "42ce5894df70c577a521240eff6493b3528193d87023449d020425a3f3129dfd", + "bytes": 680 + }, + "out/arm64/Release/obj/libv8_monolith.a": { + "sha256": "2d8316f88a010a4a3ec017158296d973c60de8a5365583867e9feef392956966", + "bytes": 99611632 + }, + "out/arm64/Release/icudtl.dat": { + "sha256": "9f48c7f9c7c94d516a14870707e910ab94d75ae640ff6842c4af53276cd26ebe", + "bytes": 10876560 + } + }, + "results": { + "graphicsEnabled": { + "build": "passed", + "parserTests": 3, + "nativeTest": "SIGSEGV", + "cmakeSettings": { + "CMAKE_BUILD_TYPE:STRING": "Release", + "CMAKE_CXX_COMPILER:FILEPATH": "/usr/bin/c++", + "WEBSCENE_CARGO_EXECUTABLE:FILEPATH": "/Users/danw/.cargo/bin/cargo", + "WEBSCENE_GRAPHICS_ANGLE_VARIANT:STRING": "angle", + "WEBSCENE_GRAPHICS_SDK_ROOT:PATH": "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64", + "WEBSCENE_NATIVE_ENGINE_BUILD_CSS_PARSER_BENCHMARK:BOOL": "OFF", + "WEBSCENE_NATIVE_ENGINE_BUILD_DOM_BINDING_BENCHMARK:BOOL": "OFF", + "WEBSCENE_NATIVE_ENGINE_BUILD_HTML_PARSER_BENCHMARK:BOOL": "OFF", + "WEBSCENE_NATIVE_ENGINE_CERTIFICATION:BOOL": "OFF", + "WEBSCENE_NATIVE_ENGINE_CSS_PARSER:STRING": "cssparser", + "WEBSCENE_NATIVE_ENGINE_DENSE_LINK:BOOL": "ON", + "WEBSCENE_NATIVE_ENGINE_DOM_BINDINGS:STRING": "generated", + "WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS:BOOL": "ON", + "WEBSCENE_NATIVE_ENGINE_ENABLE_V8:BOOL": "ON", + "WEBSCENE_NATIVE_ENGINE_ENABLE_V8_INSPECTOR:BOOL": "ON", + "WEBSCENE_NATIVE_ENGINE_HTML_PARSER:STRING": "html5ever", + "WEBSCENE_NATIVE_ENGINE_SELECTOR_PARSER:STRING": "servo", + "WEBSCENE_NATIVE_ENGINE_THIN_LTO:BOOL": "OFF", + "WEBSCENE_NATIVE_ENGINE_V8_SNAPSHOT:STRING": "bootstrap", + "WEBSCENE_V8_DIRECT_HANDLE:BOOL": "OFF", + "WEBSCENE_V8_OPTIMIZE_FOR_SIZE_DEFAULT:BOOL": "ON", + "WEBSCENE_V8_OUTPUT_ROOT:PATH": "/Volumes/SSD/repos/HtmlML/artifacts/native-engine-v8-15.3/osx-arm64/v8/out/arm64/Release", + "WEBSCENE_V8_PARTITION_ALLOC:BOOL": "OFF", + "WEBSCENE_V8_POINTER_COMPRESSION:BOOL": "ON", + "WEBSCENE_V8_POINTER_COMPRESSION_SHARED_CAGE:BOOL": "ON", + "WEBSCENE_V8_ROOT:PATH": "/Volumes/SSD/repos/HtmlML/artifacts/native-engine-v8-15.3/osx-arm64/v8", + "WEBSCENE_GRAPHICS_ANGLE_VARIANT-STRINGS:INTERNAL": "angle;angle-gl", + "WEBSCENE_NATIVE_ENGINE_CSS_PARSER-STRINGS:INTERNAL": "legacy;cssparser", + "WEBSCENE_NATIVE_ENGINE_DOM_BINDINGS-STRINGS:INTERNAL": "legacy;generated", + "WEBSCENE_NATIVE_ENGINE_HTML_PARSER-STRINGS:INTERNAL": "legacy;html5ever", + "WEBSCENE_NATIVE_ENGINE_SELECTOR_PARSER-STRINGS:INTERNAL": "legacy;servo", + "WEBSCENE_NATIVE_ENGINE_V8_SNAPSHOT-STRINGS:INTERNAL": "none;bootstrap" + } + }, + "graphicsDisabled": { + "build": "passed", + "parserTests": 3, + "nativeTest": "SIGBUS", + "cmakeSettings": { + "CMAKE_BUILD_TYPE:STRING": "Release", + "CMAKE_CXX_COMPILER:FILEPATH": "/usr/bin/c++", + "WEBSCENE_CARGO_EXECUTABLE:FILEPATH": "/Users/danw/.cargo/bin/cargo", + "WEBSCENE_NATIVE_ENGINE_BUILD_CSS_PARSER_BENCHMARK:BOOL": "OFF", + "WEBSCENE_NATIVE_ENGINE_BUILD_DOM_BINDING_BENCHMARK:BOOL": "OFF", + "WEBSCENE_NATIVE_ENGINE_BUILD_HTML_PARSER_BENCHMARK:BOOL": "OFF", + "WEBSCENE_NATIVE_ENGINE_CERTIFICATION:BOOL": "OFF", + "WEBSCENE_NATIVE_ENGINE_CSS_PARSER:STRING": "cssparser", + "WEBSCENE_NATIVE_ENGINE_DENSE_LINK:BOOL": "ON", + "WEBSCENE_NATIVE_ENGINE_DOM_BINDINGS:STRING": "generated", + "WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS:BOOL": "OFF", + "WEBSCENE_NATIVE_ENGINE_ENABLE_V8:BOOL": "ON", + "WEBSCENE_NATIVE_ENGINE_ENABLE_V8_INSPECTOR:BOOL": "ON", + "WEBSCENE_NATIVE_ENGINE_HTML_PARSER:STRING": "html5ever", + "WEBSCENE_NATIVE_ENGINE_SELECTOR_PARSER:STRING": "servo", + "WEBSCENE_NATIVE_ENGINE_THIN_LTO:BOOL": "OFF", + "WEBSCENE_NATIVE_ENGINE_V8_SNAPSHOT:STRING": "bootstrap", + "WEBSCENE_V8_DIRECT_HANDLE:BOOL": "OFF", + "WEBSCENE_V8_OPTIMIZE_FOR_SIZE_DEFAULT:BOOL": "ON", + "WEBSCENE_V8_OUTPUT_ROOT:PATH": "/Volumes/SSD/repos/HtmlML/artifacts/native-engine-v8-15.3/osx-arm64/v8/out/arm64/Release", + "WEBSCENE_V8_PARTITION_ALLOC:BOOL": "OFF", + "WEBSCENE_V8_POINTER_COMPRESSION:BOOL": "ON", + "WEBSCENE_V8_POINTER_COMPRESSION_SHARED_CAGE:BOOL": "ON", + "WEBSCENE_V8_ROOT:PATH": "/Volumes/SSD/repos/HtmlML/artifacts/native-engine-v8-15.3/osx-arm64/v8", + "WEBSCENE_NATIVE_ENGINE_CSS_PARSER-STRINGS:INTERNAL": "legacy;cssparser", + "WEBSCENE_NATIVE_ENGINE_DOM_BINDINGS-STRINGS:INTERNAL": "legacy;generated", + "WEBSCENE_NATIVE_ENGINE_HTML_PARSER-STRINGS:INTERNAL": "legacy;html5ever", + "WEBSCENE_NATIVE_ENGINE_SELECTOR_PARSER-STRINGS:INTERNAL": "legacy;servo", + "WEBSCENE_NATIVE_ENGINE_V8_SNAPSHOT-STRINGS:INTERNAL": "none;bootstrap" + } + } + }, + "interpretation": "Both controls fail; graphics linkage is not required to trigger the failure. Root cause remains unresolved." +} diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/stale-sdk-rejection.log b/docs/graphics/evidence/2026-09-07-v8-integration/stale-sdk-rejection.log new file mode 100644 index 000000000..c6e8dce53 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/stale-sdk-rejection.log @@ -0,0 +1,16 @@ +-- WebScene native engine: generated WebIDL DOM bindings selected +-- WebScene native engine: cssparser syntax parser selected +-- WebScene native engine: Servo selector parser selected +-- WebScene native engine: html5ever parser selected +-- WebScene native engine: certification telemetry excluded +-- WebScene native engine: V8 Inspector support enabled +CMake Error at cmake/VerifyV8Inspector.cmake:37 (message): + V8 Inspector header/archive ABI mismatch: the header declares the console + bridge but the monolith has no V8InspectorImpl::consoleAPICalled + implementation. Rebuild the monolith after applying + V8InspectorConsolePatch.txt; do not reuse an older archive. +Call Stack (most recent call first): + CMakeLists.txt:326 (include) + + +-- Configuring incomplete, errors occurred! diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/symbolized-control-crash.txt b/docs/graphics/evidence/2026-09-07-v8-integration/symbolized-control-crash.txt new file mode 100644 index 000000000..ce7fa900e --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/symbolized-control-crash.txt @@ -0,0 +1,21 @@ +webscene_native_engine_tests-2026-09-07-114423.ips +{'codes': '0x0000000000000002, 0x0000000105c4fc98', 'rawCodes': [2, 4391763096], 'type': 'EXC_BAD_ACCESS', 'signal': 'SIGBUS', 'subtype': 'KERN_PROTECTION_FAILURE at 0x0000000105c4fc98'} +v8_inspector::V8StackTraceId::V8StackTraceId() (in libwebscene_native_engine.dylib) + 20 +webscene_native::v8_dom_runtime::implementation::shutdown_inspector() (in libwebscene_native_engine.dylib) (webscene_v8_runtime_inspector.inc:757) +webscene_native::v8_dom_runtime::implementation::~implementation() (in libwebscene_native_engine.dylib) (webscene_v8_runtime_lifecycle.inc:98) +webscene_native::v8_dom_runtime::~v8_dom_runtime() (in libwebscene_native_engine.dylib) (webscene_v8_runtime.cpp:4067) +webscene_native::v8_dom_runtime::~v8_dom_runtime() (in libwebscene_native_engine.dylib) (webscene_v8_runtime.cpp:4067) +webscene_native::v8_dom_runtime::~v8_dom_runtime() (in libwebscene_native_engine.dylib) (webscene_v8_runtime.cpp:4067) +webscene_native::v8_dom_runtime::~v8_dom_runtime() (in libwebscene_native_engine.dylib) (webscene_v8_runtime.cpp:4067) +webscene_native::v8_dom_runtime::~v8_dom_runtime() (in libwebscene_native_engine.dylib) (webscene_v8_runtime.cpp:4067) +webscene_native::v8_dom_runtime::~v8_dom_runtime() (in libwebscene_native_engine.dylib) (webscene_v8_runtime.cpp:4067) +webscene_native::v8_dom_runtime::~v8_dom_runtime() (in libwebscene_native_engine.dylib) (webscene_v8_runtime.cpp:4067) +webscene_engine::run(std::__1::stop_token) (in libwebscene_native_engine.dylib) (webscene_native_engine_worker.inc:1014) +webscene_engine::run(std::__1::stop_token) (in libwebscene_native_engine.dylib) (webscene_native_engine_worker.inc:1014) +webscene_engine::run(std::__1::stop_token) (in libwebscene_native_engine.dylib) (webscene_native_engine_worker.inc:1014) +std::__1::__invoke_result_impl, std::__1::allocator>, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context const*, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context_v3 const*, char*, unsigned long), void*, void (*)(void*, unsigned long long, unsigned long long, float, float), void*, unsigned char (*)(void*, char const*, unsigned long, char const*, unsigned long, float, int, float, float, webscene_text_metrics*), void*, void (*)(void*), void*, void (*)(void*), void*, void (*)(void*), void*)::'lambda'(std::__1::stop_token), std::__1::stop_token>::type std::__1::__invoke[abi:nqe210106], std::__1::allocator>, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context const*, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context_v3 const*, char*, unsigned long), void*, void (*)(void*, unsigned long long, unsigned long long, float, float), void*, unsigned char (*)(void*, char const*, unsigned long, char const*, unsigned long, float, int, float, float, webscene_text_metrics*), void*, void (*)(void*), void*, void (*)(void*), void*, void (*)(void*), void*)::'lambda'(std::__1::stop_token), std::__1::stop_token>(webscene_engine::webscene_engine(unsigned int, std::__1::basic_string, std::__1::allocator>, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context const*, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context_v3 const*, char*, unsigned long), void*, void (*)(void*, unsigned long long, unsigned long long, float, float), void*, unsigned char (*)(void*, char const*, unsigned long, char const*, unsigned long, float, int, float, float, webscene_text_metrics*), void*, void (*)(void*), void*, void (*)(void*), void*, void (*)(void*), void*)::'lambda'(std::__1::stop_token)&&, std::__1::stop_token&&) (in libwebscene_native_engine.dylib) (invoke.h:87) +std::__1::__invoke_result_impl, std::__1::allocator>, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context const*, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context_v3 const*, char*, unsigned long), void*, void (*)(void*, unsigned long long, unsigned long long, float, float), void*, unsigned char (*)(void*, char const*, unsigned long, char const*, unsigned long, float, int, float, float, webscene_text_metrics*), void*, void (*)(void*), void*, void (*)(void*), void*, void (*)(void*), void*)::'lambda'(std::__1::stop_token), std::__1::stop_token>::type std::__1::__invoke[abi:nqe210106], std::__1::allocator>, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context const*, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context_v3 const*, char*, unsigned long), void*, void (*)(void*, unsigned long long, unsigned long long, float, float), void*, unsigned char (*)(void*, char const*, unsigned long, char const*, unsigned long, float, int, float, float, webscene_text_metrics*), void*, void (*)(void*), void*, void (*)(void*), void*, void (*)(void*), void*)::'lambda'(std::__1::stop_token), std::__1::stop_token>(webscene_engine::webscene_engine(unsigned int, std::__1::basic_string, std::__1::allocator>, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context const*, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context_v3 const*, char*, unsigned long), void*, void (*)(void*, unsigned long long, unsigned long long, float, float), void*, unsigned char (*)(void*, char const*, unsigned long, char const*, unsigned long, float, int, float, float, webscene_text_metrics*), void*, void (*)(void*), void*, void (*)(void*), void*, void (*)(void*), void*)::'lambda'(std::__1::stop_token)&&, std::__1::stop_token&&) (in libwebscene_native_engine.dylib) (invoke.h:87) +void* std::__1::__thread_proxy[abi:nqe210106]>, webscene_engine::webscene_engine(unsigned int, std::__1::basic_string, std::__1::allocator>, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context const*, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context_v3 const*, char*, unsigned long), void*, void (*)(void*, unsigned long long, unsigned long long, float, float), void*, unsigned char (*)(void*, char const*, unsigned long, char const*, unsigned long, float, int, float, float, webscene_text_metrics*), void*, void (*)(void*), void*, void (*)(void*), void*, void (*)(void*), void*)::'lambda'(std::__1::stop_token), std::__1::stop_token>>(void*) (in libwebscene_native_engine.dylib) (thread.h:170) +void* std::__1::__thread_proxy[abi:nqe210106]>, webscene_engine::webscene_engine(unsigned int, std::__1::basic_string, std::__1::allocator>, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context const*, char*, unsigned long), void*, unsigned long (*)(void*, unsigned int, char const*, unsigned long, char const*, unsigned long, long long, webscene_resource_request_context_v3 const*, char*, unsigned long), void*, void (*)(void*, unsigned long long, unsigned long long, float, float), void*, unsigned char (*)(void*, char const*, unsigned long, char const*, unsigned long, float, int, float, float, webscene_text_metrics*), void*, void (*)(void*), void*, void (*)(void*), void*, void (*)(void*), void*)::'lambda'(std::__1::stop_token), std::__1::stop_token>>(void*) (in libwebscene_native_engine.dylib) (thread.h:170) +_pthread_start libsystem_pthread.dylib +thread_start libsystem_pthread.dylib diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/v8-args.gn b/docs/graphics/evidence/2026-09-07-v8-integration/v8-args.gn new file mode 100644 index 000000000..03442d438 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/v8-args.gn @@ -0,0 +1,24 @@ +chrome_pgo_phase = 0 +fatal_linker_warnings = false +is_cfi = false +is_component_build = false +is_debug = false +symbol_level = 0 +target_cpu = "arm64" +treat_warnings_as_errors = false +use_clang_modules = false +use_custom_libcxx = false +use_thin_lto = false +v8_embedder_string = "-WebScene" +v8_enable_fuzztest = false +v8_enable_partition_alloc = false +v8_enable_pointer_compression = true +v8_enable_pointer_compression_shared_cage = true +v8_enable_sandbox = false +v8_enable_static_roots = false +v8_enable_31bit_smis_on_64bit_arch = false +v8_enable_temporal_support = false +v8_enable_webassembly = true +v8_monolithic = true +v8_use_external_startup_data = false +v8_target_cpu = "arm64" diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/v8-matching-sdk-sample.txt b/docs/graphics/evidence/2026-09-07-v8-integration/v8-matching-sdk-sample.txt new file mode 100644 index 000000000..27f64056e --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/v8-matching-sdk-sample.txt @@ -0,0 +1,722 @@ +Analysis of sampling webscene_native_engine_tests (pid 60853) every 1 millisecond +Process: webscene_native_engine_tests [60853] +Path: /Volumes/VOLUME/*/webscene_native_engine_tests +Load Address: 0x102f14000 +Identifier: webscene_native_engine_tests +Version: 0 +Code Type: ARM64 +Platform: macOS +Parent Process: ctest [60848] +Target Type: live task + +Date/Time: 2026-09-07 11:49:05.557 +0100 +Launch Time: 2026-09-07 11:47:47.245 +0100 +OS Version: macOS 26.6.2 (25G83) +Report Version: 7 +Analysis Tool: /usr/bin/sample + +Physical footprint: 8529K +Physical footprint (peak): 21.5M +Idle exit: untracked +---- + +Call graph: + 813 Thread_359613 DispatchQueue_1: com.apple.main-thread (serial) + + 813 start (in dyld) + 6992 [0x183ac84e4] + + 813 main (in webscene_native_engine_tests) + 18424 [0x102f1ad00] + + 813 (anonymous namespace)::test_component_catalog_mounts_interacts_and_unmounts() (in webscene_native_engine_tests) + 6384 [0x102f6c38c] + + 813 (anonymous namespace)::evaluate(webscene_engine*, std::basic_string_view, std::basic_string_view) (in webscene_native_engine_tests) + 44 [0x102f74b14] + + 813 (anonymous namespace)::invoke_binary(webscene_engine*, std::basic_string_view, std::basic_string_view) (in webscene_native_engine_tests) + 468 [0x102f74e7c] + + 813 (anonymous namespace)::fail(std::basic_string_view) (in webscene_native_engine_tests) + 68 [0x102fe23c4] + + 813 exit (in libsystem_c.dylib) + 44 [0x183d485dc] + + 813 __cxa_finalize_ranges (in libsystem_c.dylib) + 416 [0x183d487dc] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x65cd8 [0x105385cd8] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x264b5c [0x105584b5c] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x2649e0 [0x1055849e0] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x2657b4 [0x1055857b4] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x2659a4 [0x1055859a4] + + 813 _pthread_join (in libsystem_pthread.dylib) + 616 [0x183e96114] + + 813 __ulock_wait (in libsystem_kernel.dylib) + 8 [0x183e51af8] + 813 Thread_359696: V8 DefaultWorke + + 813 thread_start (in libsystem_pthread.dylib) + 8 [0x183e8ec1c] + + 813 _pthread_start (in libsystem_pthread.dylib) + 136 [0x183e93c58] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x25ecf4 [0x10557ecf4] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x265a44 [0x105585a44] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1b15450 [0x106e35450] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c39c8 [0x1054e39c8] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c3b28 [0x1054e3b28] + + 813 _pthread_cond_wait (in libsystem_pthread.dylib) + 980 [0x183e94128] + + 813 __psynch_cvwait (in libsystem_kernel.dylib) + 8 [0x183e5350c] + 813 Thread_359697: V8 DefaultWorke + + 813 thread_start (in libsystem_pthread.dylib) + 8 [0x183e8ec1c] + + 813 _pthread_start (in libsystem_pthread.dylib) + 136 [0x183e93c58] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x25ecf4 [0x10557ecf4] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x265a44 [0x105585a44] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1b15450 [0x106e35450] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c39c8 [0x1054e39c8] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c3b28 [0x1054e3b28] + + 813 _pthread_cond_wait (in libsystem_pthread.dylib) + 980 [0x183e94128] + + 813 __psynch_cvwait (in libsystem_kernel.dylib) + 8 [0x183e5350c] + 813 Thread_359698: V8 DefaultWorke + + 813 thread_start (in libsystem_pthread.dylib) + 8 [0x183e8ec1c] + + 813 _pthread_start (in libsystem_pthread.dylib) + 136 [0x183e93c58] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x25ecf4 [0x10557ecf4] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x265a44 [0x105585a44] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1b15450 [0x106e35450] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c39c8 [0x1054e39c8] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c3b28 [0x1054e3b28] + + 813 _pthread_cond_wait (in libsystem_pthread.dylib) + 980 [0x183e94128] + + 813 __psynch_cvwait (in libsystem_kernel.dylib) + 8 [0x183e5350c] + 813 Thread_359699: V8 DefaultWorke + + 813 thread_start (in libsystem_pthread.dylib) + 8 [0x183e8ec1c] + + 813 _pthread_start (in libsystem_pthread.dylib) + 136 [0x183e93c58] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x25ecf4 [0x10557ecf4] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x265a44 [0x105585a44] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1b15450 [0x106e35450] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c39c8 [0x1054e39c8] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c3b28 [0x1054e3b28] + + 813 _pthread_cond_wait (in libsystem_pthread.dylib) + 980 [0x183e94128] + + 813 __psynch_cvwait (in libsystem_kernel.dylib) + 8 [0x183e5350c] + 813 Thread_359700: V8 DefaultWorke + + 813 thread_start (in libsystem_pthread.dylib) + 8 [0x183e8ec1c] + + 813 _pthread_start (in libsystem_pthread.dylib) + 136 [0x183e93c58] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x25ecf4 [0x10557ecf4] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x265a44 [0x105585a44] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1b15450 [0x106e35450] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c39c8 [0x1054e39c8] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c3b28 [0x1054e3b28] + + 813 _pthread_cond_wait (in libsystem_pthread.dylib) + 980 [0x183e94128] + + 813 __psynch_cvwait (in libsystem_kernel.dylib) + 8 [0x183e5350c] + 813 Thread_359701: V8 DefaultWorke + + 813 thread_start (in libsystem_pthread.dylib) + 8 [0x183e8ec1c] + + 813 _pthread_start (in libsystem_pthread.dylib) + 136 [0x183e93c58] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x25ecf4 [0x10557ecf4] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x265a44 [0x105585a44] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1b15450 [0x106e35450] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c39c8 [0x1054e39c8] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c3b28 [0x1054e3b28] + + 813 _pthread_cond_wait (in libsystem_pthread.dylib) + 980 [0x183e94128] + + 813 __psynch_cvwait (in libsystem_kernel.dylib) + 8 [0x183e5350c] + 813 Thread_359702: V8 DefaultWorke + + 813 thread_start (in libsystem_pthread.dylib) + 8 [0x183e8ec1c] + + 813 _pthread_start (in libsystem_pthread.dylib) + 136 [0x183e93c58] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x25ecf4 [0x10557ecf4] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x265aac [0x105585aac] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x263fd4 [0x105583fd4] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x8d7324 [0x105bf7324] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x8d750c [0x105bf750c] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x8d75d4 [0x105bf75d4] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x8d834c [0x105bf834c] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x8ecb78 [0x105c0cb78] + + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc9368 [0x1060e9368] + + 392 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91f4 [0x1060e91f4] + + 238 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91d4 [0x1060e91d4] + + 54 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91fc [0x1060e91fc] + + 51 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91dc [0x1060e91dc] + + 34 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91d8 [0x1060e91d8] + + 32 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91f8 [0x1060e91f8] + + 6 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91d0 [0x1060e91d0] + + 3 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91e8 [0x1060e91e8] + + 3 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91ec [0x1060e91ec] + 813 Thread_359709 + 813 thread_start (in libsystem_pthread.dylib) + 8 [0x183e8ec1c] + 813 _pthread_start (in libsystem_pthread.dylib) + 136 [0x183e93c58] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x10940 [0x105330940] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x10a84 [0x105330a84] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x11cdc [0x105331cdc] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x71b88 [0x105391b88] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x72050 [0x105392050] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1a2bac [0x1054c2bac] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x2777d4 [0x1055977d4] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x397854 [0x1056b7854] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x398658 [0x1056b8658] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f2f9c [0x106c12f9c] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f32a4 [0x106c132a4] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1a53174 [0x106d73174] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x19aaa2c [0x106ccaa2c] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x87ae54 [0x105b9ae54] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x852d58 [0x105b72d58] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x852e3c [0x105b72e3c] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x852c74 [0x105b72c74] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x8549a8 [0x105b749a8] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x854304 [0x105b74304] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x81d09c [0x105b3d09c] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x828f90 [0x105b48f90] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x821d7c [0x105b41d7c] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x82349c [0x105b4349c] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x81e184 [0x105b3e184] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x81e640 [0x105b3e640] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x825b60 [0x105b45b60] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x8233a8 [0x105b433a8] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x822948 [0x105b42948] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x82b154 [0x105b4b154] + 813 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc9368 [0x1060e9368] + 378 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91f4 [0x1060e91f4] + 227 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91d4 [0x1060e91d4] + 74 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91dc [0x1060e91dc] + 68 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91fc [0x1060e91fc] + 27 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91d8 [0x1060e91d8] + 22 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91f8 [0x1060e91f8] + 9 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91d0 [0x1060e91d0] + 4 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91e8 [0x1060e91e8] + 4 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91ec [0x1060e91ec] + +Total number in stack (recursive counted multiple, when >=5): + 12 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x18f6364 [0x106c16364] + 8 _pthread_start (in libsystem_pthread.dylib) + 136 [0x183e93c58] + 8 thread_start (in libsystem_pthread.dylib) + 8 [0x183e8ec1c] + 7 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x25ecf4 [0x10557ecf4] + 6 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1b15450 [0x106e35450] + 6 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c39c8 [0x1054e39c8] + 6 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x1c3b28 [0x1054e3b28] + 6 ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0x265a44 [0x105585a44] + 6 __psynch_cvwait (in libsystem_kernel.dylib) + 0 [0x183e53504] + 6 _pthread_cond_wait (in libsystem_pthread.dylib) + 980 [0x183e94128] + +Sort by top of stack, same collapsed (when >= 5): + __psynch_cvwait (in libsystem_kernel.dylib) 4878 + __ulock_wait (in libsystem_kernel.dylib) 813 + ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91f4 [0x1060e91f4] 770 + ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91d4 [0x1060e91d4] 465 + ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91dc [0x1060e91dc] 125 + ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91fc [0x1060e91fc] 122 + ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91d8 [0x1060e91d8] 61 + ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91f8 [0x1060e91f8] 54 + ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91d0 [0x1060e91d0] 15 + ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91e8 [0x1060e91e8] 7 + ??? (in libwebscene_native_engine.dylib) load address 0x105320000 + 0xdc91ec [0x1060e91ec] 7 + +Binary Images: + 0x102f14000 - 0x10306611b +webscene_native_engine_tests (0) <261ADF73-F4F1-3903-854B-A18574BDE845> /Volumes/*/webscene_native_engine_tests + 0x1030d8000 - 0x1030e1adb +libEGL.dylib (0) <4C4C44AF-5555-3144-A120-3E4412E00745> /Volumes/*/libEGL.dylib + 0x103a88000 - 0x103e557cb +libGLESv2.dylib (0) <4C4C4498-5555-3144-A17E-05A77F85C5AE> /Volumes/*/libGLESv2.dylib + 0x105320000 - 0x10706d49f +libwebscene_native_engine.dylib (0) <1D887011-C02B-3D9F-84F0-5F0C8E94E935> /Volumes/*/libwebscene_native_engine.dylib + 0x183a20000 - 0x183a72b4b libobjc.A.dylib (951.7) <03BD9E32-CF0A-37B0-898A-3CE8DE06D842> /usr/lib/libobjc.A.dylib + 0x183a73000 - 0x183aa7d58 libdyld.dylib (1387) <957F93B3-8805-39C7-9C51-EDD1715F550E> /usr/lib/system/libdyld.dylib + 0x183aa8000 - 0x183b5b4ff dyld (1.0.0 - 1387) <74E52480-C2BD-3C8D-812D-95FE2B74A096> /usr/lib/dyld + 0x183b5c000 - 0x183b5f228 libsystem_blocks.dylib (96) /usr/lib/system/libsystem_blocks.dylib + 0x183b60000 - 0x183bb455f libxpc.dylib (3102.160.5) <33E44C2D-D65E-37A6-B85F-1A4CF524A050> /usr/lib/system/libxpc.dylib + 0x183bb5000 - 0x183bd59ff libsystem_trace.dylib (1861.160.4) <93F1DD8C-6CD9-32B9-B222-D23DA5D161B4> /usr/lib/system/libsystem_trace.dylib + 0x183bd6000 - 0x183c845f7 libcorecrypto.dylib (1922.160.10) <0642DDAD-4771-3C82-805C-E7C6701C1461> /usr/lib/system/libcorecrypto.dylib + 0x183c85000 - 0x183cd5257 libsystem_malloc.dylib (812.160.5) /usr/lib/system/libsystem_malloc.dylib + 0x183cd6000 - 0x183d1d23f libdispatch.dylib (1542.160.2) /usr/lib/system/libdispatch.dylib + 0x183d1e000 - 0x183d20ffb libsystem_featureflags.dylib (103) /usr/lib/system/libsystem_featureflags.dylib + 0x183d21000 - 0x183da21e7 libsystem_c.dylib (1752.160.4) /usr/lib/system/libsystem_c.dylib + 0x183da3000 - 0x183e33ae7 libc++.1.dylib (2100.43) /usr/lib/libc++.1.dylib + 0x183e34000 - 0x183e4e75f libc++abi.dylib (2100.43) /usr/lib/libc++abi.dylib + 0x183e4f000 - 0x183e8c2e7 libsystem_kernel.dylib (12377.161.14) /usr/lib/system/libsystem_kernel.dylib + 0x183e8d000 - 0x183e99b3b libsystem_pthread.dylib (539.100.4) /usr/lib/system/libsystem_pthread.dylib + 0x183e9a000 - 0x183ea2963 libsystem_platform.dylib (375.120.2) /usr/lib/system/libsystem_platform.dylib + 0x183ea3000 - 0x183ed26eb libsystem_info.dylib (600) <9B5FB84B-31AD-3EA7-8F89-8C700D369DC8> /usr/lib/system/libsystem_info.dylib + 0x183ed3000 - 0x1844315bf com.apple.CoreFoundation (6.9 - 5026.6.7) <9B672762-7B1F-30BC-96DE-F176B372D66D> /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation + 0x184432000 - 0x18474cfbf com.apple.LaunchServices (1141.1 - 1141.1) <01579E0C-9D85-3521-8916-4DDC990CD064> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/LaunchServices + 0x18474d000 - 0x18493595f com.apple.gpusw.MetalTools (1.0 - 1) <9C416BB2-0882-315C-AF23-F476E34983BC> /System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools + 0x184936000 - 0x1851304bf libBLAS.dylib (1551.160.2) <23402175-D2CF-3B08-88D0-AFBBCF775FEF> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib + 0x185131000 - 0x18524035f com.apple.Lexicon-framework (1.0 - 195.12) <946B1484-B180-3451-A452-B54BF5A6D392> /System/Library/PrivateFrameworks/Lexicon.framework/Versions/A/Lexicon + 0x185241000 - 0x1853b038f libSparse.dylib (184.160.6) /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparse.dylib + 0x1853b1000 - 0x1854440ff com.apple.SystemConfiguration (1.21 - 1.21) <1479C415-3678-3968-AC77-06373490860E> /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration + 0x185445000 - 0x18547957b libCRFSuite.dylib (55) <1CA9048E-57DD-30F4-A3E6-FE6E97D5BF82> /usr/lib/libCRFSuite.dylib + 0x185742000 - 0x186725a9f com.apple.Foundation (6.9 - 5026.6.7) <91DACE39-FA28-3191-818D-1FCC6A0E615A> /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation + 0x186726000 - 0x1868d583f com.apple.LanguageModeling (1.0 - 433.6) <327536E3-A27C-38C2-A67F-D6488D04CCEE> /System/Library/PrivateFrameworks/LanguageModeling.framework/Versions/A/LanguageModeling + 0x1868d6000 - 0x1869f6cbf com.apple.CoreDisplay (291.4 - 291.4) /System/Library/Frameworks/CoreDisplay.framework/Versions/A/CoreDisplay + 0x1869f7000 - 0x186db9fdf com.apple.audio.AudioToolboxCore (1.0 - 1556.704) <8AF1606D-5C93-3B80-BC81-60C5688628E2> /System/Library/PrivateFrameworks/AudioToolboxCore.framework/Versions/A/AudioToolboxCore + 0x186dba000 - 0x186fe3d9f com.apple.CoreText (877.6.0.2 - 877.6.0.2) /System/Library/Frameworks/CoreText.framework/Versions/A/CoreText + 0x186fe4000 - 0x187779e1f com.apple.audio.CoreAudio (5.0 - 5.0) /System/Library/Frameworks/CoreAudio.framework/Versions/A/CoreAudio + 0x18777a000 - 0x187b9921f com.apple.security (7.0 - 61901.160.44) <9D0387FC-E8F6-3004-9C95-CA68EA715C8B> /System/Library/Frameworks/Security.framework/Versions/A/Security + 0x187b9a000 - 0x187e6fd53 libicucore.A.dylib (76142.5.2.1) <53A3E31E-06A8-325E-B5A8-316B88AA3C92> /usr/lib/libicucore.A.dylib + 0x187e70000 - 0x187e79e5f libsystem_darwin.dylib (1752.160.4) <8E07D22E-CE5A-38A0-B091-5B0338C326F5> /usr/lib/system/libsystem_darwin.dylib + 0x187e7a000 - 0x188171cbf com.apple.CoreServices.CarbonCore (1333 - 1333) /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore + 0x188172000 - 0x1881b1f17 com.apple.CoreServicesInternal (505 - 505) <7D56DA94-31EB-35F0-B886-4010C075E035> /System/Library/PrivateFrameworks/CoreServicesInternal.framework/Versions/A/CoreServicesInternal + 0x1881b2000 - 0x1881f11bf com.apple.CSStore (1141.1 - 1141.1) <12479A32-B72F-3A09-BB03-BA56C37853B5> /System/Library/PrivateFrameworks/CoreServicesStore.framework/Versions/A/CoreServicesStore + 0x1881f2000 - 0x1882da25f com.apple.framework.IOKit (2.0.2 - 100231.120.3) <12372585-DF92-33EF-B632-714FAA13260A> /System/Library/Frameworks/IOKit.framework/Versions/A/IOKit + 0x1882db000 - 0x1882ed1b6 libsystem_notify.dylib (348.160.3) <15799128-6CBD-30D6-A2BB-B9D02B4470C0> /usr/lib/system/libsystem_notify.dylib + 0x1882ee000 - 0x18834c173 libsandbox.1.dylib (2680.160.6) /usr/lib/libsandbox.1.dylib + 0x18834d000 - 0x189a7049f com.apple.AppKit (6.9 - 2685.70.101) /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit + 0x189a71000 - 0x189c2991f com.apple.UIFoundation (1.0 - 1019.1) <659AFBBD-E22E-3474-BCFE-298DA57B1464> /System/Library/PrivateFrameworks/UIFoundation.framework/Versions/A/UIFoundation + 0x189c2a000 - 0x189c403ff com.apple.UniformTypeIdentifiers (709 - 709) /System/Library/Frameworks/UniformTypeIdentifiers.framework/Versions/A/UniformTypeIdentifiers + 0x18a11d000 - 0x18a1f595f libboringssl.dylib (532.120.8) <5BF55637-F306-3D79-B5A1-DB8A871DAD4B> /usr/lib/libboringssl.dylib + 0x18a1f6000 - 0x18a5ab1df com.apple.CFNetwork (1.0 - 3860.700.1) <4A3B95C5-AA2E-338C-9398-56895AF82D97> /System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork + 0x18a5ac000 - 0x18a5c6f7b libsystem_networkextension.dylib (2226.161.1) <9C7B1EEB-47BE-3791-93A9-CFC693CB9417> /usr/lib/system/libsystem_networkextension.dylib + 0x18a5c7000 - 0x18a5c8067 libenergytrace.dylib (23) <8E04C57D-3651-386E-83D5-4728B732F214> /usr/lib/libenergytrace.dylib + 0x18a5c9000 - 0x18a648eff libMobileGestalt.dylib (1484.120.3) /usr/lib/libMobileGestalt.dylib + 0x18a649000 - 0x18a660fdf libsystem_asl.dylib (406) <54439739-33EE-3273-839F-CBA67D7F5CB1> /usr/lib/system/libsystem_asl.dylib + 0x18a661000 - 0x18a684797 com.apple.TCC (1.0 - 1) /System/Library/PrivateFrameworks/TCC.framework/Versions/A/TCC + 0x18a685000 - 0x18ac3ba1f com.apple.SkyLight (1.600.0 - 922.13.1) <0C8F41C6-6D93-3DB3-B522-CA8CFF5C3B33> /System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/SkyLight + 0x18ac3c000 - 0x18b3915ff com.apple.CoreGraphics (2.0 - 1965.6.3) <38C8FBEC-DE88-33FE-B742-A192F22CC754> /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics + 0x18b392000 - 0x18b539b8b com.apple.ColorSync (4.13.0 - 3813.5.1) <873404F1-CC9D-30F9-AE06-8EA58D292005> /System/Library/Frameworks/ColorSync.framework/Versions/A/ColorSync + 0x18b53a000 - 0x18b5a539f com.apple.HIServices (1.22 - 818) /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/HIServices + 0x18b6a3000 - 0x18b89745f com.apple.Montreal (1.0 - 178) <95BA357E-906A-3183-A402-A41D486B5AB3> /System/Library/PrivateFrameworks/Montreal.framework/Versions/A/Montreal + 0x18b98e000 - 0x18bd7bf5f com.apple.CoreData (120 - 1526) <712AD9C1-44D2-36F4-BA8E-15038521462B> /System/Library/Frameworks/CoreData.framework/Versions/A/CoreData + 0x18bd7c000 - 0x18bd97ddf com.apple.ProtocolBuffer (1 - 310.26.4.23.2) /System/Library/PrivateFrameworks/ProtocolBuffer.framework/Versions/A/ProtocolBuffer + 0x18bd98000 - 0x18bf80a2f libsqlite3.dylib (382) /usr/lib/libsqlite3.dylib + 0x18bf81000 - 0x18c006fff com.apple.Accounts (113 - 113) <5F6B668E-00B2-3BEC-959F-26BD6B50D42B> /System/Library/Frameworks/Accounts.framework/Versions/A/Accounts + 0x18c01e000 - 0x18c10f49f com.apple.BaseBoard (732.1.1 - 732.1.1) <959C748F-8851-3A25-BFFA-5FEA80296965> /System/Library/PrivateFrameworks/BaseBoard.framework/Versions/A/BaseBoard + 0x18c110000 - 0x18c17bd5f com.apple.RunningBoardServices (1.0 - 1015.160.2.0.1) /System/Library/PrivateFrameworks/RunningBoardServices.framework/Versions/A/RunningBoardServices + 0x18c17c000 - 0x18c1efc37 com.apple.AE (944 - 944) <435D6243-695B-3543-A722-10106F5696BD> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/AE.framework/Versions/A/AE + 0x18c1f0000 - 0x18c201d87 libdns_services.dylib (2881.160.4) <88925A0C-4960-3F6D-AF3A-B1983F7B3D18> /usr/lib/libdns_services.dylib + 0x18c202000 - 0x18c20a387 libsystem_symptoms.dylib (2169.160.3) <229122B9-B8B1-3F2F-870E-8650AE3C4FB5> /usr/lib/system/libsystem_symptoms.dylib + 0x18c20b000 - 0x18da2829f com.apple.Network (1.0 - 5812.160.9) <1C7E652B-6B94-3180-93A6-EF8DBA3A5448> /System/Library/Frameworks/Network.framework/Versions/A/Network + 0x18da29000 - 0x18da58ddf com.apple.analyticsd (1.0 - 1) /System/Library/PrivateFrameworks/CoreAnalytics.framework/Versions/A/CoreAnalytics + 0x18da59000 - 0x18da5a8bb libDiagnosticMessagesClient.dylib (113) <6CD959AA-4825-306A-864A-BD69EC5F2DC0> /usr/lib/libDiagnosticMessagesClient.dylib + 0x18da5b000 - 0x18dac737f com.apple.spotlight.metadata.utilities (1.0 - 2418.6.3.9.400) <29AA0F7F-26F4-35B3-96DF-8A67B00A58AB> /System/Library/PrivateFrameworks/MetadataUtilities.framework/Versions/A/MetadataUtilities + 0x18dac8000 - 0x18db53b5f com.apple.Metadata (26.6 - 2418.6.3.9.400) /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Metadata + 0x18db54000 - 0x18db5d1eb com.apple.DiskArbitration (2.7 - 2.7) <332C4B80-5B3C-34E7-AD1F-F6131E607F95> /System/Library/Frameworks/DiskArbitration.framework/Versions/A/DiskArbitration + 0x18db5e000 - 0x18df82063 com.apple.vImage (8.1 - 632.120.2) <2B16DF37-A596-3D8A-AE47-33E580EB1354> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vImage.framework/Versions/A/vImage + 0x18df83000 - 0x18e398e3f com.apple.QuartzCore (1195.17 - 1195.17) <98CB7012-30E5-3BDD-8C84-CDBDA9DB3017> /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore + 0x18e399000 - 0x18e3e9cdf libFontRegistry.dylib (408.6.0.3) <49E7449E-1385-3B53-94CC-36EFC31E98FE> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/Resources/libFontRegistry.dylib + 0x18e3ea000 - 0x18e57083f com.apple.coreui (2.1 - 975) /System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI + 0x18e6a6000 - 0x18e6afe7f com.apple.PerformanceAnalysis (1.427 - 427) <74C54353-A613-35A2-857A-737BB48906F9> /System/Library/PrivateFrameworks/PerformanceAnalysis.framework/Versions/A/PerformanceAnalysis + 0x18e6b0000 - 0x18e6bdaff com.apple.OpenDirectory (26.6 - 666.100.1) /System/Library/Frameworks/OpenDirectory.framework/Versions/A/OpenDirectory + 0x18e6be000 - 0x18e6e727f com.apple.CFOpenDirectory (26.6 - 666.100.1) <3B7FD4C1-D1D4-3DA9-B2F8-3D4094679D76> /System/Library/Frameworks/OpenDirectory.framework/Versions/A/Frameworks/CFOpenDirectory.framework/Versions/A/CFOpenDirectory + 0x18e6e8000 - 0x18e6f491b com.apple.CoreServices.FSEvents (1413.160.2 - 1413.160.2) /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/FSEvents.framework/Versions/A/FSEvents + 0x18e6f5000 - 0x18e71e0df com.apple.coreservices.SharedFileList (225 - 225) <297AC970-E432-3BBD-986C-36782634062E> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SharedFileList.framework/Versions/A/SharedFileList + 0x18e71f000 - 0x18e72208f libapp_launch_measurement.dylib (17) /usr/lib/libapp_launch_measurement.dylib + 0x18e723000 - 0x18e76b9bf com.apple.CoreAutoLayout (1.0 - 34) <54AD73AF-852E-3CD6-8B7D-E73BE79857D3> /System/Library/PrivateFrameworks/CoreAutoLayout.framework/Versions/A/CoreAutoLayout + 0x18e76c000 - 0x18e8523c3 libxml2.2.dylib (39.10.3) <1E8A4F9E-3954-3458-B3BB-BE97F961C105> /usr/lib/libxml2.2.dylib + 0x18e853000 - 0x18e8d3bbf com.apple.CoreVideo (1.8 - 0.0) <0616AF41-149E-3F4A-906E-56E2642457BE> /System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo + 0x18e8d4000 - 0x18e8d6f5f com.apple.loginsupport (3.0 - 264.4.2) <87907862-52FF-3F24-AC29-7C1678BCD277> /System/Library/PrivateFrameworks/login.framework/Versions/A/Frameworks/loginsupport.framework/Versions/A/loginsupport + 0x18e915000 - 0x18e94165f com.apple.UserManagement (1.0 - 1) <4A78C569-FF0D-398B-9C25-33453F0CEC40> /System/Library/PrivateFrameworks/UserManagement.framework/Versions/A/UserManagement + 0x18ee40000 - 0x18f6797df com.apple.CoreML (1.0 - 3520.5.1) /System/Library/Frameworks/CoreML.framework/Versions/A/CoreML + 0x1904a7000 - 0x1904df5b7 libsystem_containermanager.dylib (725.160.3) <14B2A47F-19C8-392F-8FDB-FE8AE375DD41> /usr/lib/system/libsystem_containermanager.dylib + 0x1904e0000 - 0x1904fc25f com.apple.IOSurface (393.5.8 - 393.5.8) <5556FD64-9D47-3547-961E-3A27681F3C51> /System/Library/Frameworks/IOSurface.framework/Versions/A/IOSurface + 0x1904fd000 - 0x19050705f com.apple.IOAccelerator (487.4.3 - 487.4.3) /System/Library/PrivateFrameworks/IOAccelerator.framework/Versions/A/IOAccelerator + 0x190508000 - 0x1907e1e1f com.apple.Metal (373.7 - 373.7) <493E76D9-74D4-333B-A3B2-E5F9BC86429D> /System/Library/Frameworks/Metal.framework/Versions/A/Metal + 0x1907e2000 - 0x19080b07f com.apple.audio.caulk (1.0 - 214.701) /System/Library/PrivateFrameworks/caulk.framework/Versions/A/caulk + 0x19080c000 - 0x1909ad3ff com.apple.CoreMedia (1.0 - 3330.13.2) /System/Library/Frameworks/CoreMedia.framework/Versions/A/CoreMedia + 0x1909ae000 - 0x190c7d43f libFontParser.dylib (435.6.0.2) <9E126CE0-FBB2-3B15-953F-CCDC758E34FB> /System/Library/PrivateFrameworks/FontServices.framework/libFontParser.dylib + 0x190c7e000 - 0x190f7911f com.apple.HIToolbox (2.1.1 - 1250.1) <38408482-CE3B-359E-9465-7FEB4BB79B54> /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox + 0x190f7a000 - 0x190f8e5ff com.apple.framework.DFRFoundation (1.0 - 293.1.1) /System/Library/PrivateFrameworks/DFRFoundation.framework/Versions/A/DFRFoundation + 0x190f8f000 - 0x190f9435f com.apple.dt.XCTTargetBootstrap (26.6 - 24901) /System/Library/PrivateFrameworks/XCTTargetBootstrap.framework/Versions/A/XCTTargetBootstrap + 0x190f95000 - 0x190fd219f com.apple.CoreSVG (1.0 - 341) <986D57A7-BFF1-3DAA-8EB1-17CCAA76C731> /System/Library/PrivateFrameworks/CoreSVG.framework/Versions/A/CoreSVG + 0x190fd3000 - 0x191313f1f com.apple.ImageIO (3.3.0 - 2784.6.4) /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO + 0x191314000 - 0x1917fef9f com.apple.CoreImage (19.0.0 - 1592.120.2) <0943679D-FF88-3F18-BE4B-D8B4827AB0B5> /System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage + 0x1917ff000 - 0x1918b8ddf com.apple.MetalPerformanceShaders.MPSCore (1.0 - 1) /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSCore.framework/Versions/A/MPSCore + 0x1918b9000 - 0x1918bd5d7 libsystem_configuration.dylib (1405.160.3) /usr/lib/system/libsystem_configuration.dylib + 0x1918be000 - 0x1918c499f libsystem_sandbox.dylib (2680.160.6) <54688162-B50D-3D31-A1E8-7B9766D3530D> /usr/lib/system/libsystem_sandbox.dylib + 0x1918c5000 - 0x1918c617f com.apple.AggregateDictionary (1.0 - 1) <91BDD1F8-831B-3B01-86BA-6BBCB43373C4> /System/Library/PrivateFrameworks/AggregateDictionary.framework/Versions/A/AggregateDictionary + 0x1918c7000 - 0x1918cb4d3 com.apple.AppleSystemInfo (3.1.5 - 3.1.5) <4C6139EE-BF87-37A6-B226-830A6FDC36F8> /System/Library/PrivateFrameworks/AppleSystemInfo.framework/Versions/A/AppleSystemInfo + 0x1918cc000 - 0x1918cd46b liblangid.dylib (140) /usr/lib/liblangid.dylib + 0x1918ce000 - 0x1919ed45f com.apple.CoreNLP (1.0 - 313) /System/Library/PrivateFrameworks/CoreNLP.framework/Versions/A/CoreNLP + 0x1919ee000 - 0x1919f491f com.apple.LinguisticData (1.0 - 483.10) /System/Library/PrivateFrameworks/LinguisticData.framework/Versions/A/LinguisticData + 0x1919f5000 - 0x192a7d41f libBNNS.dylib (1961.160.8) <54A103BA-7D04-32DB-B204-179E2E0290CA> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBNNS.dylib + 0x192a7e000 - 0x192bbb46f libvDSP.dylib (1126.160.2) <4C851329-A9F4-3E9E-9E48-07FF4120DCF9> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvDSP.dylib + 0x192bbc000 - 0x192bef63f com.apple.CoreEmoji (1.0 - 261.4.6) <47AAECAD-C28C-352E-BB86-7F292E0BFBC6> /System/Library/PrivateFrameworks/CoreEmoji.framework/Versions/A/CoreEmoji + 0x192bf0000 - 0x192c29267 com.apple.IOMobileFramebuffer (343.0.0 - 343.0.0) <2BC48182-F354-3AB0-8F18-0C60CAAFE398> /System/Library/PrivateFrameworks/IOMobileFramebuffer.framework/Versions/A/IOMobileFramebuffer + 0x192cad000 - 0x192e21e5f com.apple.CoreUtils (8.3 - 830.24) /System/Library/PrivateFrameworks/CoreUtils.framework/Versions/A/CoreUtils + 0x192e22000 - 0x192e396df com.apple.MobileKeyBag (2.0 - 1.0) /System/Library/PrivateFrameworks/MobileKeyBag.framework/Versions/A/MobileKeyBag + 0x192e3a000 - 0x192e480ff com.apple.AssertionServices (1.0 - 1015.160.2.0.1) <1946F8FE-0ABC-3F8F-9116-5451ECABD14C> /System/Library/PrivateFrameworks/AssertionServices.framework/Versions/A/AssertionServices + 0x192e49000 - 0x192edba9f com.apple.securityfoundation (6.0 - 55293) <9A86DB3F-CC62-3E89-B872-35D04CFFBE42> /System/Library/Frameworks/SecurityFoundation.framework/Versions/A/SecurityFoundation + 0x192edc000 - 0x192f0d2ff com.apple.coreservices.BackgroundTaskManagement (1.0 - 104) /System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Versions/A/BackgroundTaskManagement + 0x192f19000 - 0x192f1c1fb libquarantine.dylib (196.160.2) /usr/lib/system/libquarantine.dylib + 0x192f1d000 - 0x192f282bf libCheckFix.dylib (33) <6508C698-D587-3B5A-B95B-A3A3F78CE122> /usr/lib/libCheckFix.dylib + 0x192f29000 - 0x192f407ab libcoretls.dylib (187.100.3) <0EAB1F4A-9275-3FED-8EA6-E962ACDDEE5D> /usr/lib/libcoretls.dylib + 0x192f41000 - 0x192f52273 libbsm.0.dylib (90) <633BCB5F-F063-3D5A-B52A-F72AE236824B> /usr/lib/libbsm.0.dylib + 0x192f53000 - 0x192fb1c6b libmecab.dylib (1121.5.1) /usr/lib/libmecab.dylib + 0x192fb2000 - 0x192fb441b libgermantok.dylib (31) <74E55DD6-720D-39E4-897E-EB4328E1946D> /usr/lib/libgermantok.dylib + 0x192fb5000 - 0x192fc8e3f libLinearAlgebra.dylib (1551.160.2) <407BCF3E-A91F-3A7F-8B8C-DBB8E807990F> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLinearAlgebra.dylib + 0x192fc9000 - 0x19321f17f com.apple.MetalPerformanceShaders.MPSNeuralNetwork (1.0 - 1) <199F6401-91D0-36E9-9EA9-D4B44ED1CE3A> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNeuralNetwork.framework/Versions/A/MPSNeuralNetwork + 0x193220000 - 0x19327419f com.apple.MetalPerformanceShaders.MPSRayIntersector (1.0 - 1) <2E7E2722-3821-3DBF-B25A-6EA45D1A8FD4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSRayIntersector.framework/Versions/A/MPSRayIntersector + 0x193275000 - 0x193407edf com.apple.MLCompute (1.0 - 1) /System/Library/Frameworks/MLCompute.framework/Versions/A/MLCompute + 0x193408000 - 0x19343957f com.apple.MetalPerformanceShaders.MPSMatrix (1.0 - 1) <4D134FE3-50EE-39D5-9699-04B4B673DD35> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSMatrix.framework/Versions/A/MPSMatrix + 0x19343a000 - 0x193609cdf com.apple.MetalPerformanceShaders.MPSNDArray (1.0 - 1) <3E1FE9EA-34A2-3545-B639-48B1FE1FD3D4> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSNDArray.framework/Versions/A/MPSNDArray + 0x19360a000 - 0x19369e1df com.apple.MetalPerformanceShaders.MPSImage (1.0 - 1) /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSImage.framework/Versions/A/MPSImage + 0x19369f000 - 0x1936aa4c3 com.apple.AppleFSCompression (174.160.2 - 1.0) /System/Library/PrivateFrameworks/AppleFSCompression.framework/Versions/A/AppleFSCompression + 0x1936ab000 - 0x1936b70a3 libbz2.1.0.dylib (49) <5FFE1FFA-6BD0-32AF-A815-7543731CA763> /usr/lib/libbz2.1.0.dylib + 0x1936b8000 - 0x1936bed03 libsystem_coreservices.dylib (191.5.1) /usr/lib/system/libsystem_coreservices.dylib + 0x1936bf000 - 0x1936f0adf com.apple.CoreServices.OSServices (1141.1 - 1141.1) <61677289-93B7-382F-86CA-B856361D293F> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/OSServices.framework/Versions/A/OSServices + 0x193a7c000 - 0x193acc6ff com.apple.UserNotifications (1.0 - 640.6.5) <7F1A25E4-ED0A-3502-AABA-26EDB4A0D2A7> /System/Library/Frameworks/UserNotifications.framework/Versions/A/UserNotifications + 0x193c38000 - 0x193c46d97 libz.1.dylib (100.120.1) <13EDE3A5-A7D9-3FB8-B0C2-2FB7F7272B34> /usr/lib/libz.1.dylib + 0x193c47000 - 0x193c84a77 libsystem_m.dylib (3312.100.1) /usr/lib/system/libsystem_m.dylib + 0x193c85000 - 0x193c85c9b libcharset.1.dylib (115.120.2) <1940124C-0D73-35D2-9D94-A75F116088A0> /usr/lib/libcharset.1.dylib + 0x193c86000 - 0x193c89527 libmacho.dylib (1387) <949131E5-BDA2-39BA-AA50-62651BB51802> /usr/lib/system/libmacho.dylib + 0x193c8a000 - 0x193ca2dc3 libkxld.dylib (12377.161.14) /usr/lib/system/libkxld.dylib + 0x193ca3000 - 0x193cb03a7 libcommonCrypto.dylib (600035) <3B110564-5278-3CB0-85F1-2CE8431FF935> /usr/lib/system/libcommonCrypto.dylib + 0x193cb1000 - 0x193cbaca3 libunwind.dylib (2100.2) <05FD0014-55B1-3B8A-A6BA-6C7A389C4123> /usr/lib/system/libunwind.dylib + 0x193cbb000 - 0x193cc2349 liboah.dylib (367.9) <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/liboah.dylib + 0x193cc3000 - 0x193ccdbef libcopyfile.dylib (240.160.2.0.1) /usr/lib/system/libcopyfile.dylib + 0x193cce000 - 0x193cd1987 libcompiler_rt.dylib (103.3) <6FB345CA-7F5C-3263-A23F-143F7539FD8A> /usr/lib/system/libcompiler_rt.dylib + 0x193cd2000 - 0x193cd678b libsystem_collections.dylib (1752.160.4) /usr/lib/system/libsystem_collections.dylib + 0x193cd7000 - 0x193cda4cf libsystem_secinit.dylib (168.100.7) /usr/lib/system/libsystem_secinit.dylib + 0x193cdb000 - 0x193cddbf7 libremovefile.dylib (85.100.6) <7460B5AE-469A-36A0-A7EC-6C7D69628E86> /usr/lib/system/libremovefile.dylib + 0x193cde000 - 0x193cdef27 libkeymgr.dylib (31) <7E863FCA-F3FF-32C7-8A8C-F983E946AFC3> /usr/lib/system/libkeymgr.dylib + 0x193cdf000 - 0x193ce7e37 libsystem_dnssd.dylib (2881.160.4) <305F4398-E688-3384-B351-02D865EC8A04> /usr/lib/system/libsystem_dnssd.dylib + 0x193ce8000 - 0x193ced09b libcache.dylib (95) <9CD7B1E1-3E47-339C-A193-2392E3E0ED23> /usr/lib/system/libcache.dylib + 0x193cee000 - 0x193cefce3 libSystem.B.dylib (1356) <4FED5EE2-5D3E-35B1-A170-9859C4B683BB> /usr/lib/libSystem.B.dylib + 0x193cf0000 - 0x193cf1fcf libfakelink.dylib (5) <820D290D-51A0-3064-A1F2-4F0AAF7E6BF4> /usr/lib/libfakelink.dylib + 0x193cf2000 - 0x193cf2a33 com.apple.SoftLinking (1.0 - 71) <4109E8DD-0A81-310C-B1B3-23B87186D0D8> /System/Library/PrivateFrameworks/SoftLinking.framework/Versions/A/SoftLinking + 0x193d28000 - 0x193d2f2bb libiconv.2.dylib (115.120.2) <4646F780-1D5E-3EE7-B00A-64619293CC18> /usr/lib/libiconv.2.dylib + 0x193d30000 - 0x193d42457 libcmph.dylib (9) /usr/lib/libcmph.dylib + 0x193d43000 - 0x193e3af3f libarchive.2.dylib (167.160.4) <0048DB96-1737-3FC5-AF0C-AF784FA24A03> /usr/lib/libarchive.2.dylib + 0x193e3b000 - 0x193ea085b com.apple.SearchKit (1.4.2 - 1.4.2) /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/SearchKit.framework/Versions/A/SearchKit + 0x193ea1000 - 0x193ea918f libThaiTokenizer.dylib (28) <92FAD15C-EEA5-34E9-B309-75A1CD1B620B> /usr/lib/libThaiTokenizer.dylib + 0x193eaa000 - 0x193ecdf37 com.apple.applesauce (1.0 - 17.7) /System/Library/PrivateFrameworks/AppleSauce.framework/Versions/A/AppleSauce + 0x193ece000 - 0x193ee64ab libapple_nghttp2.dylib (37.120.3) /usr/lib/libapple_nghttp2.dylib + 0x193ee7000 - 0x193f9038f libSparseBLAS.dylib (184.160.6) <669ABE12-838F-3F14-8456-D60DE5DF8EB8> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libSparseBLAS.dylib + 0x193f91000 - 0x193f9263f com.apple.MetalPerformanceShaders.MetalPerformanceShaders (1.0 - 1) /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders + 0x193f93000 - 0x193f98ff7 libpam.2.dylib (35) <7E84FD3B-E90E-317E-AC19-17B70AC809E5> /usr/lib/libpam.2.dylib + 0x193f99000 - 0x19406dd97 libcompression.dylib (193.120.2) /usr/lib/libcompression.dylib + 0x19406e000 - 0x194072267 libQuadrature.dylib (8) /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libQuadrature.dylib + 0x194073000 - 0x19524394f libLAPACK.dylib (1551.160.2) <5015CD96-C046-364D-AAE3-1F439044468B> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libLAPACK.dylib + 0x195244000 - 0x19529a4ff com.apple.DictionaryServices (1.2 - 382.0.1) <6A26D479-5926-330B-9FB8-9B7A6BE8E239> /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/DictionaryServices.framework/Versions/A/DictionaryServices + 0x19529b000 - 0x1952b94f7 liblzma.5.dylib (21) /usr/lib/liblzma.5.dylib + 0x1952ba000 - 0x1952bb85f libcoretls_cfhelpers.dylib (187.100.3) <6937D729-7EF4-3972-9E12-694C17C1C1AB> /usr/lib/libcoretls_cfhelpers.dylib + 0x1952bc000 - 0x19532f85f com.apple.APFS (2811.160.7 - 2811.160.7) /System/Library/PrivateFrameworks/APFS.framework/Versions/A/APFS + 0x195330000 - 0x19533e833 libxar.1.dylib (503.160.5) /usr/lib/libxar.1.dylib + 0x19533f000 - 0x19534279b libutil.dylib (73) /usr/lib/libutil.dylib + 0x195343000 - 0x19536dcbf libxslt.1.dylib (21.13.2) <6C426EA5-7F1E-333E-BB5D-74465EFED12B> /usr/lib/libxslt.1.dylib + 0x195376000 - 0x1953ef587 libvMisc.dylib (1126.160.2) /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libvMisc.dylib + 0x1953f0000 - 0x19547f45f libate.dylib (3.0.9) <01AAD3B4-D6BA-36D9-BA6F-D494D2AC161D> /usr/lib/libate.dylib + 0x195480000 - 0x195488923 libIOReport.dylib (107) <9E06CB59-0638-3C9F-B202-264E739433AC> /usr/lib/libIOReport.dylib + 0x195489000 - 0x19549c1bf com.apple.CrashReporterSupport (10.13 - 15140) /System/Library/PrivateFrameworks/CrashReporterSupport.framework/Versions/A/CrashReporterSupport + 0x1954be000 - 0x1955bb83f com.apple.CVNLP (1.0 - 119) /System/Library/PrivateFrameworks/CVNLP.framework/Versions/A/CVNLP + 0x1955e0000 - 0x195620f9f com.apple.pluginkit.framework (1.0 - 1) /System/Library/PrivateFrameworks/PlugInKit.framework/Versions/A/PlugInKit + 0x195621000 - 0x1956284e3 libMatch.1.dylib (49.161.1) <2F2EF0D7-2FE4-3A5A-8E4C-E1571C8D0C10> /usr/lib/libMatch.1.dylib + 0x195697000 - 0x1956df9ff com.apple.AppleVAFramework (6.2.10 - 6.2.10) <7CF84496-675C-3241-B0EF-E83C95F188FA> /System/Library/PrivateFrameworks/AppleVA.framework/Versions/A/AppleVA + 0x1956e0000 - 0x1956faccf libexpat.1.dylib (47) /usr/lib/libexpat.1.dylib + 0x1956fb000 - 0x195704bf3 libheimdal-asn1.dylib (710.160.4) <6A4A85F4-3D12-3C4C-85EC-D53D61379F28> /usr/lib/libheimdal-asn1.dylib + 0x195705000 - 0x19576561f com.apple.IconFoundation (494 - 494) /System/Library/PrivateFrameworks/IconFoundation.framework/Versions/A/IconFoundation + 0x195766000 - 0x195824c1f com.apple.IconServices (494 - 494) <10C63D59-07BC-3518-87A0-83CAC48D8A70> /System/Library/PrivateFrameworks/IconServices.framework/Versions/A/IconServices + 0x195825000 - 0x1958e6edf com.apple.MediaExperience (1.0 - 1) <52A7AD42-9DE0-393B-A6FB-A7CB6FF8F3A5> /System/Library/PrivateFrameworks/MediaExperience.framework/Versions/A/MediaExperience + 0x195914000 - 0x1959237ff com.apple.GraphVisualizer (1.0 - 307) <77D85BA0-FE1C-3B5A-92DB-70A30202C990> /System/Library/PrivateFrameworks/GraphVisualizer.framework/Versions/A/GraphVisualizer + 0x195924000 - 0x19596351f com.apple.OTSVG (1.0 - 877.6.0.2) <5D3E7FFF-AC8E-3D6F-8E99-B199E593D270> /System/Library/PrivateFrameworks/OTSVG.framework/Versions/A/OTSVG + 0x195964000 - 0x195970c7f com.apple.xpc.AppServerSupport (1.0 - 3102.160.5) <2B5FB7B0-844C-3D84-9EFD-020B285B0F8D> /System/Library/PrivateFrameworks/AppServerSupport.framework/Versions/A/AppServerSupport + 0x195971000 - 0x195977abf libspindump.dylib (419.11) <04DC06C1-2BFA-3FEE-9429-A33E41721A3E> /usr/lib/libspindump.dylib + 0x195978000 - 0x195a38e9f com.apple.Heimdal (4.0 - 2.0) /System/Library/PrivateFrameworks/Heimdal.framework/Versions/A/Heimdal + 0x195a5d000 - 0x195bddcbf com.apple.corebrightness (1.0 - 1) <1F873909-B3B8-3D55-9673-9AFA86BB085B> /System/Library/PrivateFrameworks/CoreBrightness.framework/Versions/A/CoreBrightness + 0x195d07000 - 0x195d4b6ef com.apple.AppleJPEG (1.0 - 1) <7F00413A-4D40-3DBF-8FD5-859B23E6DC03> /System/Library/PrivateFrameworks/AppleJPEG.framework/Versions/A/AppleJPEG + 0x195d4c000 - 0x195f0c92f libJP2.dylib (2784.6.6) <7304F8B3-8E0F-3813-BFAF-9A565CEA0A11> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJP2.dylib + 0x195f0d000 - 0x195f0ee1f com.apple.WatchdogClient.framework (1.0 - 333) /System/Library/PrivateFrameworks/WatchdogClient.framework/Versions/A/WatchdogClient + 0x195f0f000 - 0x195f54d7f com.apple.MultitouchSupport.framework (9460.1 - 9460.1) <57F7BB9C-649D-3360-AA86-A502815D77FA> /System/Library/PrivateFrameworks/MultitouchSupport.framework/Versions/A/MultitouchSupport + 0x195f55000 - 0x1964ab37f com.apple.VideoToolbox (1.0 - 3330.13.2) /System/Library/Frameworks/VideoToolbox.framework/Versions/A/VideoToolbox + 0x1964ac000 - 0x1964d120f libAudioToolboxUtility.dylib (1556.704) <75F77FEC-BE14-3C97-93DA-403C3B529D3B> /usr/lib/libAudioToolboxUtility.dylib + 0x1964d2000 - 0x1964fc01f libPng.dylib (2784.6.6) /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libPng.dylib + 0x1964fd000 - 0x19655db63 libTIFF.dylib (2784.6.6) /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libTIFF.dylib + 0x19655e000 - 0x19657d257 com.apple.IOPresentment (67 - 67) /System/Library/PrivateFrameworks/IOPresentment.framework/Versions/A/IOPresentment + 0x19657e000 - 0x1965827d3 com.apple.GPUWrangler (8.1.12 - 8.1.12) /System/Library/PrivateFrameworks/GPUWrangler.framework/Versions/A/GPUWrangler + 0x196583000 - 0x196585913 libRadiance.dylib (2784.6.6) /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libRadiance.dylib + 0x196586000 - 0x19658b2f3 com.apple.DSExternalDisplay (3.1 - 380) /System/Library/PrivateFrameworks/DSExternalDisplay.framework/Versions/A/DSExternalDisplay + 0x19658c000 - 0x1965b6baf libJPEG.dylib (2784.6.6) <8EA6CA42-AA01-3C0F-9672-4917481BAAAE> /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libJPEG.dylib + 0x1965b7000 - 0x1965e457f com.apple.ATSUI (1.0 - 1) <2186F196-EE17-3A59-B9DA-D6823BEDD35B> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATSUI.framework/Versions/A/ATSUI + 0x1965e5000 - 0x1965ea9fb libGIF.dylib (2784.6.6) /System/Library/Frameworks/ImageIO.framework/Versions/A/Resources/libGIF.dylib + 0x1965eb000 - 0x1965fb19f com.apple.CMCaptureCore (1.0 - 665.140.6) /System/Library/PrivateFrameworks/CMCaptureCore.framework/Versions/A/CMCaptureCore + 0x1965fc000 - 0x19666e71f com.apple.print.framework.PrintCore (19 - 601.3) /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/PrintCore + 0x19666f000 - 0x19670591f com.apple.TextureIO (3.10.12 - 3.10.12) /System/Library/PrivateFrameworks/TextureIO.framework/Versions/A/TextureIO + 0x196706000 - 0x196a0ff9f com.apple.InternationalSupport (1.0 - 74) <5ACC6C0E-51E9-3B5A-B24F-89B22D070878> /System/Library/PrivateFrameworks/InternationalSupport.framework/Versions/A/InternationalSupport + 0x196a10000 - 0x196a61d1f com.apple.datadetectorscore (8.0 - 821.7) /System/Library/PrivateFrameworks/DataDetectorsCore.framework/Versions/A/DataDetectorsCore + 0x196a62000 - 0x196ad28df com.apple.UserActivity (551 - 551) <0D6F3043-5372-3B15-96BC-6F45AD86F410> /System/Library/PrivateFrameworks/UserActivity.framework/Versions/A/UserActivity + 0x196ad3000 - 0x197573e7f com.apple.MediaToolbox (1.0 - 3330.13.2) /System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox + 0x197574000 - 0x1975e286f libusrtcp.dylib (5812.160.9) /usr/lib/libusrtcp.dylib + 0x1975e3000 - 0x197b857ff libswiftCore.dylib (6.3.2 - 6.3.2.1.11) <83794FB3-DE9B-3D23-AB5E-2C1D5D30F134> /usr/lib/swift/libswiftCore.dylib + 0x197bff000 - 0x197c3801f com.apple.locationsupport (3077.0.4 - 3077.0.4) /System/Library/PrivateFrameworks/LocationSupport.framework/Versions/A/LocationSupport + 0x197c39000 - 0x197c8e31f libSessionUtility.dylib (398.701) <1A63E9E1-2D64-3AF4-9CCD-6EF042397F84> /System/Library/PrivateFrameworks/AudioSession.framework/libSessionUtility.dylib + 0x197c8f000 - 0x197e6405f com.apple.audio.toolbox.AudioToolbox (1.14 - 1.14) /System/Library/Frameworks/AudioToolbox.framework/Versions/A/AudioToolbox + 0x197e65000 - 0x197ee5b7f com.apple.audio.AudioSession (1.0 - 398.701) /System/Library/PrivateFrameworks/AudioSession.framework/Versions/A/AudioSession + 0x197ee6000 - 0x197eff39f libAudioStatistics.dylib (262.601) <3FF99846-E48C-3C9A-814C-35B45E5F60EC> /usr/lib/libAudioStatistics.dylib + 0x197f00000 - 0x197f2d93f com.apple.speech.synthesis.framework (9.2.22 - 9.2.22) <9CDA611B-254A-3779-9356-369485134C2D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/SpeechSynthesis.framework/Versions/A/SpeechSynthesis + 0x197f2e000 - 0x197f7be5f com.apple.ApplicationServices.ATS (377 - 593.6.0.3) /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ATS.framework/Versions/A/ATS + 0x197f7c000 - 0x197f9819b libresolv.9.dylib (96) <4AB71911-9300-30D4-88CF-D20EFD75ACE6> /usr/lib/libresolv.9.dylib + 0x197f99000 - 0x197fab7e7 libsasl2.2.dylib (215) <7CF2A32E-72DD-34F7-B179-A17ED3D7DD75> /usr/lib/libsasl2.2.dylib + 0x198091000 - 0x19819387f com.apple.CoreMediaIO (1000.0 - 5617.100.5) <1035C1AB-5058-3AFA-8D77-514901516251> /System/Library/Frameworks/CoreMediaIO.framework/Versions/A/CoreMediaIO + 0x198194000 - 0x198274337 libSMC.dylib (38) <655F6374-6CE8-3D0E-994E-4D7C37F78E89> /usr/lib/libSMC.dylib + 0x198275000 - 0x1982d3c9f libcups.2.dylib (522.8) <6A5A8E21-A9E6-32A2-9BDB-8013F002AEF8> /usr/lib/libcups.2.dylib + 0x1982d4000 - 0x1982e1257 com.apple.NetAuth (6.2 - 6.2) <024DBF34-DF66-3164-825E-F77F85462E66> /System/Library/PrivateFrameworks/NetAuth.framework/Versions/A/NetAuth + 0x1982e2000 - 0x1982e6dcb com.apple.ColorSyncLegacy (4.13.0 - 1) /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ColorSyncLegacy.framework/Versions/A/ColorSyncLegacy + 0x1982e7000 - 0x1982efdef com.apple.QD (4.0 - 451) <59BBF27B-1D89-3D35-9210-8386EFA15A8D> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/QD + 0x1982f0000 - 0x1982fdb1f com.apple.perfdata (1.0 - 130) /System/Library/PrivateFrameworks/perfdata.framework/Versions/A/perfdata + 0x1982fe000 - 0x19830bc9f libperfcheck.dylib (46) <912BFF10-FB8F-3D52-9941-ACDCE1CAE36A> /usr/lib/libperfcheck.dylib + 0x19830c000 - 0x19831d187 com.apple.Kerberos (3.0 - 1) /System/Library/Frameworks/Kerberos.framework/Versions/A/Kerberos + 0x19831e000 - 0x19836faaf com.apple.GSS (4.0 - 2.0) <277D18EF-39E4-3F72-99E8-8D3DF65ED1D0> /System/Library/Frameworks/GSS.framework/Versions/A/GSS + 0x198370000 - 0x198380d6f com.apple.CommonAuth (4.0 - 2.0) <097F7235-CA53-3644-BB95-F6F912B4F2C7> /System/Library/PrivateFrameworks/CommonAuth.framework/Versions/A/CommonAuth + 0x198381000 - 0x19846505f com.apple.MobileAssets (1.0 - 1837.160.15) <91A461DE-C8E8-3868-B393-BA6E5A17DF2A> /System/Library/PrivateFrameworks/MobileAsset.framework/Versions/A/MobileAsset + 0x1984f8000 - 0x198507160 com.apple.CorePhoneNumbers (1.0 - 1) <79000980-1797-3115-B74B-60FA1E9C3C73> /System/Library/PrivateFrameworks/CorePhoneNumbers.framework/Versions/A/CorePhoneNumbers + 0x198508000 - 0x19859145f libTelephonyUtilDynamic.dylib (6392) <63A6BBA0-CD50-30F8-9CD2-81B59264EA13> /usr/lib/libTelephonyUtilDynamic.dylib + 0x199e30000 - 0x199ee7fff com.apple.Bluetooth (1.0 - 1) <1ABB6C50-A5DE-3744-8F07-8C3B5617B0B9> /System/Library/Frameworks/IOBluetooth.framework/Versions/A/IOBluetooth + 0x19a0e4000 - 0x19a1d7a36 com.apple.combine (1.0 - 3023) /System/Library/Frameworks/Combine.framework/Versions/A/Combine + 0x19a1d8000 - 0x19c037bdf com.apple.GeoServices (1.0 - 2031.26.4.23.6) <1DAFDDDA-BB7B-320E-BCFC-B7C22886D486> /System/Library/PrivateFrameworks/GeoServices.framework/Versions/A/GeoServices + 0x19c060000 - 0x19c063967 com.apple.speech.recognition.framework (6.0.5 - 6.0.5) /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/SpeechRecognition.framework/Versions/A/SpeechRecognition + 0x19c3f0000 - 0x19c412d9f com.apple.Accessibility (1.0 - 1) <22008BA9-C61B-3FAD-A1A0-F6A1CD220343> /System/Library/Frameworks/Accessibility.framework/Versions/A/Accessibility + 0x19c44d000 - 0x19c44d86f com.apple.Accelerate.vecLib (3.11 - vecLib 3.11) <8203944D-B53E-3D7E-A481-3C676CAE1B6A> /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/vecLib + 0x19c472000 - 0x19c47298f com.apple.CoreServices (1226 - 1226) <56AE2857-29E0-34E9-B2C3-EE8E951EEFC5> /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices + 0x19c6f1000 - 0x19c6f1417 com.apple.Accelerate (1.11 - Accelerate 1.11) <9171DD7D-3994-3963-9A28-BC163BF97DE6> /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate + 0x19c740000 - 0x19c75253f com.apple.MediaAccessibility (1.0 - 153) <74D313A5-4D99-35D1-A4C9-B76AB6457EF0> /System/Library/Frameworks/MediaAccessibility.framework/Versions/A/MediaAccessibility + 0x19c79a000 - 0x19ce33ebf com.apple.VN (9.5.4 - 9.5.4) <10F83439-3A9F-316B-992E-451A72876715> /System/Library/Frameworks/Vision.framework/Versions/A/Vision + 0x19ce34000 - 0x19ce343ef libswiftFoundation.dylib (2000) <14A11A94-6A52-3D24-9267-42EDAEEC5FDD> /usr/lib/swift/libswiftFoundation.dylib + 0x19d4ed000 - 0x19d5f70ff com.apple.CoreBluetooth (196.5) <515FDCCC-535A-398B-BBD3-3D35565F5423> /System/Library/Frameworks/CoreBluetooth.framework/Versions/A/CoreBluetooth + 0x19d5f8000 - 0x19d6075df com.apple.SymptomDiagnosticReporter (1.0 - 411.160.2) <737479F2-7B20-3DB6-B9F4-0DAA1B73E9D0> /System/Library/PrivateFrameworks/SymptomDiagnosticReporter.framework/Versions/A/SymptomDiagnosticReporter + 0x19d608000 - 0x19d63625f com.apple.PowerLog (1.0 - 1) /System/Library/PrivateFrameworks/PowerLog.framework/Versions/A/PowerLog + 0x19d644000 - 0x19d6f56df com.apple.DiscRecording (9.0.3 - 9030.4.5) /System/Library/Frameworks/DiscRecording.framework/Versions/A/DiscRecording + 0x19d6f6000 - 0x19d727137 com.apple.MediaKit (16 - 938) <46DD93AF-BACD-309B-AD51-9CC47C78CA2C> /System/Library/PrivateFrameworks/MediaKit.framework/Versions/A/MediaKit + 0x19d80b000 - 0x19d81723f com.apple.CoreAUC (620.1 - 620.1) <9ACFCA55-82CB-33DB-AD00-443576099FDB> /System/Library/PrivateFrameworks/CoreAUC.framework/Versions/A/CoreAUC + 0x19d818000 - 0x19d81b77b com.apple.Mangrove (1.0 - 25) <87F549F4-73CC-302B-ABDB-D3CCFADABFA9> /System/Library/PrivateFrameworks/Mangrove.framework/Versions/A/Mangrove + 0x19d81c000 - 0x19d849f87 com.apple.CoreAVCHD (6.0.0 - 6244.1) /System/Library/PrivateFrameworks/CoreAVCHD.framework/Versions/A/CoreAVCHD + 0x19dfe5000 - 0x19e1ccabf com.apple.CoreTelephony (113 - 13193) <5F090F48-E481-3737-8E75-362E5D274879> /System/Library/Frameworks/CoreTelephony.framework/Versions/A/CoreTelephony + 0x19e1e9000 - 0x19e1ff950 libswiftDispatch.dylib (1542.160.2) /usr/lib/swift/libswiftDispatch.dylib + 0x19e200000 - 0x19e46f1bf com.apple.AVFCore (1.0 - 2430.13.1) <067E2603-4FEA-3CA5-8926-45F60681EDDB> /System/Library/PrivateFrameworks/AVFCore.framework/Versions/A/AVFCore + 0x19e470000 - 0x19e5493bf com.apple.FrontBoardServices (1000.4.12 - 1000.4.12) /System/Library/PrivateFrameworks/FrontBoardServices.framework/Versions/A/FrontBoardServices + 0x19e54a000 - 0x19e5d6c3f com.apple.BoardServices (1.0 - 732.1.1) /System/Library/PrivateFrameworks/BoardServices.framework/Versions/A/BoardServices + 0x19e617000 - 0x19e62453f com.apple.GraphicsServices (1.0 - 1.0) <757FEDFF-841C-3D62-B703-CDE79E929363> /System/Library/PrivateFrameworks/GraphicsServices.framework/Versions/A/GraphicsServices + 0x19e629000 - 0x19e6aa17f com.apple.CryptoTokenKit (1.0 - 1) <714063A8-D81E-3B22-9B36-88948A979E7F> /System/Library/Frameworks/CryptoTokenKit.framework/Versions/A/CryptoTokenKit + 0x19e941000 - 0x19e962e9f com.apple.DebugSymbols (216 - 217) <7C923545-F3BB-3215-9720-85196358D9F1> /System/Library/PrivateFrameworks/DebugSymbols.framework/Versions/A/DebugSymbols + 0x19e963000 - 0x19eabe49f com.apple.CoreSymbolication (16.0 - 64575.55.1) <59136324-34E6-3367-92BB-659346907A04> /System/Library/PrivateFrameworks/CoreSymbolication.framework/Versions/A/CoreSymbolication + 0x19eabf000 - 0x19eac909f com.apple.CoreTime (334.0.16.3 - 334.0.16.3) /System/Library/PrivateFrameworks/CoreTime.framework/Versions/A/CoreTime + 0x19eaca000 - 0x19ebbdaff com.apple.Rapport (7.1 - 715.2) /System/Library/PrivateFrameworks/Rapport.framework/Versions/A/Rapport + 0x19ff57000 - 0x1a041883f com.apple.CoreWiFi (1.0 - 1006.2) <7C50137B-2ABD-3819-B033-AE65B05A6085> /System/Library/PrivateFrameworks/CoreWiFi.framework/Versions/A/CoreWiFi + 0x1a0419000 - 0x1a048b7ff com.apple.BackBoardServices (1.0 - 1.0) /System/Library/PrivateFrameworks/BackBoardServices.framework/Versions/A/BackBoardServices + 0x1a048c000 - 0x1a04c74ef com.apple.LDAPFramework (2.4.28 - 194.5) <8CABDD64-E6C6-3B77-B839-2E2B875CE0FE> /System/Library/Frameworks/LDAP.framework/Versions/A/LDAP + 0x1a04c8000 - 0x1a04c97b7 com.apple.TrustEvaluationAgent (2.0 - 38) <96C0BAAA-7FE6-3277-AFBC-31926F5935EE> /System/Library/PrivateFrameworks/TrustEvaluationAgent.framework/Versions/A/TrustEvaluationAgent + 0x1a05f7000 - 0x1a06b059f com.apple.DiskImagesFramework (683.160.3 - 683.160.3) /System/Library/PrivateFrameworks/DiskImages.framework/Versions/A/DiskImages + 0x1a06f1000 - 0x1a070759f com.apple.RemoteServiceDiscovery (1.0 - 219.160.4) <823F3D1A-65F1-3CC5-96B1-750263B8DB36> /System/Library/PrivateFrameworks/RemoteServiceDiscovery.framework/Versions/A/RemoteServiceDiscovery + 0x1a0708000 - 0x1a071dc3f com.apple.xpc.RemoteXPC (1.0 - 3102.160.5) <885F9C72-1018-368B-AD36-E8A42E87FD91> /System/Library/PrivateFrameworks/RemoteXPC.framework/Versions/A/RemoteXPC + 0x1a07b2000 - 0x1a0832f4b libcurl.4.dylib (168) <2E99AD96-DC1C-3643-9988-273AB6844EFC> /usr/lib/libcurl.4.dylib + 0x1a0839000 - 0x1a087c25f com.apple.AppSupport (1.0.0 - 29) <61B2B917-D14A-38AD-A439-16E1C635441A> /System/Library/PrivateFrameworks/AppSupport.framework/Versions/A/AppSupport + 0x1a0b4c000 - 0x1a0b4c927 com.apple.ApplicationServices (48 - 66) <086CBEED-2F64-3E75-AB99-8C8C0E0A2F1C> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices + 0x1a0b4d000 - 0x1a0b4ff3f com.apple.InternationalTextSearch (1.0 - 1) <858910C5-1D4A-37B7-BF0E-EE02E24A2ACD> /System/Library/PrivateFrameworks/InternationalTextSearch.framework/Versions/A/InternationalTextSearch + 0x1a111c000 - 0x1a111fd3f com.apple.security.CryptoKit-C-Bridging (1.0 - 1) /System/Library/PrivateFrameworks/CryptoKitCBridging.framework/Versions/A/CryptoKitCBridging + 0x1a1120000 - 0x1a11203f7 libHeimdalProxy.dylib (88) <0CB2E7E3-E96F-343B-A4E7-545E74AF0255> /System/Library/Frameworks/Kerberos.framework/Versions/A/Libraries/libHeimdalProxy.dylib + 0x1a1121000 - 0x1a1121512 com.apple.audio.units.AudioUnit (1.14 - 1.14) <093EF25B-5305-3611-B068-E65071858F52> /System/Library/Frameworks/AudioUnit.framework/Versions/A/AudioUnit + 0x1a114b000 - 0x1a1169b9f com.apple.StreamingZip (1.0 - 1) <1F2EDC7B-8F28-3721-8A60-F6E1BCFC29A3> /System/Library/PrivateFrameworks/StreamingZip.framework/Versions/A/StreamingZip + 0x1a11c3000 - 0x1a11c66bf libswiftObjectiveC.dylib (951.7) <4FD234EA-2C18-3C25-8BD0-B1F4805C6675> /usr/lib/swift/libswiftObjectiveC.dylib + 0x1a11c7000 - 0x1a11e46ff libswiftos.dylib (1082) /usr/lib/swift/libswiftos.dylib + 0x1a12ab000 - 0x1a20fc73f com.apple.vision.EspressoFramework (1.0 - 3525.1.1) <8F2949A6-43A0-30A2-B5D2-945949C5AA01> /System/Library/PrivateFrameworks/Espresso.framework/Versions/A/Espresso + 0x1a20fd000 - 0x1a212d99f com.apple.ANEServices (9.512 - 9.512) /System/Library/PrivateFrameworks/ANEServices.framework/Versions/A/ANEServices + 0x1a212e000 - 0x1a21bc85f com.apple.proactive.support.ProactiveSupport (1.0 - 418.1) <73EE1A0A-0D29-3104-98CB-BEFEDA53F7C0> /System/Library/PrivateFrameworks/ProactiveSupport.framework/Versions/A/ProactiveSupport + 0x1a22b9000 - 0x1a22dd29f com.apple.ASEProcessing (1.55.0 - 1.55.0) <4D8F39C6-B221-3AF1-BB40-CAEB0A174D61> /System/Library/PrivateFrameworks/ASEProcessing.framework/Versions/A/ASEProcessing + 0x1a3381000 - 0x1a33ef13f com.apple.CoreML.AppleNeuralEngine (1.0 - 1) /System/Library/PrivateFrameworks/AppleNeuralEngine.framework/Versions/A/AppleNeuralEngine + 0x1a353d000 - 0x1a360bc1f com.apple.audio.midi.CoreMIDI (2.0 - 88) <52BD9E26-B356-3EAA-9AD7-7FF700C61A91> /System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI + 0x1a3730000 - 0x1a3730467 com.apple.Cocoa (6.11 - 24) /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa + 0x1a424e000 - 0x1a425275f com.apple.IOSurfaceAccelerator (1.0.0 - 1.0.0) <1E529C1A-B09C-3EB7-A286-CE00E292D561> /System/Library/PrivateFrameworks/IOSurfaceAccelerator.framework/Versions/A/IOSurfaceAccelerator + 0x1a7e3b000 - 0x1a807c8ff com.apple.AVFCapture (1.0 - 665.140.6) /System/Library/PrivateFrameworks/AVFCapture.framework/Versions/A/AVFCapture + 0x1a807d000 - 0x1a81add1f com.apple.Quagga (186 - 186) <1772C40D-6EF4-3F81-BA00-6EE8B05039A6> /System/Library/PrivateFrameworks/Quagga.framework/Versions/A/Quagga + 0x1a81ae000 - 0x1a87e96ff com.apple.CMCapture (1.0 - 665.140.6) <57A10B70-C3C9-34C6-8D22-F5118B63E2F0> /System/Library/PrivateFrameworks/CMCapture.framework/Versions/A/CMCapture + 0x1a87ea000 - 0x1a896d91f com.apple.RenderBox (7.4.25 - 7.4.25) <92090A92-DFAF-3EBC-886C-655EC158A53F> /System/Library/PrivateFrameworks/RenderBox.framework/Versions/A/RenderBox + 0x1a951e000 - 0x1a95319ff com.apple.HID (1.0 - 1) /System/Library/PrivateFrameworks/HID.framework/Versions/A/HID + 0x1aa422000 - 0x1aa42317f com.apple.PhoneNumbers (1.0 - 1) <8D0ECDD1-24B6-3B8D-9CF9-CDC55FC64490> /System/Library/PrivateFrameworks/PhoneNumbers.framework/Versions/A/PhoneNumbers + 0x1aa42e000 - 0x1aa539a3f com.apple.accessibility.AXCoreUtilities (1.0 - 1) /System/Library/PrivateFrameworks/AXCoreUtilities.framework/Versions/A/AXCoreUtilities + 0x1aa53a000 - 0x1aa5707ff libAccessibility.dylib (3191.39) <5240B3A0-D035-345E-A636-BC3A92C847C4> /usr/lib/libAccessibility.dylib + 0x1ae113000 - 0x1ae12229f com.apple.NetFS (6.0 - 4.0) <49121861-2603-3B0A-B664-BAD9E729BE5D> /System/Library/Frameworks/NetFS.framework/Versions/A/NetFS + 0x1ae66e000 - 0x1ae66e357 libswiftCoreGraphics.dylib (17) /usr/lib/swift/libswiftCoreGraphics.dylib + 0x1ae66f000 - 0x1ae671707 libswiftDarwin.dylib (377.160.5) <1DB56DA9-CF6B-3023-ABDF-5A37CB79223C> /usr/lib/swift/libswiftDarwin.dylib + 0x1b025c000 - 0x1b05f629f com.apple.coremotion (3077.0.4 - 3077.0.4) <2E109991-45C6-3783-8A36-B6A8070AAD67> /System/Library/Frameworks/CoreMotion.framework/Versions/A/CoreMotion + 0x1b065e000 - 0x1b065e5a7 com.apple.avfoundation (2.0 - 2430.13.1) <816EC446-7C41-3A2F-A582-7CB856797C09> /System/Library/Frameworks/AVFoundation.framework/Versions/A/AVFoundation + 0x1b0792000 - 0x1b087b4df libquic.dylib (5812.160.9) <5E89267F-C684-348D-8356-F9DAD8B4CB13> /usr/lib/libquic.dylib + 0x1b0887000 - 0x1b08afedf com.apple.private.SystemPolicy (1.0 - 1) <6108A12D-286B-3CF2-B848-B0E7A0189DCC> /System/Library/PrivateFrameworks/SystemPolicy.framework/Versions/A/SystemPolicy + 0x1b142c000 - 0x1b14a585f com.apple.NaturalLanguage (1.0 - 114) <0C005C4D-CA12-389C-9CCE-C4ED05B187E8> /System/Library/Frameworks/NaturalLanguage.framework/Versions/A/NaturalLanguage + 0x1b176c000 - 0x1b17fabff com.apple.LoggingSupport (1.0 - 1861.160.4) /System/Library/PrivateFrameworks/LoggingSupport.framework/Versions/A/LoggingSupport + 0x1b17fb000 - 0x1b1807acb com.apple.MallocStackLogging (1.0 - 65000) <566F2D7D-0F3B-3290-A739-7A40A151F0BE> /System/Library/PrivateFrameworks/MallocStackLogging.framework/Versions/A/MallocStackLogging + 0x1b182d000 - 0x1b1887d9f libmis.dylib (463.160.2) <173A632F-20F3-30C1-BC55-EFFE977BBB8E> /usr/lib/libmis.dylib + 0x1b1888000 - 0x1b188bc5f com.apple.gpusw.GPURawCounter (34 - 34) <03470B3A-A004-39A0-B6A4-F2A4AFFFCDD3> /System/Library/PrivateFrameworks/GPURawCounter.framework/Versions/A/GPURawCounter + 0x1b188c000 - 0x1b18ab87f libswiftCoreAudio.dylib (411.701) /usr/lib/swift/libswiftCoreAudio.dylib + 0x1b18ac000 - 0x1b18b2327 libswiftCoreFoundation.dylib (2411) <4975D13C-2AC5-3473-85C0-98054A81D7C6> /usr/lib/swift/libswiftCoreFoundation.dylib + 0x1b18bf000 - 0x1b190b843 libswiftXPC.dylib (128.120.2) <24AEDAC1-C1EE-30F4-8818-72EBF8969D0C> /usr/lib/swift/libswiftXPC.dylib + 0x1b190c000 - 0x1b190c7ff libswiftCoreImage.dylib (2.2) /usr/lib/swift/libswiftCoreImage.dylib + 0x1b190d000 - 0x1b190d8a3 libswiftIOKit.dylib (1) <06A92787-4440-3757-AF32-F2B331C753A2> /usr/lib/swift/libswiftIOKit.dylib + 0x1b227c000 - 0x1b22fab1f com.apple.TrialProto (1.0 - 474.2.18.2) /System/Library/PrivateFrameworks/TrialProto.framework/Versions/A/TrialProto + 0x1b22fb000 - 0x1b23a46ff com.apple.trial (1.0 - 474.2.18.2) <53B3126E-7B01-30DD-961A-510E9CFC3CF1> /System/Library/PrivateFrameworks/Trial.framework/Versions/A/Trial + 0x1b393f000 - 0x1b39782c7 libbootpolicy.dylib (289.160.2) /usr/lib/libbootpolicy.dylib + 0x1b4a23000 - 0x1b4a519df com.apple.skp.FeedbackLogger (1.0 - 1) /System/Library/PrivateFrameworks/FeedbackLogger.framework/Versions/A/FeedbackLogger + 0x1b5406000 - 0x1b541d88b libswiftsimd.dylib (23) /usr/lib/swift/libswiftsimd.dylib + 0x1b5694000 - 0x1b586b0ff com.apple.TextInput (1.0 - 1.0) <1D5DF9CA-41FC-3B7B-B19F-2C435F3A66F2> /System/Library/PrivateFrameworks/TextInput.framework/Versions/A/TextInput + 0x1b6af0000 - 0x1b6b2c2af libncurses.5.4.dylib (79) <9EB04E94-EE2D-38A5-A214-00AF73DBE4E9> /usr/lib/libncurses.5.4.dylib + 0x1b6b2d000 - 0x1b6b3637f com.apple.IOAccelMemoryInfo (1.0 - 1) /System/Library/PrivateFrameworks/IOAccelMemoryInfo.framework/Versions/A/IOAccelMemoryInfo + 0x1b72c4000 - 0x1b81bf55f com.apple.siri.SiriInstrumentation (1.0 - 1) <8A5E0FF6-3116-3082-A0AD-20DCD6C5E1B4> /System/Library/PrivateFrameworks/SiriInstrumentation.framework/Versions/A/SiriInstrumentation + 0x1b81fd000 - 0x1b82060be libswiftCoreMIDI.dylib (6) <7AE04E20-83FD-3B1B-8846-E9869AD98DB5> /usr/lib/swift/libswiftCoreMIDI.dylib + 0x1b89ee000 - 0x1b89f4bbf com.apple.MSUDataAccessor (1.0 - 1) /System/Library/PrivateFrameworks/MSUDataAccessor.framework/Versions/A/MSUDataAccessor + 0x1b8e17000 - 0x1b8e79ddf com.apple.SoftwareUpdateCoreSupport (1.0 - 1) <13271AA6-33EA-369B-B2D1-6EC528C820E7> /System/Library/PrivateFrameworks/SoftwareUpdateCoreSupport.framework/Versions/A/SoftwareUpdateCoreSupport + 0x1baa2a000 - 0x1baa6f0ff com.apple.AttributeGraph (7.0.80 - 7.0.80) /System/Library/PrivateFrameworks/AttributeGraph.framework/Versions/A/AttributeGraph + 0x1babe1000 - 0x1bb48769f libfaceCore.dylib (9.5.4) /System/Library/Frameworks/Vision.framework/libfaceCore.dylib + 0x1bb488000 - 0x1bb6fa59f com.apple.TextRecognition (1.0 - 157) /System/Library/PrivateFrameworks/TextRecognition.framework/Versions/A/TextRecognition + 0x1bb6fb000 - 0x1bb71225f com.apple.Futhark (1.0 - 1) /System/Library/PrivateFrameworks/Futhark.framework/Versions/A/Futhark + 0x1bbc53000 - 0x1bbc8cba7 com.apple.MobileBluetooth (1.0 - 1.0) /System/Library/PrivateFrameworks/MobileBluetooth.framework/Versions/A/MobileBluetooth + 0x1bc944000 - 0x1bc99b25f com.apple.biome.BiomeFoundation (1.0 - 209.21) <455A5553-E683-30B4-A906-1F14E75F6E61> /System/Library/PrivateFrameworks/BiomeFoundation.framework/Versions/A/BiomeFoundation + 0x1bf649000 - 0x1bf6c03df com.apple.acg.InertiaCam (1.0 - 1) /System/Library/PrivateFrameworks/InertiaCam.framework/Versions/A/InertiaCam + 0x1bf882000 - 0x1bf88fa1f libswiftMetal.dylib (373.7) <7235A6A9-49B2-3B94-9DD6-C987019CDBF2> /usr/lib/swift/libswiftMetal.dylib + 0x1bf890000 - 0x1bf896e05 libswiftCompression.dylib (11) <856ACB2A-3334-3BA6-AAC8-8F344E7CDB83> /usr/lib/swift/libswiftCompression.dylib + 0x1c07c9000 - 0x1c0873d7f libFDR.dylib (1499.160.2) /usr/lib/libFDR.dylib + 0x1c0874000 - 0x1c08f48ff com.apple.TimeSync (1.0 - 1460.2) /System/Library/PrivateFrameworks/TimeSync.framework/Versions/A/TimeSync + 0x1c08f5000 - 0x1c0ec033f com.apple.biome.BiomeStreams (1.0 - 209.21) <2110407D-EFB4-373E-B963-9C92E26594B2> /System/Library/PrivateFrameworks/BiomeStreams.framework/Versions/A/BiomeStreams + 0x1c1394000 - 0x1c13a88ff com.apple.SoftwareUpdateCoreConnect (1.0 - 1) <2CA857AF-D999-34DC-94A1-3AC0E5B80416> /System/Library/PrivateFrameworks/SoftwareUpdateCoreConnect.framework/Versions/A/SoftwareUpdateCoreConnect + 0x1c2538000 - 0x1c2541b5f com.apple.audio.IOKitten (300.1 - 300.1) <0BAB3589-8D81-3C60-9F05-9D207E89F4B6> /System/Library/PrivateFrameworks/IOKitten.framework/Versions/A/IOKitten + 0x1c37ec000 - 0x1c3856c1f com.apple.osanalytics.OSAnalytics (1.0 - 1) <06728C4D-5750-308F-8290-EAF7BE91F4BB> /System/Library/PrivateFrameworks/OSAnalytics.framework/Versions/A/OSAnalytics + 0x1c4ca8000 - 0x1c4ca9f9f libswiftQuartzCore.dylib (5) <63444A8C-9E8C-3778-820D-1E0C88CA2DF7> /usr/lib/swift/libswiftQuartzCore.dylib + 0x1c509b000 - 0x1c50a08e7 com.apple.kperf (1.0 - 1) /System/Library/PrivateFrameworks/kperf.framework/Versions/A/kperf + 0x1c51b2000 - 0x1c51b763f com.apple.MobileSystemServices (1.0 - 1) <4F3BEA3B-A363-3D04-B903-9B613C993CA1> /System/Library/PrivateFrameworks/MobileSystemServices.framework/Versions/A/MobileSystemServices + 0x1c67a4000 - 0x1c67c629f libamsupport.dylib (434.160.4) <24D28E7F-A1AE-3031-8679-A0D6C6D68A86> /usr/lib/libamsupport.dylib + 0x1c6ba2000 - 0x1c6c020ff com.apple.biome.BiomePubSub (1.0 - 209.21) <0F03104F-FC8B-3ADD-8850-4B7029E2B56E> /System/Library/PrivateFrameworks/BiomePubSub.framework/Versions/A/BiomePubSub + 0x1c6c03000 - 0x1c6c4619f com.apple.biome.BiomeStorage (1.0 - 209.21) /System/Library/PrivateFrameworks/BiomeStorage.framework/Versions/A/BiomeStorage + 0x1c9740000 - 0x1c974f13f libswiftUniformTypeIdentifiers.dylib (877.5.1) /usr/lib/swift/libswiftUniformTypeIdentifiers.dylib + 0x1c9750000 - 0x1c9815a9b libswiftAccelerate.dylib (77.100.2) <625F222D-6394-39B9-A1F2-12B9EA56DD85> /usr/lib/swift/libswiftAccelerate.dylib + 0x1c996e000 - 0x1c997f11f libpartition2_dynamic.dylib (3476.160.2) /usr/lib/libpartition2_dynamic.dylib + 0x1c9edd000 - 0x1c9ee7fdf com.apple.AFKUser (1.0 - 1) /System/Library/PrivateFrameworks/AFKUser.framework/Versions/A/AFKUser + 0x1cc395000 - 0x1cc39fb3f com.apple.CPMS (1.0 - 1) <3E83115F-D04B-3C8D-8646-35204AA2DB84> /System/Library/PrivateFrameworks/CPMS.framework/Versions/A/CPMS + 0x1cd8e1000 - 0x1cd94275f libswiftCoreMedia.dylib (3330.13.2) /usr/lib/swift/libswiftCoreMedia.dylib + 0x1cf094000 - 0x1cf095bbf libswiftOSLog.dylib (10) <9670AE5C-271A-3DCB-9A0A-8E3A7CCC2726> /usr/lib/swift/libswiftOSLog.dylib + 0x1cf32d000 - 0x1cf3755df libswiftAVFoundation.dylib (2430.13.1) /usr/lib/swift/libswiftAVFoundation.dylib + 0x1d31aa000 - 0x1d32b6d5f com.apple.Symbolication (16.0 - 64575.70.1) <724D42FC-F4FD-39C7-A1BF-D0AD086231F4> /System/Library/PrivateFrameworks/Symbolication.framework/Versions/A/Symbolication + 0x1d3981000 - 0x1d398a4d3 com.apple.framework.netrb (1.0 - 1) /System/Library/PrivateFrameworks/Netrb.framework/Versions/A/Netrb + 0x1d39bf000 - 0x1d39e3c1f com.apple.CoreMaterial (1.0 - 1) /System/Library/PrivateFrameworks/CoreMaterial.framework/Versions/A/CoreMaterial + 0x1d783e000 - 0x1d784e3df com.apple.OSLog (1.0 - 1861.160.4) <869F0693-0E82-38C1-8920-C782E71735CA> /System/Library/Frameworks/OSLog.framework/Versions/A/OSLog + 0x1d7b52000 - 0x1d7c6375f com.apple.InternalSwiftProtobuf (1.0 - 1.26.0) <41F66F01-A342-3091-A832-0B2B645C922B> /System/Library/PrivateFrameworks/InternalSwiftProtobuf.framework/Versions/A/InternalSwiftProtobuf + 0x1d7d29000 - 0x1d7d340bf com.apple.HIDDisplay (1.0 - 1) <0368EA7D-01B2-3AA9-A6D6-A2A0850AC800> /System/Library/PrivateFrameworks/HIDDisplay.framework/Versions/A/HIDDisplay + 0x1da95f000 - 0x1daa04cff com.apple.security.CryptoKit (1.0 - 1) /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit + 0x1ddc15000 - 0x1ddc32d9f libedit.3.dylib (65) /usr/lib/libedit.3.dylib + 0x1dfd5d000 - 0x1dfd5daf3 com.apple.FeatureFlags (1.0 - 103) /System/Library/PrivateFrameworks/FeatureFlags.framework/Versions/A/FeatureFlags + 0x1dfd69000 - 0x1dfd70cbf libswiftNaturalLanguage.dylib (4.3) <5E36265A-7670-3D39-A2B8-71DA0AA131CF> /usr/lib/swift/libswiftNaturalLanguage.dylib + 0x1dfde2000 - 0x1dfe33d3f com.apple.WiFiPeerToPeer (861.4.0 - 861.4) <15EEE715-2670-3288-AFA8-504BAD951B4F> /System/Library/PrivateFrameworks/WiFiPeerToPeer.framework/Versions/A/WiFiPeerToPeer + 0x1e8e52000 - 0x1e8e7424f libswiftSwiftOnoneSupport.dylib (6.3.2 - 6.3.2.1.11) <76A7FE10-AD26-3505-A8CC-D37AC1C24D32> /usr/lib/swift/libswiftSwiftOnoneSupport.dylib + 0x1e94b1000 - 0x1e94b35bf com.apple.ConfigProfileHelper (18.0 - 1800) <2D1B971F-6A7F-32D0-8B0F-F8FA3A13E8F1> /System/Library/PrivateFrameworks/ConfigProfileHelper.framework/Versions/A/ConfigProfileHelper + 0x226f5c000 - 0x2285d5067 com.apple.ANECompiler (9.509.0 - 9.509.0) <465A74BC-F20D-3C05-9441-7E08EAA49FAF> /System/Library/PrivateFrameworks/ANECompiler.framework/Versions/A/ANECompiler + 0x229bd3000 - 0x229bda2c3 libCoreFSCache.dylib (352.2) <2C410B78-B9A5-30DC-8D83-FFEC1277F34C> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreFSCache.dylib + 0x229bdb000 - 0x229be0947 libCoreVMClient.dylib (352.2) <07CB5D41-C2F3-3C33-951F-67B2C8B8B662> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCoreVMClient.dylib + 0x229be1000 - 0x229bf12b7 com.apple.opengl (23.1.1 - 23.1.1) /System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL + 0x229bf2000 - 0x229bf47bf libCVMSPluginSupport.dylib (23.1.1) /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libCVMSPluginSupport.dylib + 0x229bf5000 - 0x229bfd4ff libGFXShared.dylib (23.1.1) <6CEF3932-AAC9-3F8E-905D-A826F2884C9A> /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGFXShared.dylib + 0x229bfe000 - 0x229c3154b libGLImage.dylib (23.1.1) /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLImage.dylib + 0x229c32000 - 0x229c6b5bf libGLU.dylib (23.1.1) /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib + 0x229dc2000 - 0x229dcbbc7 libGL.dylib (23.1.1) /System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib + 0x22a14e000 - 0x22a2909ff com.apple.audio.AVFAudio (1.0 - 743.508) <016C5057-625C-30B1-AD32-7BC9D082F05B> /System/Library/Frameworks/AVFAudio.framework/Versions/A/AVFAudio + 0x22a292000 - 0x22a30cebf com.apple.AVRouting (1.0 - 1) /System/Library/Frameworks/AVRouting.framework/Versions/A/AVRouting + 0x22b5e2000 - 0x22b62afdf com.apple.CoreTransferable (1.0.1 - 1) <26865685-385E-3120-9886-2082EEC20B20> /System/Library/Frameworks/CoreTransferable.framework/Versions/A/CoreTransferable + 0x22bafd000 - 0x22bb0abff com.apple.DataDetection (8.0 - 821.7) <73F6F860-69AF-3162-86E0-A683642287D6> /System/Library/Frameworks/DataDetection.framework/Versions/A/DataDetection + 0x22bb14000 - 0x22bb2b75f com.apple.dt.DeveloperToolsSupport (23.40.26 - 23.40.26) /System/Library/Frameworks/DeveloperToolsSupport.framework/Versions/A/DeveloperToolsSupport + 0x22bc5e000 - 0x22bdd1f1f com.apple.ExtensionFoundation (97 - 97) <3D533C35-3A2A-3672-92EF-5FEE9EE739AC> /System/Library/Frameworks/ExtensionFoundation.framework/Versions/A/ExtensionFoundation + 0x22d045000 - 0x22d087a1f com.apple.LightweightCodeRequirements (1.0 - 1) <8EF56F82-8CCE-3811-AD16-6D0939187B45> /System/Library/Frameworks/LightweightCodeRequirements.framework/Versions/A/LightweightCodeRequirements + 0x22deab000 - 0x22dec91ff com.apple.MPSBenchmarkLoop (1.0 - 1) /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSBenchmarkLoop.framework/Versions/A/MPSBenchmarkLoop + 0x22deca000 - 0x22dedde7f com.apple.MPSFunctions (1.0 - 1) <3103E210-FF5C-3677-BDD3-59FF17A6ACEC> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSFunctions.framework/Versions/A/MPSFunctions + 0x22dede000 - 0x22dee353f com.apple.MPSHost (1.0 - 1) <31F90368-23A5-39BB-822B-C8470C4479AE> /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/Frameworks/MPSHost.framework/Versions/A/MPSHost + 0x22dee4000 - 0x22f21b83f com.apple.MetalPerformanceShadersGraph (6.5.1 - 6.5.1) <7401E849-7B2E-39A9-99D3-5CB0A6BBDFFE> /System/Library/Frameworks/MetalPerformanceShadersGraph.framework/Versions/A/MetalPerformanceShadersGraph + 0x23118e000 - 0x2312fa4bf com.apple.SwiftData (1.0 - 135) <9F52706C-75BD-34AF-A29E-C26608124ACC> /System/Library/Frameworks/SwiftData.framework/Versions/A/SwiftData + 0x2312fb000 - 0x23225791f com.apple.SwiftUICore (7.6.1 - 7.6.1) <9EB0840F-B045-3529-9467-1470A3C6CA02> /System/Library/Frameworks/SwiftUICore.framework/Versions/A/SwiftUICore + 0x232258000 - 0x23226be3f com.apple.Symbols (1.0 - 190.4.0.1) <028E944B-66C4-39E2-A436-FB93FB6CED4E> /System/Library/Frameworks/Symbols.framework/Versions/A/Symbols + 0x232272000 - 0x2323b0b5f com.apple.DataFrame (1.0 - 52) /System/Library/Frameworks/TabularData.framework/Versions/A/TabularData + 0x2331ec000 - 0x233216b5f com.apple.CoreLocation.LocationEssentials (1.0 - 1) /System/Library/Frameworks/_LocationEssentials.framework/Versions/A/_LocationEssentials + 0x237e53000 - 0x237e67abf com.apple.AppleDeviceQuerySupport (1.0 - 408.120.3) <9594FBFB-D49D-3DF6-8820-564633EAEC2B> /System/Library/PrivateFrameworks/AppleDeviceQuerySupport.framework/Versions/A/AppleDeviceQuerySupport + 0x237eba000 - 0x237ed509f com.apple.siri.flatbuffer.AppleFlatBuffers (1) /System/Library/PrivateFrameworks/AppleFlatBuffers.framework/Versions/A/AppleFlatBuffers + 0x238290000 - 0x23831f5df com.apple.proactive.AppleIntelligenceReporting (1.0 - 1) <05EC9C98-7211-39C9-B376-796F37327801> /System/Library/PrivateFrameworks/AppleIntelligenceReporting.framework/Versions/A/AppleIntelligenceReporting + 0x238416000 - 0x2385e4b67 com.apple.cmphoto.AppleJPEGXL (1.0 - 1) <71A0C0AD-67F3-36F9-BF73-6DD5D7424AF7> /System/Library/PrivateFrameworks/AppleJPEGXL.framework/Versions/A/AppleJPEGXL + 0x2385e5000 - 0x238660b46 com.apple.AppleKeyStore (1.0 - 1.0) /System/Library/PrivateFrameworks/AppleKeyStore.framework/Versions/A/AppleKeyStore + 0x238f8d000 - 0x238fa59df com.apple.private.AppleMobileFileIntegrity-fmk (1.0 - 1) /System/Library/PrivateFrameworks/AppleMobileFileIntegrity.framework/Versions/A/AppleMobileFileIntegrity + 0x23913d000 - 0x2391b8ea7 com.apple.ArgumentParserInternal (1.0 - 1.20.2) <841D5662-2CB9-3A27-ADA7-E33AC5E45199> /System/Library/PrivateFrameworks/ArgumentParserInternal.framework/Versions/A/ArgumentParserInternal + 0x23949f000 - 0x2394b1799 com.apple.AtomicsInternal (1.1.0 - 5026.6.1) <1617DBB1-2BFF-3619-903C-2FBB31348FB6> /System/Library/PrivateFrameworks/AtomicsInternal.framework/Versions/A/AtomicsInternal + 0x23956e000 - 0x239590dff com.apple.imgaudio.AudioAnalytics (1.0 - 1) /System/Library/PrivateFrameworks/AudioAnalytics.framework/Versions/A/AudioAnalytics + 0x239bd4000 - 0x239c1c85f com.apple.BackBoardHIDEventFoundation (1.0 - 1) <7F763DF9-EA7F-3938-B599-DCCF4605E610> /System/Library/PrivateFrameworks/BackBoardHIDEventFoundation.framework/Versions/A/BackBoardHIDEventFoundation + 0x239cb5000 - 0x239cce8df com.apple.biome.BiomeDSL (1.0 - 209.21) /System/Library/PrivateFrameworks/BiomeDSL.framework/Versions/A/BiomeDSL + 0x239ccf000 - 0x23a589f7f com.apple.BiomeLibrary (274.60) <07CF779F-8F51-3764-B486-23D76868FF91> /System/Library/PrivateFrameworks/BiomeLibrary.framework/Versions/A/BiomeLibrary + 0x23a58a000 - 0x23a58fb5f com.apple.biome.BiomeSync (1.0 - 209.21) <2362E209-EC61-3FFC-9486-1244BB29BE82> /System/Library/PrivateFrameworks/BiomeSync.framework/Versions/A/BiomeSync + 0x23afe2000 - 0x23afe412f com.apple.CMCaptureDevice (665.140.6) /System/Library/PrivateFrameworks/CMCaptureDevice.framework/Versions/A/CMCaptureDevice + 0x23b0a7000 - 0x23b2af49f com.apple.CMImaging (1.0 - 665.140.6) <3B782AC2-00C4-3534-91D2-5C7242B32440> /System/Library/PrivateFrameworks/CMImaging.framework/Versions/A/CMImaging + 0x23b2b0000 - 0x23b47a67f com.apple.CMPhoto (1.0 - 1) <214294AE-C7B7-3C9A-A4F8-201C989F9779> /System/Library/PrivateFrameworks/CMPhoto.framework/Versions/A/CMPhoto + 0x23bbc0000 - 0x23bc588bf com.apple.biome.CascadeSets (1.0 - 209.21) <2091B02D-8D55-3DC4-8097-60C193D03C85> /System/Library/PrivateFrameworks/CascadeSets.framework/Versions/A/CascadeSets + 0x23bc74000 - 0x23bc79cff com.apple.Centauri (1.0 - 1) <3854272C-7B14-3A3C-9BB1-F0FBA394708A> /System/Library/PrivateFrameworks/Centauri.framework/Versions/A/Centauri + 0x23c16b000 - 0x23c1a359f com.apple.CinematicFraming (1.0 - 665.140.6) /System/Library/PrivateFrameworks/CinematicFraming.framework/Versions/A/CinematicFraming + 0x23cfee000 - 0x23d0128ff com.apple.CollectionViewCore (1.0 - 1) <4840B78C-D96B-35B9-85C7-E5889C44A7C4> /System/Library/PrivateFrameworks/CollectionViewCore.framework/Versions/A/CollectionViewCore + 0x23d013000 - 0x23d1514ee com.apple.CollectionsInternal (1.2.0 - 5026.6.1) <6098453F-4D7E-38B4-8ADC-02C9FF51E14A> /System/Library/PrivateFrameworks/CollectionsInternal.framework/Versions/A/CollectionsInternal + 0x23f40e000 - 0x23f4abfff com.apple.audio.coreaudio.Stravinsky (1.0 - 1) <7CC0621B-3B88-3533-A3FB-52E6214486EE> /System/Library/PrivateFrameworks/CoreAudioOrchestration.framework/Versions/A/CoreAudioOrchestration + 0x2432b8000 - 0x2433be25f com.apple.CoreSceneUnderstanding (1.74.0 - 1.74.0) /System/Library/PrivateFrameworks/CoreSceneUnderstanding.framework/Versions/A/CoreSceneUnderstanding + 0x24361a000 - 0x2436420bf com.apple.CoreUtilsExtras (1.0 - 1) <3DD8C4CA-23E9-35CF-AD67-549DD72D3344> /System/Library/PrivateFrameworks/CoreUtilsExtras.framework/Versions/A/CoreUtilsExtras + 0x244199000 - 0x244208e7f com.apple.aiml.dendrite.Dendrite (1.0 - 1) <0A1C4D11-C108-35E9-A921-86ED86CF7446> /System/Library/PrivateFrameworks/Dendrite.framework/Versions/A/Dendrite + 0x244209000 - 0x244386aff com.apple.DesignLibrary (7.5.2 - 7.5.2) <2BC041DF-695A-32EE-B164-1C35C2AFFC53> /System/Library/PrivateFrameworks/DesignLibrary.framework/Versions/A/DesignLibrary + 0x244729000 - 0x24473c49f com.apple.DeviceRecovery (1.0 - 1) <2FA711C7-F764-363A-BF03-295E0DA88B79> /System/Library/PrivateFrameworks/DeviceRecovery.framework/Versions/A/DeviceRecovery + 0x244b61000 - 0x244b7767f com.apple.DistributedSensing (1.0 - 1) <9B3D4CA3-7BCF-36C9-AA99-27BDFE7854CD> /System/Library/PrivateFrameworks/DistributedSensing.framework/Versions/A/DistributedSensing + 0x246800000 - 0x24685353f com.apple.UIKit.FocusEngine (9126.6.8) /System/Library/PrivateFrameworks/FocusEngine.framework/Versions/A/FocusEngine + 0x2469ce000 - 0x2469d12ff com.apple.FontServices (1.0 - 1) /System/Library/PrivateFrameworks/FontServices.framework/Versions/A/FontServices + 0x2469d2000 - 0x246ac1e2f libXTFontStaticRegistryData.dylib (335.4.0.6) /System/Library/PrivateFrameworks/FontServices.framework/libXTFontStaticRegistryData.dylib + 0x246ac3000 - 0x246acfc9f com.apple.FramePacing (1.0 - 1) <1FDD3B19-C04A-3EE7-B7DF-E1F89954A696> /System/Library/PrivateFrameworks/FramePacing.framework/Versions/A/FramePacing + 0x246ad0000 - 0x246b9139f com.apple.FrontBoard (1000.4.12 - 1000.4.12) <7CDC68D4-0845-3053-AB80-C5A1F354060F> /System/Library/PrivateFrameworks/FrontBoard.framework/Versions/A/FrontBoard + 0x247bf1000 - 0x247bf6f07 libGPUCompilerUtils.dylib (32023.886.1) /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libGPUCompilerUtils.dylib + 0x24c064000 - 0x24c0a3797 libllvm-flatbuffers.dylib (32023.886.1) <526C249F-FF2E-3DC4-A639-B41A032E8CCE> /System/Library/PrivateFrameworks/GPUCompiler.framework/Versions/32023/Libraries/libllvm-flatbuffers.dylib + 0x24f7d7000 - 0x24f8247a6 com.apple.GenerativeFunctions.GenerativeFunctions (1.0 - 222.46) <418985BB-52A3-34D4-8379-40DC4C63AA32> /System/Library/PrivateFrameworks/GenerativeFunctions.framework/Versions/A/GenerativeFunctions + 0x24f825000 - 0x24f8ba87f com.apple.GenerativeFunctions.GenerativeFunctionsFoundation (1.0 - 222.46) <63FD423F-836C-3034-BA48-100AE09A9140> /System/Library/PrivateFrameworks/GenerativeFunctionsFoundation.framework/Versions/A/GenerativeFunctionsFoundation + 0x24f8bb000 - 0x24f92097f com.apple.GenerativeFunctions.GenerativeFunctionsInstrumentation (1.0 - 222.46) /System/Library/PrivateFrameworks/GenerativeFunctionsInstrumentation.framework/Versions/A/GenerativeFunctionsInstrumentation + 0x24f921000 - 0x24fa02f5f com.apple.GenerativeFunctions.GenerativeModels (1.0 - 222.46) <0E502870-00F4-35D4-AF82-E7059244798E> /System/Library/PrivateFrameworks/GenerativeModels.framework/Versions/A/GenerativeModels + 0x24fa03000 - 0x24fa78b9f com.apple.GenerativeFunctions.GenerativeModelsFoundation (1.0 - 222.46) /System/Library/PrivateFrameworks/GenerativeModelsFoundation.framework/Versions/A/GenerativeModelsFoundation + 0x24fc1e000 - 0x24fc2333f com.apple.GeoServices (1.0 - 2031.26.4.23.6) <38EE3C42-06D6-3A46-A420-DF701A4EA911> /System/Library/PrivateFrameworks/GeoServicesCore.framework/Versions/A/GeoServicesCore + 0x24fdcc000 - 0x24fe8fe9f com.apple.Gestures (9126.1.5 - 9126.1.5) /System/Library/PrivateFrameworks/Gestures.framework/Versions/A/Gestures + 0x252ef5000 - 0x252f5631f com.apple.IO80211 (1.0 - 1) <236517AD-8D16-3E62-8603-EBE7B65ACACA> /System/Library/PrivateFrameworks/IO80211.framework/Versions/A/IO80211 + 0x252f60000 - 0x252f6a83f com.apple.IPConfiguration (1.21 - 1.21) <6661265C-7B78-3158-9011-4BFDFFEF7807> /System/Library/PrivateFrameworks/IPConfiguration.framework/Versions/A/IPConfiguration + 0x252fa0000 - 0x253011adf com.apple.cocoa.IconRendering (1.0 - 92.3) <6A34A62A-16D4-34F0-B34B-2D96B53C20AD> /System/Library/PrivateFrameworks/IconRendering.framework/Versions/A/IconRendering + 0x2539e7000 - 0x253af02bf com.apple.InstalledContentLibrary (1.0 - 1.0) <1ACDAA8A-EB43-37C7-B661-39B1C0E05290> /System/Library/PrivateFrameworks/InstalledContentLibrary.framework/Versions/A/InstalledContentLibrary + 0x256414000 - 0x256be2d3f com.apple.IntelligencePlatformLibrary (274.60) /System/Library/PrivateFrameworks/IntelligencePlatformLibrary.framework/Versions/A/IntelligencePlatformLibrary + 0x256d90000 - 0x256dc7b5f com.apple.audio.CoreAudio.IsolatedCoreAudioClient (1.0 - 1) /System/Library/PrivateFrameworks/IsolatedCoreAudioClient.framework/Versions/A/IsolatedCoreAudioClient + 0x2583a6000 - 0x2583aa5df com.apple.CoreLocation.LocationLogEncryption (3077.0.4) <8A2C8C17-E138-3B34-8643-ED4FB1C9049E> /System/Library/PrivateFrameworks/LocationLogEncryption.framework/Versions/A/LocationLogEncryption + 0x2584c4000 - 0x258b5a923 com.apple.MIL (3520.4 - 3520.4.1) <97C5C585-F5EE-323A-B949-69EAE9080871> /System/Library/PrivateFrameworks/MIL.framework/Versions/A/MIL + 0x258b5b000 - 0x258bd803f com.apple.CoreML.MLAssetIO (1.0 - 3520.5.1) <6028DD46-8E5A-33F0-93B2-41FA480366CC> /System/Library/PrivateFrameworks/MLAssetIO.framework/Versions/A/MLAssetIO + 0x258bd9000 - 0x258c32b0f com.apple.mlcompiler.runtime (3404.3.1 - 3404.3.1) <22B4CD07-5C72-3CA4-9CD1-2C87686CDDE5> /System/Library/PrivateFrameworks/MLCompilerRuntime.framework/Versions/A/MLCompilerRuntime + 0x258c33000 - 0x258c4c827 com.apple.mlcompiler.services (3404.3.1 - 3404.3.1) /System/Library/PrivateFrameworks/MLCompilerServices.framework/Versions/A/MLCompilerServices + 0x25b8c0000 - 0x25b8d731f com.apple.ggml.ModelAsset (1.0 - 1) /System/Library/PrivateFrameworks/MLModelAsset.framework/Versions/A/MLModelAsset + 0x25d725000 - 0x25d786abf com.apple.MessageSecurity (1.0 - 195.160.36) /System/Library/PrivateFrameworks/MessageSecurity.framework/Versions/A/MessageSecurity + 0x25e6be000 - 0x25e95cb1f com.apple.ModelCatalog.ModelCatalog (1.0 - 233.41) /System/Library/PrivateFrameworks/ModelCatalog.framework/Versions/A/ModelCatalog + 0x25e9fd000 - 0x25eb957df com.apple.ModelManagerServices (1.0 - 1) <882BC08E-B1E1-3E52-AE8A-AC22A1BF2BE8> /System/Library/PrivateFrameworks/ModelManagerServices.framework/Versions/A/ModelManagerServices + 0x262519000 - 0x2627913df com.apple.mlpt.ODIE (1.0 - 1) /System/Library/PrivateFrameworks/ODIE.framework/Versions/A/ODIE + 0x262797000 - 0x2627c11bf com.apple.OSEligibility (319.160.17) <62740FDD-2B16-3319-B5C9-022D45C6B03A> /System/Library/PrivateFrameworks/OSEligibility.framework/Versions/A/OSEligibility + 0x263424000 - 0x26353c85f com.apple.ParsingInternal (0.0.1 - 5026.6.1) <11E757EC-72FB-3C53-8ED7-641428AB6169> /System/Library/PrivateFrameworks/ParsingInternal.framework/Versions/A/ParsingInternal + 0x2668d5000 - 0x2668f6e1f com.apple.accessibility.PhotosensitivityProcessing (1.0 - 1) /System/Library/PrivateFrameworks/PhotosensitivityProcessing.framework/Versions/A/PhotosensitivityProcessing + 0x266cea000 - 0x266d26a3c com.apple.PoirotSQLite (1.0 - 1) <24779350-BC29-3465-AAB3-F7CD0DA5844A> /System/Library/PrivateFrameworks/PoirotSQLite.framework/Versions/A/PoirotSQLite + 0x266d27000 - 0x266d9425f com.apple.PoirotSchematizer (1.0 - 1) <42CDC0E6-51BA-3804-BD3E-EDF87FC74034> /System/Library/PrivateFrameworks/PoirotSchematizer.framework/Versions/A/PoirotSchematizer + 0x266d95000 - 0x266dcabff com.apple.PoirotUDFs (1.0 - 1) /System/Library/PrivateFrameworks/PoirotUDFs.framework/Versions/A/PoirotUDFs + 0x267f63000 - 0x26809eddf com.apple.ProDisplayLibrary (10.6.1 - 10.6.1) /System/Library/PrivateFrameworks/ProDisplayLibrary.framework/Versions/A/ProDisplayLibrary + 0x268101000 - 0x26812e59f com.apple.intelligenceflow.ProactiveDaemonSupport (1.0 - 3525.11.14) <12245228-2B9A-3B24-8C5E-10111D68BE65> /System/Library/PrivateFrameworks/ProactiveDaemonSupport.framework/Versions/A/ProactiveDaemonSupport + 0x2686eb000 - 0x26889a95f com.apple.GenerativeFunctions.PromptKit (1.0 - 222.46) /System/Library/PrivateFrameworks/PromptKit.framework/Versions/A/PromptKit + 0x269059000 - 0x2690614df com.apple.ReflectionInternal (1.0.0 - 5026.6.1) <9A1279D4-575A-3E48-A460-A631A3F82D18> /System/Library/PrivateFrameworks/ReflectionInternal.framework/Versions/A/ReflectionInternal + 0x269fad000 - 0x269fc09d7 com.apple.RuntimeInternal (1.0.0 - 5026.6.1) <6D89CD71-A86D-3D78-A64B-96AB79550F79> /System/Library/PrivateFrameworks/RuntimeInternal.framework/Versions/A/RuntimeInternal + 0x26a192000 - 0x26a217a3f com.apple.SFSymbolsFramework (1 - 190.4.0.1) <968B5A5F-9749-3527-AF2A-66B599785308> /System/Library/PrivateFrameworks/SFSymbols.framework/Versions/A/SFSymbols + 0x26a218000 - 0x26a285abf com.apple.SILManager (53.19 - 53.19) <1B4C0154-843C-3CEE-9628-22978082DD2D> /System/Library/PrivateFrameworks/SILManager.framework/Versions/A/SILManager + 0x26b8c2000 - 0x26b9af0bf com.apple.SensitiveContentAnalysisML (1) <67C3B698-8279-30F1-9167-4730E6F41F5A> /System/Library/PrivateFrameworks/SensitiveContentAnalysisML.framework/Versions/A/SensitiveContentAnalysisML + 0x26bb92000 - 0x26bc248ff com.apple.SentencePieceInternal (57.3) <642E3357-AB6D-3039-A818-EDB5D6A189C2> /System/Library/PrivateFrameworks/SentencePieceInternal.framework/Versions/A/SentencePieceInternal + 0x26c6a8000 - 0x26c7c513f com.apple.siri.SiriAnalytics (1.0 - 1) <600E036E-9B18-35BE-B40B-E8D2D53AC90D> /System/Library/PrivateFrameworks/SiriAnalytics.framework/Versions/A/SiriAnalytics + 0x275733000 - 0x27575f4a1 com.apple.security.SwiftASN1Internal (1.0 - 1) /System/Library/PrivateFrameworks/SwiftASN1Internal.framework/Versions/A/SwiftASN1Internal + 0x2765a1000 - 0x2765fc6df com.apple.SystemStatus (1.0 - 1) /System/Library/PrivateFrameworks/SystemStatus.framework/Versions/A/SystemStatus + 0x278e71000 - 0x278ead1df com.apple.tightbeam (1.0 - 483.100.88) /System/Library/PrivateFrameworks/Tightbeam.framework/Versions/A/Tightbeam + 0x279376000 - 0x2795ad17f com.apple.TokenGeneration (1.0 - 1) /System/Library/PrivateFrameworks/TokenGeneration.framework/Versions/A/TokenGeneration + 0x2795ae000 - 0x27977bf3f com.apple.TokenGenerationCore (1.0 - 1) <3166486F-3F65-31DB-8018-779FFA32DC71> /System/Library/PrivateFrameworks/TokenGenerationCore.framework/Versions/A/TokenGenerationCore + 0x27afbf000 - 0x27b0f551f com.apple.UIIntelligenceSupport (1.0 - 1) /System/Library/PrivateFrameworks/UIIntelligenceSupport.framework/Versions/A/UIIntelligenceSupport + 0x27b69a000 - 0x27b75f4bf com.apple.UnifiedAssetFramework (1.0 - 1) <54A2CBB8-623D-3629-904A-D0399ED13547> /System/Library/PrivateFrameworks/UnifiedAssetFramework.framework/Versions/A/UnifiedAssetFramework + 0x27bf0c000 - 0x27bf0e1ff com.apple.UpdateCycle (1 - 1) <365E81B9-B9BA-3F4F-83FD-86A35CFBC8AC> /System/Library/PrivateFrameworks/UpdateCycle.framework/Versions/A/UpdateCycle + 0x27d5e9000 - 0x27d5ea2cf com.apple.VideoToolboxParavirtualizationSupport (64.4.7 - 64.4.7) <825E8416-E246-338E-A5CF-AA81A1B01DD9> /System/Library/PrivateFrameworks/VideoToolboxParavirtualizationSupport.framework/Versions/A/VideoToolboxParavirtualizationSupport + 0x27e1ff000 - 0x27e24b09f com.apple.VisionCore (9.5.4 - 9.5.4) <4E70B4ED-C8E0-3636-80E4-0939FE56BB63> /System/Library/PrivateFrameworks/VisionCore.framework/Versions/A/VisionCore + 0x280962000 - 0x2809821bf com.apple.WindowManagement (1.0 - 341.6.1) <6ECD36F7-0A2E-3631-A57F-FD0152173454> /System/Library/PrivateFrameworks/WindowManagement.framework/Versions/A/WindowManagement + 0x281b6b000 - 0x281b6f9ff com.apple.WritingTools (1.0 - 1) <84FB5635-42EE-3BB5-B7CD-8A354AD7DB0A> /System/Library/PrivateFrameworks/WritingTools.framework/Versions/A/WritingTools + 0x28773e000 - 0x28774189f com.apple.UIUtilities (9126.6.8) <183FD4D6-D766-34FC-B8E1-7C4D17435AC3> /System/Library/SubFrameworks/UIUtilities.framework/Versions/A/UIUtilities + 0x287821000 - 0x287823d5f libAXSafeCategoryBundle.dylib (3191.39) /usr/lib/libAXSafeCategoryBundle.dylib + 0x28785e000 - 0x2878f871f libAppleArchive.dylib (450.160.2) <9A8926C8-36A6-3DB4-A485-059C1F630984> /usr/lib/libAppleArchive.dylib + 0x2879b3000 - 0x2879bd45f libCoreEntitlements.dylib (80.100.6) /usr/lib/libCoreEntitlements.dylib + 0x287ca9000 - 0x287cb087f libReverseProxyDevice.dylib (104.120.2) <29367004-5D60-38DB-831F-9E5EE9364B21> /usr/lib/libReverseProxyDevice.dylib + 0x287cb1000 - 0x287cb8349 libRosetta.dylib (367.9) <0C7397C6-D747-31F2-8BC1-4096213BDE5C> /usr/lib/libRosetta.dylib + 0x287d1e000 - 0x287d1e35f libSpatial.dylib (108) <68B7C15F-537C-3FEF-838B-DC548772A45F> /usr/lib/libSpatial.dylib + 0x287d21000 - 0x287d2a3ff libTLE.dylib (80.100.6) <90E600A3-0A27-348A-AA57-D1DF4FB305E8> /usr/lib/libTLE.dylib + 0x288537000 - 0x288648b17 libcrypto.46.dylib (109.100.2) <46D13DA8-E7BD-37DC-91DD-D5E6CE00C2B8> /usr/lib/libcrypto.46.dylib + 0x288783000 - 0x28879f762 libhvf.dylib (11) <23C577A8-DB0B-3A0A-9058-1289483C262A> /usr/lib/libhvf.dylib + 0x288bd3000 - 0x288bdb28a libmrc.dylib (2881.160.4) <2B49C295-4EA2-3DE3-90B4-DC03A96F2657> /usr/lib/libmrc.dylib + 0x2890ba000 - 0x2890f2527 libssl.48.dylib (109.100.2) <07D5F4C6-1A13-344C-882B-0B0A08048DE5> /usr/lib/libssl.48.dylib + 0x28912d000 - 0x28915b36f libswiftPrespecialized.dylib (0) <9E3C7597-446F-3C50-9930-2425D9252C0C> /usr/lib/libswiftPrespecialized.dylib + 0x2893f7000 - 0x289409043 libswiftDistributed.dylib (6.3.2 - 6.3.2.1.11) <2EDB2E62-942F-3AB5-82AF-8E1328544E17> /usr/lib/swift/libswiftDistributed.dylib + 0x289411000 - 0x28941831f libswiftMLCompute.dylib (84) /usr/lib/swift/libswiftMLCompute.dylib + 0x289420000 - 0x28942febc libswiftObservation.dylib (6.3.2 - 6.3.2.1.11) /usr/lib/swift/libswiftObservation.dylib + 0x28943f000 - 0x28944b817 libswiftRegexBuilder.dylib (6.3.2 - 6.3.2.1.11) <82D79BDA-26A0-3A44-AAC8-911411801FDE> /usr/lib/swift/libswiftRegexBuilder.dylib + 0x2894e2000 - 0x28955872f libswiftSpatial.dylib (108) <713AEF7A-43B6-3735-8AB5-361F5EE06BBA> /usr/lib/swift/libswiftSpatial.dylib + 0x28955c000 - 0x28956f8ef libswiftSynchronization.dylib (6.3.2 - 6.3.2.1.11) /usr/lib/swift/libswiftSynchronization.dylib + 0x289570000 - 0x289588e60 libswiftSystem.dylib (75) <7CD9BDE7-F36B-3471-9295-38E181D6D9E5> /usr/lib/swift/libswiftSystem.dylib + 0x28958a000 - 0x2895a079f libswiftVideoToolbox.dylib (3330.13.2) <9247A5B6-A883-3A07-BEE7-A223840317A4> /usr/lib/swift/libswiftVideoToolbox.dylib + 0x2895a2000 - 0x2895a2669 libswift_Builtin_float.dylib (6.3.2.1.11) <52F59382-A6A6-3F55-8A85-D9FB822D370F> /usr/lib/swift/libswift_Builtin_float.dylib + 0x2895a3000 - 0x28962e095 libswift_Concurrency.dylib (6.3.2 - 6.3.2.1.11) <8E168857-47F4-349F-A718-A18DB144FCB0> /usr/lib/swift/libswift_Concurrency.dylib + 0x28962f000 - 0x289631f83 libswift_DarwinFoundation1.dylib (377.160.5) <85246B9A-A757-3F67-B792-3A2F7BB2BB25> /usr/lib/swift/libswift_DarwinFoundation1.dylib + 0x289632000 - 0x289632eeb libswift_DarwinFoundation2.dylib (377.160.5) /usr/lib/swift/libswift_DarwinFoundation2.dylib + 0x289633000 - 0x2896337a7 libswift_DarwinFoundation3.dylib (377.160.5) <8D2C31B5-FB10-3BF6-8566-F0DCD56C8582> /usr/lib/swift/libswift_DarwinFoundation3.dylib + 0x289634000 - 0x2896d249f libswift_RegexParser.dylib (6.3.2 - 6.3.2.1.11) <7B63C2BF-8C7C-3ECA-ACD9-F1B75DBE018C> /usr/lib/swift/libswift_RegexParser.dylib + 0x2896d3000 - 0x28975f0cd libswift_StringProcessing.dylib (6.3.2 - 6.3.2.1.11) <8DF0116D-DFC9-3906-9DF6-F1DBC47E324B> /usr/lib/swift/libswift_StringProcessing.dylib + 0x289768000 - 0x2897683d3 libswiftsys_time.dylib (377.160.5) <4B5C0268-23EB-3E20-8F57-CDECFB6E3205> /usr/lib/swift/libswiftsys_time.dylib + 0x2898c4000 - 0x2898c7a4b libsystem_darwindirectory.dylib (122) <971A4F65-493D-39F3-846D-0D33FA2769FD> /usr/lib/system/libsystem_darwindirectory.dylib + 0x2898c8000 - 0x2898d239b libsystem_eligibility.dylib (319.160.17) <750CA446-92EA-3A56-9A7B-CC0841686C50> /usr/lib/system/libsystem_eligibility.dylib + 0x2898d3000 - 0x2898da88b libsystem_sanitizers.dylib (26.1) /usr/lib/system/libsystem_sanitizers.dylib + 0x2898db000 - 0x2898dbbb7 libsystem_trial.dylib (474.2.18.2) <7194FF5B-A6C5-3D67-B00A-90209F10D603> /usr/lib/system/libsystem_trial.dylib diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/v8-matching-sdk-tests.log b/docs/graphics/evidence/2026-09-07-v8-integration/v8-matching-sdk-tests.log new file mode 100644 index 000000000..aac90df5a --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/v8-matching-sdk-tests.log @@ -0,0 +1,19 @@ +Test project /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled + Start 1: webscene_html_parser_tests +1/4 Test #1: webscene_html_parser_tests ....... Passed 0.01 sec + Start 2: webscene_css_parser_tests +2/4 Test #2: webscene_css_parser_tests ........ Passed 0.01 sec + Start 3: webscene_selector_parser_tests +3/4 Test #3: webscene_selector_parser_tests ... Passed 0.01 sec + Start 4: webscene_native_engine_tests +4/4 Test #4: webscene_native_engine_tests .....Subprocess terminated***Exception: 117.81 sec +webscene_native_engine_tests: binary invocation did not complete + + +75% tests passed, 1 tests failed out of 4 + +Total Test time (real) = 117.85 sec + +The following tests FAILED: + 4 - webscene_native_engine_tests (Subprocess terminated) +Errors while running CTest diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/v8-upstream-disabled-tests.log b/docs/graphics/evidence/2026-09-07-v8-integration/v8-upstream-disabled-tests.log new file mode 100644 index 000000000..8b8525ba0 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/v8-upstream-disabled-tests.log @@ -0,0 +1,13 @@ +Test project /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled + Start 1: webscene_html_parser_tests +1/4 Test #1: webscene_html_parser_tests ....... Passed 0.39 sec + Start 2: webscene_css_parser_tests +2/4 Test #2: webscene_css_parser_tests ........ Passed 0.00 sec + Start 3: webscene_selector_parser_tests +3/4 Test #3: webscene_selector_parser_tests ... Passed 0.00 sec + Start 4: webscene_native_engine_tests +4/4 Test #4: webscene_native_engine_tests ..... Passed 12.52 sec + +100% tests passed, 0 tests failed out of 4 + +Total Test time (real) = 12.92 sec diff --git a/docs/graphics/evidence/2026-09-07-v8-integration/v8-upstream-enabled-tests.log b/docs/graphics/evidence/2026-09-07-v8-integration/v8-upstream-enabled-tests.log new file mode 100644 index 000000000..f8b48abb4 --- /dev/null +++ b/docs/graphics/evidence/2026-09-07-v8-integration/v8-upstream-enabled-tests.log @@ -0,0 +1,19 @@ +Test project /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled + Start 1: webscene_html_parser_tests +1/4 Test #1: webscene_html_parser_tests ....... Passed 0.33 sec + Start 2: webscene_css_parser_tests +2/4 Test #2: webscene_css_parser_tests ........ Passed 0.01 sec + Start 3: webscene_selector_parser_tests +3/4 Test #3: webscene_selector_parser_tests ... Passed 0.01 sec + Start 4: webscene_native_engine_tests +4/4 Test #4: webscene_native_engine_tests .....***Timeout 60.02 sec +webscene_native_engine_tests: binary invocation did not complete + + +75% tests passed, 1 tests failed out of 4 + +Total Test time (real) = 60.37 sec + +The following tests FAILED: + 4 - webscene_native_engine_tests (Timeout) +Errors while running CTest diff --git a/docs/graphics/evidence/2026-09-08-chrome-complete/README.md b/docs/graphics/evidence/2026-09-08-chrome-complete/README.md new file mode 100644 index 000000000..c4ffc381d --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-complete/README.md @@ -0,0 +1,9 @@ +# Complete-source Chrome reference matrix + +The corrected harness completed all 32 hardware-confirmed Chrome runs: courtyard, supplied fixture, seeded 10k and 100k lines, DPR 1/2, dark/light themes, twice each with 180 timed pans. All 16 comparisons have exact before/after PNG matches for composite, WebGPU and 2D overlay layers. All 232 referenced files verify against recorded hashes, including six capture-tool sources. + +`reference.json.gz` retains complete metadata, samples and hashes. `summary.json` records measured scope and limits. `harness/` retains the exact source bytes. Full raw images, traces and generated inputs remain in `artifacts/chrome-reference-final-navigation-20260908`; permanent remote retention is still outstanding. Earlier failed or differing archives remain preserved separately. + +Reproduce with `node tests/GraphicsCompatibility/capture-chrome-reference.mjs --output artifacts/chrome-reference-new`, then `python3 tests/GraphicsCompatibility/verify-reference-archive.py artifacts/chrome-reference-new`. + +Chrome reporter timing is derived from platform presentation feedback for the verified browser revision; it is separate from CPU submission and RAF timing and does not prove physical WebScene presentation. This matrix does not close #23 or epic #22; other platform, baseline, package and conformance gates remain. diff --git a/docs/graphics/evidence/2026-09-08-chrome-complete/archive.json b/docs/graphics/evidence/2026-09-08-chrome-complete/archive.json new file mode 100644 index 000000000..56db36f47 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-complete/archive.json @@ -0,0 +1,9 @@ +{ + "path": "artifacts/chrome-reference-final-navigation-20260908.tar.gz", + "bytes": 189531386, + "sha256": "247f070c0e2126d2fb59a304c9e591703fd5085b65a5980fb23a88656b96a6db", + "verifiedFiles": 287, + "verification": "Every archived regular file compared by SHA-256 with capture directory; no extraction performed", + "retention": "Local archive only; remote permanent storage outstanding", + "command": "COPYFILE_DISABLE=1 tar -czf artifacts/chrome-reference-final-navigation-20260908.tar.gz -C artifacts chrome-reference-final-navigation-20260908" +} diff --git a/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/capture-chrome-reference.mjs b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/capture-chrome-reference.mjs new file mode 100644 index 000000000..498642962 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/capture-chrome-reference.mjs @@ -0,0 +1,337 @@ +#!/usr/bin/env node +import { createHash } from "node:crypto"; +import { spawnSync } from "node:child_process"; +import { createServer } from "node:http"; +import { existsSync } from "node:fs"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { gzipSync } from "node:zlib"; +import { startChrome, stopChrome, evaluate, waitFor, delay } from "./chrome-session.mjs"; +import { lineProject, referenceCases } from "./reference-workloads.mjs"; +import { analyzePresentation, distribution } from "./presentation-trace.mjs"; + +const here = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(here, "../.."); +const sha = bytes => createHash("sha256").update(bytes).digest("hex"); + +export function hardwareAssessment(system, app) { + const descriptions = [system.gpu?.auxAttributes?.glRenderer, app.adapter?.description, + ...(system.gpu?.devices ?? []).map(device => device.deviceString)].join(" "); + const confirmed = app.secureContext === true && app.backend === "WebGPU" + && app.adapter?.isFallbackAdapter === false && system.gpu?.featureStatus?.webgpu === "enabled" + && system.gpu?.featureStatus?.gpu_compositing === "enabled" + && system.gpu?.devices?.length > 0 && !/swiftshader|llvmpipe|softpipe|software|warp/i.test(descriptions); + return { status: confirmed ? "confirmed" : "unavailable", hardwareAccelerated: confirmed, + reason: confirmed ? "Non-fallback WebGPU adapter and hardware browser GPU features confirmed" + : "Required hardware/non-fallback evidence is missing or reports software" }; +} + +function options(argv) { + const result = { chrome: process.env.CHROME_BIN, repeat: 2, frames: 180 }; + for (let index = 0; index < argv.length; index += 2) { + const key = argv[index], value = argv[index + 1]; + if (!value) throw new Error(`Missing value for ${key}`); + if (key === "--chrome") result.chrome = value; + else if (key === "--output") result.output = path.resolve(value); + else if (key === "--case") result.case = value; + else if (key === "--repeat") result.repeat = Number(value); + else if (key === "--frames") result.frames = Number(value); + else throw new Error(`Unknown option ${key}`); + } + if (!result.output) throw new Error("--output must name a new evidence directory"); + if (!Number.isInteger(result.repeat) || result.repeat < 1 || result.repeat > 10) throw new Error("Invalid repeat count"); + if (!Number.isInteger(result.frames) || result.frames < 10 || result.frames > 1800) throw new Error("Invalid frame count"); + if (result.case && !referenceCases.some(test => test.id === result.case)) throw new Error("Unknown reference case"); + result.chrome ??= ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + "/Volumes/SSD/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + "/usr/bin/google-chrome", "/usr/bin/chromium", + path.join(process.env.PROGRAMFILES ?? "", "Google/Chrome/Application/chrome.exe")].find(existsSync); + if (!result.chrome || !existsSync(result.chrome)) throw new Error("Set --chrome or CHROME_BIN to a hardware-capable Chrome installation"); + return result; +} + +async function fixtureServer(directory, generated) { + const types = { ".html": "text/html", ".js": "text/javascript", ".css": "text/css", + ".json": "application/json", ".kcad": "application/json", ".png": "image/png" }; + const server = createServer(async (request, response) => { + try { + const pathname = decodeURIComponent(new URL(request.url, "http://localhost").pathname); + if (request.method !== "GET") { response.writeHead(405).end(); return; } + let content = generated.get(pathname); + if (!content) { + const filename = path.resolve(directory, "." + (pathname === "/" ? "/index.html" : pathname)); + if (!filename.startsWith(directory + path.sep)) { response.writeHead(403).end(); return; } + content = await readFile(filename); + } + response.writeHead(200, { "Content-Type": types[path.extname(pathname)] ?? "application/octet-stream", + "Cache-Control": "no-store" }); + response.end(content); + } catch { response.writeHead(404).end(); } + }); + await new Promise((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", resolve); }); + return { server, url: `http://127.0.0.1:${server.address().port}` }; +} + +async function settled(page) { + await evaluate(page, `(async()=>{ + await new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve))); + if(kestrel.renderer.device) await kestrel.renderer.device.queue.onSubmittedWorkDone(); + return true; + })()`); +} + +async function settleUi(page) { + // A neutral title click blurs the command input through the app's normal handlers. + // Merely moving the pointer leaves its startup suggestion menu open on some navigations. + const point = await evaluate(page, "(()=>{const r=document.getElementById('title-name').getBoundingClientRect();return {x:r.x+r.width/2,y:r.y+r.height/2};})()"); + for (const event of [{ type: "mouseMoved", ...point }, + { type: "mousePressed", ...point, button: "left", buttons: 1, clickCount: 1 }, + { type: "mouseReleased", ...point, button: "left", buttons: 0, clickCount: 1 }]) + await page.send("Input.dispatchMouseEvent", event); + await waitFor(page, "document.getElementById('command-suggestions').hidden && document.getElementById('file-menu').hidden"); + await delay(200); // Allow the app's 150 ms blur handler and hover transitions to finish. + await settled(page); +} + +export function validateReferenceUi(state) { + if (!state || state.tool !== null || state.bannerHidden !== true + || state.suggestionsHidden !== true || state.fileMenuHidden !== true) { + throw new Error(`Reference UI is not neutral: ${JSON.stringify(state)}`); + } +} + +async function referenceUiState(page) { + const state = await evaluate(page, `({tool:kestrel.tool?.id ?? null, + bannerHidden:document.getElementById('tool-banner').hidden, + suggestionsHidden:document.getElementById('command-suggestions').hidden, + fileMenuHidden:document.getElementById('file-menu').hidden})`); + validateReferenceUi(state); + return state; +} + +async function snapshot(page, output, name, clip) { + const uiBefore = await referenceUiState(page); + const capture = await page.send("Page.captureScreenshot", { format: "png", fromSurface: true, + captureBeyondViewport: false, clip: { ...clip, scale: 1 } }); + const bytes = Buffer.from(capture.data, "base64"); + await writeFile(path.join(output, name), bytes); + const canvasData = await evaluate(page, "({gpu:kestrel.renderer.canvas.toDataURL('image/png'),overlay:kestrel.renderer.overlay.toDataURL('image/png')})"); + const layers = {}; + for (const [layer, dataUrl] of Object.entries(canvasData)) { + if (!dataUrl.startsWith("data:image/png;base64,")) throw new Error("Canvas PNG serialization failed"); + const layerBytes = Buffer.from(dataUrl.slice("data:image/png;base64,".length), "base64"); + const layerName = name.replace(/\.png$/, `-${layer}.png`); + await writeFile(path.join(output, layerName), layerBytes); + layers[layer] = { file: layerName, sha256: sha(layerBytes), width: layerBytes.readUInt32BE(16), height: layerBytes.readUInt32BE(20) }; + } + const uiAfter = await referenceUiState(page); + return { uiBefore, uiAfter, file: name, sha256: sha(bytes), width: bytes.readUInt32BE(16), height: bytes.readUInt32BE(20), + layers, purpose: "Diagnostic reference capture outside the timed interaction; not a presentation path" }; +} + +async function beginTrace(browser) { + let timer; + const completion = new Promise((resolve, reject) => { + timer = setTimeout(() => reject(new Error("Trace completion timed out")), 120_000); + browser.on("Tracing.tracingComplete", event => { clearTimeout(timer); resolve(event); }); + }); + // Avoid an unhandled rejection if an earlier page command fails; callers still observe rejection. + completion.catch(() => {}); + await browser.send("Tracing.start", { transferMode: "ReturnAsStream", streamFormat: "json", + traceConfig: { recordMode: "recordAsMuchAsPossible", includedCategories: ["benchmark", "cc", "gpu", "viz", + "blink.user_timing", "devtools.timeline", "disabled-by-default-devtools.timeline.frame"] } }); + return { completion, cancel: () => clearTimeout(timer) }; +} + +async function endTrace(browser, tracing, output, filename, revision) { + await browser.send("Tracing.end"); + const result = await tracing.completion; + if (result.dataLossOccurred) throw new Error("Chrome lost trace events"); + if (!result.stream) throw new Error("Chrome returned no trace stream"); + const chunks = []; + try { + while (true) { + const chunk = await browser.send("IO.read", { handle: result.stream, size: 1024 * 1024 }); + chunks.push(Buffer.from(chunk.data, chunk.base64Encoded ? "base64" : "utf8")); + if (chunk.eof) break; + } + } finally { await browser.send("IO.close", { handle: result.stream }); } + const raw = Buffer.concat(chunks); + const presentationTiming = analyzePresentation(JSON.parse(raw.toString("utf8")), revision); + const bytes = gzipSync(raw); + await writeFile(path.join(output, filename), bytes); + return { file: filename, sha256: sha(bytes), dataLossOccurred: false, + presentationTiming }; +} + +async function captureCase(chrome, serverUrl, output, test, repetition) { + const page = chrome.page, errors = []; + page.on("Runtime.exceptionThrown", error => errors.push(error)); + await page.send("Emulation.setDeviceMetricsOverride", { ...test.documentViewport, deviceScaleFactor: test.dpr, mobile: false }); + await page.send("Page.navigate", { url: `${serverUrl}/index.html?reference=${test.id}&repeat=${repetition}` }); + await page.send("Page.bringToFront"); + await waitFor(page, "document.documentElement?.dataset.ready==='true' && !!window.kestrel?.backendReady"); + const projectPath = test.scene.startsWith("lines-") ? `/reference/${test.scene}.kcad` : `/examples/${test.scene}.kcad`; + await evaluate(page, `(async()=>{ + const response=await fetch(${JSON.stringify(projectPath)}); + if(!response.ok) throw new Error('Project fetch failed'); + await kestrel.openFile(new File([await response.text()],${JSON.stringify(test.scene + ".kcad")},{type:'application/json'})); + kestrel.theme=${JSON.stringify(test.theme)}; kestrel.applyTheme(); + kestrel.setView(${JSON.stringify(test.view)}); kestrel.setStyle(${JSON.stringify(test.style)}); + kestrel.settings.grid=true; kestrel.settings.lineweights=false; kestrel.fit(false); + return true; + })()`); + // Let the app's own transient UI expire; do not patch/remove its DOM for stable screenshots. + await waitFor(page, "document.querySelectorAll('#toast-stack .toast').length===0"); + await settleUi(page); + const app = await evaluate(page, `(()=>{ + const r=kestrel.renderer, info=r.adapter?.info, rect=document.getElementById('viewport').getBoundingClientRect(); + return {backend:r.backend,secureContext:isSecureContext,fallbackReason:r.fallbackReason, + adapter:info?{vendor:info.vendor,architecture:info.architecture,device:info.device,description:info.description,isFallbackAdapter:info.isFallbackAdapter}:null, + clip:{x:rect.x,y:rect.y,width:rect.width,height:rect.height}, + documentViewport:{width:innerWidth,height:innerHeight,dpr:devicePixelRatio}, + canvas:{width:r.canvas.width,height:r.canvas.height},stats:{...r.stats}, + entities:kestrel.doc.entities.length,camera:kestrel.camera.serialize(),visibility:document.visibilityState,focused:document.hasFocus()}; + })()`); + const system = await chrome.browser.send("SystemInfo.getInfo"); + const hardware = hardwareAssessment(system, app); + if (!hardware.hardwareAccelerated) return { test, repetition, status: "unavailable", hardware, app, system }; + if (app.documentViewport.width !== test.documentViewport.width || app.documentViewport.height !== test.documentViewport.height + || app.documentViewport.dpr !== test.dpr || app.visibility !== "visible") throw new Error("Viewport/DPR/visibility mismatch"); + const prefix = `${test.id}-run${repetition}`; + const before = await snapshot(page, output, `${prefix}-before.png`, app.clip); + const tracing = await beginTrace(chrome.browser); + let trace, samples, input; + try { + // Exercise real browser mouse input once, then restore the camera for deterministic timed pans. + const x = app.clip.x + app.clip.width / 2, y = app.clip.y + app.clip.height / 2; + const events = [{ type: "mouseMoved", x, y }, { type: "mousePressed", x, y, button: "middle", buttons: 4, clickCount: 1 }, + { type: "mouseMoved", x: x + 40, y: y + 20, button: "middle", buttons: 4 }, + { type: "mouseReleased", x: x + 40, y: y + 20, button: "middle", buttons: 0, clickCount: 1 }]; + for (const event of events) await page.send("Input.dispatchMouseEvent", event); + await settled(page); + input = await evaluate(page, "({target:kestrel.camera.target.slice(),camera:kestrel.camera.serialize()})"); + input.events = events; + input.changedCamera = JSON.stringify(input.target) !== JSON.stringify(app.camera.target); + if (!input.changedCamera) throw new Error("Middle-button input did not pan the Kestrel camera"); + await settleUi(page); + await evaluate(page, `kestrel.camera.restore(${JSON.stringify(app.camera)});kestrel.invalidate();true`); + await settled(page); + samples = await evaluate(page, `new Promise(resolve=>{ + const app=kestrel, config=${JSON.stringify(test.interaction)}, samples=[]; + const buffers=Object.fromEntries(Object.entries(app.renderer.buffers).map(([key,value])=>[key,value.buffer])); + let previous=null, moved=0; + performance.mark('webscene-reference-pan-start'); + const step=timestamp=>{ + if(previous!==null) samples.push({rafIntervalMs:timestamp-previous,cpuRenderSubmissionMs:app.renderer.stats.cpuMs}); + if(moved===config.frames) { + performance.mark('webscene-reference-pan-end'); + resolve({samples,finalCamera:app.camera.serialize(),stats:{...app.renderer.stats}, + retainedBuffers:Object.fromEntries(Object.entries(buffers).map(([key,value])=>[key,app.renderer.buffers[key]?.buffer===value])),gpuErrors:app.renderer.gpuErrors.slice()}); + return; + } + app.camera.pan(config.dxCssPixels,config.dyCssPixels);app.invalidate();moved++;previous=timestamp; + requestAnimationFrame(step); + }; requestAnimationFrame(step); + })`); + await settled(page); + await delay(100); // Allow platform presentation feedback for the last submitted update to arrive. + trace = await endTrace(chrome.browser, tracing, output, `${prefix}-trace.json.gz`, chrome.revision); + } finally { tracing.cancel(); } + await settled(page); + const after = await snapshot(page, output, `${prefix}-after.png`, app.clip); + if (samples.gpuErrors.length || errors.length) throw new Error("Kestrel reported browser/GPU errors"); + samples.cpuRenderSubmissionMilliseconds = distribution(samples.samples.map(sample => sample.cpuRenderSubmissionMs)); + samples.rafIntervalMilliseconds = distribution(samples.samples.map(sample => sample.rafIntervalMs)); + return { test, repetition, status: "captured", hardware, app, system, input, before, after, trace, samples, + timingScope: "Application CPU render/submission plus rAF intervals; GPU execution and presentation require separate trace analysis" }; +} + +export async function main(argv) { + const args = options(argv); + await mkdir(path.dirname(args.output), { recursive: true }); + await mkdir(args.output); // Never overwrite evidence, including failed attempts. + const evidence = { schemaVersion: 1, status: "running", capturedAt: new Date().toISOString(), + scope: "Hardware Chrome reference for G01; not WebScene support or full epic qualification", + host: { platform: os.platform(), release: os.release(), architecture: os.arch() }, results: [] }; + let chrome, service; + try { + const fixtureRoot = path.join(args.output, "fixture-input"); + const validation = spawnSync(process.env.PYTHON ?? (process.platform === "win32" ? "python" : "python3"), + [path.join(here, "prepare-kestrel.py"), "--destination", fixtureRoot], { encoding: "utf8" }); + if (validation.status !== 0) throw new Error(validation.stderr || validation.stdout); + evidence.fixture = JSON.parse(await readFile(path.join(here, "fixtures/kestrel.json"), "utf8")); + evidence.repositoryCommit = spawnSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).stdout.trim(); + const harnessArchive = await archiveReferenceHarness(args.output); + evidence.harness = harnessArchive.hashes; + evidence.harnessFiles = harnessArchive.files; + await mkdir(path.join(args.output, "generated-inputs"), { recursive: true }); + const generated = new Map(); + evidence.generatedProjects = {}; + for (const count of [10_000, 100_000]) { + const bytes = Buffer.from(JSON.stringify(lineProject(count))); + generated.set(`/reference/lines-${count}.kcad`, bytes); + const file = `generated-inputs/lines-${count}.kcad`; + await writeFile(path.join(args.output, file), bytes); + evidence.generatedProjects[`lines-${count}`] = { file, sha256: sha(bytes), bytes: bytes.length, count }; + } + service = await fixtureServer(path.join(fixtureRoot, "Kestrel-CAD"), generated); + chrome = await startChrome(args.chrome); + evidence.browser = await chrome.browser.send("Browser.getVersion"); + chrome.revision = evidence.browser.revision; + evidence.launch = { executable: args.chrome, args: chrome.args, headless: false }; + await chrome.page.send("Page.addScriptToEvaluateOnNewDocument", { source: "try { localStorage.clear(); } catch {}" }); + for (const test of referenceCases.filter(test => !args.case || test.id === args.case)) { + for (let repetition = 1; repetition <= args.repeat; ++repetition) { + const configured = { ...test, interaction: { ...test.interaction, frames: args.frames } }; + const result = await captureCase(chrome, service.url, args.output, configured, repetition); + evidence.results.push(result); + await writeFile(path.join(args.output, "reference.json"), JSON.stringify(evidence, null, 2) + "\n"); + console.log(`${test.id} run ${repetition}: ${result.status}`); + if (result.status === "unavailable") break; + } + } + evidence.status = evidence.results.every(result => result.status === "captured") ? "captured" : "unavailable"; + evidence.repeatability = referenceCases.filter(test => !args.case || test.id === args.case).map(test => { + const runs = evidence.results.filter(result => result.test.id === test.id && result.status === "captured"); + return { case: test.id, runs: runs.length, beforeExactPngMatch: runs.length >= 2 && new Set(runs.map(run => run.before.sha256)).size === 1, + afterExactPngMatch: runs.length >= 2 && new Set(runs.map(run => run.after.sha256)).size === 1, + layerMatches: Object.fromEntries(["before", "after"].flatMap(phase => ["gpu", "overlay"].map(layer => + [`${phase}-${layer}`, runs.length >= 2 && new Set(runs.map(run => run[phase].layers[layer].sha256)).size === 1]))) }; + }); + evidence.remainingVerification = ["Presentation timing is unavailable for unverified Chrome revisions or incomplete traces", + "Repeat pixel differences require inspection if hashes differ", + "Other target hardware and WebScene comparison remain separate gates"]; + } catch (error) { + evidence.status = "failed"; evidence.error = error.stack ?? String(error); + console.error(evidence.error); + } finally { + if (chrome) { evidence.chromeStderr = chrome.stderr; await stopChrome(chrome); } + if (service) await new Promise(resolve => service.server.close(resolve)); + await writeFile(path.join(args.output, "reference.json"), JSON.stringify(evidence, null, 2) + "\n"); + } + return evidence.status === "captured" ? 0 : evidence.status === "unavailable" ? 77 : 1; +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + main(process.argv.slice(2)).then(code => { process.exitCode = code; }).catch(error => { console.error(error); process.exitCode = 1; }); +} + +// Persist the exact source bytes used to identify a reference capture, including +// uncommitted harness edits. A repository SHA alone cannot recover those bytes. +export async function archiveReferenceHarness(output, sourceDirectory = here) { + const hashes = {}, files = {}; + await mkdir(path.join(output, "harness"), { recursive: true }); + for (const name of ["capture-chrome-reference.mjs", "chrome-session.mjs", "reference-workloads.mjs", "presentation-trace.mjs", + "prepare-kestrel.py", "../WebPlatformSubset/chrome/cdp-client.mjs"]) { + const bytes = await readFile(path.join(sourceDirectory, name)); + const file = path.posix.join("harness/tests/GraphicsCompatibility", name); + await mkdir(path.dirname(path.join(output, file)), { recursive: true }); + await writeFile(path.join(output, file), bytes); + hashes[name] = sha(bytes); + files[name] = { file, sha256: hashes[name], bytes: bytes.length }; + } + return { hashes, files }; +} diff --git a/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/chrome-session.mjs b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/chrome-session.mjs new file mode 100644 index 000000000..3d01e6d96 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/chrome-session.mjs @@ -0,0 +1,74 @@ +import { spawn } from "node:child_process"; +import { mkdtemp, rm } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { CdpClient } from "../WebPlatformSubset/chrome/cdp-client.mjs"; + +export const delay = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds)); + +export async function evaluate(client, expression) { + const response = await client.send("Runtime.evaluate", { expression, awaitPromise: true, returnByValue: true }, 60_000); + if (response.exceptionDetails) throw new Error(response.exceptionDetails.exception?.description ?? response.exceptionDetails.text); + return response.result.value; +} + +export async function startChrome(executable) { + const userDataDirectory = await mkdtemp(path.join(os.tmpdir(), "webscene-kestrel-chrome-")); + const args = ["--disable-background-networking", "--disable-component-update", "--disable-default-apps", + "--disable-extensions", "--disable-sync", "--no-first-run", "--no-default-browser-check", + "--remote-debugging-port=0", "--window-size=1960,1200", `--user-data-dir=${userDataDirectory}`, "about:blank"]; + const child = spawn(executable, args, { stdio: ["ignore", "ignore", "pipe"] }); + const session = { child, userDataDirectory, args, stderr: "", browser: null, page: null }; + try { + const endpoint = await new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error("Chrome DevTools startup timed out")), 30_000); + child.stderr.setEncoding("utf8"); + child.stderr.on("data", chunk => { + session.stderr += chunk; + const match = session.stderr.match(/DevTools listening on (ws:\/\/[^\s]+)/); + if (match) { clearTimeout(timer); resolve(match[1]); } + }); + child.once("error", error => { clearTimeout(timer); reject(error); }); + child.once("exit", code => { clearTimeout(timer); reject(new Error(`Chrome exited: ${code}`)); }); + }); + session.browser = await CdpClient.connect(endpoint); + const url = new URL(endpoint); + let target; + for (let attempt = 0; attempt < 100 && !target; ++attempt) { + const targets = await (await fetch(`http://${url.host}/json/list`)).json(); + target = targets.find(value => value.type === "page" && value.webSocketDebuggerUrl); + if (!target) await delay(50); + } + if (!target) throw new Error("Chrome page target unavailable"); + session.page = await CdpClient.connect(target.webSocketDebuggerUrl); + await session.page.send("Page.enable"); + await session.page.send("Runtime.enable"); + await session.page.send("Page.bringToFront"); + return session; + } catch (error) { + await stopChrome(session); + throw error; + } +} + +export async function stopChrome(session) { + try { await session.browser?.send("Browser.close", {}, 2_000); } + catch { session.child.kill("SIGTERM"); } + session.page?.close(); + session.browser?.close(); + if (session.child.exitCode === null && session.child.signalCode === null) { + await new Promise(resolve => { + const timer = setTimeout(() => { session.child.kill("SIGKILL"); resolve(); }, 2_000); + session.child.once("exit", () => { clearTimeout(timer); resolve(); }); + }); + } + await rm(session.userDataDirectory, { recursive: true, force: true }); +} + +export async function waitFor(client, expression) { + for (let attempt = 0; attempt < 300; ++attempt) { + if (await evaluate(client, expression)) return; + await delay(100); + } + throw new Error(`Timed out waiting for ${expression}`); +} diff --git a/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/prepare-kestrel.py b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/prepare-kestrel.py new file mode 100644 index 000000000..1e8780b05 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/prepare-kestrel.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Verify the immutable Kestrel input and optionally extract a disposable copy.""" +import argparse +import hashlib +import json +from pathlib import Path, PurePosixPath +import zipfile + + +def prepare(destination=None): + fixtures = Path(__file__).resolve().parent / "fixtures" + manifest = json.loads((fixtures / "kestrel.json").read_text()) + archive = fixtures / manifest["archive"] + if hashlib.sha256(archive.read_bytes()).hexdigest() != manifest["sha256"]: + raise ValueError("Kestrel archive checksum mismatch") + with zipfile.ZipFile(archive) as bundle: + entries = bundle.infolist() + names = [entry.filename for entry in entries] + if len(set(names)) != len(names) or set(names) != set(manifest["files"]): + raise ValueError("Kestrel archive inventory mismatch") + for entry in entries: + path = PurePosixPath(entry.filename) + if path.is_absolute() or ".." in path.parts or "\\" in entry.filename: + raise ValueError("Unsafe archive path") + data = bundle.read(entry) + if hashlib.sha256(data).hexdigest() != manifest["files"][entry.filename]: + raise ValueError(f"Kestrel file checksum mismatch: {entry.filename}") + if bundle.read("Kestrel-CAD/LICENSE") != (fixtures / manifest["licenseFile"]).read_bytes(): + raise ValueError("Kestrel license mismatch") + if destination is not None: + # A fresh destination prevents prior test output or symlinks contaminating the fixture. + destination.mkdir(parents=True, exist_ok=False) + for entry in entries: + output = destination / entry.filename + output.parent.mkdir(parents=True, exist_ok=True) + output.write_bytes(bundle.read(entry)) + print(f"Verified {len(names)} immutable Kestrel files; no GPU qualification implied.") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--destination", type=Path, help="New directory for a disposable extraction") + args = parser.parse_args() + prepare(args.destination) diff --git a/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/presentation-trace.mjs b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/presentation-trace.mjs new file mode 100644 index 000000000..04110e097 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/presentation-trace.mjs @@ -0,0 +1,62 @@ +// This source contract was inspected at the Chrome revision below. Unknown revisions stay unqualified. +export const supportedChromeRevision = "@529d9a34b491745086b59458f58a5aae8292adaa"; +const sourceRoot = `https://chromium.googlesource.com/chromium/src/+/${supportedChromeRevision.slice(1)}/cc/metrics/`; + +export function distribution(values) { + if (!values.length || values.some(value => !Number.isFinite(value))) return null; + const sorted = [...values].sort((a, b) => a - b); + const percentile = fraction => sorted[Math.max(0, Math.ceil(fraction * sorted.length) - 1)]; + return { count: sorted.length, min: sorted[0], median: percentile(0.5), p95: percentile(0.95), + max: sorted.at(-1), mean: sorted.reduce((sum, value) => sum + value, 0) / sorted.length }; +} + +export function analyzePresentation(trace, revision) { + const unavailable = reason => ({ status: "unavailable", reason }); + if (revision !== supportedChromeRevision) return unavailable("Chrome revision has not had its presentation trace source contract verified"); + const events = trace.traceEvents; + if (!Array.isArray(events)) return unavailable("Missing trace event array"); + const starts = events.filter(event => event.name === "webscene-reference-pan-start" && event.ph === "I"); + const ends = events.filter(event => event.name === "webscene-reference-pan-end" && event.ph === "I"); + if (starts.length !== 1 || ends.length !== 1 || starts[0].pid !== ends[0].pid || ends[0].ts <= starts[0].ts) + return unavailable("Missing or ambiguous interaction markers"); + const start = starts[0], end = ends[0], active = new Map(), frames = [], reporterStates = {}; + let ambiguous = false; + const reporters = events.filter(event => event.name === "PipelineReporter" && event.pid === start.pid) + .sort((a, b) => a.ts - b.ts); + for (const event of reporters) { + // Trace local IDs are strings. Do not use the 64-bit numeric surface/display IDs: JSON loses precision. + const identity = event.id2?.local ?? event.id2?.global; + if (!identity) continue; + if (event.ph === "b") { + if (active.has(identity)) ambiguous = true; + active.set(identity, event); + } else if (event.ph === "e") { + const begin = active.get(identity); + active.delete(identity); + if (!begin || begin.ts < start.ts || begin.ts > end.ts) continue; + const info = begin.args?.frame_reporter; + if (!info?.state || event.ts < begin.ts || !Number.isFinite(event.ts)) { ambiguous = true; continue; } + reporterStates[info.state] = (reporterStates[info.state] ?? 0) + 1; + if (["STATE_PRESENTED_ALL", "STATE_PRESENTED_PARTIAL"].includes(info.state)) { + frames.push({ beginMicroseconds: begin.ts, presentedMicroseconds: event.ts, + sequence: info.frame_sequence, source: info.frame_source, layerTreeHost: info.layer_tree_host_id, + partial: info.state === "STATE_PRESENTED_PARTIAL", missingContent: info.has_missing_content === true }); + } + } + } + if (ambiguous) return unavailable("Ambiguous or malformed PipelineReporter event pairing"); + if ([...active.values()].some(event => event.ts >= start.ts && event.ts <= end.ts)) + return unavailable("Trace ended before all interaction reporters completed"); + const timestamps = [...new Set(frames.map(frame => frame.presentedMicroseconds))].sort((a, b) => a - b); + if (timestamps.length < 2) return unavailable("Fewer than two distinct platform presentation feedback timestamps"); + const intervals = timestamps.slice(1).map((timestamp, index) => (timestamp - timestamps[index]) / 1000); + return { status: "measured", source: "Presented PipelineReporter termination timestamps (platform presentation feedback)", + sourceContract: [sourceRoot + "compositor_frame_reporting_controller.cc", sourceRoot + "compositor_frame_reporter.cc"], + revision, rendererPid: start.pid, interactionStartMicroseconds: start.ts, interactionEndMicroseconds: end.ts, + uniquePresentedFrames: timestamps.length, + framesPerSecond: (timestamps.length - 1) * 1e6 / (timestamps.at(-1) - timestamps[0]), + intervalMilliseconds: distribution(intervals), partialReporters: frames.filter(frame => frame.partial).length, + missingContentReporters: frames.filter(frame => frame.missingContent).length, + reporterStates, frames, + note: "Reporter state counts include compositor bookkeeping; they are not counts of distinct application frames. Presentation feedback is separate from CPU submission and rAF delivery." }; +} diff --git a/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/reference-workloads.mjs b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/reference-workloads.mjs new file mode 100644 index 000000000..90c7b58f3 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/GraphicsCompatibility/reference-workloads.mjs @@ -0,0 +1,34 @@ +// Synthetic drawing data only. Kestrel application files remain byte-for-byte unchanged. +export const referenceSeed = 0x22c0ffee; + +export function lineProject(count, seed = referenceSeed) { + if (!Number.isInteger(count) || count < 1 || count > 200_000) throw new Error("Invalid line count"); + if (!Number.isInteger(seed) || seed < 0 || seed > 0xffffffff) throw new Error("Invalid uint32 seed"); + let state = seed >>> 0; + const random = () => { + state = (Math.imul(state, 1664525) + 1013904223) >>> 0; + return (state >>> 8) / 0x1000000; + }; + const quantize = value => Math.round(value * 1024) / 1024; + const entities = Array.from({ length: count }, (_, index) => { + const x = quantize((random() - 0.5) * 1000); + const y = quantize((random() - 0.5) * 1000); + const dx = quantize(2 + random() * 18); + const dy = quantize((random() - 0.5) * 40); + return { id: `reference-line-${index}`, type: "LINE", layer: "0", color: "bylayer", + linetype: "ByLayer", points: [[x, y, 0], [quantize(x + dx), quantize(y + dy), 0]] }; + }); + return { format: "kestrel-cad", version: 1, name: `Seeded ${count} lines`, units: "mm", + currentLayer: "0", layers: [{ id: "0", name: "REFERENCE", color: "#59c8d9", visible: true, + locked: false, linetype: "Continuous", lineweight: 0.25 }], entities, camera: null }; +} + +export const referenceCases = ["courtyard", "fixture", "lines-10000", "lines-100000"].flatMap(scene => + [1, 2].flatMap(dpr => ["dark", "light"].map(theme => ({ + id: `${scene}-dpr${dpr}-${theme}`, scene, dpr, theme, + style: scene === "fixture" ? "shaded-edges" : "wireframe", + view: scene === "fixture" ? "iso" : "top", + documentViewport: { width: 1920, height: 1080 }, seed: referenceSeed, + interaction: { kind: "camera-pan", frames: 180, dxCssPixels: 0.5, dyCssPixels: 0.25 } + }))) +); diff --git a/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/WebPlatformSubset/chrome/cdp-client.mjs b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/WebPlatformSubset/chrome/cdp-client.mjs new file mode 100644 index 000000000..7dcac8aff --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-complete/harness/tests/WebPlatformSubset/chrome/cdp-client.mjs @@ -0,0 +1,74 @@ +export class CdpClient { + constructor(socket) { + this.socket = socket; + this.nextId = 0; + this.pending = new Map(); + this.listeners = new Map(); + socket.addEventListener("message", event => this.#receive(JSON.parse(event.data))); + socket.addEventListener("close", () => this.#rejectPending(new Error("CDP WebSocket closed."))); + socket.addEventListener("error", () => this.#rejectPending(new Error("CDP WebSocket failed."))); + } + + static async connect(webSocketUrl) { + const socket = new WebSocket(webSocketUrl); + await new Promise((resolve, reject) => { + socket.addEventListener("open", resolve, { once: true }); + socket.addEventListener("error", reject, { once: true }); + }); + return new CdpClient(socket); + } + + send(method, params = {}, timeoutMilliseconds = 20_000) { + const id = ++this.nextId; + this.socket.send(JSON.stringify({ id, method, params })); + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + if (!this.pending.delete(id)) return; + reject(new Error(`CDP command '${method}' timed out after ${timeoutMilliseconds} ms.`)); + }, timeoutMilliseconds); + this.pending.set(id, { + resolve: value => { + clearTimeout(timeout); + resolve(value); + }, + reject: error => { + clearTimeout(timeout); + reject(error); + } + }); + }); + } + + on(method, listener) { + const listeners = this.listeners.get(method) ?? []; + listeners.push(listener); + this.listeners.set(method, listeners); + } + + close() { + this.#rejectPending(new Error("CDP client closed.")); + this.socket.close(); + } + + #rejectPending(error) { + for (const continuation of this.pending.values()) continuation.reject(error); + this.pending.clear(); + } + + #receive(message) { + if (message.id) { + const continuation = this.pending.get(message.id); + if (!continuation) return; + this.pending.delete(message.id); + if (message.error) continuation.reject(new Error(JSON.stringify(message.error))); + else continuation.resolve(message.result); + return; + } + for (const listener of this.listeners.get(message.method) ?? []) { + Promise.resolve(listener(message.params)).catch(error => { + process.stderr.write( + `CDP event listener failed for ${message.method}: ${error.stack ?? error}\n`); + }); + } + } +} diff --git a/docs/graphics/evidence/2026-09-08-chrome-complete/reference.json.gz b/docs/graphics/evidence/2026-09-08-chrome-complete/reference.json.gz new file mode 100644 index 000000000..49be48b33 Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-chrome-complete/reference.json.gz differ diff --git a/docs/graphics/evidence/2026-09-08-chrome-complete/summary.json b/docs/graphics/evidence/2026-09-08-chrome-complete/summary.json new file mode 100644 index 000000000..d3d6d355e --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-complete/summary.json @@ -0,0 +1,31 @@ +{ + "status": "captured", + "runs": 32, + "pairedCases": 16, + "allRepeatedImagesExact": true, + "hardwareConfirmedRuns": 32, + "verifiedReferencedFiles": 232, + "browser": { + "protocolVersion": "1.3", + "product": "Chrome/152.0.7977.77", + "revision": "@529d9a34b491745086b59458f58a5aae8292adaa", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36", + "jsVersion": "15.2.124.19" + }, + "sourceCommit": "dde007e77cae2de36c9c6fe8659e7c8bb5498e56", + "reporterCadenceRange": [ + 58.99933134091147, + 60.33652274215002 + ], + "reporterP95IntervalMillisecondsRange": [ + 16.667, + 33.333 + ], + "scope": "Chrome platform-feedback-derived reporter cadence; not WebScene presentation or physical scanout qualification", + "rawArchiveDirectory": "artifacts/chrome-reference-final-navigation-20260908", + "limits": [ + "Full raw artifact set currently local; permanent remote retention remains outstanding", + "Does not qualify WebScene GPU conformance or physical 60fps", + "Earlier failed/differing captures remain separate and are not relabelled as passes" + ] +} diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/README.md b/docs/graphics/evidence/2026-09-08-chrome-metal/README.md new file mode 100644 index 000000000..f6c989bcf --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-metal/README.md @@ -0,0 +1,27 @@ +# Fresh macOS Chrome reference capture + +All 32 runs completed with hardware acceleration confirmed. Archive verification checked all 230 referenced files successfully. All 16 WebGPU before/after layer comparisons match exactly. Fifteen of 16 composited before/after pairs match; three cases have exported 2D-overlay differences. This is captured evidence with unresolved repeatability differences, not complete pixel qualification. + +The full local archive is `artifacts/chrome-reference-20260908-current`. This committed subset retains compressed metadata, the four recorded harness source files, and measured overlay differences; it does not include all raw images and traces. Durable full archival remains outstanding. The recorded repository revision identifies additional harness dependencies. + +Reproduction: `node tests/GraphicsCompatibility/capture-chrome-reference.mjs --output artifacts/chrome-reference-new`. Integrity: `python3 tests/GraphicsCompatibility/verify-reference-archive.py artifacts/chrome-reference-new`. + +The fixture-dpr1-light pair has identical initial/final camera state and clipping geometry with empty GPU error arrays, despite overlay export differences. Their cause is unresolved. Do not attribute them to WebScene: this capture runs unchanged Kestrel in Chrome. Physical WebScene presentation and Windows/Linux qualification remain separate outstanding gates. + +## Composite mismatch inspection + +The `lines-10000-dpr1-light` before and after composite pairs differ within pixel bounds `(528,636)-(918,743)` (39,344 changed RGB pixels each). Visual inspection of the crops shows an active `ERASE` selection-command banner in run 2, absent in run 1. GPU and overlay layer hashes for this pair still match. Therefore this pair is not a neutral, repeatable composite baseline. The source of command activation is unproven; no renderer defect or external-input cause is asserted. + +The capture harness now checks that no tool is active and the tool banner, suggestions and file menu are hidden immediately before and after snapshot capture. It rejects contaminated captures rather than removing application UI or accepting those pixels. This does not explain the other cases' separately exported overlay differences. + +The new guard passes a targeted two-run `lines-10000-dpr1-light` capture with 30 timed pans per run, and all composite/GPU/overlay before/after hashes match. Archive integrity verifies 20 referenced files. Metadata: `neutral-ui-check.json.gz`; raw archive: `artifacts/chrome-reference-neutral-ui-check`. Seven harness tests pass. This short check does not replace the full matrix or its 180-pan workload. + +## Full-workload overlay recheck + +A targeted `fixture-dpr1-light` rerun with the neutral-UI guard and two repetitions of the full 180-pan workload completed successfully. All before/after composite, WebGPU and overlay PNG hashes match between repetitions; all 20 referenced archive files pass integrity verification. Metadata: `overlay-recheck.json.gz`; complete local archive: `artifacts/chrome-reference-overlay-recheck`. This does not establish why the earlier exported overlays differed, and does not replace the full matrix. Inspection of the earlier trace event names did not supply direct evidence of Canvas2D readback/backend switching; that hypothesis remains unproven. + +## Complete-source matrix attempt: navigation failure + +`artifacts/chrome-reference-complete-sources-20260908` stopped with exit 1 after 31 captured runs. The last navigation exposed a null documentElement before readiness polling, causing a TypeError; the readiness expression now uses optional chaining so polling can wait for the root. This is a harness failure, not a WebScene rendering result. Among completed pairs, `lines-10000-dpr1-light` has before/after composite differences, `lines-10000-dpr2-light` has a before-overlay difference, and `lines-100000-dpr1-light` has a before-composite difference. All completed WebGPU layer pairs match. The matrix is incomplete and not accepted. Exact failed metadata is retained in `complete-sources-failed.json.gz`; raw files and all six source helpers remain in the local archive. + +The corrected readiness check passes a targeted two-run `lines-100000-dpr2-light` capture with 180 pans per run. All composite/GPU/overlay before/after hashes match, and the stronger archive verifier validates 22 referenced files including six source helpers. Metadata: `navigation-root-recheck.json.gz`. This targeted run does not complete the failed matrix. diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/angle-context-loss.json b/docs/graphics/evidence/2026-09-08-chrome-metal/angle-context-loss.json new file mode 100644 index 000000000..d1403cead --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-metal/angle-context-loss.json @@ -0,0 +1,26 @@ +{ + "date": "2026-09-08", + "platform": "macOS arm64, Apple M4, ANGLE Metal", + "scope": "Native ANGLE context owner; ES2 and ES3 WebGL-compatible contexts", + "implementation": [ + "Require EGL reset notification support and request lose-context-on-reset", + "Poll reset status at scope entry and exit; retain sticky loss state and reject reentry", + "Restore independent prior context; unbind instead of restoring the same lost context" + ], + "verification": { + "command": "ctest --test-dir artifacts/graphics-build/native-v8-enabled -R '^webscene_graphics_(angle_(es3_)?context|service)_tests$' --output-on-failure", + "result": "3/3 passed, 0.60 seconds after successful build", + "cases": [ + "Injected GL_CHROMIUM_lose_context observed on scope exit", + "Lost context reentry rejected without disturbing surviving context", + "Nested same-context loss unbinds and outer scope restores independent context", + "Surviving context renders and reads expected texture pixels after loss" + ] + }, + "limitations": [ + "Diagnostic context loss, not physical device reset", + "Does not implement DOM WebGL context-lost events or restoration", + "Windows and Linux unqualified", + "Readback used for test verification only, not presentation transport" + ] +} diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/complete-sources-composite-differences.json b/docs/graphics/evidence/2026-09-08-chrome-metal/complete-sources-composite-differences.json new file mode 100644 index 000000000..fa1b9d5cd --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-metal/complete-sources-composite-differences.json @@ -0,0 +1,117 @@ +{ + "scope": "Decoded pixel comparison of the incomplete Chrome matrix, not WebScene output", + "comparisons": [ + { + "case": "lines-10000-dpr1-light", + "phase": "before", + "changedPixels": 237203, + "boundsAnyColor": [ + 0, + 0, + 1446, + 743 + ], + "channelExtrema": [ + [ + 0, + 8 + ], + [ + 0, + 2 + ], + [ + 0, + 1 + ], + [ + 0, + 0 + ] + ] + }, + { + "case": "lines-10000-dpr1-light", + "phase": "after", + "changedPixels": 239136, + "boundsAnyColor": [ + 0, + 0, + 1446, + 743 + ], + "channelExtrema": [ + [ + 0, + 8 + ], + [ + 0, + 1 + ], + [ + 0, + 1 + ], + [ + 0, + 0 + ] + ] + }, + { + "case": "lines-100000-dpr1-light", + "phase": "before", + "changedPixels": 1, + "boundsAnyColor": [ + 13, + 57, + 14, + 58 + ], + "channelExtrema": [ + [ + 0, + 2 + ], + [ + 0, + 1 + ], + [ + 0, + 1 + ], + [ + 0, + 0 + ] + ] + }, + { + "case": "lines-100000-dpr1-light", + "phase": "after", + "changedPixels": 0, + "boundsAnyColor": null, + "channelExtrema": [ + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ], + [ + 0, + 0 + ] + ] + } + ], + "finding": "Inspected before images show the same visible UI and drawing; differences are color-value changes. Cause unproven; exact repeatability is not established." +} diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/complete-sources-failed.json.gz b/docs/graphics/evidence/2026-09-08-chrome-metal/complete-sources-failed.json.gz new file mode 100644 index 000000000..98a7c7af8 Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-chrome-metal/complete-sources-failed.json.gz differ diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/harness/capture-chrome-reference.mjs b/docs/graphics/evidence/2026-09-08-chrome-metal/harness/capture-chrome-reference.mjs new file mode 100644 index 000000000..d1f38f4e3 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-metal/harness/capture-chrome-reference.mjs @@ -0,0 +1,317 @@ +#!/usr/bin/env node +import { createHash } from "node:crypto"; +import { spawnSync } from "node:child_process"; +import { createServer } from "node:http"; +import { existsSync } from "node:fs"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { gzipSync } from "node:zlib"; +import { startChrome, stopChrome, evaluate, waitFor, delay } from "./chrome-session.mjs"; +import { lineProject, referenceCases } from "./reference-workloads.mjs"; +import { analyzePresentation, distribution } from "./presentation-trace.mjs"; + +const here = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(here, "../.."); +const sha = bytes => createHash("sha256").update(bytes).digest("hex"); + +export function hardwareAssessment(system, app) { + const descriptions = [system.gpu?.auxAttributes?.glRenderer, app.adapter?.description, + ...(system.gpu?.devices ?? []).map(device => device.deviceString)].join(" "); + const confirmed = app.secureContext === true && app.backend === "WebGPU" + && app.adapter?.isFallbackAdapter === false && system.gpu?.featureStatus?.webgpu === "enabled" + && system.gpu?.featureStatus?.gpu_compositing === "enabled" + && system.gpu?.devices?.length > 0 && !/swiftshader|llvmpipe|softpipe|software|warp/i.test(descriptions); + return { status: confirmed ? "confirmed" : "unavailable", hardwareAccelerated: confirmed, + reason: confirmed ? "Non-fallback WebGPU adapter and hardware browser GPU features confirmed" + : "Required hardware/non-fallback evidence is missing or reports software" }; +} + +function options(argv) { + const result = { chrome: process.env.CHROME_BIN, repeat: 2, frames: 180 }; + for (let index = 0; index < argv.length; index += 2) { + const key = argv[index], value = argv[index + 1]; + if (!value) throw new Error(`Missing value for ${key}`); + if (key === "--chrome") result.chrome = value; + else if (key === "--output") result.output = path.resolve(value); + else if (key === "--case") result.case = value; + else if (key === "--repeat") result.repeat = Number(value); + else if (key === "--frames") result.frames = Number(value); + else throw new Error(`Unknown option ${key}`); + } + if (!result.output) throw new Error("--output must name a new evidence directory"); + if (!Number.isInteger(result.repeat) || result.repeat < 1 || result.repeat > 10) throw new Error("Invalid repeat count"); + if (!Number.isInteger(result.frames) || result.frames < 10 || result.frames > 1800) throw new Error("Invalid frame count"); + if (result.case && !referenceCases.some(test => test.id === result.case)) throw new Error("Unknown reference case"); + result.chrome ??= ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + "/Volumes/SSD/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + "/usr/bin/google-chrome", "/usr/bin/chromium", + path.join(process.env.PROGRAMFILES ?? "", "Google/Chrome/Application/chrome.exe")].find(existsSync); + if (!result.chrome || !existsSync(result.chrome)) throw new Error("Set --chrome or CHROME_BIN to a hardware-capable Chrome installation"); + return result; +} + +async function fixtureServer(directory, generated) { + const types = { ".html": "text/html", ".js": "text/javascript", ".css": "text/css", + ".json": "application/json", ".kcad": "application/json", ".png": "image/png" }; + const server = createServer(async (request, response) => { + try { + const pathname = decodeURIComponent(new URL(request.url, "http://localhost").pathname); + if (request.method !== "GET") { response.writeHead(405).end(); return; } + let content = generated.get(pathname); + if (!content) { + const filename = path.resolve(directory, "." + (pathname === "/" ? "/index.html" : pathname)); + if (!filename.startsWith(directory + path.sep)) { response.writeHead(403).end(); return; } + content = await readFile(filename); + } + response.writeHead(200, { "Content-Type": types[path.extname(pathname)] ?? "application/octet-stream", + "Cache-Control": "no-store" }); + response.end(content); + } catch { response.writeHead(404).end(); } + }); + await new Promise((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", resolve); }); + return { server, url: `http://127.0.0.1:${server.address().port}` }; +} + +async function settled(page) { + await evaluate(page, `(async()=>{ + await new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve))); + if(kestrel.renderer.device) await kestrel.renderer.device.queue.onSubmittedWorkDone(); + return true; + })()`); +} + +async function settleUi(page) { + // A neutral title click blurs the command input through the app's normal handlers. + // Merely moving the pointer leaves its startup suggestion menu open on some navigations. + const point = await evaluate(page, "(()=>{const r=document.getElementById('title-name').getBoundingClientRect();return {x:r.x+r.width/2,y:r.y+r.height/2};})()"); + for (const event of [{ type: "mouseMoved", ...point }, + { type: "mousePressed", ...point, button: "left", buttons: 1, clickCount: 1 }, + { type: "mouseReleased", ...point, button: "left", buttons: 0, clickCount: 1 }]) + await page.send("Input.dispatchMouseEvent", event); + await waitFor(page, "document.getElementById('command-suggestions').hidden && document.getElementById('file-menu').hidden"); + await delay(200); // Allow the app's 150 ms blur handler and hover transitions to finish. + await settled(page); +} + +async function snapshot(page, output, name, clip) { + const capture = await page.send("Page.captureScreenshot", { format: "png", fromSurface: true, + captureBeyondViewport: false, clip: { ...clip, scale: 1 } }); + const bytes = Buffer.from(capture.data, "base64"); + await writeFile(path.join(output, name), bytes); + const canvasData = await evaluate(page, "({gpu:kestrel.renderer.canvas.toDataURL('image/png'),overlay:kestrel.renderer.overlay.toDataURL('image/png')})"); + const layers = {}; + for (const [layer, dataUrl] of Object.entries(canvasData)) { + if (!dataUrl.startsWith("data:image/png;base64,")) throw new Error("Canvas PNG serialization failed"); + const layerBytes = Buffer.from(dataUrl.slice("data:image/png;base64,".length), "base64"); + const layerName = name.replace(/\.png$/, `-${layer}.png`); + await writeFile(path.join(output, layerName), layerBytes); + layers[layer] = { file: layerName, sha256: sha(layerBytes), width: layerBytes.readUInt32BE(16), height: layerBytes.readUInt32BE(20) }; + } + return { file: name, sha256: sha(bytes), width: bytes.readUInt32BE(16), height: bytes.readUInt32BE(20), + layers, purpose: "Diagnostic reference capture outside the timed interaction; not a presentation path" }; +} + +async function beginTrace(browser) { + let timer; + const completion = new Promise((resolve, reject) => { + timer = setTimeout(() => reject(new Error("Trace completion timed out")), 120_000); + browser.on("Tracing.tracingComplete", event => { clearTimeout(timer); resolve(event); }); + }); + // Avoid an unhandled rejection if an earlier page command fails; callers still observe rejection. + completion.catch(() => {}); + await browser.send("Tracing.start", { transferMode: "ReturnAsStream", streamFormat: "json", + traceConfig: { recordMode: "recordAsMuchAsPossible", includedCategories: ["benchmark", "cc", "gpu", "viz", + "blink.user_timing", "devtools.timeline", "disabled-by-default-devtools.timeline.frame"] } }); + return { completion, cancel: () => clearTimeout(timer) }; +} + +async function endTrace(browser, tracing, output, filename, revision) { + await browser.send("Tracing.end"); + const result = await tracing.completion; + if (result.dataLossOccurred) throw new Error("Chrome lost trace events"); + if (!result.stream) throw new Error("Chrome returned no trace stream"); + const chunks = []; + try { + while (true) { + const chunk = await browser.send("IO.read", { handle: result.stream, size: 1024 * 1024 }); + chunks.push(Buffer.from(chunk.data, chunk.base64Encoded ? "base64" : "utf8")); + if (chunk.eof) break; + } + } finally { await browser.send("IO.close", { handle: result.stream }); } + const raw = Buffer.concat(chunks); + const presentationTiming = analyzePresentation(JSON.parse(raw.toString("utf8")), revision); + const bytes = gzipSync(raw); + await writeFile(path.join(output, filename), bytes); + return { file: filename, sha256: sha(bytes), dataLossOccurred: false, + presentationTiming }; +} + +async function captureCase(chrome, serverUrl, output, test, repetition) { + const page = chrome.page, errors = []; + page.on("Runtime.exceptionThrown", error => errors.push(error)); + await page.send("Emulation.setDeviceMetricsOverride", { ...test.documentViewport, deviceScaleFactor: test.dpr, mobile: false }); + await page.send("Page.navigate", { url: `${serverUrl}/index.html?reference=${test.id}&repeat=${repetition}` }); + await page.send("Page.bringToFront"); + await waitFor(page, "document.documentElement.dataset.ready==='true' && !!window.kestrel?.backendReady"); + const projectPath = test.scene.startsWith("lines-") ? `/reference/${test.scene}.kcad` : `/examples/${test.scene}.kcad`; + await evaluate(page, `(async()=>{ + const response=await fetch(${JSON.stringify(projectPath)}); + if(!response.ok) throw new Error('Project fetch failed'); + await kestrel.openFile(new File([await response.text()],${JSON.stringify(test.scene + ".kcad")},{type:'application/json'})); + kestrel.theme=${JSON.stringify(test.theme)}; kestrel.applyTheme(); + kestrel.setView(${JSON.stringify(test.view)}); kestrel.setStyle(${JSON.stringify(test.style)}); + kestrel.settings.grid=true; kestrel.settings.lineweights=false; kestrel.fit(false); + return true; + })()`); + // Let the app's own transient UI expire; do not patch/remove its DOM for stable screenshots. + await waitFor(page, "document.querySelectorAll('#toast-stack .toast').length===0"); + await settleUi(page); + const app = await evaluate(page, `(()=>{ + const r=kestrel.renderer, info=r.adapter?.info, rect=document.getElementById('viewport').getBoundingClientRect(); + return {backend:r.backend,secureContext:isSecureContext,fallbackReason:r.fallbackReason, + adapter:info?{vendor:info.vendor,architecture:info.architecture,device:info.device,description:info.description,isFallbackAdapter:info.isFallbackAdapter}:null, + clip:{x:rect.x,y:rect.y,width:rect.width,height:rect.height}, + documentViewport:{width:innerWidth,height:innerHeight,dpr:devicePixelRatio}, + canvas:{width:r.canvas.width,height:r.canvas.height},stats:{...r.stats}, + entities:kestrel.doc.entities.length,camera:kestrel.camera.serialize(),visibility:document.visibilityState,focused:document.hasFocus()}; + })()`); + const system = await chrome.browser.send("SystemInfo.getInfo"); + const hardware = hardwareAssessment(system, app); + if (!hardware.hardwareAccelerated) return { test, repetition, status: "unavailable", hardware, app, system }; + if (app.documentViewport.width !== test.documentViewport.width || app.documentViewport.height !== test.documentViewport.height + || app.documentViewport.dpr !== test.dpr || app.visibility !== "visible") throw new Error("Viewport/DPR/visibility mismatch"); + const prefix = `${test.id}-run${repetition}`; + const before = await snapshot(page, output, `${prefix}-before.png`, app.clip); + const tracing = await beginTrace(chrome.browser); + let trace, samples, input; + try { + // Exercise real browser mouse input once, then restore the camera for deterministic timed pans. + const x = app.clip.x + app.clip.width / 2, y = app.clip.y + app.clip.height / 2; + const events = [{ type: "mouseMoved", x, y }, { type: "mousePressed", x, y, button: "middle", buttons: 4, clickCount: 1 }, + { type: "mouseMoved", x: x + 40, y: y + 20, button: "middle", buttons: 4 }, + { type: "mouseReleased", x: x + 40, y: y + 20, button: "middle", buttons: 0, clickCount: 1 }]; + for (const event of events) await page.send("Input.dispatchMouseEvent", event); + await settled(page); + input = await evaluate(page, "({target:kestrel.camera.target.slice(),camera:kestrel.camera.serialize()})"); + input.events = events; + input.changedCamera = JSON.stringify(input.target) !== JSON.stringify(app.camera.target); + if (!input.changedCamera) throw new Error("Middle-button input did not pan the Kestrel camera"); + await settleUi(page); + await evaluate(page, `kestrel.camera.restore(${JSON.stringify(app.camera)});kestrel.invalidate();true`); + await settled(page); + samples = await evaluate(page, `new Promise(resolve=>{ + const app=kestrel, config=${JSON.stringify(test.interaction)}, samples=[]; + const buffers=Object.fromEntries(Object.entries(app.renderer.buffers).map(([key,value])=>[key,value.buffer])); + let previous=null, moved=0; + performance.mark('webscene-reference-pan-start'); + const step=timestamp=>{ + if(previous!==null) samples.push({rafIntervalMs:timestamp-previous,cpuRenderSubmissionMs:app.renderer.stats.cpuMs}); + if(moved===config.frames) { + performance.mark('webscene-reference-pan-end'); + resolve({samples,finalCamera:app.camera.serialize(),stats:{...app.renderer.stats}, + retainedBuffers:Object.fromEntries(Object.entries(buffers).map(([key,value])=>[key,app.renderer.buffers[key]?.buffer===value])),gpuErrors:app.renderer.gpuErrors.slice()}); + return; + } + app.camera.pan(config.dxCssPixels,config.dyCssPixels);app.invalidate();moved++;previous=timestamp; + requestAnimationFrame(step); + }; requestAnimationFrame(step); + })`); + await settled(page); + await delay(100); // Allow platform presentation feedback for the last submitted update to arrive. + trace = await endTrace(chrome.browser, tracing, output, `${prefix}-trace.json.gz`, chrome.revision); + } finally { tracing.cancel(); } + await settled(page); + const after = await snapshot(page, output, `${prefix}-after.png`, app.clip); + if (samples.gpuErrors.length || errors.length) throw new Error("Kestrel reported browser/GPU errors"); + samples.cpuRenderSubmissionMilliseconds = distribution(samples.samples.map(sample => sample.cpuRenderSubmissionMs)); + samples.rafIntervalMilliseconds = distribution(samples.samples.map(sample => sample.rafIntervalMs)); + return { test, repetition, status: "captured", hardware, app, system, input, before, after, trace, samples, + timingScope: "Application CPU render/submission plus rAF intervals; GPU execution and presentation require separate trace analysis" }; +} + +export async function main(argv) { + const args = options(argv); + await mkdir(path.dirname(args.output), { recursive: true }); + await mkdir(args.output); // Never overwrite evidence, including failed attempts. + const evidence = { schemaVersion: 1, status: "running", capturedAt: new Date().toISOString(), + scope: "Hardware Chrome reference for G01; not WebScene support or full epic qualification", + host: { platform: os.platform(), release: os.release(), architecture: os.arch() }, results: [] }; + let chrome, service; + try { + const fixtureRoot = path.join(args.output, "fixture-input"); + const validation = spawnSync(process.env.PYTHON ?? (process.platform === "win32" ? "python" : "python3"), + [path.join(here, "prepare-kestrel.py"), "--destination", fixtureRoot], { encoding: "utf8" }); + if (validation.status !== 0) throw new Error(validation.stderr || validation.stdout); + evidence.fixture = JSON.parse(await readFile(path.join(here, "fixtures/kestrel.json"), "utf8")); + evidence.repositoryCommit = spawnSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).stdout.trim(); + const harnessArchive = await archiveReferenceHarness(args.output); + evidence.harness = harnessArchive.hashes; + evidence.harnessFiles = harnessArchive.files; + await mkdir(path.join(args.output, "generated-inputs"), { recursive: true }); + const generated = new Map(); + evidence.generatedProjects = {}; + for (const count of [10_000, 100_000]) { + const bytes = Buffer.from(JSON.stringify(lineProject(count))); + generated.set(`/reference/lines-${count}.kcad`, bytes); + const file = `generated-inputs/lines-${count}.kcad`; + await writeFile(path.join(args.output, file), bytes); + evidence.generatedProjects[`lines-${count}`] = { file, sha256: sha(bytes), bytes: bytes.length, count }; + } + service = await fixtureServer(path.join(fixtureRoot, "Kestrel-CAD"), generated); + chrome = await startChrome(args.chrome); + evidence.browser = await chrome.browser.send("Browser.getVersion"); + chrome.revision = evidence.browser.revision; + evidence.launch = { executable: args.chrome, args: chrome.args, headless: false }; + await chrome.page.send("Page.addScriptToEvaluateOnNewDocument", { source: "try { localStorage.clear(); } catch {}" }); + for (const test of referenceCases.filter(test => !args.case || test.id === args.case)) { + for (let repetition = 1; repetition <= args.repeat; ++repetition) { + const configured = { ...test, interaction: { ...test.interaction, frames: args.frames } }; + const result = await captureCase(chrome, service.url, args.output, configured, repetition); + evidence.results.push(result); + await writeFile(path.join(args.output, "reference.json"), JSON.stringify(evidence, null, 2) + "\n"); + console.log(`${test.id} run ${repetition}: ${result.status}`); + if (result.status === "unavailable") break; + } + } + evidence.status = evidence.results.every(result => result.status === "captured") ? "captured" : "unavailable"; + evidence.repeatability = referenceCases.filter(test => !args.case || test.id === args.case).map(test => { + const runs = evidence.results.filter(result => result.test.id === test.id && result.status === "captured"); + return { case: test.id, runs: runs.length, beforeExactPngMatch: runs.length >= 2 && new Set(runs.map(run => run.before.sha256)).size === 1, + afterExactPngMatch: runs.length >= 2 && new Set(runs.map(run => run.after.sha256)).size === 1, + layerMatches: Object.fromEntries(["before", "after"].flatMap(phase => ["gpu", "overlay"].map(layer => + [`${phase}-${layer}`, runs.length >= 2 && new Set(runs.map(run => run[phase].layers[layer].sha256)).size === 1]))) }; + }); + evidence.remainingVerification = ["Presentation timing is unavailable for unverified Chrome revisions or incomplete traces", + "Repeat pixel differences require inspection if hashes differ", + "Other target hardware and WebScene comparison remain separate gates"]; + } catch (error) { + evidence.status = "failed"; evidence.error = error.stack ?? String(error); + console.error(evidence.error); + } finally { + if (chrome) { evidence.chromeStderr = chrome.stderr; await stopChrome(chrome); } + if (service) await new Promise(resolve => service.server.close(resolve)); + await writeFile(path.join(args.output, "reference.json"), JSON.stringify(evidence, null, 2) + "\n"); + } + return evidence.status === "captured" ? 0 : evidence.status === "unavailable" ? 77 : 1; +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + main(process.argv.slice(2)).then(code => { process.exitCode = code; }).catch(error => { console.error(error); process.exitCode = 1; }); +} + +// Persist the exact source bytes used to identify a reference capture, including +// uncommitted harness edits. A repository SHA alone cannot recover those bytes. +export async function archiveReferenceHarness(output, sourceDirectory = here) { + const hashes = {}, files = {}; + await mkdir(path.join(output, "harness"), { recursive: true }); + for (const name of ["capture-chrome-reference.mjs", "chrome-session.mjs", "reference-workloads.mjs", "presentation-trace.mjs"]) { + const bytes = await readFile(path.join(sourceDirectory, name)); + const file = `harness/${name}`; + await writeFile(path.join(output, file), bytes); + hashes[name] = sha(bytes); + files[name] = { file, sha256: hashes[name], bytes: bytes.length }; + } + return { hashes, files }; +} diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/harness/chrome-session.mjs b/docs/graphics/evidence/2026-09-08-chrome-metal/harness/chrome-session.mjs new file mode 100644 index 000000000..3d01e6d96 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-metal/harness/chrome-session.mjs @@ -0,0 +1,74 @@ +import { spawn } from "node:child_process"; +import { mkdtemp, rm } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { CdpClient } from "../WebPlatformSubset/chrome/cdp-client.mjs"; + +export const delay = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds)); + +export async function evaluate(client, expression) { + const response = await client.send("Runtime.evaluate", { expression, awaitPromise: true, returnByValue: true }, 60_000); + if (response.exceptionDetails) throw new Error(response.exceptionDetails.exception?.description ?? response.exceptionDetails.text); + return response.result.value; +} + +export async function startChrome(executable) { + const userDataDirectory = await mkdtemp(path.join(os.tmpdir(), "webscene-kestrel-chrome-")); + const args = ["--disable-background-networking", "--disable-component-update", "--disable-default-apps", + "--disable-extensions", "--disable-sync", "--no-first-run", "--no-default-browser-check", + "--remote-debugging-port=0", "--window-size=1960,1200", `--user-data-dir=${userDataDirectory}`, "about:blank"]; + const child = spawn(executable, args, { stdio: ["ignore", "ignore", "pipe"] }); + const session = { child, userDataDirectory, args, stderr: "", browser: null, page: null }; + try { + const endpoint = await new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error("Chrome DevTools startup timed out")), 30_000); + child.stderr.setEncoding("utf8"); + child.stderr.on("data", chunk => { + session.stderr += chunk; + const match = session.stderr.match(/DevTools listening on (ws:\/\/[^\s]+)/); + if (match) { clearTimeout(timer); resolve(match[1]); } + }); + child.once("error", error => { clearTimeout(timer); reject(error); }); + child.once("exit", code => { clearTimeout(timer); reject(new Error(`Chrome exited: ${code}`)); }); + }); + session.browser = await CdpClient.connect(endpoint); + const url = new URL(endpoint); + let target; + for (let attempt = 0; attempt < 100 && !target; ++attempt) { + const targets = await (await fetch(`http://${url.host}/json/list`)).json(); + target = targets.find(value => value.type === "page" && value.webSocketDebuggerUrl); + if (!target) await delay(50); + } + if (!target) throw new Error("Chrome page target unavailable"); + session.page = await CdpClient.connect(target.webSocketDebuggerUrl); + await session.page.send("Page.enable"); + await session.page.send("Runtime.enable"); + await session.page.send("Page.bringToFront"); + return session; + } catch (error) { + await stopChrome(session); + throw error; + } +} + +export async function stopChrome(session) { + try { await session.browser?.send("Browser.close", {}, 2_000); } + catch { session.child.kill("SIGTERM"); } + session.page?.close(); + session.browser?.close(); + if (session.child.exitCode === null && session.child.signalCode === null) { + await new Promise(resolve => { + const timer = setTimeout(() => { session.child.kill("SIGKILL"); resolve(); }, 2_000); + session.child.once("exit", () => { clearTimeout(timer); resolve(); }); + }); + } + await rm(session.userDataDirectory, { recursive: true, force: true }); +} + +export async function waitFor(client, expression) { + for (let attempt = 0; attempt < 300; ++attempt) { + if (await evaluate(client, expression)) return; + await delay(100); + } + throw new Error(`Timed out waiting for ${expression}`); +} diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/harness/presentation-trace.mjs b/docs/graphics/evidence/2026-09-08-chrome-metal/harness/presentation-trace.mjs new file mode 100644 index 000000000..04110e097 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-metal/harness/presentation-trace.mjs @@ -0,0 +1,62 @@ +// This source contract was inspected at the Chrome revision below. Unknown revisions stay unqualified. +export const supportedChromeRevision = "@529d9a34b491745086b59458f58a5aae8292adaa"; +const sourceRoot = `https://chromium.googlesource.com/chromium/src/+/${supportedChromeRevision.slice(1)}/cc/metrics/`; + +export function distribution(values) { + if (!values.length || values.some(value => !Number.isFinite(value))) return null; + const sorted = [...values].sort((a, b) => a - b); + const percentile = fraction => sorted[Math.max(0, Math.ceil(fraction * sorted.length) - 1)]; + return { count: sorted.length, min: sorted[0], median: percentile(0.5), p95: percentile(0.95), + max: sorted.at(-1), mean: sorted.reduce((sum, value) => sum + value, 0) / sorted.length }; +} + +export function analyzePresentation(trace, revision) { + const unavailable = reason => ({ status: "unavailable", reason }); + if (revision !== supportedChromeRevision) return unavailable("Chrome revision has not had its presentation trace source contract verified"); + const events = trace.traceEvents; + if (!Array.isArray(events)) return unavailable("Missing trace event array"); + const starts = events.filter(event => event.name === "webscene-reference-pan-start" && event.ph === "I"); + const ends = events.filter(event => event.name === "webscene-reference-pan-end" && event.ph === "I"); + if (starts.length !== 1 || ends.length !== 1 || starts[0].pid !== ends[0].pid || ends[0].ts <= starts[0].ts) + return unavailable("Missing or ambiguous interaction markers"); + const start = starts[0], end = ends[0], active = new Map(), frames = [], reporterStates = {}; + let ambiguous = false; + const reporters = events.filter(event => event.name === "PipelineReporter" && event.pid === start.pid) + .sort((a, b) => a.ts - b.ts); + for (const event of reporters) { + // Trace local IDs are strings. Do not use the 64-bit numeric surface/display IDs: JSON loses precision. + const identity = event.id2?.local ?? event.id2?.global; + if (!identity) continue; + if (event.ph === "b") { + if (active.has(identity)) ambiguous = true; + active.set(identity, event); + } else if (event.ph === "e") { + const begin = active.get(identity); + active.delete(identity); + if (!begin || begin.ts < start.ts || begin.ts > end.ts) continue; + const info = begin.args?.frame_reporter; + if (!info?.state || event.ts < begin.ts || !Number.isFinite(event.ts)) { ambiguous = true; continue; } + reporterStates[info.state] = (reporterStates[info.state] ?? 0) + 1; + if (["STATE_PRESENTED_ALL", "STATE_PRESENTED_PARTIAL"].includes(info.state)) { + frames.push({ beginMicroseconds: begin.ts, presentedMicroseconds: event.ts, + sequence: info.frame_sequence, source: info.frame_source, layerTreeHost: info.layer_tree_host_id, + partial: info.state === "STATE_PRESENTED_PARTIAL", missingContent: info.has_missing_content === true }); + } + } + } + if (ambiguous) return unavailable("Ambiguous or malformed PipelineReporter event pairing"); + if ([...active.values()].some(event => event.ts >= start.ts && event.ts <= end.ts)) + return unavailable("Trace ended before all interaction reporters completed"); + const timestamps = [...new Set(frames.map(frame => frame.presentedMicroseconds))].sort((a, b) => a - b); + if (timestamps.length < 2) return unavailable("Fewer than two distinct platform presentation feedback timestamps"); + const intervals = timestamps.slice(1).map((timestamp, index) => (timestamp - timestamps[index]) / 1000); + return { status: "measured", source: "Presented PipelineReporter termination timestamps (platform presentation feedback)", + sourceContract: [sourceRoot + "compositor_frame_reporting_controller.cc", sourceRoot + "compositor_frame_reporter.cc"], + revision, rendererPid: start.pid, interactionStartMicroseconds: start.ts, interactionEndMicroseconds: end.ts, + uniquePresentedFrames: timestamps.length, + framesPerSecond: (timestamps.length - 1) * 1e6 / (timestamps.at(-1) - timestamps[0]), + intervalMilliseconds: distribution(intervals), partialReporters: frames.filter(frame => frame.partial).length, + missingContentReporters: frames.filter(frame => frame.missingContent).length, + reporterStates, frames, + note: "Reporter state counts include compositor bookkeeping; they are not counts of distinct application frames. Presentation feedback is separate from CPU submission and rAF delivery." }; +} diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/harness/reference-workloads.mjs b/docs/graphics/evidence/2026-09-08-chrome-metal/harness/reference-workloads.mjs new file mode 100644 index 000000000..90c7b58f3 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-metal/harness/reference-workloads.mjs @@ -0,0 +1,34 @@ +// Synthetic drawing data only. Kestrel application files remain byte-for-byte unchanged. +export const referenceSeed = 0x22c0ffee; + +export function lineProject(count, seed = referenceSeed) { + if (!Number.isInteger(count) || count < 1 || count > 200_000) throw new Error("Invalid line count"); + if (!Number.isInteger(seed) || seed < 0 || seed > 0xffffffff) throw new Error("Invalid uint32 seed"); + let state = seed >>> 0; + const random = () => { + state = (Math.imul(state, 1664525) + 1013904223) >>> 0; + return (state >>> 8) / 0x1000000; + }; + const quantize = value => Math.round(value * 1024) / 1024; + const entities = Array.from({ length: count }, (_, index) => { + const x = quantize((random() - 0.5) * 1000); + const y = quantize((random() - 0.5) * 1000); + const dx = quantize(2 + random() * 18); + const dy = quantize((random() - 0.5) * 40); + return { id: `reference-line-${index}`, type: "LINE", layer: "0", color: "bylayer", + linetype: "ByLayer", points: [[x, y, 0], [quantize(x + dx), quantize(y + dy), 0]] }; + }); + return { format: "kestrel-cad", version: 1, name: `Seeded ${count} lines`, units: "mm", + currentLayer: "0", layers: [{ id: "0", name: "REFERENCE", color: "#59c8d9", visible: true, + locked: false, linetype: "Continuous", lineweight: 0.25 }], entities, camera: null }; +} + +export const referenceCases = ["courtyard", "fixture", "lines-10000", "lines-100000"].flatMap(scene => + [1, 2].flatMap(dpr => ["dark", "light"].map(theme => ({ + id: `${scene}-dpr${dpr}-${theme}`, scene, dpr, theme, + style: scene === "fixture" ? "shaded-edges" : "wireframe", + view: scene === "fixture" ? "iso" : "top", + documentViewport: { width: 1920, height: 1080 }, seed: referenceSeed, + interaction: { kind: "camera-pan", frames: 180, dxCssPixels: 0.5, dyCssPixels: 0.25 } + }))) +); diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/navigation-root-recheck.json.gz b/docs/graphics/evidence/2026-09-08-chrome-metal/navigation-root-recheck.json.gz new file mode 100644 index 000000000..8f9ee9025 Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-chrome-metal/navigation-root-recheck.json.gz differ diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/neutral-ui-check.json.gz b/docs/graphics/evidence/2026-09-08-chrome-metal/neutral-ui-check.json.gz new file mode 100644 index 000000000..c2fb7a04f Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-chrome-metal/neutral-ui-check.json.gz differ diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/overlay-differences.json b/docs/graphics/evidence/2026-09-08-chrome-metal/overlay-differences.json new file mode 100644 index 000000000..f13c00dbf --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-chrome-metal/overlay-differences.json @@ -0,0 +1,174 @@ +[ + { + "case": "courtyard-dpr2-dark", + "phase": "after", + "files": [ + { + "file": "courtyard-dpr2-dark-run1-after-overlay.png", + "sha256": "0d999736f93b0e3514f6648e1c9804757331edb528ec16c6f95285a894b223f5", + "width": 2892, + "height": 1486 + }, + { + "file": "courtyard-dpr2-dark-run2-after-overlay.png", + "sha256": "1d6f47f5b6a8302499027cb829b746ccc0f43204683ae9364f88d9d816b25434", + "width": 2892, + "height": 1486 + } + ], + "changedPixels": 684, + "differenceBounds": [ + 78, + 270, + 2232, + 1414 + ], + "channelDifferenceExtrema": [ + [ + 0, + 170 + ], + [ + 0, + 185 + ], + [ + 0, + 203 + ], + [ + 0, + 217 + ] + ] + }, + { + "case": "fixture-dpr1-dark", + "phase": "before", + "files": [ + { + "file": "fixture-dpr1-dark-run1-before-overlay.png", + "sha256": "e9c126af3eb314fd96255d8b91f5c01a1accd6ef8631b15e12ba42d6f5b8d762", + "width": 1446, + "height": 743 + }, + { + "file": "fixture-dpr1-dark-run2-before-overlay.png", + "sha256": "beaec590bb09b2c4f5edd73297973a1534bd41ef18365ded14e2c89cb2e8015b", + "width": 1446, + "height": 743 + } + ], + "changedPixels": 245, + "differenceBounds": [ + 39, + 547, + 1035, + 717 + ], + "channelDifferenceExtrema": [ + [ + 0, + 170 + ], + [ + 0, + 185 + ], + [ + 0, + 203 + ], + [ + 0, + 163 + ] + ] + }, + { + "case": "fixture-dpr1-light", + "phase": "before", + "files": [ + { + "file": "fixture-dpr1-light-run1-before-overlay.png", + "sha256": "da5fbf43053064390a9f4158621f83a2fd892919749eb9e474a40dd52f9ca8c3", + "width": 1446, + "height": 743 + }, + { + "file": "fixture-dpr1-light-run2-before-overlay.png", + "sha256": "cd2cdeed3eb0c727f3939a764e6d91d2ca138e22c70e71bb4d7f185ef854ddf0", + "width": 1446, + "height": 743 + } + ], + "changedPixels": 247, + "differenceBounds": [ + 39, + 547, + 1035, + 717 + ], + "channelDifferenceExtrema": [ + [ + 0, + 255 + ], + [ + 0, + 255 + ], + [ + 0, + 255 + ], + [ + 0, + 163 + ] + ] + }, + { + "case": "fixture-dpr1-light", + "phase": "after", + "files": [ + { + "file": "fixture-dpr1-light-run1-after-overlay.png", + "sha256": "c755fe8dec9905c153bc0439f604c500ad8d46b4ab38ac9978837fe422bff239", + "width": 1446, + "height": 743 + }, + { + "file": "fixture-dpr1-light-run2-after-overlay.png", + "sha256": "a307fe2c09cb38916aa9772328cad64e124aa5219023ed53291a78e18a0bbe79", + "width": 1446, + "height": 743 + } + ], + "changedPixels": 11993, + "differenceBounds": [ + 39, + 273, + 1205, + 717 + ], + "channelDifferenceExtrema": [ + [ + 0, + 255 + ], + [ + 0, + 255 + ], + [ + 0, + 255 + ], + [ + 0, + 255 + ] + ] + } +] diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/overlay-recheck.json.gz b/docs/graphics/evidence/2026-09-08-chrome-metal/overlay-recheck.json.gz new file mode 100644 index 000000000..aef496a86 Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-chrome-metal/overlay-recheck.json.gz differ diff --git a/docs/graphics/evidence/2026-09-08-chrome-metal/reference.json.gz b/docs/graphics/evidence/2026-09-08-chrome-metal/reference.json.gz new file mode 100644 index 000000000..11ad9d837 Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-chrome-metal/reference.json.gz differ diff --git a/docs/graphics/evidence/2026-09-08-dialog-opening/README.md b/docs/graphics/evidence/2026-09-08-dialog-opening/README.md new file mode 100644 index 000000000..29ce0c372 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-dialog-opening/README.md @@ -0,0 +1,11 @@ +# Dialog opening checkpoint + +Native implementation commit: `8f43c5bb`, tested in a detached clean checkout without the main worktree's two uncommitted dialog UA/test edits. All 19 native CTest tests passed in 13.04 seconds. The Release Metal host ran the original checksum-pinned Kestrel archive with `--mesh-kestrel --verify-kestrel`: BOX opened the original dialog and submission increased the object count from 265 to 266, with no history or modal errors and exit status 0. + +The final five-subtest `contracts/dialog-opening-lifecycle.html` fixture passes against that clean library and Chrome 152.0.7977.77. Coverage includes generated method shape/branding, opening modes, disconnected modal rejection, cancellation/reentrant detachment, autofocus, focus restoration, and the distinction between removing `open` and removing the dialog. An earlier draft incorrectly expected removing `open` to release modal blocking; Chrome and the HTML cleanup algorithm disproved that assumption. That attempted production change was removed. Earlier failed attempts remain under the local artifact directories and are not counted as passes. + +Reference: https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element + +Reproduce the native fixture with the subset runner using `--manifest tests/WebPlatformSubset/webscene-component-profile.json --selection required --test dialog-opening-lifecycle --native-library --output `. Run the same fixture in Chrome using `node tests/WebPlatformSubset/chrome/run-contracts.mjs --path contracts/dialog-opening-lifecycle.html --output ` with `CHROME_BIN` set to the installed executable. + +This is a Kestrel application-path fix, not full dialog conformance. Dedicated ToggleEvent interface semantics, coalesced asynchronous toggle delivery, popover/close-watcher interoperability and complete focusing/event reentrancy remain unqualified. No physical frame-rate claim is made by these checks. diff --git a/docs/graphics/evidence/2026-09-08-dialog-opening/chrome-wpt.json b/docs/graphics/evidence/2026-09-08-dialog-opening/chrome-wpt.json new file mode 100644 index 000000000..42c8cdb8e --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-dialog-opening/chrome-wpt.json @@ -0,0 +1,62 @@ +{ + "schema": "webscene-wpt-contract-chrome-result-v1", + "engine": "chrome", + "identity": "Google Chrome 152.0.7977.77", + "viewport": { + "width": 800, + "height": 600, + "deviceScaleFactor": 1 + }, + "origin": "http://127.0.0.1:56732/", + "recordedAt": "2026-09-08T14:49:30.633Z", + "summary": { + "tests": 1, + "passed": 1, + "failed": 0, + "timedOut": 0, + "subtests": 5, + "subtestsPassed": 5, + "subtestsFailed": 0 + }, + "results": [ + { + "path": "contracts/dialog-opening-lifecycle.html", + "status": "PASS", + "duration": 58, + "message": null, + "subtests": [ + { + "name": "show and showModal preserve mode and reject switching while open", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "showModal requires connection; show supports disconnected dialogs", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "beforetoggle can cancel opening or detach the dialog", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "modal focus uses autofocus and close restores background interaction", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Removing open alone retains modal state; detachment releases it", + "status": "PASS", + "message": null, + "stack": null + } + ], + "diagnostics": [] + } + ] +} diff --git a/docs/graphics/evidence/2026-09-08-dialog-opening/clean-build.log.gz b/docs/graphics/evidence/2026-09-08-dialog-opening/clean-build.log.gz new file mode 100644 index 000000000..253853999 Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-dialog-opening/clean-build.log.gz differ diff --git a/docs/graphics/evidence/2026-09-08-dialog-opening/kestrel-box.log.gz b/docs/graphics/evidence/2026-09-08-dialog-opening/kestrel-box.log.gz new file mode 100644 index 000000000..45015b116 Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-dialog-opening/kestrel-box.log.gz differ diff --git a/docs/graphics/evidence/2026-09-08-dialog-opening/native-tests.log.gz b/docs/graphics/evidence/2026-09-08-dialog-opening/native-tests.log.gz new file mode 100644 index 000000000..40e76506d Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-dialog-opening/native-tests.log.gz differ diff --git a/docs/graphics/evidence/2026-09-08-dialog-opening/native-wpt.json b/docs/graphics/evidence/2026-09-08-dialog-opening/native-wpt.json new file mode 100644 index 000000000..cdbd49d56 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-dialog-opening/native-wpt.json @@ -0,0 +1,66 @@ +{ + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "fa57b9ef6dcafd4fdc06b60232197a33862b69f03cb67693f26bc7c6825c4b79", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=0d7713fdf473573f0fb029115b11a61f7f874ad0fed3d9876a678797aeec9ba1", + "chromiumIdentity": null, + "startedAt": "2026-09-08T14:49:44.728732+00:00", + "duration": "00:00:00.1390755", + "selection": "required", + "summary": { + "tests": 1, + "passed": 1, + "failed": 0, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 5, + "subtestsPassed": 5, + "subtestsFailed": 0 + }, + "results": [ + { + "path": "contracts/dialog-opening-lifecycle.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.1371428", + "message": null, + "subtests": [ + { + "name": "show and showModal preserve mode and reject switching while open", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "showModal requires connection; show supports disconnected dialogs", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "beforetoggle can cancel opening or detach the dialog", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "modal focus uses autofocus and close restores background interaction", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Removing open alone retains modal state; detachment releases it", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] +} \ No newline at end of file diff --git a/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/README.md b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/README.md new file mode 100644 index 000000000..42b64598a --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/README.md @@ -0,0 +1,9 @@ +# Same-source non-GPU graphics-toggle measurements + +This is a partial measurement for epic #22 / issue #23, not the required pre-epic regression gate. Both Release native builds use the same worktree at `b3930ae9`, including the two existing uncommitted dialog edits recorded in the manifest. Their WebScene CMake options differ only in graphics enablement and the associated SDK settings. One shared Release managed benchmark executable was used throughout. + +Twenty fresh processes per variant ran in ten OFF/ON/ON/OFF blocks, each with four engines, ten startup samples, a 1.5-second idle interval, and the existing timer/RAF, console and DOM workloads. The manifest records run order and original output hashes. Compressed sample collections map each filename to its original JSON text, preserving the individual original-file hashes. The exact capture and analysis scripts are included. Write each collection value verbatim to its filename in the variant directory to inspect or reanalyze it; the original full capture remains at the artifact directory named in the scripts. + +The analysis resamples whole ABBA blocks 10,000 times to estimate median-ratio intervals. Only an upper interval bound no greater than 1.05 is classified within 5%; crossing that boundary is inconclusive. Timer/RAF elapsed time, populated/workload process RSS and the measured view/lifecycle managed allocations meet that narrow bound. Startup, idle CPU, console and DOM timings remain inconclusive. No metric has a lower interval bound above 1.05. Completed-work counters match exactly across all runs. + +Manual sample windows stayed open. Background contention is not controlled, and no physical presentation was measured. Per-engine V8 memory counters report zero in this probe and cannot prove memory equivalence. RSS is a separate process-level observation. This comparison isolates graphics build enablement; it does not compare all changes in the PR against the pre-epic baseline. Neither issue #23 nor the epic's full non-GPU acceptance gate is complete. diff --git a/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/analyze.py b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/analyze.py new file mode 100644 index 000000000..94ed2a8fa --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/analyze.py @@ -0,0 +1,33 @@ +import json,pathlib,random,statistics,hashlib +root=pathlib.Path('artifacts/graphics-nongpu-abba-20260908') +m=json.loads((root/'manifest.json').read_text());assert m['status']=='captured' +for entry in m['order']: + assert hashlib.sha256((root/entry['file']).read_bytes()).hexdigest()==entry['sha256'] +samples={name:[json.loads(p.read_text()) for p in sorted((root/name).glob('*.json'))] for name in ['control','candidate']} +assert all(len(s)==20 for s in samples.values()) +assert all(s['options']==samples['control'][0]['options'] for group in samples.values() for s in group) +def val(s,path): + for key in path.split('.'):s=s[key] + return s +paths=['startup.prewarmMilliseconds','startup.warmContextCreateMilliseconds.mean','startup.firstSceneMilliseconds.mean','idle.processCpuMilliseconds','timerAndAnimationFrame.elapsedMilliseconds','timerAndAnimationFrame.processCpuMilliseconds','consoleHeavy.elapsedMilliseconds','consoleHeavy.processCpuMilliseconds','representativeWorkload.elapsedMilliseconds','representativeWorkload.processCpuMilliseconds','memory.populatedViewsWorkingSetBytes','memory.workloadWorkingSetBytes','managedAllocations.ordinaryViewConstructionBytes','managedAllocations.blankLifecycleBytes.median'] +metrics={} +for path in paths: + a=[val(s,path) for s in samples['control']];b=[val(s,path) for s in samples['candidate']] + av,bv=statistics.median(a),statistics.median(b) + rng=random.Random(20260908);ratios=[] + for _ in range(10000): + blocks=[rng.randrange(10) for _ in range(10)] + indices=[2*k+j for k in blocks for j in [0,1]] + x=statistics.median(a[i] for i in indices);y=statistics.median(b[i] for i in indices) + if x>0:ratios.append(y/x) + ratios.sort();interval=[ratios[249],ratios[9749]] if len(ratios)==10000 else None + status='inconclusive' + if interval:status='within-5-percent' if interval[1]<=1.05 else 'regression' if interval[0]>1.05 else 'inconclusive' + metrics[path]={'controlMedian':av,'candidateMedian':bv,'ratio':bv/av if av else None,'blockBootstrap95RatioInterval':interval,'classification':status} +exact={} +for path in ['timerAndAnimationFrame.timersFired','timerAndAnimationFrame.animationFramesInvoked','consoleHeavy.calls','consoleHeavy.completionSignals','representativeWorkload.completionSignals']: + groups={k:sorted(set(val(s,path) for s in v)) for k,v in samples.items()};exact[path]=groups + assert len(groups['control'])==1 and groups['control']==groups['candidate'],(path,groups) +report={'scope':'same-source graphics OFF versus ON','status':'measured; epic baseline gate remains incomplete','method':'20 fresh processes per variant, ten ABBA blocks; 10000 paired block bootstrap resamples of median ratios; 5% boundary. Intervals crossing boundary are inconclusive, not passing.','metrics':metrics,'exactWork':exact,'limitations':['Not pre-epic baseline','Manual application windows remained open; system contention may affect measurements','Probe reports engine/CPU behavior, not physical presentation','Reported V8 heap counters are zero and cannot establish V8 memory equivalence','No Windows/Linux measurement']} +(root/'comparison.json').write_text(json.dumps(report,indent=2)+'\n') +for k,v in metrics.items():print(k,round(v['ratio'],3) if v['ratio'] else None,v['classification']) diff --git a/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/candidate-samples.json.gz b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/candidate-samples.json.gz new file mode 100644 index 000000000..14f1ddcea Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/candidate-samples.json.gz differ diff --git a/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/capture.py b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/capture.py new file mode 100644 index 000000000..47c9e0c12 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/capture.py @@ -0,0 +1,31 @@ +import os,json,subprocess,pathlib,hashlib,datetime +root=pathlib.Path.cwd();out=root/'artifacts/graphics-nongpu-abba-20260908';out.mkdir(exist_ok=False) +exe=root/'benchmarks/WebScene.NativeEngine.Benchmarks/bin/Release/net10.0/WebScene.NativeEngine.Benchmarks' +libs={name:root/f'artifacts/graphics-build/native-v8-{kind}/libwebscene_native_engine.dylib' for name,kind in [('control','disabled'),('candidate','enabled')]} +digest=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() +manifest={'status':'running','source':subprocess.check_output(['git','rev-parse','HEAD'],text=True).strip(),'dirty':subprocess.check_output(['git','status','--short'],text=True),'started':datetime.datetime.now(datetime.timezone.utc).isoformat(),'executable':str(exe),'executableHash':digest(exe),'libraries':{k:{'path':str(v),'sha256':digest(v)} for k,v in libs.items()},'order':[],'comparison':'Same source graphics OFF versus ON; not pre-epic baseline'} +(out/'manifest.json').write_text(json.dumps(manifest,indent=2)) +counts={k:0 for k in libs} +try: + for block in range(10): + for name in ['control','candidate','candidate','control']: + counts[name]+=1;dest=out/name;dest.mkdir(exist_ok=True) + path=dest/f'{counts[name]:02d}.json' + args=[str(exe),'probe','native-inspector-disabled-performance','--contexts','4','--samples','10','--duration-ms','1500'] + result=subprocess.run(args,env={**os.environ,'WEBSCENE_NATIVE_ENGINE_PATH':str(libs[name])},capture_output=True,text=True,timeout=90) + path.with_suffix('.stderr').write_text(result.stderr) + path.write_text(result.stdout) + if result.returncode:raise RuntimeError(f'{name} {counts[name]} exited {result.returncode}') + data=json.loads(result.stdout) + if data['representativeWorkload']['completionSignals']!=4:raise RuntimeError('missing completion') + manifest['order'].append({'variant':name,'file':str(path.relative_to(out)),'sha256':digest(path)}) + (out/'manifest.json').write_text(json.dumps(manifest,indent=2)) + print(f'{block+1}/10 {name} {counts[name]}',flush=True) + for name,path in libs.items(): + if digest(path)!=manifest['libraries'][name]['sha256']:raise RuntimeError('library changed during experiment') + manifest['status']='captured' +except Exception as e: + manifest['status']='failed';manifest['error']=str(e);raise +finally: + manifest['finished']=datetime.datetime.now(datetime.timezone.utc).isoformat() + (out/'manifest.json').write_text(json.dumps(manifest,indent=2)) diff --git a/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/comparison.json b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/comparison.json new file mode 100644 index 000000000..2051412b4 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/comparison.json @@ -0,0 +1,196 @@ +{ + "scope": "same-source graphics OFF versus ON", + "status": "measured; epic baseline gate remains incomplete", + "method": "20 fresh processes per variant, ten ABBA blocks; 10000 paired block bootstrap resamples of median ratios; 5% boundary. Intervals crossing boundary are inconclusive, not passing.", + "metrics": { + "startup.prewarmMilliseconds": { + "controlMedian": 1.5623, + "candidateMedian": 1.6202, + "ratio": 1.0370607437752033, + "blockBootstrap95RatioInterval": [ + 0.9945424013434089, + 1.0876594669698119 + ], + "classification": "inconclusive" + }, + "startup.warmContextCreateMilliseconds.mean": { + "controlMedian": 0.2289, + "candidateMedian": 0.22627999999999998, + "ratio": 0.9885539536915683, + "blockBootstrap95RatioInterval": [ + 0.9173977207510041, + 1.0946375022049744 + ], + "classification": "inconclusive" + }, + "startup.firstSceneMilliseconds.mean": { + "controlMedian": 1.30496, + "candidateMedian": 1.3390849999999999, + "ratio": 1.0261502268268758, + "blockBootstrap95RatioInterval": [ + 0.9650302519721223, + 1.0609214592732052 + ], + "classification": "inconclusive" + }, + "idle.processCpuMilliseconds": { + "controlMedian": 6.288, + "candidateMedian": 6.598, + "ratio": 1.0493002544529262, + "blockBootstrap95RatioInterval": [ + 0.9908787360534246, + 1.1479917184265012 + ], + "classification": "inconclusive" + }, + "timerAndAnimationFrame.elapsedMilliseconds": { + "controlMedian": 719.2196, + "candidateMedian": 717.74685, + "ratio": 0.9979522944035452, + "blockBootstrap95RatioInterval": [ + 0.9929227306642944, + 1.0027917712397967 + ], + "classification": "within-5-percent" + }, + "timerAndAnimationFrame.processCpuMilliseconds": { + "controlMedian": 41.6725, + "candidateMedian": 37.967, + "ratio": 0.9110804487371768, + "blockBootstrap95RatioInterval": [ + 0.8371205973056323, + 1.0721480573879099 + ], + "classification": "inconclusive" + }, + "consoleHeavy.elapsedMilliseconds": { + "controlMedian": 3.732, + "candidateMedian": 4.0625, + "ratio": 1.0885584137191853, + "blockBootstrap95RatioInterval": [ + 0.8388716470849035, + 1.2217289916556626 + ], + "classification": "inconclusive" + }, + "consoleHeavy.processCpuMilliseconds": { + "controlMedian": 12.1935, + "candidateMedian": 12.655000000000001, + "ratio": 1.0378480337884939, + "blockBootstrap95RatioInterval": [ + 0.8281554511062708, + 1.25742621465694 + ], + "classification": "inconclusive" + }, + "representativeWorkload.elapsedMilliseconds": { + "controlMedian": 79.80445, + "candidateMedian": 73.43095, + "ratio": 0.9201360325144775, + "blockBootstrap95RatioInterval": [ + 0.8891992852472665, + 1.0630895391597501 + ], + "classification": "inconclusive" + }, + "representativeWorkload.processCpuMilliseconds": { + "controlMedian": 316.45349999999996, + "candidateMedian": 291.647, + "ratio": 0.921610915979757, + "blockBootstrap95RatioInterval": [ + 0.892301790399539, + 1.0607639866581497 + ], + "classification": "inconclusive" + }, + "memory.populatedViewsWorkingSetBytes": { + "controlMedian": 102719488.0, + "candidateMedian": 103718912.0, + "ratio": 1.0097296435122418, + "blockBootstrap95RatioInterval": [ + 1.008523179862992, + 1.010369306851719 + ], + "classification": "within-5-percent" + }, + "memory.workloadWorkingSetBytes": { + "controlMedian": 148897792.0, + "candidateMedian": 149766144.0, + "ratio": 1.005831866197183, + "blockBootstrap95RatioInterval": [ + 0.9900467747198956, + 1.017085038151056 + ], + "classification": "within-5-percent" + }, + "managedAllocations.ordinaryViewConstructionBytes": { + "controlMedian": 599533.0, + "candidateMedian": 599533.0, + "ratio": 1.0, + "blockBootstrap95RatioInterval": [ + 1.0, + 1.0 + ], + "classification": "within-5-percent" + }, + "managedAllocations.blankLifecycleBytes.median": { + "controlMedian": 2088.0, + "candidateMedian": 2088.0, + "ratio": 1.0, + "blockBootstrap95RatioInterval": [ + 1.0, + 1.0 + ], + "classification": "within-5-percent" + } + }, + "exactWork": { + "timerAndAnimationFrame.timersFired": { + "control": [ + 800 + ], + "candidate": [ + 800 + ] + }, + "timerAndAnimationFrame.animationFramesInvoked": { + "control": [ + 240 + ], + "candidate": [ + 240 + ] + }, + "consoleHeavy.calls": { + "control": [ + 4000 + ], + "candidate": [ + 4000 + ] + }, + "consoleHeavy.completionSignals": { + "control": [ + 4 + ], + "candidate": [ + 4 + ] + }, + "representativeWorkload.completionSignals": { + "control": [ + 4 + ], + "candidate": [ + 4 + ] + } + }, + "limitations": [ + "Not pre-epic baseline", + "Manual application windows remained open; system contention may affect measurements", + "Probe reports engine/CPU behavior, not physical presentation", + "Reported V8 heap counters are zero and cannot establish V8 memory equivalence", + "No Windows/Linux measurement" + ] +} diff --git a/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/control-samples.json.gz b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/control-samples.json.gz new file mode 100644 index 000000000..d46c3fbbf Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/control-samples.json.gz differ diff --git a/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/disabled-cmake-cache.txt.gz b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/disabled-cmake-cache.txt.gz new file mode 100644 index 000000000..4b4f9bd71 Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/disabled-cmake-cache.txt.gz differ diff --git a/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/enabled-cmake-cache.txt.gz b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/enabled-cmake-cache.txt.gz new file mode 100644 index 000000000..b05a2895c Binary files /dev/null and b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/enabled-cmake-cache.txt.gz differ diff --git a/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/manifest.json b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/manifest.json new file mode 100644 index 000000000..dae785f72 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/manifest.json @@ -0,0 +1,222 @@ +{ + "status": "captured", + "source": "b3930ae9ddbbaaad51aaa79f900a6f4889e526d9", + "dirty": " M experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_css_cascade.inc\n M experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_runtime_tests.cpp\n?? out/\n", + "started": "2026-09-08T14:29:05.104744+00:00", + "executable": "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/benchmarks/WebScene.NativeEngine.Benchmarks/bin/Release/net10.0/WebScene.NativeEngine.Benchmarks", + "executableHash": "7ba9e7966f456874d6e99d45f089083fffdbdde8a42452e2a8bb198855b3b6e1", + "libraries": { + "control": { + "path": "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-disabled/libwebscene_native_engine.dylib", + "sha256": "f04270ba42bd4977ad730ab6c9bcc1097b5890bb746f037dee29d3727673d02d" + }, + "candidate": { + "path": "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-build/native-v8-enabled/libwebscene_native_engine.dylib", + "sha256": "3f9303f707f7c9ae750069bcd4e10f17da0f9cf4c3c0e42a063fe4f108cfe8ad" + } + }, + "order": [ + { + "variant": "control", + "file": "control/01.json", + "sha256": "bc44b369b9eb076cad89617275a81e682410f3e57d2fc006ec3d9e9640c368a6" + }, + { + "variant": "candidate", + "file": "candidate/01.json", + "sha256": "6386d21c53b3d0ff2f90a424b4b67a26ec91e05890b5d261b66a1c6d50f9d51a" + }, + { + "variant": "candidate", + "file": "candidate/02.json", + "sha256": "c0e97c6f75c0ada4595f63b2285a91530551660cd8f46c62f0d6d27f844f4f11" + }, + { + "variant": "control", + "file": "control/02.json", + "sha256": "a38162ab9644b64d3ad961877f05a417826c384bba8fce19af736a00b09bd452" + }, + { + "variant": "control", + "file": "control/03.json", + "sha256": "c640386252fce5fb4db424245534b77477f4c87d8637bfbf85d0d0fc569aa9aa" + }, + { + "variant": "candidate", + "file": "candidate/03.json", + "sha256": "84d4d7088c8dcf9b857b02de3d67d343cb6c45a05ae345426222e4d314e777dc" + }, + { + "variant": "candidate", + "file": "candidate/04.json", + "sha256": "48795c1157f183719a88e43f7b34e95b18488b210496c259c54a6ad07b51d178" + }, + { + "variant": "control", + "file": "control/04.json", + "sha256": "1c9a5f8369e7b0c038e35bb35785e3ce2e7162b25d4697f0c361d83dd99af986" + }, + { + "variant": "control", + "file": "control/05.json", + "sha256": "b35b0c2f0f5d4cfb199f5a768eb59ff3dcc008132b3394e58ad28ec136166c2f" + }, + { + "variant": "candidate", + "file": "candidate/05.json", + "sha256": "e84ec12494f163d65052103a1ca543ce4b7cd9f8c6339381e6be8f31d0b713a8" + }, + { + "variant": "candidate", + "file": "candidate/06.json", + "sha256": "62d08209da6a54276ba2394e11c92b236be28300e931efdc7840e5f7b92ed2b7" + }, + { + "variant": "control", + "file": "control/06.json", + "sha256": "15a076b675fb78b4766bd9ec7c6a3153fb1f0510406103a4c0a7f8eed61def5b" + }, + { + "variant": "control", + "file": "control/07.json", + "sha256": "265370e026e2abb1ba6254a8d1c8abb0063a4be23e9fe14c79f176234c6b1c0b" + }, + { + "variant": "candidate", + "file": "candidate/07.json", + "sha256": "15f5672df5645cb22b1b23794c4849a7715da40ee681069f6fd282e716be3d42" + }, + { + "variant": "candidate", + "file": "candidate/08.json", + "sha256": "99eed7ce30717b0ac0f4d2908b98861f38a304a8b4d34f7978ef1549ab9a84f9" + }, + { + "variant": "control", + "file": "control/08.json", + "sha256": "e3b1bc3344807c5f4602b59e6041fd4eba95abe1a109925995dd0fa91c26af8f" + }, + { + "variant": "control", + "file": "control/09.json", + "sha256": "e9971e5d7fccde09332570ce2b8411a68004374c1d967370b2853393044b1a52" + }, + { + "variant": "candidate", + "file": "candidate/09.json", + "sha256": "c66b85ce9983abc42f0f4c842b75f65c5a1a00a5051c0251cc57f5af7f4908a5" + }, + { + "variant": "candidate", + "file": "candidate/10.json", + "sha256": "889eb5ccc6420ca75c0f1236a9f3fa4509ac24d360faabe39ad7349e4ee4be9c" + }, + { + "variant": "control", + "file": "control/10.json", + "sha256": "3713c5780b64a7630c2a322f7bdcfea4583f4ca5ecb2fda81dfd92ec79eb6997" + }, + { + "variant": "control", + "file": "control/11.json", + "sha256": "032bab003119469088a883895d70e6db229cce09449082c22a0e04e40936283b" + }, + { + "variant": "candidate", + "file": "candidate/11.json", + "sha256": "7f2c2fc799ae1f722866d70378f857b682517d8e915c13e8149d993b75bc4482" + }, + { + "variant": "candidate", + "file": "candidate/12.json", + "sha256": "b561641c12213d99b19db162e7375b0159a774b8adb10db727d96a45bd30173b" + }, + { + "variant": "control", + "file": "control/12.json", + "sha256": "9336a8d62c787c80155233ae7ccd0b246e70a178a07b1e2bae676610faa3d640" + }, + { + "variant": "control", + "file": "control/13.json", + "sha256": "6b3abcac440a735b07a61df8402e70ae5265414f7e7d6f28af593b41b402a332" + }, + { + "variant": "candidate", + "file": "candidate/13.json", + "sha256": "2b8a095599c1d2967e40eb42d7ab36d84922186e38f1751638dc15b3b9b6510f" + }, + { + "variant": "candidate", + "file": "candidate/14.json", + "sha256": "45e349d54247cb429f3f7f0f0ebfb4f63ed776b4c0b1137297499245862b1754" + }, + { + "variant": "control", + "file": "control/14.json", + "sha256": "a99ab0c75cbc2bc6e0da2724a010f8b1a6ffa7950e63139f309e1bba68167ca3" + }, + { + "variant": "control", + "file": "control/15.json", + "sha256": "f54ebcbcaa20e5ac76105d851194ec733f6fad77b79603c81aa380ae0941a66f" + }, + { + "variant": "candidate", + "file": "candidate/15.json", + "sha256": "fcc4c2c231ea8ab0fff3da3208a5d65c0f85e15e4f38b9bce469a69f279681c4" + }, + { + "variant": "candidate", + "file": "candidate/16.json", + "sha256": "d2a9499610adc07ef8f55abb3391de77cff03e718a050a6d1179da09ea49e5db" + }, + { + "variant": "control", + "file": "control/16.json", + "sha256": "c7ece8055d04beeb9bb1f4967bdbdc9b4bfa9890fbf9c4ba689dd2293cbaa34f" + }, + { + "variant": "control", + "file": "control/17.json", + "sha256": "707696b83816c904d56ea878636d7691e86b0b7a5a4998a5c41d04c7baa7048d" + }, + { + "variant": "candidate", + "file": "candidate/17.json", + "sha256": "27baae8b507379061d079da7671588e221c127ec2f97c22208d9541cc47af08c" + }, + { + "variant": "candidate", + "file": "candidate/18.json", + "sha256": "4164953ed3f3c5466ddecb2d2a279f197892940ec1d14f872f8e80442eac4b02" + }, + { + "variant": "control", + "file": "control/18.json", + "sha256": "66940a9edd8b9f0cc14ff34088f34a8859d8d2dabbacd1f832518c32f0fa7f7e" + }, + { + "variant": "control", + "file": "control/19.json", + "sha256": "bd0dc615acdbd5c7661db00359ce72a84d9de5018c20774436409e27d5bdc43e" + }, + { + "variant": "candidate", + "file": "candidate/19.json", + "sha256": "77af0651e11f1a0eebda39c37552979e2a61cd909ef8d451463d0c474031eafd" + }, + { + "variant": "candidate", + "file": "candidate/20.json", + "sha256": "9f03dbe352c648e98d39bd348573186c733b76ee1894ac1e004bc9a8f2630d80" + }, + { + "variant": "control", + "file": "control/20.json", + "sha256": "b0e6e560b0e25e6a59edae6bfe0a86fa19b060b31940de1839db24c6fc2374e1" + } + ], + "comparison": "Same source graphics OFF versus ON; not pre-epic baseline", + "finished": "2026-09-08T14:30:46.292246+00:00" +} \ No newline at end of file diff --git a/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/system.txt b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/system.txt new file mode 100644 index 000000000..14233bc01 --- /dev/null +++ b/docs/graphics/evidence/2026-09-08-nongpu-graphics-toggle/system.txt @@ -0,0 +1,4 @@ +ProductName: macOS +ProductVersion: 26.6.2 +BuildVersion: 25G83 +Darwin MacMini 25.6.0 Darwin Kernel Version 25.6.0: Fri Jul 31 19:11:03 PDT 2026; root:xnu-12377.161.14~5/RELEASE_ARM64_T8132 arm64 diff --git a/docs/graphics/evidence/2026-09-09-macos-windows-integration/README.md b/docs/graphics/evidence/2026-09-09-macos-windows-integration/README.md new file mode 100644 index 000000000..b6f1106e8 --- /dev/null +++ b/docs/graphics/evidence/2026-09-09-macos-windows-integration/README.md @@ -0,0 +1,32 @@ +# macOS validation of Windows integration + +Validated production revision `755f808837b89e5181a6a4b05f9a7f22558b57b1` in a clean checkout on the existing Apple M4 macOS host, with the pinned macOS Dawn/ANGLE SDKs and graphics enabled. Existing uncommitted dialog UA/test edits in the original worktree were excluded. + +- Release native and managed GPU host builds passed. +- All 19 native CTest suites passed (17.08 seconds). +- The unmodified Avalonia tests found an exact-pixel failure in `RasterCheckpointPreservesFractionalClearPathTransformAndExport` on both frameworks: alpha 0x91 versus 0x92. The comparison bitmap used the platform default whereas PNG export uses BGRA8888/premultiplied. Matching that surface format fixes the test without tolerances or production changes. After this correction, with the current native library supplied, .NET 8 and .NET 10 each passed 310 tests with 8 skipped. +- Unchanged original Kestrel ZIP/document checksums were verified by the host. Native Metal WebGPU startup, line edit/undo/redo, four stepped window sizes (980x680, 1440x900, 1100x740, 1280x800; DPR 2), validated pan and left-sidebar drag, and BOX creation passed. Each verification process exited 0. Command-history errors remained zero. +- The screenshot was taken from a separate live run of this production revision. + +Commands from the checkout root: + +```sh +ctest --test-dir artifacts/checkpoint-native --output-on-failure +WEBSCENE_TEST_NATIVE_LIBRARY="$PWD/artifacts/checkpoint-native/libwebscene_native_engine.dylib" dotnet test tests/WebScene.Backend.Avalonia.Tests -c Release -m:1 +``` + +For each Kestrel check use `dotnet run --no-build -c Release --project experiments/WebScene.GpuHost.Probe -- --webgpu-metal --kestrel tests/GraphicsCompatibility/fixtures/Kestrel-CAD.zip`, with that same native library override, followed by: + +- `--edit-kestrel --resize-kestrel --verify-kestrel` +- `--pan-kestrel --sidebar-kestrel --verify-kestrel` +- `--mesh-kestrel --verify-kestrel` + +Scope: this validates the existing Metal Kestrel path after integrating Windows changes, not full WebGPU/WebGL conformance, physical 60fps, exhaustive app behavior, or all skipped platform tests. Pan telemetry contains an engine ScriptErrors count of 1 despite zero Kestrel command-history errors and successful workload validation; this report does not claim zero engine-wide errors or a new performance baseline. No native user-resize recording or full Chrome comparison was repeated. + +## Stabilization follow-up + +The previously unexplained engine ScriptErrors counter came from the pan probe's own ResizeObserver: it appended to `p.widths` without initializing that array, and the observer was left connected after cleanup. The probe now initializes the array, disconnects the observer, prints runtime/JavaScript failures, and rejects Kestrel verification when uncaught JavaScript exceptions occurred. Two subsequent pan attempts recorded engine errors 0 before/after but were rejected for unrelated zero-button pointer movements from the desktop. They are not new interaction or performance passes. + +The graphics metadata fixture explicitly targets `win-x64`; an additional host-OS-dependent compiler assertion conflicted with that target on macOS/Linux. Removing that duplicate preserves the existing exact `clang-cl.exe` assertion. The tooling suite passes locally (16 passed, one Windows-specific test skipped). + +Final original-Kestrel BOX plus four-size resize/startup verification exited 0 with zero uncaught JavaScript exceptions; see `stabilization-box-resize.log.gz`. Production renderer code was unchanged by these probe/test corrections. diff --git a/docs/graphics/evidence/2026-09-09-macos-windows-integration/avalonia-tests.log.gz b/docs/graphics/evidence/2026-09-09-macos-windows-integration/avalonia-tests.log.gz new file mode 100644 index 000000000..6eb29ff29 Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-macos-windows-integration/avalonia-tests.log.gz differ diff --git a/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-box.log.gz b/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-box.log.gz new file mode 100644 index 000000000..3c164d28c Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-box.log.gz differ diff --git a/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-edit-resize.log.gz b/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-edit-resize.log.gz new file mode 100644 index 000000000..0eb920069 Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-edit-resize.log.gz differ diff --git a/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-metal.png b/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-metal.png new file mode 100644 index 000000000..0d9adc4d7 Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-metal.png differ diff --git a/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-pan-sidebar.log.gz b/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-pan-sidebar.log.gz new file mode 100644 index 000000000..9ba1fcc11 Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-macos-windows-integration/kestrel-pan-sidebar.log.gz differ diff --git a/docs/graphics/evidence/2026-09-09-macos-windows-integration/native-tests.log.gz b/docs/graphics/evidence/2026-09-09-macos-windows-integration/native-tests.log.gz new file mode 100644 index 000000000..55f368783 Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-macos-windows-integration/native-tests.log.gz differ diff --git a/docs/graphics/evidence/2026-09-09-macos-windows-integration/stabilization-box-resize.log.gz b/docs/graphics/evidence/2026-09-09-macos-windows-integration/stabilization-box-resize.log.gz new file mode 100644 index 000000000..244f5869a Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-macos-windows-integration/stabilization-box-resize.log.gz differ diff --git a/docs/graphics/evidence/2026-09-09-native-aot/README.md b/docs/graphics/evidence/2026-09-09-native-aot/README.md new file mode 100644 index 000000000..326d48867 --- /dev/null +++ b/docs/graphics/evidence/2026-09-09-native-aot/README.md @@ -0,0 +1,37 @@ +# NativeAOT Kestrel on macOS + +The initial NativeAOT publish initialized Kestrel/WebGPU but displayed a white window. First-chance exception tracing reported repeated `The macOS IOSurface route requires a current host CGL context`. Reflection over the runtime host type could no longer identify `IMetalDevice` after trimming, so presentation selected the wrong importer. + +The shared Metal importer now checks the interface directly and reflects its hidden reference-assembly properties through the statically known interface type. NativeAOT retains those accessors. The attached window screenshot confirms the native executable rendered the UI and original CAD canvas. + +Validation on Apple M4/macOS: +- NativeAOT publish with reflection JSON disabled; native executable, no CoreCLR required. +- Original immutable Kestrel ZIP, WebGPU active, BOX, LINE, undo/redo and four window sizes; zero document exceptions and successful scene presentations. `--verify-kestrel` now fails if no scene is successfully rendered. +- `--aot-serialization-probe` verifies checkpoint PNG/state fields and capture/replay resource archive in the actual native executable. +- Generated JSON contracts replace reflection serialization in canvas checkpoints and resource archives. Presenter/checkpoint diagnostic JSON uses explicit nodes. +- Avalonia regressions: 310 passed, 8 skipped per net8.0/net10.0. Checkpoint and HTTP capture/replay regressions also pass with reflection JSON disabled on both targets. +- CI publishes and runs the native serialization probe on macOS and Windows and rejects linker AOT/trim warnings in reachable production source. This headless check does not qualify GPU presentation on hosted runners. + +Publish command: + +```sh +dotnet publish experiments/WebScene.GpuHost.Probe -c Release -r osx-arm64 \ + -p:PublishAot=true -p:JsonSerializerIsReflectionEnabledByDefault=false \ + -o artifacts/kestrel-aot/publish +``` + +The local app bundle additionally contains the graphics-enabled native engine, Dawn/ANGLE and Skia/Avalonia libraries, ICU/snapshot data and original Kestrel archive. Absolute SDK rpaths were removed from bundled libraries; sibling libraries resolve through loader-relative paths. This is a local ad-hoc signed multi-file app bundle, not a notarized release or single binary. JavaScript still runs in V8. + +AOT compatibility is a required product constraint. This evidence qualifies the exercised Kestrel paths only. Other generic interop/converter APIs and probe diagnostics still produce compile-time warnings and need a broader audit; no blanket codebase AOT certification is claimed. NuGet graphics integration and removal of ANGLE from the macOS runtime are addressed in the follow-up below; hosted verification remains required. + +## Dawn-only macOS and release packaging follow-up + +macOS now compiles the native service without ANGLE contexts and links only Dawn. The 16 remaining native tests passed, including actual Metal/Dawn hardware tests locally. The three ANGLE-specific tests still apply on Windows/Linux builds, where ANGLE remains enabled. + +The release workflow now supplies the verified graphics SDK to macOS and Windows native package builders. Both it and SDK validation use one shared exact-cache recipe. SDK cache identities now include that shared action, causing one cold rebuild; later exact hits reuse compiled SDKs. Linux release packages retain their existing non-GPU scope. + +Hosted package builders explicitly exclude hardware-labelled tests; they compile GPU code and test CPU/package contracts. They do not substitute for the locally recorded hardware evidence. The V8 graphics runtime suite is correctly labelled hardware because it requests actual devices. + +A real local macOS NuGet package was packed, its graphics manifest/library hashes validated, then restored into an independent NativeAOT consumer and published. dyld confirmed the engine and Dawn both loaded from that consumer's publish directory. No ANGLE library is in that package. The local test package uses the existing non-PartitionAlloc development SDK; the release workflow retains its pinned PartitionAlloc configuration and still requires CI validation. + +The updated Dawn-only app bundle passed BOX and four window sizes with 239 successful scene renders and zero JavaScript exceptions. Release verification now rejects the previous non-graphics macOS package, a missing/corrupt Windows D3D compiler, and accidental ANGLE inclusion on macOS. Graphics tooling tests: 20 passed, one Windows-only skip. diff --git a/docs/graphics/evidence/2026-09-09-native-aot/aot-interactions.log.gz b/docs/graphics/evidence/2026-09-09-native-aot/aot-interactions.log.gz new file mode 100644 index 000000000..faff64144 Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-native-aot/aot-interactions.log.gz differ diff --git a/docs/graphics/evidence/2026-09-09-native-aot/dawn-only-aot.log.gz b/docs/graphics/evidence/2026-09-09-native-aot/dawn-only-aot.log.gz new file mode 100644 index 000000000..e597ab0a9 Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-native-aot/dawn-only-aot.log.gz differ diff --git a/docs/graphics/evidence/2026-09-09-native-aot/dawn-only-native-tests.log.gz b/docs/graphics/evidence/2026-09-09-native-aot/dawn-only-native-tests.log.gz new file mode 100644 index 000000000..445d7b0b8 Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-native-aot/dawn-only-native-tests.log.gz differ diff --git a/docs/graphics/evidence/2026-09-09-native-aot/kestrel.png b/docs/graphics/evidence/2026-09-09-native-aot/kestrel.png new file mode 100644 index 000000000..c518b647b Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-native-aot/kestrel.png differ diff --git a/docs/graphics/evidence/2026-09-09-native-aot/managed-tests.log.gz b/docs/graphics/evidence/2026-09-09-native-aot/managed-tests.log.gz new file mode 100644 index 000000000..c953d7d16 Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-native-aot/managed-tests.log.gz differ diff --git a/docs/graphics/evidence/2026-09-09-native-aot/nuget-aot-consumer.log.gz b/docs/graphics/evidence/2026-09-09-native-aot/nuget-aot-consumer.log.gz new file mode 100644 index 000000000..a2204c14f Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-native-aot/nuget-aot-consumer.log.gz differ diff --git a/docs/graphics/evidence/2026-09-09-native-aot/reflection-disabled-tests.log.gz b/docs/graphics/evidence/2026-09-09-native-aot/reflection-disabled-tests.log.gz new file mode 100644 index 000000000..ed1122e36 Binary files /dev/null and b/docs/graphics/evidence/2026-09-09-native-aot/reflection-disabled-tests.log.gz differ diff --git a/docs/graphics/evidence/2026-09-10-merge-stabilization/README.md b/docs/graphics/evidence/2026-09-10-merge-stabilization/README.md new file mode 100644 index 000000000..719e331b8 --- /dev/null +++ b/docs/graphics/evidence/2026-09-10-merge-stabilization/README.md @@ -0,0 +1,36 @@ +# Merge stabilization + +Native runtime revision: c65ab896. Kestrel probe rebuilt with generated JSON +serialization for pan/sidebar diagnostics; reflection serialization disabled. +Original Kestrel zip remains unchanged. Avalonia 12 sample, macOS arm64. + +Combined flags: `--edit-kestrel --exercise-kestrel --pan-kestrel --sidebar-kestrel +--continuous-resize-kestrel --resize-kestrel --verify-kestrel` with `--webgpu-metal`. +Native path: staged c65ab896 engine with matching Dawn/ICU/snapshot sidecars. + +Result: exit 0. Editing/undo/redo, application exercise, pan, sidebar and both +resize workloads completed. Selected runtime output: + +```text +Kestrel pan workload validated (physical presentation remains unqualified). +Kestrel sidebar workload validated (physical presentation remains unqualified). +Kestrel continuous window resize workload validated (physical presentation and native user drag remain unqualified). +Kestrel uncaught JavaScript exceptions: 0 +Kestrel successfully rendered scenes: 616 +Kestrel WebGPU startup check passed (interaction qualification remains). +``` + +`pan-analysis.json` contains callback/queue timings, not physical scanout evidence. +The AOT pan/sidebar trace failure was fixed with typed records and generated +serialization; no reflection fallback was enabled. + +Other checks at the same runtime revision: +- Native CTest 18/18; shared media contracts 9/9; macOS video contract 1/1. +- Frameforge packaged AOT executable, using bundle libraries/assets: media test + passed, nine seek positions, actual RMS and track checks, worker waveform error + 1.49e-8, 30 scenes; MIME/HEAD/range/416 server verification passed. +- TradingView live desktop startup readiness passed, wall time 3259ms; this does + not qualify interactive TradingView panning or physical 60fps. +- Graphics Python tooling: 6/6 passed. + +Final pushed revision CI/package-consumer results remain the merge gate. diff --git a/docs/graphics/evidence/2026-09-10-merge-stabilization/pan-analysis.json b/docs/graphics/evidence/2026-09-10-merge-stabilization/pan-analysis.json new file mode 100644 index 000000000..f7c32ccee --- /dev/null +++ b/docs/graphics/evidence/2026-09-10-merge-stabilization/pan-analysis.json @@ -0,0 +1,33 @@ +{ + "physicalPresentationVerified": false, + "publicationToAcceptance": { + "count": 82, + "medianMilliseconds": 11.352354, + "p95Milliseconds": 12.516375, + "maximumMilliseconds": 16.419667 + }, + "acceptanceToDrawCallbackEnd": { + "count": 82, + "medianMilliseconds": 0.9744375000000001, + "p95Milliseconds": 1.373416, + "maximumMilliseconds": 1.720291 + }, + "publicationToDrawCallbackEnd": { + "count": 82, + "medianMilliseconds": 12.302521, + "p95Milliseconds": 13.486375, + "maximumMilliseconds": 17.418 + }, + "inputToPublishedConsumptionWatermark": { + "count": 80, + "medianMilliseconds": 11.8970415, + "p95Milliseconds": 15.074291, + "maximumMilliseconds": 29.400167 + }, + "unmatchedInputCount": 0, + "limitations": [ + "Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived." + ] +} diff --git a/docs/graphics/evidence/angle-context-texture-isolation.json b/docs/graphics/evidence/angle-context-texture-isolation.json new file mode 100644 index 000000000..9c839985f --- /dev/null +++ b/docs/graphics/evidence/angle-context-texture-isolation.json @@ -0,0 +1,22 @@ +{ + "sourceBaseCommit": "038c0678e5b4667f36f68cdee68861460cbd761f", + "scope": "Native ANGLE Metal ES2/ES3 context and texture isolation on macOS", + "buildCommand": "cmake --build artifacts/graphics-build/native-v8-enabled --target webscene_graphics_angle_context_tests -j2", + "testCommand": "ctest --test-dir artifacts/graphics-build/native-v8-enabled -R '^webscene_graphics_angle_(es3_)?context_tests$' --output-on-failure", + "result": { + "passed": 2, + "failed": 0, + "seconds": 0.42 + }, + "checks": [ + "Texture name from first context is not a texture in independent second context", + "Each context uploads and reads back its own exact RGBA pixel via complete framebuffer", + "First texture remains valid and correct after second context and its texture are destroyed", + "Nested current-context restoration and execution-thread rejection remain covered" + ], + "limits": [ + "Diagnostic one-pixel readback is not production presentation transport", + "No WebGL browser API/fallback or context-loss qualification", + "Windows/Linux target branches still require hardware runs" + ] +} diff --git a/docs/graphics/evidence/angle-service-loss/README.md b/docs/graphics/evidence/angle-service-loss/README.md new file mode 100644 index 000000000..bbb647071 --- /dev/null +++ b/docs/graphics/evidence/angle-service-loss/README.md @@ -0,0 +1,7 @@ +# ANGLE service loss reporting + +`graphics_service::with_angle_context` throws `angle_context_lost` when activation detects a reset or when a normally returning command has lost its context. The native owner keeps loss sticky. Further commands reject before their callback runs. Independent contexts remain executable, and ordinary scope unwinding restores the previous context. + +Native command callbacks are `noexcept`: a caller that submits context operations must translate `angle_context_lost` into its own completion/cancellation result. It must not catch all exceptions and silently treat invalid handles or programming errors as context loss. This service does not dispatch DOM events or recreate a browser context. + +The hardware test queues four commands in order: inject loss, use the lost context, use an independent context, use the lost context again. It verifies typed loss for the first command, no callback entry for both later lost-context commands, successful independent execution, FIFO outcome order, and zero resources after teardown. ES2/ES3 owner tests separately cover nested scope restoration and texture isolation. See `result.json` and compressed build/test logs for the exact commands and scope limits. diff --git a/docs/graphics/evidence/angle-service-loss/build.log.gz b/docs/graphics/evidence/angle-service-loss/build.log.gz new file mode 100644 index 000000000..204d757b6 Binary files /dev/null and b/docs/graphics/evidence/angle-service-loss/build.log.gz differ diff --git a/docs/graphics/evidence/angle-service-loss/result.json b/docs/graphics/evidence/angle-service-loss/result.json new file mode 100644 index 000000000..8889d231f --- /dev/null +++ b/docs/graphics/evidence/angle-service-loss/result.json @@ -0,0 +1,27 @@ +{ + "date": "2026-09-08", + "platform": "macOS arm64 / Apple M4 / ANGLE Metal", + "change": "Typed ANGLE loss reported at activation and after graphics-service command execution", + "tests": { + "build": "cmake --build artifacts/graphics-build/native-v8-enabled -j2", + "test": "ctest --test-dir artifacts/graphics-build/native-v8-enabled --output-on-failure", + "result": "19/19 passed, 13.12 seconds", + "queue_result": [ + "loss reported for injecting command", + "next command for lost context rejected before callback", + "independent context command executes", + "subsequent lost-context command rejected before callback" + ], + "teardown": "zero live contexts and zero queued commands" + }, + "limitations": [ + "Injected ANGLE context loss; not physical GPU reset", + "No browser WebGL events, restoration or conformance claim", + "Windows/Linux not tested", + "Working tree includes pre-existing uncommitted dialog UA/runtime-test edits, excluded from this commit" + ], + "logs": { + "build.log.gz": "2af44c8ce4a337fd171ebd77490d47af2a6e01ac42365ac338b9de8aa023a615", + "tests.log.gz": "c7e79c478a9f58f96c90b7b6506b70ecf5225a5bdcf661c540eac27b7c2d2a52" + } +} diff --git a/docs/graphics/evidence/angle-service-loss/tests.log.gz b/docs/graphics/evidence/angle-service-loss/tests.log.gz new file mode 100644 index 000000000..31353b166 Binary files /dev/null and b/docs/graphics/evidence/angle-service-loss/tests.log.gz differ diff --git a/docs/graphics/evidence/avalonia-host/dawn-graphite-window.png b/docs/graphics/evidence/avalonia-host/dawn-graphite-window.png new file mode 100644 index 000000000..3849a6612 Binary files /dev/null and b/docs/graphics/evidence/avalonia-host/dawn-graphite-window.png differ diff --git a/docs/graphics/evidence/avalonia-host/shared-gl-window.png b/docs/graphics/evidence/avalonia-host/shared-gl-window.png new file mode 100644 index 000000000..7ba3fa4b5 Binary files /dev/null and b/docs/graphics/evidence/avalonia-host/shared-gl-window.png differ diff --git a/docs/graphics/evidence/completion-cancellation-stress.json b/docs/graphics/evidence/completion-cancellation-stress.json new file mode 100644 index 000000000..f9bcdf5b6 --- /dev/null +++ b/docs/graphics/evidence/completion-cancellation-stress.json @@ -0,0 +1,35 @@ +{ + "scope": "Native completion mailbox only; not Dawn/ANGLE device loss or complete graphics service qualification", + "sourceBaseCommit": "ee3695f8edf64028f58cee80774380d99a1cd2c3", + "compiler": "Apple clang version 21.0.0 (clang-2100.1.1.101)\nTarget: arm64-apple-darwin25.6.0\nThread model: posix\nInstalledDir: /Volumes/SSD/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin", + "rounds": 1000, + "operations": 16000, + "capacity": 16, + "saturatedReservations": 1000, + "endOfEveryRound": { + "pending": 0, + "ready": 0, + "nativePending": 0, + "occupied": 0 + }, + "validation": [ + "Exactly-once engine-thread delivery", + "Cancelled-owner isolation from successful-owner completions", + "Late/duplicate callback rejection", + "Safe slot reuse after native callback retirement" + ], + "commands": [ + { + "build": "clang++ -std=c++20 -g -O1 -fsanitize=address,undefined -fno-omit-frame-pointer -pthread -I experiments/WebScene.NativeEngine.Probe/native experiments/WebScene.NativeEngine.Probe/tests/graphics_completion_tests.cpp -o /tmp/webscene-completion-stress-asan", + "run": "/tmp/webscene-completion-stress-asan", + "exitCode": 0, + "sanitizerDiagnostics": false + }, + { + "build": "clang++ -std=c++20 -g -O1 -fsanitize=thread -fno-omit-frame-pointer -pthread -I experiments/WebScene.NativeEngine.Probe/native experiments/WebScene.NativeEngine.Probe/tests/graphics_completion_tests.cpp -o /tmp/webscene-completion-stress-tsan", + "run": "/tmp/webscene-completion-stress-tsan", + "exitCode": 0, + "sanitizerDiagnostics": false + } + ] +} diff --git a/docs/graphics/evidence/completion-shutdown-stress.json b/docs/graphics/evidence/completion-shutdown-stress.json new file mode 100644 index 000000000..9be20b2ed --- /dev/null +++ b/docs/graphics/evidence/completion-shutdown-stress.json @@ -0,0 +1,33 @@ +{ + "scope": "Native completion mailbox shutdown; device_lost callback status is injected, not physical device-loss qualification", + "sourceBaseCommit": "ba757275bdc26b152b9ced282c20342595560870", + "shutdownRounds": 500, + "operations": 8000, + "mailboxCapacity": 16, + "checks": [ + "Exactly one cancellation per admitted operation after close", + "Admission rejected after close", + "Native callbacks retire occupied slots after logical cancellation", + "Wake ownership released after callbacks finish", + "Duplicate late callbacks rejected" + ], + "sanitizerRuns": [ + { + "flags": "-fsanitize=address,undefined", + "exitCode": 0 + }, + { + "flags": "-fsanitize=thread", + "exitCode": 0 + } + ], + "buildTemplate": "clang++ -std=c++20 -g -O1 -fno-omit-frame-pointer -pthread -I experiments/WebScene.NativeEngine.Probe/native experiments/WebScene.NativeEngine.Probe/tests/graphics_completion_tests.cpp -o /tmp/webscene-completion-shutdown-test", + "runCommand": "/tmp/webscene-completion-shutdown-test", + "cmakeBuild": "cmake --build artifacts/graphics-build/native-v8-enabled --target webscene_graphics_completion_tests -j4", + "ctest": "ctest --test-dir artifacts/graphics-build/native-v8-enabled -R '^webscene_graphics_completion_tests$' --output-on-failure", + "ctestResult": { + "passed": 1, + "failed": 0, + "seconds": 0.31 + } +} diff --git a/docs/graphics/evidence/concurrent-upload-queue.json b/docs/graphics/evidence/concurrent-upload-queue.json new file mode 100644 index 000000000..1f6918fe2 --- /dev/null +++ b/docs/graphics/evidence/concurrent-upload-queue.json @@ -0,0 +1,33 @@ +{ + "scope": "Native bounded command/upload queue; not V8 typed-array detachment or GPU execution qualification", + "sourceBaseCommit": "5154a48c358fb1b1de50668a8fe4b531f418883d", + "producers": 4, + "commandsPerProducer": 5000, + "capacity": 8, + "uploadBytesPerCommand": 32, + "totalUploadBytes": 640000, + "checks": [ + "Global admission serials delivered in order", + "Per-producer command order preserved", + "Source mutation after admission does not affect queued payload", + "Payload remains stable across yields during consumer access", + "All commands delivered; queue depth returns to zero; high-water does not exceed capacity" + ], + "sanitizers": [ + "AddressSanitizer", + "UndefinedBehaviorSanitizer", + "ThreadSanitizer" + ], + "sanitizerExitCodes": [ + 0, + 0 + ], + "buildTemplate": "clang++ -std=c++20 -g -O1 -fsanitize= -fno-omit-frame-pointer -pthread -I experiments/WebScene.NativeEngine.Probe/native experiments/WebScene.NativeEngine.Probe/tests/graphics_queue_tests.cpp -o /tmp/webscene-upload-queue-test", + "runCommand": "/tmp/webscene-upload-queue-test", + "ctestCommand": "ctest --test-dir artifacts/graphics-build/native-v8-enabled -R '^webscene_graphics_queue_tests$' --output-on-failure", + "ctest": { + "passed": 1, + "failed": 0, + "seconds": 0.37 + } +} diff --git a/docs/graphics/evidence/dawn-event-service-recheck.json b/docs/graphics/evidence/dawn-event-service-recheck.json new file mode 100644 index 000000000..c492bad39 --- /dev/null +++ b/docs/graphics/evidence/dawn-event-service-recheck.json @@ -0,0 +1,23 @@ +{ + "sourceCommit": "39f62ce45920a6a4e220be13e8bde88e2ecfb097", + "sourceSha256": "a9c24f5d03309b41c158ea7781e39ab062a23f11f70a0a479bcda729f2455361", + "command": "ctest --test-dir artifacts/graphics-build/native-v8-enabled -R '^webscene_graphics_dawn_event_tests$' --output-on-failure", + "buildCommand": "cmake --build artifacts/graphics-build/native-v8-enabled --target webscene_graphics_dawn_event_tests -j4", + "result": { + "passed": 1, + "failed": 0, + "seconds": 0.5 + }, + "reviewedCoverage": [ + "Dawn ForceLoss with pending mapping; owner completion becomes device_lost", + "Late native map callback cannot deliver the operation twice; occupied and native_pending counters return to zero", + "Destroyed device rejects stale service handle; independent device performs upload/map verification", + "Completion progress through native service pumping without UI or RAF" + ], + "limits": [ + "ForceLoss is a Dawn diagnostic injection, not physical GPU reset", + "Independent-device survivor test covers explicit destruction, not forced loss", + "Does not establish end-to-end V8 promise and presenter image retirement on loss", + "No Windows/Linux hardware or full conformance qualification" + ] +} diff --git a/docs/graphics/evidence/dawn-forced-loss-isolation.json b/docs/graphics/evidence/dawn-forced-loss-isolation.json new file mode 100644 index 000000000..08ff2b7ee --- /dev/null +++ b/docs/graphics/evidence/dawn-forced-loss-isolation.json @@ -0,0 +1,26 @@ +{ + "sourceBaseCommit": "e40a675867d22ceb75da35509007c44b7388f9b5", + "scope": "Native Dawn service device isolation under diagnostic ForceLoss on macOS", + "test": "webscene_graphics_dawn_event_tests", + "buildCommand": "cmake --build artifacts/graphics-build/native-v8-enabled --target webscene_graphics_dawn_event_tests -j4", + "testCommand": "ctest --test-dir artifacts/graphics-build/native-v8-enabled -R '^webscene_graphics_dawn_event_tests$' --output-on-failure", + "result": { + "passed": 1, + "failed": 0, + "seconds": 0.52 + }, + "checks": [ + "Independent device adopted before sibling ForceLoss", + "Lost sibling pending mapping cancelled once and late callback retired", + "Survivor service handle remains usable", + "Survivor writes and maps 4096 bytes after loss; all 1024 words match call-time data after source mutation", + "Survivor loss signal remains clear", + "Survivor destruction returns device count and mailbox occupancy to expected values" + ], + "developmentFailure": "Initial setup reused a consumed adapter; Dawn rejected RequestDevice. Corrected by requesting a fresh adapter object before the independent device. Earlier failures are not passes.", + "limits": [ + "Dawn ForceLoss injection, not physical GPU reset", + "No end-to-end V8/presenter loss qualification", + "No cross-platform hardware claim" + ] +} diff --git a/docs/graphics/evidence/ganesh-host/detached-retirement.json b/docs/graphics/evidence/ganesh-host/detached-retirement.json new file mode 100644 index 000000000..767c575fa --- /dev/null +++ b/docs/graphics/evidence/ganesh-host/detached-retirement.json @@ -0,0 +1,12 @@ +{ + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "detachedBeforeRetirement": true, + "retirementThreadDiffersFromRenderingThread": true, + "normalCompositionHandlerConnected": false +} diff --git a/docs/graphics/evidence/ganesh-host/frame-texture-expiry.json b/docs/graphics/evidence/ganesh-host/frame-texture-expiry.json new file mode 100644 index 000000000..d67befe01 --- /dev/null +++ b/docs/graphics/evidence/ganesh-host/frame-texture-expiry.json @@ -0,0 +1,12 @@ +{ + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 1, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 4, + "physicalPresentationVerified": false, + "expiredTextureSubmissionRejected": true, + "javascriptCanvasConnected": false, + "verificationCommand": "WEBSCENE_TEST_NATIVE_LIBRARY=\"$PWD/artifacts/graphics-build/native-v8-enabled/libwebscene_native_engine.dylib\" WEBSCENE_TEST_GPU_FIXTURE_LIBRARY=\"$PWD/artifacts/graphics-build/native-v8-enabled/libwebscene_graphics_iosurface_fixture.dylib\" dotnet run --project experiments/WebScene.GpuHost.Probe -- --ganesh-window --verify-window-pixels" +} diff --git a/docs/graphics/evidence/ganesh-host/mixed-canvas-pixels.json b/docs/graphics/evidence/ganesh-host/mixed-canvas-pixels.json new file mode 100644 index 000000000..f146e89bd --- /dev/null +++ b/docs/graphics/evidence/ganesh-host/mixed-canvas-pixels.json @@ -0,0 +1 @@ +{"route":"Dawn-IOSurface-CGL-Ganesh","renderedFrames":32,"imports":1,"gpuRetirementCompleted":true,"explicitTransportCopies":0,"diagnosticReadbacks":4,"physicalPresentationVerified":false} diff --git a/docs/graphics/evidence/ganesh-host/no-readback.json b/docs/graphics/evidence/ganesh-host/no-readback.json new file mode 100644 index 000000000..9582f390a --- /dev/null +++ b/docs/graphics/evidence/ganesh-host/no-readback.json @@ -0,0 +1 @@ +{"route":"Dawn-IOSurface-CGL-Ganesh","renderedFrames":32,"imports":1,"gpuRetirementCompleted":true,"explicitTransportCopies":0,"diagnosticReadbacks":0,"physicalPresentationVerified":false} diff --git a/docs/graphics/evidence/ganesh-host/ordered-scene-pixels.json b/docs/graphics/evidence/ganesh-host/ordered-scene-pixels.json new file mode 100644 index 000000000..49f760005 --- /dev/null +++ b/docs/graphics/evidence/ganesh-host/ordered-scene-pixels.json @@ -0,0 +1 @@ +{"route":"Dawn-IOSurface-CGL-Ganesh","renderedFrames":32,"imports":1,"gpuRetirementCompleted":true,"explicitTransportCopies":0,"diagnosticReadbacks":3,"physicalPresentationVerified":false} diff --git a/docs/graphics/evidence/ganesh-host/pixel-check.json b/docs/graphics/evidence/ganesh-host/pixel-check.json new file mode 100644 index 000000000..f753b150f --- /dev/null +++ b/docs/graphics/evidence/ganesh-host/pixel-check.json @@ -0,0 +1 @@ +{"route":"Dawn-IOSurface-CGL-Ganesh","renderedFrames":32,"imports":1,"gpuRetirementCompleted":true,"explicitTransportCopies":0,"diagnosticReadbacks":2,"physicalPresentationVerified":false} diff --git a/docs/graphics/evidence/ganesh-host/scene-image-replacement.json b/docs/graphics/evidence/ganesh-host/scene-image-replacement.json new file mode 100644 index 000000000..c986802d8 --- /dev/null +++ b/docs/graphics/evidence/ganesh-host/scene-image-replacement.json @@ -0,0 +1,12 @@ +{ + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "replacementAfterFrames": 16, + "replacementRetainsSameSourceImage": true, + "normalCompositionHandlerConnected": false +} diff --git a/docs/graphics/evidence/ganesh-host/submitted-frame-discard.json b/docs/graphics/evidence/ganesh-host/submitted-frame-discard.json new file mode 100644 index 000000000..4335a34d9 --- /dev/null +++ b/docs/graphics/evidence/ganesh-host/submitted-frame-discard.json @@ -0,0 +1,14 @@ +{ + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 1, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 4, + "physicalPresentationVerified": false, + "uninitializedTextureDiscardPassed": true, + "submittedTextureDiscardPassed": true, + "discardExposesNoSceneLease": true, + "poolReleasedAfterCompletion": true, + "javascriptCanvasConnected": false +} diff --git a/docs/graphics/evidence/ganesh-host/submitted-frame-handoff.json b/docs/graphics/evidence/ganesh-host/submitted-frame-handoff.json new file mode 100644 index 000000000..df5891885 --- /dev/null +++ b/docs/graphics/evidence/ganesh-host/submitted-frame-handoff.json @@ -0,0 +1,13 @@ +{ + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 1, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 4, + "physicalPresentationVerified": false, + "applicationSubmissionBeforeHandoff": true, + "foreignAllocationRejected": true, + "expiredTextureSubmissionRejected": true, + "javascriptCanvasConnected": false +} diff --git a/docs/graphics/evidence/graphics-disabled-current/build-test.log.gz b/docs/graphics/evidence/graphics-disabled-current/build-test.log.gz new file mode 100644 index 000000000..179a3c787 Binary files /dev/null and b/docs/graphics/evidence/graphics-disabled-current/build-test.log.gz differ diff --git a/docs/graphics/evidence/graphics-disabled-current/result.json b/docs/graphics/evidence/graphics-disabled-current/result.json new file mode 100644 index 000000000..0236a6056 --- /dev/null +++ b/docs/graphics/evidence/graphics-disabled-current/result.json @@ -0,0 +1,19 @@ +{ + "sourceCommit": "10fec5a7a77461b3dde9edd112e6343bebe10e18", + "workingTreeNote": "Build includes existing uncommitted dialog CSS/test edits; these remain unstaged and were not part of this change.", + "settings": { + "buildType": "Release", + "V8": true, + "graphics": false + }, + "buildCommand": "cmake --build artifacts/graphics-build/native-v8-disabled -j2", + "testCommand": "ctest --test-dir artifacts/graphics-build/native-v8-disabled --output-on-failure", + "tests": { + "passed": 13, + "failed": 0, + "seconds": 13.13 + }, + "librarySha256": "f04270ba42bd4977ad730ab6c9bcc1097b5890bb746f037dee29d3727673d02d", + "linkedLibraries": "artifacts/graphics-build/native-v8-disabled/libwebscene_native_engine.dylib:\n\t@rpath/libwebscene_native_engine.dylib (compatibility version 0.0.0, current version 0.0.0)\n\t/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 5026.5.4)\n\t/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)\n\t/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 5026.5.4)\n\t/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 61901.120.67)\n\t/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.12)\n\t/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 2100.43.0)\n\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)\n", + "scope": "Current macOS V8-enabled graphics-disabled build and tests, not clean checkout or non-GPU performance comparison" +} diff --git a/docs/graphics/evidence/graphite-clean-build/README.md b/docs/graphics/evidence/graphite-clean-build/README.md new file mode 100644 index 000000000..15e1df23f --- /dev/null +++ b/docs/graphics/evidence/graphite-clean-build/README.md @@ -0,0 +1,27 @@ +# Clean Graphite output build + +At WebScene commit f5b8de5, the build entry point completed all 797 Ninja steps +in a new out/webscene-graphite-clean-01 directory on macOS arm64. The sealed +Dawn SDK and previously synchronized Skia source/DEPS were reused. This is a +clean Graphite object build, not a fresh source acquisition or clean Dawn build. + +Reproduction command: + +```sh +python3 eng/graphics/probes/graphite/build-probe.py --skia artifacts/graphics-src/skia --dawn-sdk artifacts/graphics-sdk/osx-arm64/dawn --output out/webscene-graphite-clean-01 --jobs 8 +``` + +build.log.gz records the complete target graph build. probe-build.json records +the compiler, commands, source input hashes and hashes of libskia.a and both +probe binaries. No binaries are committed here. + +The resulting standalone executable ran with the pinned Dawn SDK on its loader +path and passed all 68 IOSurface/CGL pixels (pixels.json). The Avalonia probe ran +with the newly built host dylib directory first on DYLD_LIBRARY_PATH, using +--graphite --verify-markers. It completed 64 changing-marker checks and 64 +host updates, with one device, one Graphite context, two canvas textures and one +output allocation (host.json). Both commands exited zero. + +files.json seals the machine-readable evidence and compressed build log. +This does not qualify production WebScene integration, physical presentation, +other operating systems, source dependency acquisition or the full epic. diff --git a/docs/graphics/evidence/graphite-clean-build/build.log.gz b/docs/graphics/evidence/graphite-clean-build/build.log.gz new file mode 100644 index 000000000..36aa8ecfe Binary files /dev/null and b/docs/graphics/evidence/graphite-clean-build/build.log.gz differ diff --git a/docs/graphics/evidence/graphite-clean-build/files.json b/docs/graphics/evidence/graphite-clean-build/files.json new file mode 100644 index 000000000..4cd9e777d --- /dev/null +++ b/docs/graphics/evidence/graphite-clean-build/files.json @@ -0,0 +1,6 @@ +{ + "build.log.gz": "dee9b32e137e8cb397dc17bb943c076141a03675e9ed3586fe9b78a03092ede8", + "host.json": "aacc49aaca2b27453cfeeec72faa26ee966e1ffa4b287025c786fb98f0a564ff", + "pixels.json": "38231a8dc54cc818fe7cecc3d79f888e52179b7e787ce7e6fbfcf1d3983d5f7f", + "probe-build.json": "534edf4677eadaddd2225c1a40531b1d2f8dc98d3a9158453f5502599166fb57" +} diff --git a/docs/graphics/evidence/graphite-clean-build/host.json b/docs/graphics/evidence/graphite-clean-build/host.json new file mode 100644 index 000000000..15ab4810f --- /dev/null +++ b/docs/graphics/evidence/graphite-clean-build/host.json @@ -0,0 +1 @@ +{"schemaVersion":1,"probe":"avalonia-gpu-host","avalonia":"11.3.4.0","status":"available","imageTypes":[],"semaphoreTypes":[],"isLost":false,"canCreateSharedOpenGlContext":true,"graphiteSource":true,"graphiteSubmissionsCompleted":64,"hostUpdatesCompleted":64,"diagnosticMarkersVerified":64,"canvasTextureAllocations":2,"outputTextureAllocations":1,"graphiteContextInitializations":1,"dawnDeviceInitializations":1,"sharedTextureUpdateCompleted":true,"visualCommitCompleted":true,"presentationVerified":false} diff --git a/docs/graphics/evidence/graphite-clean-build/pixels.json b/docs/graphics/evidence/graphite-clean-build/pixels.json new file mode 100644 index 000000000..30b364934 --- /dev/null +++ b/docs/graphics/evidence/graphite-clean-build/pixels.json @@ -0,0 +1 @@ +{"schemaVersion":1,"probe":"graphite-shared-device","status":"passed","hardwareAccelerated":true,"backend":"metal","adapter":"Apple M4","vendor":"apple","driver":"Metal driver on macOS Version 26.6.2 (Build 25G83)","vendorId":4203,"deviceId":0,"iosurfaceOutput":true,"verifiedPixels":68,"backgroundRGBA":[51,102,153,255],"compositedRGBA":[153,51,77,255],"tolerance":1,"diagnosticReadback":true} diff --git a/docs/graphics/evidence/graphite-clean-build/probe-build.json b/docs/graphics/evidence/graphite-clean-build/probe-build.json new file mode 100644 index 000000000..bc01fa089 --- /dev/null +++ b/docs/graphics/evidence/graphite-clean-build/probe-build.json @@ -0,0 +1,162 @@ +{ + "schemaVersion": 1, + "status": "built", + "hardwareQualified": false, + "skiaRevision": "0f366c36621fc156664662b8ea5426d2f41cefe1", + "skiaDawnBuildSha256": "46356c26d35bb14e1481129e5a39634e76dc2a8007b3f1c225777e642b4ad67f", + "dawnSdkManifestSha256": "8b4396475c8adc6d8f762fdba161ff6f3910953d37c872c336a4d76c243a07a6", + "inputs": { + "eng/graphics/probes/graphite/build-probe.py": "0a5fb58d63c0543f3d429d588296c5319d0c63620674546309fa025b4626fc75", + "eng/graphics/probes/graphite/graphite_probe.cpp": "496e8070921be6fd09bcb1a1e4bb500b89c7a52b8d1ea5221a447ceae639886b", + "eng/graphics/probes/graphite/iosurface_gl_check.h": "e540b17dc60c4795946bc17a0426875ce40798e026aa68e80ba858946c61f1be", + "eng/graphics/probes/graphite/spike-args.gn": "077f32087f0584816788b3a919fb44fdcc9fc255115d6c997d63fdb043ee8230", + "eng/graphics/probes/graphite/use-external-dawn.py": "8e5e976d040e7ff340a0c02fbea106c3a6f7198f97cdeebeba11d2fea1f8bdd3", + "experiments/WebScene.NativeEngine.Probe/native/graphics/angle_context.h": "74521d66ac13f51d7f9918f57239dd67398734b50428e07db3f475e0be98baa9", + "experiments/WebScene.NativeEngine.Probe/native/graphics/angle_display.h": "960fa6d435b610f4519c4c1dcbd0de10fc3ba2187ca9a35810aaf7862510e6fc", + "experiments/WebScene.NativeEngine.Probe/native/graphics/canvas_backing.h": "4a9e5fc2d75da66e9a1bc7ce5f57f02c6ac5710eedc2afaadcb209e7f36d509d", + "experiments/WebScene.NativeEngine.Probe/native/graphics/command_channel.h": "1d26ea69fe820a074690fa84ec883753ea5d32e2e965b8a0e8e0100f88df2581", + "experiments/WebScene.NativeEngine.Probe/native/graphics/completion_mailbox.h": "6ec26c8c3293b95aa299e21123cc978ed8f7676bc981934ad6621361f1526f9a", + "experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_canvas_images.h": "6ec8019a778683a056bff366564db0e100b571103e4c46493f009d89e8d7b180", + "experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_fence_waits.h": "9b83053d92e259c84ae2d042c9068ee01e5ff4592e27486f3b49b0e9ee9d147e", + "experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_shared_color.h": "0547f957fa8a5f0731e25ea27dcb632a514e1d93b16e3ff7bda1dc332349fb2b", + "experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_canvas_images.h": "5c9a10440cb58f48cd5d478d1c5c5f585040e9b1d89ec3aa5a252d2dade74534", + "experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_device.h": "9fa1a26aae976e45d298441992342152bd6d8af965db297a514105e16e889019", + "experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_fences.h": "4de2c0795eb208d56b1f827f6158df4e66fe5460774544550c00cd6ddb1621e8", + "experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_image.h": "2de29a98db4032b9e3befa0d424ea90edb40aba64e07ff97ff21d2ca986bff96", + "experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_event_service.h": "9ef468076a5568b7bd82edb028420ad3a0c96dae4e0d3f791d201ba06538d27f", + "experiments/WebScene.NativeEngine.Probe/native/graphics/dxgi_bridge_contract.h": "9a463595855c3d74327cb3b691789fe400627106ac0fa784c85fd30ff251bf18", + "experiments/WebScene.NativeEngine.Probe/native/graphics/dxgi_device_identity.h": "7c5073e09076212c2de0bea56041765862fd4d5e2387ae979c6f291214460f39", + "experiments/WebScene.NativeEngine.Probe/native/graphics/engine_wake.h": "6d952032471b24387c7d1e95695a42377d7f4d68e6b163d0c363abe751ed4e5e", + "experiments/WebScene.NativeEngine.Probe/native/graphics/graphics_service.h": "84f1292e6cf8d1c3b200e3aa69c0600174a81061300e3c318a8a86fe92ffa1ba", + "experiments/WebScene.NativeEngine.Probe/native/graphics/image_lease_abi.h": "e4e32189b5488602fb9699c94fd9ed35462d82c744122f7a8a6f1cb1d6fc9b51", + "experiments/WebScene.NativeEngine.Probe/native/graphics/image_lease_pool.h": "070634f19abfe367d16d92d42cfb1db1c5e13fbaff0ade5acd50b6e1af73781b", + "experiments/WebScene.NativeEngine.Probe/native/graphics/image_metadata.h": "ea7ba3dcb12386f7baeb004e5ad31819b857830c4ca334c7663c64e494092d7c", + "experiments/WebScene.NativeEngine.Probe/native/graphics/nt_handle.h": "625a12a903296f5555a38468be66da3e7c0c6c06a1d3d2a882def7893430e794", + "experiments/WebScene.NativeEngine.Probe/native/graphics/owned_image_pool.h": "fb36e48f959170d6fd645107efd913e9d7ddadb7e505aba26d6424d0098027da", + "experiments/WebScene.NativeEngine.Probe/native/graphics/release_channel.h": "8f513f173c244f125276e78d09b98dd2df88b1de31060f283e37a66fbdf651d8", + "experiments/WebScene.NativeEngine.Probe/native/graphics/resource_table.h": "c4e87bf8e690f70022696dc17d934c9130ca93725a92d63969c72f5db6ede8e3", + "experiments/WebScene.NativeEngine.Probe/native/graphics/v8_release_registry.h": "e40a3b98ba248062289cfd6c2c2abc80d1f17afd0bed364d035fc7be0a2e3fcb", + "experiments/WebScene.NativeEngine.Probe/native/graphics/work_queue.h": "0a3adbe1de6cfd6738572ea05abfcd15cdc12d4e6343c589cfd1e6d4a501fcb1" + }, + "products": { + "graphite_probe": "08d165980f79d2473372b95174c516d355006b4fda767c65c12cc40f4cc256e4", + "libwebscene_graphite_host_probe.dylib": "36cd78b8aec2146c0967959bba35c1da82ad6a38ac006c4fe610c8f22d116b71", + "libskia.a": "e9fa98ba1f9900647131ccf45f6509bd2f69b69ab60a35a5e5b8c0b44749ba4d" + }, + "commands": [ + [ + "/opt/homebrew/opt/python@3.14/bin/python3.14", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/eng/graphics/verify-sdk.py", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn", + "--component", + "dawn", + "--rid", + "osx-arm64" + ], + [ + "/opt/homebrew/opt/python@3.14/bin/python3.14", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/eng/graphics/probes/graphite/use-external-dawn.py", + "--skia", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/skia", + "--dawn-sdk", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn" + ], + [ + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/skia/bin/gn", + "gen", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/out/webscene-graphite-clean-01", + "--root=/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/skia", + "--args=is_debug = false\nis_official_build = true\nskia_enable_graphite = true\nskia_use_dawn = true\nskia_enable_ganesh = false\nskia_enable_tools = false\nskia_use_icu = false\nskia_use_harfbuzz = false\nskia_use_freetype = false\nskia_use_fontconfig = false\nskia_use_libavif = false\nskia_use_libjxl_decode = false\nskia_use_dng_sdk = false\nskia_use_piex = false\nskia_use_libjpeg_turbo_decode = false\nskia_use_libjpeg_turbo_encode = false\nskia_use_libpng_decode = false\nskia_use_libpng_encode = false\nskia_use_libwebp_decode = false\nskia_use_libwebp_encode = false\nskia_use_zlib = false\nskia_enable_pdf = false\nskia_use_expat = false\nskia_use_rust_png_decode = false\nskia_use_rust_png_encode = false\n" + ], + [ + "ninja", + "-C", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/out/webscene-graphite-clean-01", + "skia", + "-j", + "8" + ], + [ + "clang++", + "-std=c++20", + "-O2", + "-DGL_SILENCE_DEPRECATION", + "-DSK_GRAPHITE", + "-DSK_DAWN", + "-I", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/skia", + "-I", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/eng/graphics/probes/graphite/graphite_probe.cpp", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/out/webscene-graphite-clean-01/libskia.a", + "-L", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/lib", + "-lwebgpu_dawn", + "-framework", + "CoreFoundation", + "-framework", + "CoreGraphics", + "-framework", + "CoreText", + "-framework", + "Foundation", + "-framework", + "ImageIO", + "-framework", + "Metal", + "-framework", + "QuartzCore", + "-framework", + "IOSurface", + "-framework", + "CoreVideo", + "-framework", + "OpenGL", + "-o", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/out/webscene-graphite-clean-01/graphite_probe" + ], + [ + "clang++", + "-std=c++20", + "-O2", + "-DGL_SILENCE_DEPRECATION", + "-DSK_GRAPHITE", + "-DSK_DAWN", + "-I", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-src/skia", + "-I", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/include", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/eng/graphics/probes/graphite/graphite_probe.cpp", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/out/webscene-graphite-clean-01/libskia.a", + "-L", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/graphics-sdk/osx-arm64/dawn/lib", + "-lwebgpu_dawn", + "-framework", + "CoreFoundation", + "-framework", + "CoreGraphics", + "-framework", + "CoreText", + "-framework", + "Foundation", + "-framework", + "ImageIO", + "-framework", + "Metal", + "-framework", + "QuartzCore", + "-framework", + "IOSurface", + "-framework", + "CoreVideo", + "-framework", + "OpenGL", + "-DWEBSCENE_GRAPHITE_HOST_PROBE", + "-Dmain=graphite_probe_main", + "-dynamiclib", + "-o", + "/Volumes/SSD/repos/worktrees/aa5a/HtmlML/out/webscene-graphite-clean-01/libwebscene_graphite_host_probe.dylib" + ] + ], + "compiler": "Apple clang version 21.0.0 (clang-2100.1.1.101)\nTarget: arm64-apple-darwin25.6.0\nThread model: posix\nInstalledDir: /Volumes/SSD/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin\n" +} diff --git a/docs/graphics/evidence/kestrel/acceptance-to-draw-timeline.json b/docs/graphics/evidence/kestrel/acceptance-to-draw-timeline.json new file mode 100644 index 000000000..2732a93e3 --- /dev/null +++ b/docs/graphics/evidence/kestrel/acceptance-to-draw-timeline.json @@ -0,0 +1,1444 @@ +{ + "workloadValidated": true, + "physicalPresentationVerified": false, + "matchedRevisions": 16, + "timestampOrderingVerified": true, + "medianPublicationToAcceptanceMilliseconds": 59.7738545, + "medianAcceptanceToDrawMilliseconds": 2.9949375, + "records": { + "Kestrel pan performance": { + "elapsedMilliseconds": 1911.2758, + "baseline": { + "ContextId": 1, + "Timestamp": 92198872674833, + "Engine": { + "EnqueuedInputs": 16, + "DroppedInputs": 0, + "ConsumedInputs": 16, + "PublishedScenes": 9, + "AcquiredScenes": 9, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3577416, + "InputEventsDispatched": 71, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 584041, + "LastScenePublicationNanoseconds": 1900333, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 5, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 3, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 6, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 1346791, + "LastSceneBuildNanoseconds": 1668250, + "MaximumScenePublicationNanoseconds": 2215833 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 524375, + "MaximumDispatchNanoseconds": 2110917, + "LastDispatchSequence": 639244582178224070, + "DispatchedInputs": 4, + "TotalDispatchNanoseconds": 3828459 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 6, + "TotalDispatchNanoseconds": 6249, + "LastDispatchNanoseconds": 1041, + "MaximumDispatchNanoseconds": 2042, + "LastTimestampMicroseconds": 92197206516 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 9, + "BlockedPublications": 0, + "AcknowledgedScenes": 9, + "TotalAcknowledgementNanoseconds": 486512706, + "LastAcknowledgementNanoseconds": 2007875, + "MaximumAcknowledgementNanoseconds": 204066416, + "AcknowledgedRevision": 9 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3097000, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1798144, + "LatestSceneBytes": 127052, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 663612, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1927996, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 190720, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127200, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 7, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 1, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 1, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 92200785835541, + "Engine": { + "EnqueuedInputs": 124, + "DroppedInputs": 0, + "ConsumedInputs": 124, + "PublishedScenes": 37, + "AcquiredScenes": 37, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1376, + "LayoutPasses": 68, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3577416, + "InputEventsDispatched": 157, + "InputCallbacksInvoked": 70, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 584041, + "LastScenePublicationNanoseconds": 1230250, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 53, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 35, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 32, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 796292, + "LastSceneBuildNanoseconds": 316000, + "MaximumScenePublicationNanoseconds": 3995792 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 2953209, + "MaximumDispatchNanoseconds": 14201333, + "LastDispatchSequence": 639244582178224152, + "DispatchedInputs": 38, + "TotalDispatchNanoseconds": 199772459 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 32, + "TotalDispatchNanoseconds": 37039, + "LastDispatchNanoseconds": 1958, + "MaximumDispatchNanoseconds": 5459, + "LastTimestampMicroseconds": 92200320922 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 56, + "BlockedPublications": 0, + "AcknowledgedScenes": 37, + "TotalAcknowledgementNanoseconds": 2223957872, + "LastAcknowledgementNanoseconds": 50281458, + "MaximumAcknowledgementNanoseconds": 204066416, + "AcknowledgedRevision": 37 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 26, + "AnimationFramesInvoked": 26, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 112, + "WorkerWaits": 171, + "WorkerSignalledWakes": 128, + "WorkerTimeoutWakes": 42, + "SceneBuilds": 28, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3097000, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1798144, + "LatestSceneBytes": 159708, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 663612, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1927996, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 190720, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127200, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5970848, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 1418, + "StringBytes": 96438, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 23, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 65, + "Renders": 16, + "AppliedDiffs": 28, + "InvalidationCalls": 16, + "DamageRectangles": 79, + "ChangedLayers": 26, + "EmptyDamageDiffs": 1, + "PartialDamageDiffs": 27, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 26, + "SkippedEmptyAnimationFrames": 39, + "RenderCallbacks": 16, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.9131607", + "EnqueuedInputs": 108, + "DroppedInputs": 0, + "ConsumedInputs": 108, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 60, + "AppliedAnimationFrames": 26, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 47, + "BlockedPublications": 0, + "PublishedScenes": 28, + "AcquiredScenes": 28, + "AcknowledgedScenes": 28, + "RenderedScenes": 16, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 26, + "AnimationFramesInvoked": 26, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 112, + "WorkerWaits": 171, + "WorkerSignalledWakes": 128, + "WorkerTimeoutWakes": 42, + "SceneBuilds": 28, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 64, + "CompositionRenders": 16, + "CompositionAppliedDiffs": 28, + "CompositionInvalidations": 16, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 26, + "CompositionSkippedEmptyAnimationFrames": 38, + "CompositionRenderCallbacks": 16, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "Kestrel pan diagnostics": { + "events": [ + { + "type": "pointerdown", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 92198888.699958, + "panning": false + }, + { + "type": "pointermove", + "x": 629, + "y": 437.5, + "button": 2, + "buttons": 2, + "time": 92198891.215041, + "panning": true + }, + { + "type": "pointermove", + "x": 633, + "y": 438.5, + "button": 2, + "buttons": 2, + "time": 92198908.521041, + "panning": true + }, + { + "type": "pointermove", + "x": 637, + "y": 439.5, + "button": 2, + "buttons": 2, + "time": 92198934.974625, + "panning": true + }, + { + "type": "pointermove", + "x": 641, + "y": 440.5, + "button": 2, + "buttons": 2, + "time": 92198940.491916, + "panning": true + }, + { + "type": "pointermove", + "x": 681, + "y": 450.5, + "button": 2, + "buttons": 2, + "time": 92199153.932125, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 92199158.535291, + "panning": true + }, + { + "type": "pointermove", + "x": 689, + "y": 452.5, + "button": 2, + "buttons": 2, + "time": 92199178.2625, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 92199262.300708, + "panning": true + }, + { + "type": "pointermove", + "x": 713, + "y": 458.5, + "button": 2, + "buttons": 2, + "time": 92199277.190125, + "panning": true + }, + { + "type": "pointermove", + "x": 729, + "y": 462.5, + "button": 2, + "buttons": 2, + "time": 92199357.625375, + "panning": true + }, + { + "type": "pointermove", + "x": 733, + "y": 463.5, + "button": 2, + "buttons": 2, + "time": 92199378.229083, + "panning": true + }, + { + "type": "pointermove", + "x": 737, + "y": 464.5, + "button": 2, + "buttons": 2, + "time": 92199383.227458, + "panning": true + }, + { + "type": "pointermove", + "x": 753, + "y": 468.5, + "button": 2, + "buttons": 2, + "time": 92199458.225083, + "panning": true + }, + { + "type": "pointermove", + "x": 757, + "y": 469.5, + "button": 2, + "buttons": 2, + "time": 92199476.212083, + "panning": true + }, + { + "type": "pointermove", + "x": 777, + "y": 474.5, + "button": 2, + "buttons": 2, + "time": 92199560.778583, + "panning": true + }, + { + "type": "pointermove", + "x": 781, + "y": 475.5, + "button": 2, + "buttons": 2, + "time": 92199579.835708, + "panning": true + }, + { + "type": "pointermove", + "x": 769, + "y": 472.5, + "button": 2, + "buttons": 2, + "time": 92199666.907875, + "panning": true + }, + { + "type": "pointermove", + "x": 765, + "y": 471.5, + "button": 2, + "buttons": 2, + "time": 92199670.282, + "panning": true + }, + { + "type": "pointermove", + "x": 761, + "y": 470.5, + "button": 2, + "buttons": 2, + "time": 92199697.043958, + "panning": true + }, + { + "type": "pointermove", + "x": 741, + "y": 465.5, + "button": 2, + "buttons": 2, + "time": 92199781.446291, + "panning": true + }, + { + "type": "pointermove", + "x": 737, + "y": 464.5, + "button": 2, + "buttons": 2, + "time": 92199792.17125, + "panning": true + }, + { + "type": "pointermove", + "x": 721, + "y": 460.5, + "button": 2, + "buttons": 2, + "time": 92199858.498041, + "panning": true + }, + { + "type": "pointermove", + "x": 717, + "y": 459.5, + "button": 2, + "buttons": 2, + "time": 92199876.201416, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 92199948.035083, + "panning": true + }, + { + "type": "pointermove", + "x": 697, + "y": 454.5, + "button": 2, + "buttons": 2, + "time": 92199961.732708, + "panning": true + }, + { + "type": "pointermove", + "x": 681, + "y": 450.5, + "button": 2, + "buttons": 2, + "time": 92200032.341458, + "panning": true + }, + { + "type": "pointermove", + "x": 669, + "y": 447.5, + "button": 2, + "buttons": 2, + "time": 92200077.3335, + "panning": true + }, + { + "type": "pointermove", + "x": 657, + "y": 444.5, + "button": 2, + "buttons": 2, + "time": 92200145.490833, + "panning": true + }, + { + "type": "pointermove", + "x": 653, + "y": 443.5, + "button": 2, + "buttons": 2, + "time": 92200153.41575, + "panning": true + }, + { + "type": "pointermove", + "x": 649, + "y": 442.5, + "button": 2, + "buttons": 2, + "time": 92200176.025833, + "panning": true + }, + { + "type": "pointermove", + "x": 645, + "y": 441.5, + "button": 2, + "buttons": 2, + "time": 92200180.497833, + "panning": true + }, + { + "type": "pointermove", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 92200263.951791, + "panning": true + }, + { + "type": "pointerup", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 0, + "time": 92200277.740833, + "panning": false + } + ], + "captures": [], + "frames": [ + { + "timestamp": 92198889.95854099, + "start": 92198909.721375, + "duration": 1.8963329941034317 + }, + { + "timestamp": 92198923.300958, + "start": 92198942.694916, + "duration": 2.4628340005874634 + }, + { + "timestamp": 92199148.87058301, + "start": 92199159.186125, + "duration": 0.6860000044107437 + }, + { + "timestamp": 92199173.300958, + "start": 92199178.96075, + "duration": 1.0353749990463257 + }, + { + "timestamp": 92199259.09833299, + "start": 92199263.975333, + "duration": 1.0976669937372208 + }, + { + "timestamp": 92199273.29387501, + "start": 92199278.452583, + "duration": 0.7050829976797104 + }, + { + "timestamp": 92199353.344208, + "start": 92199359.039625, + "duration": 0.6854999959468842 + }, + { + "timestamp": 92199373.305916, + "start": 92199383.911583, + "duration": 0.7290829867124557 + }, + { + "timestamp": 92199454.18525, + "start": 92199459.063958, + "duration": 1.210332989692688 + }, + { + "timestamp": 92199473.312958, + "start": 92199477.403958, + "duration": 1.216375008225441 + }, + { + "timestamp": 92199557.809, + "start": 92199561.452958, + "duration": 0.9934999942779541 + }, + { + "timestamp": 92199573.29629101, + "start": 92199581.483958, + "duration": 0.8420419991016388 + }, + { + "timestamp": 92199664.075666, + "start": 92199671.787666, + "duration": 1.912875011563301 + }, + { + "timestamp": 92199689.95958301, + "start": 92199698.933291, + "duration": 1.4028339982032776 + }, + { + "timestamp": 92199778.71804099, + "start": 92199782.159416, + "duration": 0.7217499911785126 + }, + { + "timestamp": 92199789.96987501, + "start": 92199792.847041, + "duration": 0.6696670055389404 + }, + { + "timestamp": 92199855.31054099, + "start": 92199859.3615, + "duration": 0.6178750097751617 + }, + { + "timestamp": 92199873.30104099, + "start": 92199877.048958, + "duration": 0.789792001247406 + }, + { + "timestamp": 92199943.129916, + "start": 92199948.793916, + "duration": 0.9280000030994415 + }, + { + "timestamp": 92199956.650041, + "start": 92199963.385, + "duration": 4.722124993801117 + }, + { + "timestamp": 92200029.814666, + "start": 92200033.237416, + "duration": 0.6137090027332306 + }, + { + "timestamp": 92200074.808958, + "start": 92200077.96525, + "duration": 0.6231250017881393 + }, + { + "timestamp": 92200138.86358301, + "start": 92200155.035125, + "duration": 1.3160829991102219 + }, + { + "timestamp": 92200172.744208, + "start": 92200182.003208, + "duration": 2.8371670097112656 + }, + { + "timestamp": 92200261.118208, + "start": 92200264.622333, + "duration": 0.5416669994592667 + }, + { + "timestamp": 92200320.922125, + "start": 92200320.971208, + "duration": 1.2224579900503159 + } + ], + "panning": false, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + "Kestrel pan composition timeline": { + "timestampFrequency": 1000000000, + "traceStarted": 92198874593291, + "publications": [ + { + "Timestamp": 92198889397000, + "Revision": 10, + "ConsumedInputSequence": 639244582178224071, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92198915742000, + "Revision": 11, + "ConsumedInputSequence": 639244582178224073, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92198948101416, + "Revision": 12, + "ConsumedInputSequence": 639244582178224075, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199161340333, + "Revision": 13, + "ConsumedInputSequence": 639244582178224086, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199182953375, + "Revision": 14, + "ConsumedInputSequence": 639244582178224087, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199266325291, + "Revision": 15, + "ConsumedInputSequence": 639244582178224092, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199280478625, + "Revision": 16, + "ConsumedInputSequence": 639244582178224093, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199361137583, + "Revision": 17, + "ConsumedInputSequence": 639244582178224097, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199385938458, + "Revision": 18, + "ConsumedInputSequence": 639244582178224099, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199462745625, + "Revision": 19, + "ConsumedInputSequence": 639244582178224103, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199481064416, + "Revision": 20, + "ConsumedInputSequence": 639244582178224104, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199565089208, + "Revision": 21, + "ConsumedInputSequence": 639244582178224109, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199583639000, + "Revision": 22, + "ConsumedInputSequence": 639244582178224110, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199675598083, + "Revision": 23, + "ConsumedInputSequence": 639244582178224116, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199703180333, + "Revision": 24, + "ConsumedInputSequence": 639244582178224117, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199784385041, + "Revision": 25, + "ConsumedInputSequence": 639244582178224122, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199794712875, + "Revision": 26, + "ConsumedInputSequence": 639244582178224123, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199861203083, + "Revision": 27, + "ConsumedInputSequence": 639244582178224127, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199879019166, + "Revision": 28, + "ConsumedInputSequence": 639244582178224128, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199950829375, + "Revision": 29, + "ConsumedInputSequence": 639244582178224132, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92199969571083, + "Revision": 30, + "ConsumedInputSequence": 639244582178224133, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92200035171416, + "Revision": 31, + "ConsumedInputSequence": 639244582178224137, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92200079710083, + "Revision": 32, + "ConsumedInputSequence": 639244582178224140, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92200159213208, + "Revision": 33, + "ConsumedInputSequence": 639244582178224144, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92200187469833, + "Revision": 34, + "ConsumedInputSequence": 639244582178224146, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92200266627791, + "Revision": 35, + "ConsumedInputSequence": 639244582178224151, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92200280811708, + "Revision": 36, + "ConsumedInputSequence": 639244582178224152, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92200323492208, + "Revision": 37, + "ConsumedInputSequence": 639244582178224152, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 92198901448708, + "Revision": 10, + "ConsumedInputSequence": 639244582178224071, + "AcceptedTimestamp": 92198890060125 + }, + { + "Timestamp": 92199146908541, + "Revision": 12, + "ConsumedInputSequence": 639244582178224075, + "AcceptedTimestamp": 92199143246791 + }, + { + "Timestamp": 92199257387083, + "Revision": 14, + "ConsumedInputSequence": 639244582178224087, + "AcceptedTimestamp": 92199254233083 + }, + { + "Timestamp": 92199351487666, + "Revision": 16, + "ConsumedInputSequence": 639244582178224093, + "AcceptedTimestamp": 92199348433416 + }, + { + "Timestamp": 92199452136833, + "Revision": 18, + "ConsumedInputSequence": 639244582178224099, + "AcceptedTimestamp": 92199447524083 + }, + { + "Timestamp": 92199556062500, + "Revision": 20, + "ConsumedInputSequence": 639244582178224104, + "AcceptedTimestamp": 92199552765458 + }, + { + "Timestamp": 92199662444875, + "Revision": 22, + "ConsumedInputSequence": 639244582178224110, + "AcceptedTimestamp": 92199659462000 + }, + { + "Timestamp": 92199777067583, + "Revision": 24, + "ConsumedInputSequence": 639244582178224117, + "AcceptedTimestamp": 92199774331958 + }, + { + "Timestamp": 92199853962083, + "Revision": 26, + "ConsumedInputSequence": 639244582178224123, + "AcceptedTimestamp": 92199850988291 + }, + { + "Timestamp": 92199939907875, + "Revision": 28, + "ConsumedInputSequence": 639244582178224128, + "AcceptedTimestamp": 92199936981250 + }, + { + "Timestamp": 92200028170208, + "Revision": 30, + "ConsumedInputSequence": 639244582178224133, + "AcceptedTimestamp": 92200025682750 + }, + { + "Timestamp": 92200073178583, + "Revision": 31, + "ConsumedInputSequence": 639244582178224137, + "AcceptedTimestamp": 92200070171583 + }, + { + "Timestamp": 92200135577708, + "Revision": 32, + "ConsumedInputSequence": 639244582178224140, + "AcceptedTimestamp": 92200129248208 + }, + { + "Timestamp": 92200259014250, + "Revision": 34, + "ConsumedInputSequence": 639244582178224146, + "AcceptedTimestamp": 92200256279958 + }, + { + "Timestamp": 92200319246500, + "Revision": 36, + "ConsumedInputSequence": 639244582178224152, + "AcceptedTimestamp": 92200316521750 + }, + { + "Timestamp": 92200376545208, + "Revision": 37, + "ConsumedInputSequence": 639244582178224152, + "AcceptedTimestamp": 92200373775541 + } + ], + "drawCallbackCompletions": [ + 92198900837000, + 92199146905083, + 92199257383541, + 92199351483208, + 92199452133166, + 92199556059500, + 92199662441083, + 92199777062458, + 92199853959250, + 92199939900750, + 92200028165625, + 92200073150291, + 92200135573291, + 92200259008958, + 92200319239833, + 92200376540208 + ], + "physicalPresentationVerified": false + } + } +} diff --git a/docs/graphics/evidence/kestrel/acquisition-reasons-trace.json b/docs/graphics/evidence/kestrel/acquisition-reasons-trace.json new file mode 100644 index 000000000..a27760f13 --- /dev/null +++ b/docs/graphics/evidence/kestrel/acquisition-reasons-trace.json @@ -0,0 +1,1360 @@ +{ + "workloadValidated": true, + "physicalPresentationVerified": false, + "limitations": [ + "Temporary synchronous logging affects timing; use reason counts diagnostically only." + ], + "reasonCounts": { + "native-Success": 35, + "presenter-Applied": 35 + }, + "trace": [ + { + "event": "native-Success", + "timestamp": 92397791872083 + }, + { + "event": "presenter-Applied", + "timestamp": 92397791975000 + }, + { + "event": "native-Success", + "timestamp": 92397807814083 + }, + { + "event": "presenter-Applied", + "timestamp": 92397844934000 + }, + { + "event": "native-Success", + "timestamp": 92397844964375 + }, + { + "event": "presenter-Applied", + "timestamp": 92397881938166 + }, + { + "event": "native-Success", + "timestamp": 92397908527708 + }, + { + "event": "presenter-Applied", + "timestamp": 92397940867000 + }, + { + "event": "native-Success", + "timestamp": 92397940907500 + }, + { + "event": "presenter-Applied", + "timestamp": 92397974278625 + }, + { + "event": "native-Success", + "timestamp": 92397990938500 + }, + { + "event": "presenter-Applied", + "timestamp": 92398023839541 + }, + { + "event": "native-Success", + "timestamp": 92398023882458 + }, + { + "event": "presenter-Applied", + "timestamp": 92398056003500 + }, + { + "event": "native-Success", + "timestamp": 92398074260708 + }, + { + "event": "presenter-Applied", + "timestamp": 92398106953041 + }, + { + "event": "native-Success", + "timestamp": 92398106992458 + }, + { + "event": "presenter-Applied", + "timestamp": 92398145786208 + }, + { + "event": "native-Success", + "timestamp": 92398158550750 + }, + { + "event": "presenter-Applied", + "timestamp": 92398190824375 + }, + { + "event": "native-Success", + "timestamp": 92398208540000 + }, + { + "event": "presenter-Applied", + "timestamp": 92398238361000 + }, + { + "event": "native-Success", + "timestamp": 92398238405125 + }, + { + "event": "presenter-Applied", + "timestamp": 92398269179041 + }, + { + "event": "native-Success", + "timestamp": 92398291581250 + }, + { + "event": "presenter-Applied", + "timestamp": 92398321082041 + }, + { + "event": "native-Success", + "timestamp": 92398321127958 + }, + { + "event": "presenter-Applied", + "timestamp": 92398350377333 + }, + { + "event": "native-Success", + "timestamp": 92398375206875 + }, + { + "event": "presenter-Applied", + "timestamp": 92398406550875 + }, + { + "event": "native-Success", + "timestamp": 92398406632958 + }, + { + "event": "presenter-Applied", + "timestamp": 92398436876791 + }, + { + "event": "native-Success", + "timestamp": 92398458618750 + }, + { + "event": "presenter-Applied", + "timestamp": 92398487162958 + }, + { + "event": "native-Success", + "timestamp": 92398487200666 + }, + { + "event": "presenter-Applied", + "timestamp": 92398515494708 + }, + { + "event": "native-Success", + "timestamp": 92398525205166 + }, + { + "event": "presenter-Applied", + "timestamp": 92398554773333 + }, + { + "event": "native-Success", + "timestamp": 92398554817166 + }, + { + "event": "presenter-Applied", + "timestamp": 92398582067375 + }, + { + "event": "native-Success", + "timestamp": 92398591877333 + }, + { + "event": "presenter-Applied", + "timestamp": 92398623059916 + }, + { + "event": "native-Success", + "timestamp": 92398641886250 + }, + { + "event": "presenter-Applied", + "timestamp": 92398670532958 + }, + { + "event": "native-Success", + "timestamp": 92398691926500 + }, + { + "event": "presenter-Applied", + "timestamp": 92398721878625 + }, + { + "event": "native-Success", + "timestamp": 92398721923666 + }, + { + "event": "presenter-Applied", + "timestamp": 92398749982958 + }, + { + "event": "native-Success", + "timestamp": 92398775239125 + }, + { + "event": "presenter-Applied", + "timestamp": 92398804375500 + }, + { + "event": "native-Success", + "timestamp": 92398804417166 + }, + { + "event": "presenter-Applied", + "timestamp": 92398832882541 + }, + { + "event": "native-Success", + "timestamp": 92398858523416 + }, + { + "event": "presenter-Applied", + "timestamp": 92398895384375 + }, + { + "event": "native-Success", + "timestamp": 92398895442166 + }, + { + "event": "presenter-Applied", + "timestamp": 92398923480875 + }, + { + "event": "native-Success", + "timestamp": 92398941873458 + }, + { + "event": "presenter-Applied", + "timestamp": 92398970350708 + }, + { + "event": "native-Success", + "timestamp": 92398970379958 + }, + { + "event": "presenter-Applied", + "timestamp": 92399002278291 + }, + { + "event": "native-Success", + "timestamp": 92399024253291 + }, + { + "event": "presenter-Applied", + "timestamp": 92399052506416 + }, + { + "event": "native-Success", + "timestamp": 92399052540958 + }, + { + "event": "presenter-Applied", + "timestamp": 92399080529208 + }, + { + "event": "native-Success", + "timestamp": 92399091958666 + }, + { + "event": "presenter-Applied", + "timestamp": 92399120534166 + }, + { + "event": "native-Success", + "timestamp": 92399140921583 + }, + { + "event": "presenter-Applied", + "timestamp": 92399155642791 + }, + { + "event": "native-Success", + "timestamp": 92399155683833 + }, + { + "event": "presenter-Applied", + "timestamp": 92399183663875 + } + ], + "records": { + "Kestrel pan performance": { + "elapsedMilliseconds": 1852.9948, + "baseline": { + "ContextId": 1, + "Timestamp": 92397761666833, + "Engine": { + "EnqueuedInputs": 27, + "DroppedInputs": 0, + "ConsumedInputs": 27, + "PublishedScenes": 11, + "AcquiredScenes": 11, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 5832625, + "InputEventsDispatched": 91, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 588833, + "LastScenePublicationNanoseconds": 1877416, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 13, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 6, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 6, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 1429458, + "LastSceneBuildNanoseconds": 329625, + "MaximumScenePublicationNanoseconds": 1877416 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 172750, + "MaximumDispatchNanoseconds": 694042, + "LastDispatchSequence": 639244584167191381, + "DispatchedInputs": 7, + "TotalDispatchNanoseconds": 2356500 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 6, + "TotalDispatchNanoseconds": 6875, + "LastDispatchNanoseconds": 917, + "MaximumDispatchNanoseconds": 2333, + "LastTimestampMicroseconds": 92394875206 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 19, + "BlockedPublications": 8, + "AcknowledgedScenes": 11, + "TotalAcknowledgementNanoseconds": 608477542, + "LastAcknowledgementNanoseconds": 30888500, + "MaximumAcknowledgementNanoseconds": 167645667, + "AcknowledgedRevision": 11 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5963776, + "V8UsedHeapBytes": 2729736, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5963776, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1859584, + "LatestSceneBytes": 127068, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 303856, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1919492, + "V8OldSpacePhysicalBytes": 2359296, + "V8CodeSpaceUsedBytes": 191232, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127684, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 7, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 92399619121625, + "Engine": { + "EnqueuedInputs": 142, + "DroppedInputs": 0, + "ConsumedInputs": 142, + "PublishedScenes": 46, + "AcquiredScenes": 46, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1385, + "LayoutPasses": 77, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 5832625, + "InputEventsDispatched": 181, + "InputCallbacksInvoked": 74, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 588833, + "LastScenePublicationNanoseconds": 1708, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 59, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 40, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 39, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 476709, + "LastSceneBuildNanoseconds": 285417, + "MaximumScenePublicationNanoseconds": 1877416 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 5022083, + "MaximumDispatchNanoseconds": 15625500, + "LastDispatchSequence": 639244584167191463, + "DispatchedInputs": 43, + "TotalDispatchNanoseconds": 144298124 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 39, + "TotalDispatchNanoseconds": 28709, + "LastDispatchNanoseconds": 500, + "MaximumDispatchNanoseconds": 2333, + "LastTimestampMicroseconds": 92399127510 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 97, + "BlockedPublications": 8, + "AcknowledgedScenes": 46, + "TotalAcknowledgementNanoseconds": 2178736126, + "LastAcknowledgementNanoseconds": 54033792, + "MaximumAcknowledgementNanoseconds": 167645667, + "AcknowledgedRevision": 46 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 33, + "AnimationFramesInvoked": 33, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 123, + "WorkerWaits": 208, + "WorkerSignalledWakes": 168, + "WorkerTimeoutWakes": 39, + "SceneBuilds": 35, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5963776, + "V8UsedHeapBytes": 2729736, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5963776, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1859584, + "LatestSceneBytes": 159136, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 303856, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1919492, + "V8OldSpacePhysicalBytes": 2359296, + "V8CodeSpaceUsedBytes": 191232, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127684, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5970848, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 1950, + "StringBytes": 117616, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 27, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 74, + "Renders": 20, + "AppliedDiffs": 35, + "InvalidationCalls": 20, + "DamageRectangles": 100, + "ChangedLayers": 33, + "EmptyDamageDiffs": 1, + "PartialDamageDiffs": 34, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 33, + "SkippedEmptyAnimationFrames": 41, + "RenderCallbacks": 20, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.8574547", + "EnqueuedInputs": 115, + "DroppedInputs": 0, + "ConsumedInputs": 115, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 69, + "AppliedAnimationFrames": 33, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 78, + "BlockedPublications": 0, + "PublishedScenes": 35, + "AcquiredScenes": 35, + "AcknowledgedScenes": 35, + "RenderedScenes": 20, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 33, + "AnimationFramesInvoked": 33, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 123, + "WorkerWaits": 208, + "WorkerSignalledWakes": 168, + "WorkerTimeoutWakes": 39, + "SceneBuilds": 35, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 74, + "CompositionRenders": 20, + "CompositionAppliedDiffs": 35, + "CompositionInvalidations": 20, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 33, + "CompositionSkippedEmptyAnimationFrames": 41, + "CompositionRenderCallbacks": 20, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "Kestrel pan composition timeline": { + "timestampFrequency": 1000000000, + "traceStarted": 92397766207500, + "publications": [ + { + "Timestamp": 92397782815916, + "Revision": 12, + "ConsumedInputSequence": 639244584167191382, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92397805083916, + "Revision": 13, + "ConsumedInputSequence": 639244584167191384, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92397818007333, + "Revision": 14, + "ConsumedInputSequence": 639244584167191385, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92397893773625, + "Revision": 15, + "ConsumedInputSequence": 639244584167191390, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92397913599916, + "Revision": 16, + "ConsumedInputSequence": 639244584167191391, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92397984472583, + "Revision": 17, + "ConsumedInputSequence": 639244584167191395, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92397995693500, + "Revision": 18, + "ConsumedInputSequence": 639244584167191396, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398065565791, + "Revision": 19, + "ConsumedInputSequence": 639244584167191400, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398078850458, + "Revision": 20, + "ConsumedInputSequence": 639244584167191401, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398158304583, + "Revision": 21, + "ConsumedInputSequence": 639244584167191406, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398200323125, + "Revision": 22, + "ConsumedInputSequence": 639244584167191408, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398219655416, + "Revision": 23, + "ConsumedInputSequence": 639244584167191409, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398278809500, + "Revision": 24, + "ConsumedInputSequence": 639244584167191413, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398296160000, + "Revision": 25, + "ConsumedInputSequence": 639244584167191414, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398361454333, + "Revision": 26, + "ConsumedInputSequence": 639244584167191418, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398380643916, + "Revision": 27, + "ConsumedInputSequence": 639244584167191419, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398446581416, + "Revision": 28, + "ConsumedInputSequence": 639244584167191423, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398463928166, + "Revision": 29, + "ConsumedInputSequence": 639244584167191424, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398524832833, + "Revision": 30, + "ConsumedInputSequence": 639244584167191427, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398542647125, + "Revision": 31, + "ConsumedInputSequence": 639244584167191428, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398591505000, + "Revision": 32, + "ConsumedInputSequence": 639244584167191431, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398633448458, + "Revision": 33, + "ConsumedInputSequence": 639244584167191434, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398679874458, + "Revision": 34, + "ConsumedInputSequence": 639244584167191436, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398697344375, + "Revision": 35, + "ConsumedInputSequence": 639244584167191437, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398759270458, + "Revision": 36, + "ConsumedInputSequence": 639244584167191441, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398780047750, + "Revision": 37, + "ConsumedInputSequence": 639244584167191442, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398842360375, + "Revision": 38, + "ConsumedInputSequence": 639244584167191446, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398863461916, + "Revision": 39, + "ConsumedInputSequence": 639244584167191447, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398932908583, + "Revision": 40, + "ConsumedInputSequence": 639244584167191451, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92398946504083, + "Revision": 41, + "ConsumedInputSequence": 639244584167191452, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92399016385625, + "Revision": 42, + "ConsumedInputSequence": 639244584167191456, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92399034789625, + "Revision": 43, + "ConsumedInputSequence": 639244584167191457, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92399089898875, + "Revision": 44, + "ConsumedInputSequence": 639244584167191461, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92399127517083, + "Revision": 45, + "ConsumedInputSequence": 639244584167191463, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92399129631083, + "Revision": 46, + "ConsumedInputSequence": 639244584167191463, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 92397799996750, + "Revision": 12, + "ConsumedInputSequence": 639244584167191382, + "AcceptedTimestamp": 92397791990541 + }, + { + "Timestamp": 92397884907625, + "Revision": 14, + "ConsumedInputSequence": 639244584167191385, + "AcceptedTimestamp": 92397881975500 + }, + { + "Timestamp": 92397977000958, + "Revision": 16, + "ConsumedInputSequence": 639244584167191391, + "AcceptedTimestamp": 92397974322291 + }, + { + "Timestamp": 92398058715250, + "Revision": 18, + "ConsumedInputSequence": 639244584167191396, + "AcceptedTimestamp": 92398056044583 + }, + { + "Timestamp": 92398148423625, + "Revision": 20, + "ConsumedInputSequence": 639244584167191401, + "AcceptedTimestamp": 92398145826958 + }, + { + "Timestamp": 92398193506083, + "Revision": 21, + "ConsumedInputSequence": 639244584167191406, + "AcceptedTimestamp": 92398190862750 + }, + { + "Timestamp": 92398271888500, + "Revision": 23, + "ConsumedInputSequence": 639244584167191409, + "AcceptedTimestamp": 92398269216791 + }, + { + "Timestamp": 92398353048958, + "Revision": 25, + "ConsumedInputSequence": 639244584167191414, + "AcceptedTimestamp": 92398350406291 + }, + { + "Timestamp": 92398439541208, + "Revision": 27, + "ConsumedInputSequence": 639244584167191419, + "AcceptedTimestamp": 92398436927375 + }, + { + "Timestamp": 92398518051750, + "Revision": 29, + "ConsumedInputSequence": 639244584167191424, + "AcceptedTimestamp": 92398515525041 + }, + { + "Timestamp": 92398584559375, + "Revision": 31, + "ConsumedInputSequence": 639244584167191428, + "AcceptedTimestamp": 92398582097625 + }, + { + "Timestamp": 92398626558916, + "Revision": 32, + "ConsumedInputSequence": 639244584167191431, + "AcceptedTimestamp": 92398623148166 + }, + { + "Timestamp": 92398673212458, + "Revision": 33, + "ConsumedInputSequence": 639244584167191434, + "AcceptedTimestamp": 92398670572708 + }, + { + "Timestamp": 92398752644750, + "Revision": 35, + "ConsumedInputSequence": 639244584167191437, + "AcceptedTimestamp": 92398750021416 + }, + { + "Timestamp": 92398835543416, + "Revision": 37, + "ConsumedInputSequence": 639244584167191442, + "AcceptedTimestamp": 92398832918375 + }, + { + "Timestamp": 92398926160083, + "Revision": 39, + "ConsumedInputSequence": 639244584167191447, + "AcceptedTimestamp": 92398923520708 + }, + { + "Timestamp": 92399004916166, + "Revision": 41, + "ConsumedInputSequence": 639244584167191452, + "AcceptedTimestamp": 92399002314166 + }, + { + "Timestamp": 92399083173583, + "Revision": 43, + "ConsumedInputSequence": 639244584167191457, + "AcceptedTimestamp": 92399080565458 + }, + { + "Timestamp": 92399125593125, + "Revision": 44, + "ConsumedInputSequence": 639244584167191461, + "AcceptedTimestamp": 92399120560458 + }, + { + "Timestamp": 92399186288541, + "Revision": 46, + "ConsumedInputSequence": 639244584167191463, + "AcceptedTimestamp": 92399183687541 + } + ], + "drawCallbackCompletions": [ + 92397799654958, + 92397884906000, + 92397976999750, + 92398058713541, + 92398148416916, + 92398193505041, + 92398271887541, + 92398353047625, + 92398439540083, + 92398518048083, + 92398584556333, + 92398626551291, + 92398673209416, + 92398752643291, + 92398835539625, + 92398926156708, + 92399004912833, + 92399083169666, + 92399125585250, + 92399186286833 + ], + "physicalPresentationVerified": false + } + } +} diff --git a/docs/graphics/evidence/kestrel/all-host-frame-publication.json b/docs/graphics/evidence/kestrel/all-host-frame-publication.json new file mode 100644 index 000000000..d34a9edf0 --- /dev/null +++ b/docs/graphics/evidence/kestrel/all-host-frame-publication.json @@ -0,0 +1,1715 @@ +{ + "change": "All host RAF boundaries bypass the producer-only 16ms publication timer; GPU and mailbox gates retained", + "physicalPresentationVerified": false, + "nativeSuitesPassed": 2, + "conclusion": "No established performance improvement; redraw starvation remains", + "Kestrel sidebar diagnostics": { + "events": [ + { + "type": "pointerdown", + "x": 173.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 175.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 177.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 179.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 181.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 183.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 185.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 187.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 189.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 191.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 193.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 195.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 197.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 199.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 201.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 203.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 205.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 207.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 209.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 211.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 213.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 215.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 217.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 219.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 221.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 223.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 225.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 227.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 229.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 231.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 233.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 235.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 237.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 239.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 241.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 243.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 245.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 247.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 249.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 251.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 253.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 255.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 257.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 259.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 261.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 263.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 265.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 267.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 269.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 271.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 273.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 275.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 277.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 279.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 281.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 283.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 285.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 287.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 289.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 291.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 293.5, + "y": 475.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointerup", + "x": 293.5, + "y": 475.5, + "button": 0, + "buttons": 0 + } + ], + "panning": false, + "errors": 0 + }, + "Kestrel sidebar timeline": { + "traceStarted": 98186980642416, + "timestampFrequency": 1000000000, + "originalWidth": 175, + "width": 295, + "initialGeometry": { + "x": 173.5, + "y": 475.5, + "width": 175, + "viewport": [ + 792, + 878 + ], + "dpr": 2, + "canvas": [ + 1234, + 1082 + ] + }, + "baseline": { + "ContextId": 1, + "Timestamp": 98186975791958, + "Engine": { + "EnqueuedInputs": 2, + "DroppedInputs": 0, + "ConsumedInputs": 2, + "PublishedScenes": 5, + "AcquiredScenes": 3, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 4966417, + "InputEventsDispatched": 1, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 617000, + "BusiestCanvasHeightMilli": 541000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 86708, + "LastScenePublicationNanoseconds": 740875, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 0, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 1, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 408458, + "LastSceneBuildNanoseconds": 247208, + "MaximumScenePublicationNanoseconds": 1066333 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "LastDispatchSequence": 0, + "DispatchedInputs": 0, + "TotalDispatchNanoseconds": 0 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 1, + "TotalDispatchNanoseconds": 459, + "LastDispatchNanoseconds": 459, + "MaximumDispatchNanoseconds": 459, + "LastTimestampMicroseconds": 98183955172 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 6, + "BlockedPublications": 0, + "AcknowledgedScenes": 3, + "TotalAcknowledgementNanoseconds": 285939250, + "LastAcknowledgementNanoseconds": 12654500, + "MaximumAcknowledgementNanoseconds": 144546292, + "AcknowledgedRevision": 5 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5439488, + "V8UsedHeapBytes": 3004004, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5439488, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1499136, + "LatestSceneBytes": 127052, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 992, + "NativeDomInlineBytes": 1042592, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 320, + "NativeTextMeasurementCacheStorageBytes": 73578, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1079296, + "NativeDomNodePoolPeakBytes": 1079296, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 570036, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920288, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 195648, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 130560, + "V8TrustedSpacePhysicalBytes": 524288, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1294, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1294, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 2, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 98188553955166, + "Engine": { + "EnqueuedInputs": 128, + "DroppedInputs": 0, + "ConsumedInputs": 128, + "PublishedScenes": 17, + "AcquiredScenes": 15, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1368, + "LayoutPasses": 172, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 5125500, + "InputEventsDispatched": 151, + "InputCallbacksInvoked": 177, + "BusiestCanvasWidthMilli": 497000, + "BusiestCanvasHeightMilli": 541000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 86708, + "LastScenePublicationNanoseconds": 4459, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 60, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 64, + "CoalescedAnimationFrames": 1, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 366542, + "LastSceneBuildNanoseconds": 283459, + "MaximumScenePublicationNanoseconds": 5220667 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 133792, + "MaximumDispatchNanoseconds": 11678458, + "LastDispatchSequence": 639244642062108503, + "DispatchedInputs": 62, + "TotalDispatchNanoseconds": 310146500 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 64, + "TotalDispatchNanoseconds": 38043, + "LastDispatchNanoseconds": 1125, + "MaximumDispatchNanoseconds": 3875, + "LastTimestampMicroseconds": 98188055200 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 259, + "BlockedPublications": 0, + "AcknowledgedScenes": 15, + "TotalAcknowledgementNanoseconds": 589321292, + "LastAcknowledgementNanoseconds": 25915667, + "MaximumAcknowledgementNanoseconds": 144546292, + "AcknowledgedRevision": 17 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 52, + "AnimationFramesInvoked": 52, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 255, + "WorkerWaits": 233, + "WorkerSignalledWakes": 209, + "WorkerTimeoutWakes": 23, + "SceneBuilds": 12, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5439488, + "V8UsedHeapBytes": 3004004, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5439488, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1499136, + "LatestSceneBytes": 157132, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 992, + "NativeDomInlineBytes": 1042592, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 320, + "NativeTextMeasurementCacheStorageBytes": 73578, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1079296, + "NativeDomNodePoolPeakBytes": 1079296, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 570036, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920288, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 195648, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 130560, + "V8TrustedSpacePhysicalBytes": 524288, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1294, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1294, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 4302032, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 390, + "StringCount": 927, + "StringBytes": 77736, + "TypefaceCount": 2, + "SvgPictureCount": 70, + "ProcessSvgPictureCount": 70, + "ProcessSvgPictureReferenceCount": 70, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 14, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 94, + "Renders": 12, + "AppliedDiffs": 12, + "InvalidationCalls": 12, + "DamageRectangles": 34, + "ChangedLayers": 11, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 12, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 64, + "SkippedEmptyAnimationFrames": 30, + "RenderCallbacks": 12, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.5781632", + "EnqueuedInputs": 126, + "DroppedInputs": 0, + "ConsumedInputs": 126, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 164, + "AppliedAnimationFrames": 63, + "CoalescedAnimationFrames": 1, + "PublicationAttempts": 253, + "BlockedPublications": 0, + "PublishedScenes": 12, + "AcquiredScenes": 12, + "AcknowledgedScenes": 12, + "RenderedScenes": 12, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 1, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 52, + "AnimationFramesInvoked": 52, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 255, + "WorkerWaits": 233, + "WorkerSignalledWakes": 209, + "WorkerTimeoutWakes": 23, + "SceneBuilds": 12, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 94, + "CompositionRenders": 12, + "CompositionAppliedDiffs": 12, + "CompositionInvalidations": 12, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 64, + "CompositionSkippedEmptyAnimationFrames": 30, + "CompositionRenderCallbacks": 12, + "CompositionUnchangedRenderCallbacks": 0 + }, + "submittedMoves": [ + { + "sequence": 639244642062108443, + "submittedAt": 98186980766791, + "step": 1, + "x": 175.5, + "y": 475.5 + }, + { + "sequence": 639244642062108444, + "submittedAt": 98186998966208, + "step": 2, + "x": 177.5, + "y": 475.5 + }, + { + "sequence": 639244642062108445, + "submittedAt": 98187017126083, + "step": 3, + "x": 179.5, + "y": 475.5 + }, + { + "sequence": 639244642062108446, + "submittedAt": 98187035233416, + "step": 4, + "x": 181.5, + "y": 475.5 + }, + { + "sequence": 639244642062108447, + "submittedAt": 98187053330291, + "step": 5, + "x": 183.5, + "y": 475.5 + }, + { + "sequence": 639244642062108448, + "submittedAt": 98187071520708, + "step": 6, + "x": 185.5, + "y": 475.5 + }, + { + "sequence": 639244642062108449, + "submittedAt": 98187089675541, + "step": 7, + "x": 187.5, + "y": 475.5 + }, + { + "sequence": 639244642062108450, + "submittedAt": 98187107732291, + "step": 8, + "x": 189.5, + "y": 475.5 + }, + { + "sequence": 639244642062108451, + "submittedAt": 98187124067791, + "step": 9, + "x": 191.5, + "y": 475.5 + }, + { + "sequence": 639244642062108452, + "submittedAt": 98187145014125, + "step": 10, + "x": 193.5, + "y": 475.5 + }, + { + "sequence": 639244642062108453, + "submittedAt": 98187163106625, + "step": 11, + "x": 195.5, + "y": 475.5 + }, + { + "sequence": 639244642062108454, + "submittedAt": 98187180025833, + "step": 12, + "x": 197.5, + "y": 475.5 + }, + { + "sequence": 639244642062108455, + "submittedAt": 98187198159708, + "step": 13, + "x": 199.5, + "y": 475.5 + }, + { + "sequence": 639244642062108456, + "submittedAt": 98187216313958, + "step": 14, + "x": 201.5, + "y": 475.5 + }, + { + "sequence": 639244642062108457, + "submittedAt": 98187234395166, + "step": 15, + "x": 203.5, + "y": 475.5 + }, + { + "sequence": 639244642062108458, + "submittedAt": 98187252115291, + "step": 16, + "x": 205.5, + "y": 475.5 + }, + { + "sequence": 639244642062108459, + "submittedAt": 98187269832083, + "step": 17, + "x": 207.5, + "y": 475.5 + }, + { + "sequence": 639244642062108460, + "submittedAt": 98187287999000, + "step": 18, + "x": 209.5, + "y": 475.5 + }, + { + "sequence": 639244642062108461, + "submittedAt": 98187306067666, + "step": 19, + "x": 211.5, + "y": 475.5 + }, + { + "sequence": 639244642062108462, + "submittedAt": 98187323654000, + "step": 20, + "x": 213.5, + "y": 475.5 + }, + { + "sequence": 639244642062108463, + "submittedAt": 98187340785625, + "step": 21, + "x": 215.5, + "y": 475.5 + }, + { + "sequence": 639244642062108464, + "submittedAt": 98187358840041, + "step": 22, + "x": 217.5, + "y": 475.5 + }, + { + "sequence": 639244642062108465, + "submittedAt": 98187375055875, + "step": 23, + "x": 219.5, + "y": 475.5 + }, + { + "sequence": 639244642062108466, + "submittedAt": 98187393178416, + "step": 24, + "x": 221.5, + "y": 475.5 + }, + { + "sequence": 639244642062108467, + "submittedAt": 98187410034333, + "step": 25, + "x": 223.5, + "y": 475.5 + }, + { + "sequence": 639244642062108468, + "submittedAt": 98187428190375, + "step": 26, + "x": 225.5, + "y": 475.5 + }, + { + "sequence": 639244642062108469, + "submittedAt": 98187446234291, + "step": 27, + "x": 227.5, + "y": 475.5 + }, + { + "sequence": 639244642062108470, + "submittedAt": 98187462927375, + "step": 28, + "x": 229.5, + "y": 475.5 + }, + { + "sequence": 639244642062108471, + "submittedAt": 98187480736541, + "step": 29, + "x": 231.5, + "y": 475.5 + }, + { + "sequence": 639244642062108472, + "submittedAt": 98187498099666, + "step": 30, + "x": 233.5, + "y": 475.5 + }, + { + "sequence": 639244642062108473, + "submittedAt": 98187516205333, + "step": 31, + "x": 235.5, + "y": 475.5 + }, + { + "sequence": 639244642062108474, + "submittedAt": 98187534271375, + "step": 32, + "x": 237.5, + "y": 475.5 + }, + { + "sequence": 639244642062108475, + "submittedAt": 98187552335291, + "step": 33, + "x": 239.5, + "y": 475.5 + }, + { + "sequence": 639244642062108476, + "submittedAt": 98187569796833, + "step": 34, + "x": 241.5, + "y": 475.5 + }, + { + "sequence": 639244642062108477, + "submittedAt": 98187587949541, + "step": 35, + "x": 243.5, + "y": 475.5 + }, + { + "sequence": 639244642062108478, + "submittedAt": 98187606024000, + "step": 36, + "x": 245.5, + "y": 475.5 + }, + { + "sequence": 639244642062108479, + "submittedAt": 98187624090458, + "step": 37, + "x": 247.5, + "y": 475.5 + }, + { + "sequence": 639244642062108480, + "submittedAt": 98187642171125, + "step": 38, + "x": 249.5, + "y": 475.5 + }, + { + "sequence": 639244642062108481, + "submittedAt": 98187660213166, + "step": 39, + "x": 251.5, + "y": 475.5 + }, + { + "sequence": 639244642062108482, + "submittedAt": 98187678299958, + "step": 40, + "x": 253.5, + "y": 475.5 + }, + { + "sequence": 639244642062108483, + "submittedAt": 98187696386041, + "step": 41, + "x": 255.5, + "y": 475.5 + }, + { + "sequence": 639244642062108484, + "submittedAt": 98187714533916, + "step": 42, + "x": 257.5, + "y": 475.5 + }, + { + "sequence": 639244642062108485, + "submittedAt": 98187731442375, + "step": 43, + "x": 259.5, + "y": 475.5 + }, + { + "sequence": 639244642062108486, + "submittedAt": 98187749508583, + "step": 44, + "x": 261.5, + "y": 475.5 + }, + { + "sequence": 639244642062108487, + "submittedAt": 98187767567583, + "step": 45, + "x": 263.5, + "y": 475.5 + }, + { + "sequence": 639244642062108488, + "submittedAt": 98187784681708, + "step": 46, + "x": 265.5, + "y": 475.5 + }, + { + "sequence": 639244642062108489, + "submittedAt": 98187802849625, + "step": 47, + "x": 267.5, + "y": 475.5 + }, + { + "sequence": 639244642062108490, + "submittedAt": 98187820955583, + "step": 48, + "x": 269.5, + "y": 475.5 + }, + { + "sequence": 639244642062108491, + "submittedAt": 98187839074416, + "step": 49, + "x": 271.5, + "y": 475.5 + }, + { + "sequence": 639244642062108492, + "submittedAt": 98187857195500, + "step": 50, + "x": 273.5, + "y": 475.5 + }, + { + "sequence": 639244642062108493, + "submittedAt": 98187874762541, + "step": 51, + "x": 275.5, + "y": 475.5 + }, + { + "sequence": 639244642062108494, + "submittedAt": 98187892379541, + "step": 52, + "x": 277.5, + "y": 475.5 + }, + { + "sequence": 639244642062108495, + "submittedAt": 98187910436333, + "step": 53, + "x": 279.5, + "y": 475.5 + }, + { + "sequence": 639244642062108496, + "submittedAt": 98187928514583, + "step": 54, + "x": 281.5, + "y": 475.5 + }, + { + "sequence": 639244642062108497, + "submittedAt": 98187946567875, + "step": 55, + "x": 283.5, + "y": 475.5 + }, + { + "sequence": 639244642062108498, + "submittedAt": 98187964701166, + "step": 56, + "x": 285.5, + "y": 475.5 + }, + { + "sequence": 639244642062108499, + "submittedAt": 98187982855541, + "step": 57, + "x": 287.5, + "y": 475.5 + }, + { + "sequence": 639244642062108500, + "submittedAt": 98187999458916, + "step": 58, + "x": 289.5, + "y": 475.5 + }, + { + "sequence": 639244642062108501, + "submittedAt": 98188017520958, + "step": 59, + "x": 291.5, + "y": 475.5 + }, + { + "sequence": 639244642062108502, + "submittedAt": 98188034031250, + "step": 60, + "x": 293.5, + "y": 475.5 + } + ], + "publications": [ + { + "Timestamp": 98186983494333, + "Revision": 6, + "ConsumedInputSequence": 639244642062108442, + "ViewportWidth": 792, + "ViewportHeight": 878 + }, + { + "Timestamp": 98187091546208, + "Revision": 7, + "ConsumedInputSequence": 639244642062108448, + "ViewportWidth": 792, + "ViewportHeight": 878 + }, + { + "Timestamp": 98187145476708, + "Revision": 8, + "ConsumedInputSequence": 639244642062108451, + "ViewportWidth": 792, + "ViewportHeight": 878 + }, + { + "Timestamp": 98187308995666, + "Revision": 9, + "ConsumedInputSequence": 639244642062108460, + "ViewportWidth": 792, + "ViewportHeight": 878 + }, + { + "Timestamp": 98187343083291, + "Revision": 10, + "ConsumedInputSequence": 639244642062108462, + "ViewportWidth": 792, + "ViewportHeight": 878 + }, + { + "Timestamp": 98187379161875, + "Revision": 11, + "ConsumedInputSequence": 639244642062108464, + "ViewportWidth": 792, + "ViewportHeight": 878 + }, + { + "Timestamp": 98187423561791, + "Revision": 12, + "ConsumedInputSequence": 639244642062108467, + "ViewportWidth": 792, + "ViewportHeight": 878 + }, + { + "Timestamp": 98187611374708, + "Revision": 13, + "ConsumedInputSequence": 639244642062108477, + "ViewportWidth": 792, + "ViewportHeight": 878 + }, + { + "Timestamp": 98187643865666, + "Revision": 14, + "ConsumedInputSequence": 639244642062108479, + "ViewportWidth": 792, + "ViewportHeight": 878 + }, + { + "Timestamp": 98187841055708, + "Revision": 15, + "ConsumedInputSequence": 639244642062108490, + "ViewportWidth": 792, + "ViewportHeight": 878 + }, + { + "Timestamp": 98187875944875, + "Revision": 16, + "ConsumedInputSequence": 639244642062108492, + "ViewportWidth": 792, + "ViewportHeight": 878 + }, + { + "Timestamp": 98188059120666, + "Revision": 17, + "ConsumedInputSequence": 639244642062108503, + "ViewportWidth": 792, + "ViewportHeight": 878 + } + ], + "renderedScenes": [ + { + "Timestamp": 98187018202625, + "Revision": 6, + "ConsumedInputSequence": 639244642062108442, + "AcceptedTimestamp": 98187011171833 + }, + { + "Timestamp": 98187125025916, + "Revision": 7, + "ConsumedInputSequence": 639244642062108448, + "AcceptedTimestamp": 98187120416125 + }, + { + "Timestamp": 98187172555625, + "Revision": 8, + "ConsumedInputSequence": 639244642062108451, + "AcceptedTimestamp": 98187168131833 + }, + { + "Timestamp": 98187338572333, + "Revision": 9, + "ConsumedInputSequence": 639244642062108460, + "AcceptedTimestamp": 98187334662875 + }, + { + "Timestamp": 98187372572375, + "Revision": 10, + "ConsumedInputSequence": 639244642062108462, + "AcceptedTimestamp": 98187368829750 + }, + { + "Timestamp": 98187405045916, + "Revision": 11, + "ConsumedInputSequence": 639244642062108464, + "AcceptedTimestamp": 98187401286125 + }, + { + "Timestamp": 98187458091458, + "Revision": 12, + "ConsumedInputSequence": 639244642062108467, + "AcceptedTimestamp": 98187454616041 + }, + { + "Timestamp": 98187637872208, + "Revision": 13, + "ConsumedInputSequence": 639244642062108477, + "AcceptedTimestamp": 98187634096500 + }, + { + "Timestamp": 98187670662375, + "Revision": 14, + "ConsumedInputSequence": 639244642062108479, + "AcceptedTimestamp": 98187667062125 + }, + { + "Timestamp": 98187869022125, + "Revision": 15, + "ConsumedInputSequence": 639244642062108490, + "AcceptedTimestamp": 98187866034458 + }, + { + "Timestamp": 98187901407375, + "Revision": 16, + "ConsumedInputSequence": 639244642062108492, + "AcceptedTimestamp": 98187898598958 + }, + { + "Timestamp": 98188088520625, + "Revision": 17, + "ConsumedInputSequence": 639244642062108503, + "AcceptedTimestamp": 98188085031208 + } + ], + "physicalPresentationVerified": false + } +} diff --git a/docs/graphics/evidence/kestrel/border-functional-color-after.json b/docs/graphics/evidence/kestrel/border-functional-color-after.json new file mode 100644 index 000000000..4c47c2a8d --- /dev/null +++ b/docs/graphics/evidence/kestrel/border-functional-color-after.json @@ -0,0 +1,42 @@ +{ + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "c1c75c0be77be83be12e86ca30b9b7f9e228928ddf4b8889f91f004130f75742", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=6679af609a792823629201d107a75b68e7eb503393cd047251b8cd0a19d412a2", + "chromiumIdentity": null, + "startedAt": "2026-09-08T08:04:00.933833+00:00", + "duration": "00:00:00.1936510", + "selection": "candidate", + "summary": { + "tests": 1, + "passed": 1, + "failed": 0, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 1, + "subtestsPassed": 1, + "subtestsFailed": 0 + }, + "results": [ + { + "path": "contracts/css-border-functional-color-width.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.1913083", + "message": null, + "subtests": [ + { + "name": "Color-mix percentage must not overwrite border width", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] +} \ No newline at end of file diff --git a/docs/graphics/evidence/kestrel/border-functional-color-before.json b/docs/graphics/evidence/kestrel/border-functional-color-before.json new file mode 100644 index 000000000..2edca621d --- /dev/null +++ b/docs/graphics/evidence/kestrel/border-functional-color-before.json @@ -0,0 +1,42 @@ +{ + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "c1c75c0be77be83be12e86ca30b9b7f9e228928ddf4b8889f91f004130f75742", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=a00426df211450c31b1d36d11e6e01a9a749d2dffdaf6233dfd34ad6a3a181d4", + "chromiumIdentity": null, + "startedAt": "2026-09-08T08:03:16.213736+00:00", + "duration": "00:00:00.2776676", + "selection": "candidate", + "summary": { + "tests": 1, + "passed": 0, + "failed": 1, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 1, + "subtestsPassed": 0, + "subtestsFailed": 1 + }, + "results": [ + { + "path": "contracts/css-border-functional-color-width.html", + "type": "testharness", + "status": "FAIL", + "duration": "00:00:00.2742081", + "message": "diagnostic: activeElement=", + "subtests": [ + { + "name": "Color-mix percentage must not overwrite border width", + "status": "FAIL", + "message": "assert_equals: expected \u00221px\u0022 but got \u002240px\u0022", + "stack": "Error\n at get_stack (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4802:21)\n at new AssertionError (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4795:22)\n at assert (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4779:19)\n at assert_equals (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1598:9)\n at assert_wrapper (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1518:30)\n at Test.\u003Canonymous\u003E (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:4:2)\n at Test.step (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:2869:25)\n at test (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:633:30)\n at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:2:1" + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] +} \ No newline at end of file diff --git a/docs/graphics/evidence/kestrel/bounded-mailbox-drain-trial.json b/docs/graphics/evidence/kestrel/bounded-mailbox-drain-trial.json new file mode 100644 index 000000000..06b735e17 --- /dev/null +++ b/docs/graphics/evidence/kestrel/bounded-mailbox-drain-trial.json @@ -0,0 +1,1933 @@ +{ + "status": "Prototype under evaluation; not qualified", + "workloadValidated": true, + "physicalPresentationVerified": false, + "publicationToDrawMedianMilliseconds": 28.41925, + "change": "Apply at most two ordered GPU scene diffs before one draw, combining damage. Manual frames excluded.", + "limitations": [ + "Single run; no statistical performance claim.", + "Needs dedicated combined-damage and multi-scene lifetime regression coverage.", + "Skipped intermediate draw revisions are expected; all scene diffs must still apply in order." + ], + "records": { + "Kestrel pan performance": { + "elapsedMilliseconds": 1862.345, + "baseline": { + "ContextId": 1, + "Timestamp": 91844124145250, + "Engine": { + "EnqueuedInputs": 4, + "DroppedInputs": 0, + "ConsumedInputs": 4, + "PublishedScenes": 5, + "AcquiredScenes": 5, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 4366293, + "InputEventsDispatched": 1, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 505708, + "LastScenePublicationNanoseconds": 4946833, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 0, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 3, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 4006792, + "LastSceneBuildNanoseconds": 691833, + "MaximumScenePublicationNanoseconds": 4946833 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "LastDispatchSequence": 0, + "DispatchedInputs": 0, + "TotalDispatchNanoseconds": 0 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 3, + "TotalDispatchNanoseconds": 5124, + "LastDispatchNanoseconds": 416, + "MaximumDispatchNanoseconds": 4000, + "LastTimestampMicroseconds": 91841087046 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 7, + "BlockedPublications": 0, + "AcknowledgedScenes": 5, + "TotalAcknowledgementNanoseconds": 383783500, + "LastAcknowledgementNanoseconds": 42483083, + "MaximumAcknowledgementNanoseconds": 156600959, + "AcknowledgedRevision": 5 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5439488, + "V8UsedHeapBytes": 3250624, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5439488, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1798144, + "LatestSceneBytes": 127068, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 826172, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1919060, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 190720, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127200, + "V8TrustedSpacePhysicalBytes": 524288, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 4, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 91845992098125, + "Engine": { + "EnqueuedInputs": 134, + "DroppedInputs": 0, + "ConsumedInputs": 134, + "PublishedScenes": 55, + "AcquiredScenes": 55, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1415, + "LayoutPasses": 107, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 4366293, + "InputEventsDispatched": 121, + "InputCallbacksInvoked": 104, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 505708, + "LastScenePublicationNanoseconds": 1375, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 31, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 49, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 51, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 527041, + "LastSceneBuildNanoseconds": 290417, + "MaximumScenePublicationNanoseconds": 4946833 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 2820584, + "MaximumDispatchNanoseconds": 24580958, + "LastDispatchSequence": 639244578631062943, + "DispatchedInputs": 51, + "TotalDispatchNanoseconds": 196887003 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 51, + "TotalDispatchNanoseconds": 38417, + "LastDispatchNanoseconds": 2417, + "MaximumDispatchNanoseconds": 4000, + "LastTimestampMicroseconds": 91845519026 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 123, + "BlockedPublications": 0, + "AcknowledgedScenes": 55, + "TotalAcknowledgementNanoseconds": 1693372043, + "LastAcknowledgementNanoseconds": 29505709, + "MaximumAcknowledgementNanoseconds": 156600959, + "AcknowledgedRevision": 55 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 48, + "AnimationFramesInvoked": 48, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 168, + "WorkerWaits": 245, + "WorkerSignalledWakes": 211, + "WorkerTimeoutWakes": 33, + "SceneBuilds": 50, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5439488, + "V8UsedHeapBytes": 3250624, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5439488, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1798144, + "LatestSceneBytes": 161736, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 826172, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1919060, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 190720, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127200, + "V8TrustedSpacePhysicalBytes": 524288, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5970848, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 2850, + "StringBytes": 153252, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 34, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 93, + "Renders": 30, + "AppliedDiffs": 50, + "InvalidationCalls": 30, + "DamageRectangles": 145, + "ChangedLayers": 48, + "EmptyDamageDiffs": 1, + "PartialDamageDiffs": 49, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 48, + "SkippedEmptyAnimationFrames": 45, + "RenderCallbacks": 30, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.8679528", + "EnqueuedInputs": 130, + "DroppedInputs": 0, + "ConsumedInputs": 130, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 99, + "AppliedAnimationFrames": 48, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 116, + "BlockedPublications": 0, + "PublishedScenes": 50, + "AcquiredScenes": 50, + "AcknowledgedScenes": 50, + "RenderedScenes": 30, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 48, + "AnimationFramesInvoked": 48, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 168, + "WorkerWaits": 245, + "WorkerSignalledWakes": 211, + "WorkerTimeoutWakes": 33, + "SceneBuilds": 50, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 93, + "CompositionRenders": 30, + "CompositionAppliedDiffs": 50, + "CompositionInvalidations": 30, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 48, + "CompositionSkippedEmptyAnimationFrames": 45, + "CompositionRenderCallbacks": 30, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "Kestrel pan diagnostics": { + "events": [ + { + "type": "pointerdown", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 91844154.32175, + "panning": false + }, + { + "type": "pointermove", + "x": 637, + "y": 439.5, + "button": 2, + "buttons": 2, + "time": 91844170.778208, + "panning": true + }, + { + "type": "pointermove", + "x": 641, + "y": 440.5, + "button": 2, + "buttons": 2, + "time": 91844192.405916, + "panning": true + }, + { + "type": "pointermove", + "x": 653, + "y": 443.5, + "button": 2, + "buttons": 2, + "time": 91844241.202833, + "panning": true + }, + { + "type": "pointermove", + "x": 657, + "y": 444.5, + "button": 2, + "buttons": 2, + "time": 91844256.903708, + "panning": true + }, + { + "type": "pointermove", + "x": 669, + "y": 447.5, + "button": 2, + "buttons": 2, + "time": 91844303.120708, + "panning": true + }, + { + "type": "pointermove", + "x": 673, + "y": 448.5, + "button": 2, + "buttons": 2, + "time": 91844322.934916, + "panning": true + }, + { + "type": "pointermove", + "x": 681, + "y": 450.5, + "button": 2, + "buttons": 2, + "time": 91844369.19275, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 91844372.856833, + "panning": true + }, + { + "type": "pointermove", + "x": 689, + "y": 452.5, + "button": 2, + "buttons": 2, + "time": 91844389.775625, + "panning": true + }, + { + "type": "pointermove", + "x": 697, + "y": 454.5, + "button": 2, + "buttons": 2, + "time": 91844433.207625, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 91844438.6825, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 91844482.567291, + "panning": true + }, + { + "type": "pointermove", + "x": 713, + "y": 458.5, + "button": 2, + "buttons": 2, + "time": 91844489.595166, + "panning": true + }, + { + "type": "pointermove", + "x": 721, + "y": 460.5, + "button": 2, + "buttons": 2, + "time": 91844532.292583, + "panning": true + }, + { + "type": "pointermove", + "x": 725, + "y": 461.5, + "button": 2, + "buttons": 2, + "time": 91844539.713458, + "panning": true + }, + { + "type": "pointermove", + "x": 733, + "y": 463.5, + "button": 2, + "buttons": 2, + "time": 91844580.737458, + "panning": true + }, + { + "type": "pointermove", + "x": 741, + "y": 465.5, + "button": 2, + "buttons": 2, + "time": 91844612.704916, + "panning": true + }, + { + "type": "pointermove", + "x": 749, + "y": 467.5, + "button": 2, + "buttons": 2, + "time": 91844643.597625, + "panning": true + }, + { + "type": "pointermove", + "x": 757, + "y": 469.5, + "button": 2, + "buttons": 2, + "time": 91844685.604375, + "panning": true + }, + { + "type": "pointermove", + "x": 761, + "y": 470.5, + "button": 2, + "buttons": 2, + "time": 91844708.194833, + "panning": true + }, + { + "type": "pointermove", + "x": 765, + "y": 471.5, + "button": 2, + "buttons": 2, + "time": 91844713.516791, + "panning": true + }, + { + "type": "pointermove", + "x": 773, + "y": 473.5, + "button": 2, + "buttons": 2, + "time": 91844751.631208, + "panning": true + }, + { + "type": "pointermove", + "x": 777, + "y": 474.5, + "button": 2, + "buttons": 2, + "time": 91844772.747958, + "panning": true + }, + { + "type": "pointermove", + "x": 785, + "y": 476.5, + "button": 2, + "buttons": 2, + "time": 91844809.693541, + "panning": true + }, + { + "type": "pointermove", + "x": 781, + "y": 475.5, + "button": 2, + "buttons": 2, + "time": 91844822.646, + "panning": true + }, + { + "type": "pointermove", + "x": 773, + "y": 473.5, + "button": 2, + "buttons": 2, + "time": 91844858.470083, + "panning": true + }, + { + "type": "pointermove", + "x": 769, + "y": 472.5, + "button": 2, + "buttons": 2, + "time": 91844872.39375, + "panning": true + }, + { + "type": "pointermove", + "x": 761, + "y": 470.5, + "button": 2, + "buttons": 2, + "time": 91844909.104, + "panning": true + }, + { + "type": "pointermove", + "x": 757, + "y": 469.5, + "button": 2, + "buttons": 2, + "time": 91844922.039791, + "panning": true + }, + { + "type": "pointermove", + "x": 749, + "y": 467.5, + "button": 2, + "buttons": 2, + "time": 91844957.792791, + "panning": true + }, + { + "type": "pointermove", + "x": 745, + "y": 466.5, + "button": 2, + "buttons": 2, + "time": 91844973.058541, + "panning": true + }, + { + "type": "pointermove", + "x": 737, + "y": 464.5, + "button": 2, + "buttons": 2, + "time": 91845009.287083, + "panning": true + }, + { + "type": "pointermove", + "x": 733, + "y": 463.5, + "button": 2, + "buttons": 2, + "time": 91845022.285541, + "panning": true + }, + { + "type": "pointermove", + "x": 725, + "y": 461.5, + "button": 2, + "buttons": 2, + "time": 91845056.992541, + "panning": true + }, + { + "type": "pointermove", + "x": 721, + "y": 460.5, + "button": 2, + "buttons": 2, + "time": 91845072.637458, + "panning": true + }, + { + "type": "pointermove", + "x": 713, + "y": 458.5, + "button": 2, + "buttons": 2, + "time": 91845112.211541, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 91845122.660208, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 91845159.072458, + "panning": true + }, + { + "type": "pointermove", + "x": 697, + "y": 454.5, + "button": 2, + "buttons": 2, + "time": 91845172.42575, + "panning": true + }, + { + "type": "pointermove", + "x": 689, + "y": 452.5, + "button": 2, + "buttons": 2, + "time": 91845208.279583, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 91845223.237875, + "panning": true + }, + { + "type": "pointermove", + "x": 677, + "y": 449.5, + "button": 2, + "buttons": 2, + "time": 91845258.730375, + "panning": true + }, + { + "type": "pointermove", + "x": 673, + "y": 448.5, + "button": 2, + "buttons": 2, + "time": 91845272.73525, + "panning": true + }, + { + "type": "pointermove", + "x": 665, + "y": 446.5, + "button": 2, + "buttons": 2, + "time": 91845307.785291, + "panning": true + }, + { + "type": "pointermove", + "x": 657, + "y": 444.5, + "button": 2, + "buttons": 2, + "time": 91845342.519458, + "panning": true + }, + { + "type": "pointermove", + "x": 649, + "y": 442.5, + "button": 2, + "buttons": 2, + "time": 91845377.102291, + "panning": true + }, + { + "type": "pointermove", + "x": 641, + "y": 440.5, + "button": 2, + "buttons": 2, + "time": 91845408.969, + "panning": true + }, + { + "type": "pointermove", + "x": 633, + "y": 438.5, + "button": 2, + "buttons": 2, + "time": 91845444.354041, + "panning": true + }, + { + "type": "pointermove", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 91845477.063125, + "panning": true + }, + { + "type": "pointerup", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 0, + "time": 91845491.23375, + "panning": false + } + ], + "captures": [], + "frames": [ + { + "timestamp": 91844169.835125, + "start": 91844175.632166, + "duration": 1.773958995938301 + }, + { + "timestamp": 91844187.083375, + "start": 91844194.273541, + "duration": 1.3454589992761612 + }, + { + "timestamp": 91844236.53412499, + "start": 91844241.942375, + "duration": 0.7792499959468842 + }, + { + "timestamp": 91844253.72083299, + "start": 91844257.675583, + "duration": 1.0627499967813492 + }, + { + "timestamp": 91844300.29629101, + "start": 91844303.819208, + "duration": 0.881167009472847 + }, + { + "timestamp": 91844320.388541, + "start": 91844323.751166, + "duration": 0.7004590034484863 + }, + { + "timestamp": 91844366.417, + "start": 91844373.444708, + "duration": 0.591499999165535 + }, + { + "timestamp": 91844387.052375, + "start": 91844390.415916, + "duration": 0.570917010307312 + }, + { + "timestamp": 91844430.558291, + "start": 91844433.859, + "duration": 0.7068749964237213 + }, + { + "timestamp": 91844436.326708, + "start": 91844439.391791, + "duration": 0.657709002494812 + }, + { + "timestamp": 91844480.02625, + "start": 91844483.288708, + "duration": 0.581375002861023 + }, + { + "timestamp": 91844487.05275, + "start": 91844490.254583, + "duration": 0.5378330051898956 + }, + { + "timestamp": 91844529.670083, + "start": 91844532.916458, + "duration": 0.4925830066204071 + }, + { + "timestamp": 91844537.06025, + "start": 91844540.373541, + "duration": 0.8868750035762787 + }, + { + "timestamp": 91844578.199708, + "start": 91844581.3765, + "duration": 0.6403750032186508 + }, + { + "timestamp": 91844610.292916, + "start": 91844613.34175, + "duration": 0.6335000097751617 + }, + { + "timestamp": 91844641.19604099, + "start": 91844644.198208, + "duration": 0.512374997138977 + }, + { + "timestamp": 91844681.612166, + "start": 91844687.128708, + "duration": 1.4412919878959656 + }, + { + "timestamp": 91844703.73579101, + "start": 91844714.219333, + "duration": 1.4324170053005219 + }, + { + "timestamp": 91844749.07729101, + "start": 91844752.381625, + "duration": 0.9706249982118607 + }, + { + "timestamp": 91844770.38475, + "start": 91844773.413083, + "duration": 0.7292079925537109 + }, + { + "timestamp": 91844807.26925, + "start": 91844810.294208, + "duration": 0.6178749948740005 + }, + { + "timestamp": 91844820.39862499, + "start": 91844824.000625, + "duration": 0.5395829975605011 + }, + { + "timestamp": 91844856.081375, + "start": 91844859.663666, + "duration": 0.5314170122146606 + }, + { + "timestamp": 91844870.163458, + "start": 91844873.065708, + "duration": 0.5214579999446869 + }, + { + "timestamp": 91844906.7605, + "start": 91844909.732958, + "duration": 0.4722919911146164 + }, + { + "timestamp": 91844919.91608301, + "start": 91844922.606416, + "duration": 0.4706670045852661 + }, + { + "timestamp": 91844955.443666, + "start": 91844958.418916, + "duration": 0.4726250022649765 + }, + { + "timestamp": 91844970.41604099, + "start": 91844973.7225, + "duration": 0.6950410008430481 + }, + { + "timestamp": 91845006.7575, + "start": 91845009.975375, + "duration": 0.5772909969091415 + }, + { + "timestamp": 91845020.12408301, + "start": 91845023.059083, + "duration": 0.5713330060243607 + }, + { + "timestamp": 91845054.514916, + "start": 91845057.711291, + "duration": 0.5313339978456497 + }, + { + "timestamp": 91845070.39429101, + "start": 91845073.346208, + "duration": 0.6195829957723618 + }, + { + "timestamp": 91845109.77212499, + "start": 91845112.914208, + "duration": 0.6747500002384186 + }, + { + "timestamp": 91845120.39158301, + "start": 91845123.404333, + "duration": 0.550792008638382 + }, + { + "timestamp": 91845156.682666, + "start": 91845159.765125, + "duration": 0.5379579961299896 + }, + { + "timestamp": 91845170.220375, + "start": 91845173.152208, + "duration": 0.56700000166893 + }, + { + "timestamp": 91845205.932416, + "start": 91845208.965416, + "duration": 0.5456250011920929 + }, + { + "timestamp": 91845219.977291, + "start": 91845225.404041, + "duration": 0.6435419917106628 + }, + { + "timestamp": 91845256.215125, + "start": 91845259.332541, + "duration": 0.5229589939117432 + }, + { + "timestamp": 91845270.392375, + "start": 91845273.429333, + "duration": 0.5488329976797104 + }, + { + "timestamp": 91845305.415458, + "start": 91845308.466875, + "duration": 0.5344579964876175 + }, + { + "timestamp": 91845340.108166, + "start": 91845343.244958, + "duration": 0.5485830008983612 + }, + { + "timestamp": 91845374.730958, + "start": 91845377.794041, + "duration": 0.5206670016050339 + }, + { + "timestamp": 91845406.514583, + "start": 91845409.68975, + "duration": 0.5442499965429306 + }, + { + "timestamp": 91845441.56675, + "start": 91845445.150583, + "duration": 0.5276249945163727 + }, + { + "timestamp": 91845474.67537501, + "start": 91845477.7775, + "duration": 0.4939579963684082 + }, + { + "timestamp": 91845519.026666, + "start": 91845519.083958, + "duration": 0.9104170054197311 + } + ], + "panning": false, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + "Kestrel pan composition timeline": { + "timestampFrequency": 1000000000, + "traceStarted": 91844129853458, + "publications": [ + { + "Timestamp": 91844155436125, + "Revision": 6, + "ConsumedInputSequence": 639244578631062862, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844181298416, + "Revision": 7, + "ConsumedInputSequence": 639244578631062865, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844197955958, + "Revision": 8, + "ConsumedInputSequence": 639244578631062866, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844244268875, + "Revision": 9, + "ConsumedInputSequence": 639244578631062869, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844260444500, + "Revision": 10, + "ConsumedInputSequence": 639244578631062870, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844306054458, + "Revision": 11, + "ConsumedInputSequence": 639244578631062873, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844325656000, + "Revision": 12, + "ConsumedInputSequence": 639244578631062874, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844375252708, + "Revision": 13, + "ConsumedInputSequence": 639244578631062877, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844392137500, + "Revision": 14, + "ConsumedInputSequence": 639244578631062878, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844436047791, + "Revision": 15, + "ConsumedInputSequence": 639244578631062880, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844453286041, + "Revision": 16, + "ConsumedInputSequence": 639244578631062881, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844484923291, + "Revision": 17, + "ConsumedInputSequence": 639244578631062883, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844503748875, + "Revision": 18, + "ConsumedInputSequence": 639244578631062884, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844534787458, + "Revision": 19, + "ConsumedInputSequence": 639244578631062886, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844554953291, + "Revision": 20, + "ConsumedInputSequence": 639244578631062887, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844583111208, + "Revision": 21, + "ConsumedInputSequence": 639244578631062889, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844614894833, + "Revision": 22, + "ConsumedInputSequence": 639244578631062891, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844646878041, + "Revision": 23, + "ConsumedInputSequence": 639244578631062893, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844689831166, + "Revision": 24, + "ConsumedInputSequence": 639244578631062895, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844717827250, + "Revision": 25, + "ConsumedInputSequence": 639244578631062897, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844755667000, + "Revision": 26, + "ConsumedInputSequence": 639244578631062899, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844775224208, + "Revision": 27, + "ConsumedInputSequence": 639244578631062900, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844812103666, + "Revision": 28, + "ConsumedInputSequence": 639244578631062902, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844825934333, + "Revision": 29, + "ConsumedInputSequence": 639244578631062903, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844861585291, + "Revision": 30, + "ConsumedInputSequence": 639244578631062905, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844874793125, + "Revision": 31, + "ConsumedInputSequence": 639244578631062906, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844911926458, + "Revision": 32, + "ConsumedInputSequence": 639244578631062908, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844924224208, + "Revision": 33, + "ConsumedInputSequence": 639244578631062909, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844960246166, + "Revision": 34, + "ConsumedInputSequence": 639244578631062911, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91844980142000, + "Revision": 35, + "ConsumedInputSequence": 639244578631062912, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845011900416, + "Revision": 36, + "ConsumedInputSequence": 639244578631062914, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845032020208, + "Revision": 37, + "ConsumedInputSequence": 639244578631062915, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845059631166, + "Revision": 38, + "ConsumedInputSequence": 639244578631062917, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845077569875, + "Revision": 39, + "ConsumedInputSequence": 639244578631062918, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845114729875, + "Revision": 40, + "ConsumedInputSequence": 639244578631062920, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845125148375, + "Revision": 41, + "ConsumedInputSequence": 639244578631062921, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845161893083, + "Revision": 42, + "ConsumedInputSequence": 639244578631062923, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845175014708, + "Revision": 43, + "ConsumedInputSequence": 639244578631062924, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845210900916, + "Revision": 44, + "ConsumedInputSequence": 639244578631062926, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845227485000, + "Revision": 45, + "ConsumedInputSequence": 639244578631062927, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845261214250, + "Revision": 46, + "ConsumedInputSequence": 639244578631062929, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845275215625, + "Revision": 47, + "ConsumedInputSequence": 639244578631062930, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845310615458, + "Revision": 48, + "ConsumedInputSequence": 639244578631062932, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845345338166, + "Revision": 49, + "ConsumedInputSequence": 639244578631062934, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845380267416, + "Revision": 50, + "ConsumedInputSequence": 639244578631062936, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845411607125, + "Revision": 51, + "ConsumedInputSequence": 639244578631062938, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845447120041, + "Revision": 52, + "ConsumedInputSequence": 639244578631062940, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845480282250, + "Revision": 53, + "ConsumedInputSequence": 639244578631062942, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845494183291, + "Revision": 54, + "ConsumedInputSequence": 639244578631062943, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91845521682208, + "Revision": 55, + "ConsumedInputSequence": 639244578631062943, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 91844178791291, + "Revision": 6, + "ConsumedInputSequence": 639244578631062862 + }, + { + "Timestamp": 91844234299083, + "Revision": 8, + "ConsumedInputSequence": 639244578631062866 + }, + { + "Timestamp": 91844298707250, + "Revision": 10, + "ConsumedInputSequence": 639244578631062870 + }, + { + "Timestamp": 91844364817541, + "Revision": 12, + "ConsumedInputSequence": 639244578631062874 + }, + { + "Timestamp": 91844429017458, + "Revision": 14, + "ConsumedInputSequence": 639244578631062878 + }, + { + "Timestamp": 91844478620875, + "Revision": 16, + "ConsumedInputSequence": 639244578631062881 + }, + { + "Timestamp": 91844528104583, + "Revision": 18, + "ConsumedInputSequence": 639244578631062884 + }, + { + "Timestamp": 91844576619791, + "Revision": 20, + "ConsumedInputSequence": 639244578631062887 + }, + { + "Timestamp": 91844608850916, + "Revision": 21, + "ConsumedInputSequence": 639244578631062889 + }, + { + "Timestamp": 91844639694125, + "Revision": 22, + "ConsumedInputSequence": 639244578631062891 + }, + { + "Timestamp": 91844679196833, + "Revision": 23, + "ConsumedInputSequence": 639244578631062893 + }, + { + "Timestamp": 91844747503833, + "Revision": 25, + "ConsumedInputSequence": 639244578631062897 + }, + { + "Timestamp": 91844805754416, + "Revision": 27, + "ConsumedInputSequence": 639244578631062900 + }, + { + "Timestamp": 91844854454250, + "Revision": 29, + "ConsumedInputSequence": 639244578631062903 + }, + { + "Timestamp": 91844905266208, + "Revision": 31, + "ConsumedInputSequence": 639244578631062906 + }, + { + "Timestamp": 91844953922041, + "Revision": 33, + "ConsumedInputSequence": 639244578631062909 + }, + { + "Timestamp": 91845005283541, + "Revision": 35, + "ConsumedInputSequence": 639244578631062912 + }, + { + "Timestamp": 91845053141833, + "Revision": 37, + "ConsumedInputSequence": 639244578631062915 + }, + { + "Timestamp": 91845108236125, + "Revision": 39, + "ConsumedInputSequence": 639244578631062918 + }, + { + "Timestamp": 91845155120000, + "Revision": 41, + "ConsumedInputSequence": 639244578631062921 + }, + { + "Timestamp": 91845204303333, + "Revision": 43, + "ConsumedInputSequence": 639244578631062924 + }, + { + "Timestamp": 91845254740833, + "Revision": 45, + "ConsumedInputSequence": 639244578631062927 + }, + { + "Timestamp": 91845303899458, + "Revision": 47, + "ConsumedInputSequence": 639244578631062930 + }, + { + "Timestamp": 91845338674916, + "Revision": 48, + "ConsumedInputSequence": 639244578631062932 + }, + { + "Timestamp": 91845373262708, + "Revision": 49, + "ConsumedInputSequence": 639244578631062934 + }, + { + "Timestamp": 91845404924958, + "Revision": 50, + "ConsumedInputSequence": 639244578631062936 + }, + { + "Timestamp": 91845439925708, + "Revision": 51, + "ConsumedInputSequence": 639244578631062938 + }, + { + "Timestamp": 91845473156250, + "Revision": 52, + "ConsumedInputSequence": 639244578631062940 + }, + { + "Timestamp": 91845517262458, + "Revision": 54, + "ConsumedInputSequence": 639244578631062943 + }, + { + "Timestamp": 91845553828125, + "Revision": 55, + "ConsumedInputSequence": 639244578631062943 + } + ], + "drawCallbackCompletions": [ + 91844178396833, + 91844234291000, + 91844298704416, + 91844364809791, + 91844429016083, + 91844478619750, + 91844528099125, + 91844576614375, + 91844608848166, + 91844639689875, + 91844679194125, + 91844747500708, + 91844805750833, + 91844854451250, + 91844905261166, + 91844953912125, + 91845005278416, + 91845053140541, + 91845108230708, + 91845155118583, + 91845204301958, + 91845254739125, + 91845303895250, + 91845338670166, + 91845373256916, + 91845404923041, + 91845439920125, + 91845473150041, + 91845517258625, + 91845553826083 + ], + "physicalPresentationVerified": false + } + } +} diff --git a/docs/graphics/evidence/kestrel/browser-native-sidebar-geometry.json b/docs/graphics/evidence/kestrel/browser-native-sidebar-geometry.json new file mode 100644 index 000000000..98288e605 --- /dev/null +++ b/docs/graphics/evidence/kestrel/browser-native-sidebar-geometry.json @@ -0,0 +1,102 @@ +{ + "browser": { + "viewport": [ + 792, + 878 + ], + "dpr": 2, + "sidebarBefore": 175, + "sidebarAfter": 295, + "canvasAfter": [ + 994, + 1082 + ], + "backend": "WebGPU", + "errors": 0, + "observation": "CUA drag and endpoint screenshot; no intermediate-frame or physical timing capture", + "restoredSidebarWidth": 175, + "temporaryTabClosed": true + }, + "native": { + "initialGeometry": { + "x": 173.5, + "y": 475.5, + "width": 175, + "viewport": [ + 792, + 878 + ], + "dpr": 2, + "canvas": [ + 1234, + 1082 + ] + }, + "sidebarAfter": 295, + "workloadValidated": true, + "delta": { + "Elapsed": "00:00:01.5265003", + "EnqueuedInputs": 123, + "DroppedInputs": 0, + "ConsumedInputs": 123, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 161, + "AppliedAnimationFrames": 61, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 242, + "BlockedPublications": 0, + "PublishedScenes": 11, + "AcquiredScenes": 11, + "AcknowledgedScenes": 11, + "RenderedScenes": 11, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 1, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 51, + "AnimationFramesInvoked": 51, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 251, + "WorkerWaits": 227, + "WorkerSignalledWakes": 200, + "WorkerTimeoutWakes": 26, + "SceneBuilds": 11, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 91, + "CompositionRenders": 11, + "CompositionAppliedDiffs": 11, + "CompositionInvalidations": 11, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 61, + "CompositionSkippedEmptyAnimationFrames": 30, + "CompositionRenderCallbacks": 11, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "physicalPresentationVerified": false, + "comparisonLimitations": [ + "Viewport/DPR/sidebar endpoints now align", + "Gesture pacing differs; no performance ratio or flicker classification", + "Browser restored locally autosaved workspace; full drawing-state equivalence unverified" + ] +} diff --git a/docs/graphics/evidence/kestrel/callback-cadence-audit.json b/docs/graphics/evidence/kestrel/callback-cadence-audit.json new file mode 100644 index 000000000..fc35fc9e6 --- /dev/null +++ b/docs/graphics/evidence/kestrel/callback-cadence-audit.json @@ -0,0 +1,43 @@ +{ + "physicalPresentationVerified": false, + "ratesAreNotFps": true, + "limitations": [ + "Intervals include settling.", + "Runs differ in tracing overhead and background activity.", + "Counts do not measure display scanout or identify why callbacks are absent." + ], + "runs": [ + { + "source": "pan-composition-timeline.json", + "intervalSecondsIncludingSettle": 1.8570006000000001, + "compositorCallbackRate": 51.696267626407874, + "renderedSceneRate": 34.46417841760525, + "submittedHostFrameRate": 33.38717284205509, + "applicationCallbackRate": 30.694658903179675 + }, + { + "source": "acceptance-to-draw-timeline.json", + "intervalSecondsIncludingSettle": 1.9112757999999999, + "compositorCallbackRate": 33.48548650069237, + "renderedSceneRate": 8.371371625173092, + "submittedHostFrameRate": 13.603478890906274, + "applicationCallbackRate": 13.603478890906274 + }, + { + "source": "unimported-scene-release.json", + "intervalSecondsIncludingSettle": 1.8539381000000001, + "compositorCallbackRate": 35.06050175030116, + "renderedSceneRate": 8.630277353920285, + "submittedHostFrameRate": 15.642377703980515, + "applicationCallbackRate": 15.642377703980515 + }, + { + "source": "acquisition-reasons-trace.json", + "intervalSecondsIncludingSettle": 1.8529947999999998, + "compositorCallbackRate": 39.93535222009258, + "renderedSceneRate": 10.79333843786286, + "submittedHostFrameRate": 17.809008422473717, + "applicationCallbackRate": 17.809008422473717 + } + ] +} diff --git a/docs/graphics/evidence/kestrel/chromium-resize-front-buffer.json b/docs/graphics/evidence/kestrel/chromium-resize-front-buffer.json new file mode 100644 index 000000000..9e0a3e0cb --- /dev/null +++ b/docs/graphics/evidence/kestrel/chromium-resize-front-buffer.json @@ -0,0 +1,27 @@ +{ + "sources": [ + { + "name": "gpu_canvas_context", + "url": "https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/webgpu/gpu_canvas_context.cc", + "sha256": "54a6bd062ca84cd860b1a8b909f805501c5ff931177a6227bbbc0cebd7040796" + }, + { + "name": "swap_buffer_provider", + "url": "https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc", + "sha256": "fadd272dd6df5bad58dbef192383f51d5fab80d176521ca66d12f518766c18e5" + }, + { + "name": "texture_layer", + "url": "https://chromium.googlesource.com/chromium/src/+/main/cc/layers/texture_layer.cc", + "sha256": "7ecb16da23a7da047e74c8429e6f2025289d9aaf868d179eb8066242b6fc04fa" + } + ], + "observations": [ + "GPUCanvasContext::Reshape calls ReplaceDrawingBuffer(false) and updates the texture dimensions.", + "ReplaceDrawingBuffer(false) discards current drawing storage without neutering the swap buffer provider.", + "ExportCurrentSharedImage returns null when there is no current buffer; PrepareTransferableResource then returns false.", + "TextureLayer updates its transferable resource only when PrepareTransferableResource returns true." + ], + "inference": "The existing compositor resource is distinct from the current resized drawing buffer; WebScene should model these separately instead of holding the whole scene.", + "qualification": "Source inspection; not a browser pixel or performance test." +} diff --git a/docs/graphics/evidence/kestrel/complete-host-raf-batch.json b/docs/graphics/evidence/kestrel/complete-host-raf-batch.json new file mode 100644 index 000000000..ea11a430f --- /dev/null +++ b/docs/graphics/evidence/kestrel/complete-host-raf-batch.json @@ -0,0 +1,145 @@ +{ + "change": "Drain complete admitted RAF batch on ordinary host frames", + "nativeRegression": "Before: host evaluation observed first callback alone; after: complete callback list, cancellation and next-frame timestamps pass", + "nativeSuitesPassed": [ + "webscene_native_engine_tests", + "webscene_graphics_v8_runtime_tests" + ], + "wptCandidate": { + "documentsPassed": 1, + "subtestsPassed": 5, + "upstreamQualification": false + }, + "sidebarRuns": [ + { + "label": "before", + "workloadValidated": true, + "originalWidth": 222, + "finalWidth": 342, + "delta": { + "Elapsed": "00:00:01.5703415", + "EnqueuedInputs": 122, + "DroppedInputs": 0, + "ConsumedInputs": 122, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 155, + "AppliedAnimationFrames": 59, + "CoalescedAnimationFrames": 1, + "PublicationAttempts": 198, + "BlockedPublications": 0, + "PublishedScenes": 11, + "AcquiredScenes": 11, + "AcknowledgedScenes": 11, + "RenderedScenes": 11, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 1, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 49, + "AnimationFramesInvoked": 49, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 241, + "WorkerWaits": 217, + "WorkerSignalledWakes": 194, + "WorkerTimeoutWakes": 22, + "SceneBuilds": 11, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 92, + "CompositionRenders": 11, + "CompositionAppliedDiffs": 11, + "CompositionInvalidations": 11, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 60, + "CompositionSkippedEmptyAnimationFrames": 32, + "CompositionRenderCallbacks": 11, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + { + "label": "after", + "workloadValidated": true, + "originalWidth": 222, + "finalWidth": 342, + "delta": { + "Elapsed": "00:00:01.5696242", + "EnqueuedInputs": 125, + "DroppedInputs": 0, + "ConsumedInputs": 125, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 155, + "AppliedAnimationFrames": 63, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 208, + "BlockedPublications": 0, + "PublishedScenes": 16, + "AcquiredScenes": 16, + "AcknowledgedScenes": 16, + "RenderedScenes": 16, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 1, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 48, + "AnimationFramesInvoked": 48, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 243, + "WorkerWaits": 208, + "WorkerSignalledWakes": 183, + "WorkerTimeoutWakes": 24, + "SceneBuilds": 16, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 94, + "CompositionRenders": 16, + "CompositionAppliedDiffs": 16, + "CompositionInvalidations": 16, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 63, + "CompositionSkippedEmptyAnimationFrames": 31, + "CompositionRenderCallbacks": 16, + "CompositionUnchangedRenderCallbacks": 0 + } + } + ], + "physicalPresentationVerified": false, + "performanceConclusion": "Single before/after diagnostic runs; no established speedup. Continuous resize publication deficit remains." +} diff --git a/docs/graphics/evidence/kestrel/completion-invalidation-and-full-capture.json b/docs/graphics/evidence/kestrel/completion-invalidation-and-full-capture.json new file mode 100644 index 000000000..c692f6545 --- /dev/null +++ b/docs/graphics/evidence/kestrel/completion-invalidation-and-full-capture.json @@ -0,0 +1,90 @@ +{ + "originalSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "change": "Avoid a second scene-generation invalidation when ordinary completion matches the already captured GPU output.", + "tests": { + "nativeEngineSeconds": 11.49, + "gpuRuntimeBeforeFullCaptureTestSeconds": 0.68, + "gpuRuntimeWithFullCaptureTestSeconds": 0.65, + "fullCaptureSequence": "Real document/scene builder: complete A; pending B; live C; complete B; complete C. CPU colors and GPU content serials remain paired; acknowledgements use scene ABI v3." + }, + "pan": { + "requestedMoves": 80, + "deliveredMoves": 73, + "coalescedMoves": 7, + "allMovesEnteredPan": true, + "releasedPan": true, + "delta": { + "Elapsed": "00:00:01.8640525", + "EnqueuedInputs": 144, + "DroppedInputs": 0, + "ConsumedInputs": 144, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 117, + "AppliedAnimationFrames": 62, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 117, + "BlockedPublications": 0, + "PublishedScenes": 62, + "AcquiredScenes": 62, + "AcknowledgedScenes": 62, + "RenderedScenes": 62, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 42, + "AnimationFramesInvoked": 42, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 210, + "WorkerWaits": 236, + "WorkerSignalledWakes": 208, + "WorkerTimeoutWakes": 27, + "SceneBuilds": 62, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 96, + "CompositionRenders": 62, + "CompositionAppliedDiffs": 62, + "CompositionInvalidations": 66, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 62, + "CompositionSkippedEmptyAnimationFrames": 34, + "CompositionRenderCallbacks": 66, + "CompositionUnchangedRenderCallbacks": 0 + }, + "elapsedMilliseconds": 1861.9205 + }, + "comparison": { + "previousFile": "native-right-button-pan.json", + "previousNoDamageSceneBuilds": 10, + "currentNoDamageSceneBuilds": 1, + "previousRenderedScenes": 66, + "currentRenderedScenes": 62, + "previousBlockedPublications": 9, + "currentBlockedPublications": 0 + }, + "limitations": [ + "Single short run per configuration, with differing coalescing and app RAF counts; not a statistically qualified performance improvement.", + "A/B/C uses native controlled completion and image leases; no physical GPU stall or presented-frame capture.", + "Multi-canvas out-of-order completion, browser baseline, resize and physical smoothness remain unqualified." + ] +} diff --git a/docs/graphics/evidence/kestrel/compositor-demand-trace.json b/docs/graphics/evidence/kestrel/compositor-demand-trace.json new file mode 100644 index 000000000..f1d96b30e --- /dev/null +++ b/docs/graphics/evidence/kestrel/compositor-demand-trace.json @@ -0,0 +1,2021 @@ +{ + "workloadValidated": true, + "physicalPresentationVerified": false, + "limitations": [ + "Temporary synchronous tracing affects timing." + ], + "ticks": [ + { + "timestamp": 92557209917708, + "demand": 0, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557226573541, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557243244375, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557259933750, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557284683000, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557293105333, + "demand": 0, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557309912166, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557355037250, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557359910041, + "demand": 0, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557376205500, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557419240208, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557426619625, + "demand": 0, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557443255125, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557486917791, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557493242250, + "demand": 0, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557509957333, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557552351083, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557559925000, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557585277458, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557593248375, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557615834625, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557626576791, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557648078833, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557659912250, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557680928083, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557693254333, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557715545041, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557726609166, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557747361583, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557759906125, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557798454541, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557809909583, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557848881500, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557859907833, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557899708708, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557909914875, + "demand": 0, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557926588666, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557961626750, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92557976633458, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558014898666, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558026128125, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558061657958, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558076635958, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558112955750, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558126576041, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558161686750, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558176609000, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558214961250, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558226574416, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558246903375, + "demand": 4, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558283502166, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558293300000, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558312617958, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558326587416, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558345703750, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558359926541, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558381002375, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558393303708, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558412673666, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558426626041, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558445861416, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558459971625, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558479147750, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558493277375, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558513823375, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558526589916, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558546014750, + "demand": 4, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558559969916, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558594262458, + "demand": 1, + "pending": 0, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + }, + { + "timestamp": 92558609960375, + "demand": 0, + "pending": 1, + "acceptedAwaitingDraw": false, + "gpuNeedsRender": false, + "retirements": false + } + ], + "acceptanceWindows": [ + { + "revision": 8, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 9, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 11, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 13, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 15, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 17, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 18, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 19, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 20, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 21, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 22, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 23, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 25, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 27, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 29, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 31, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 33, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 35, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 37, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 39, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 41, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 42, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 44, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 45, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 46, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 47, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 48, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 49, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 50, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 51, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 52, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + }, + { + "revision": 54, + "ticksBeforeAcceptance": 0, + "pendingCounts": [] + }, + { + "revision": 55, + "ticksBeforeAcceptance": 1, + "pendingCounts": [ + 1 + ] + } + ], + "records": { + "Kestrel pan performance": { + "elapsedMilliseconds": 1856.651, + "baseline": { + "ContextId": 1, + "Timestamp": 92557206050375, + "Engine": { + "EnqueuedInputs": 5, + "DroppedInputs": 0, + "ConsumedInputs": 5, + "PublishedScenes": 7, + "AcquiredScenes": 7, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 4094584, + "InputEventsDispatched": 35, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 583333, + "LastScenePublicationNanoseconds": 3044000, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 1, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 2, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 2327042, + "LastSceneBuildNanoseconds": 530625, + "MaximumScenePublicationNanoseconds": 3044000 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 482917, + "MaximumDispatchNanoseconds": 482917, + "LastDispatchSequence": 639244585761879203, + "DispatchedInputs": 2, + "TotalDispatchNanoseconds": 734959 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 2, + "TotalDispatchNanoseconds": 1667, + "LastDispatchNanoseconds": 1417, + "MaximumDispatchNanoseconds": 1417, + "LastTimestampMicroseconds": 92554166931 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 15, + "BlockedPublications": 8, + "AcknowledgedScenes": 7, + "TotalAcknowledgementNanoseconds": 473129626, + "LastAcknowledgementNanoseconds": 28432167, + "MaximumAcknowledgementNanoseconds": 170731667, + "AcknowledgedRevision": 7 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 2728208, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1853440, + "LatestSceneBytes": 127068, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 293108, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1928416, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 191424, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127788, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 5, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 92559065729083, + "Engine": { + "EnqueuedInputs": 133, + "DroppedInputs": 0, + "ConsumedInputs": 133, + "PublishedScenes": 55, + "AcquiredScenes": 55, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1421, + "LayoutPasses": 113, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 4094584, + "InputEventsDispatched": 171, + "InputCallbacksInvoked": 120, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 583333, + "LastScenePublicationNanoseconds": 1750, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 23, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 58, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 48, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 622750, + "LastSceneBuildNanoseconds": 300834, + "MaximumScenePublicationNanoseconds": 3044000 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 2921083, + "MaximumDispatchNanoseconds": 16506166, + "LastDispatchSequence": 639244585761879285, + "DispatchedInputs": 61, + "TotalDispatchNanoseconds": 212966504 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 48, + "TotalDispatchNanoseconds": 34831, + "LastDispatchNanoseconds": 2208, + "MaximumDispatchNanoseconds": 2208, + "LastTimestampMicroseconds": 92558594262 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 137, + "BlockedPublications": 8, + "AcknowledgedScenes": 55, + "TotalAcknowledgementNanoseconds": 1645773296, + "LastAcknowledgementNanoseconds": 28964125, + "MaximumAcknowledgementNanoseconds": 170731667, + "AcknowledgedRevision": 55 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 46, + "AnimationFramesInvoked": 46, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 182, + "WorkerWaits": 232, + "WorkerSignalledWakes": 201, + "WorkerTimeoutWakes": 30, + "SceneBuilds": 48, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 2728208, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1853440, + "LatestSceneBytes": 161008, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 293108, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1928416, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 191424, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127788, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 327, + "LogicalBitmapBytes": 5970848, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 2612, + "StringBytes": 143158, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 38, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 97, + "Renders": 33, + "AppliedDiffs": 48, + "InvalidationCalls": 33, + "DamageRectangles": 139, + "ChangedLayers": 46, + "EmptyDamageDiffs": 1, + "PartialDamageDiffs": 47, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 46, + "SkippedEmptyAnimationFrames": 51, + "RenderCallbacks": 33, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.8596787", + "EnqueuedInputs": 128, + "DroppedInputs": 0, + "ConsumedInputs": 128, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 105, + "AppliedAnimationFrames": 46, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 122, + "BlockedPublications": 0, + "PublishedScenes": 48, + "AcquiredScenes": 48, + "AcknowledgedScenes": 48, + "RenderedScenes": 33, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 46, + "AnimationFramesInvoked": 46, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 182, + "WorkerWaits": 232, + "WorkerSignalledWakes": 201, + "WorkerTimeoutWakes": 30, + "SceneBuilds": 48, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 97, + "CompositionRenders": 33, + "CompositionAppliedDiffs": 48, + "CompositionInvalidations": 33, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 46, + "CompositionSkippedEmptyAnimationFrames": 51, + "CompositionRenderCallbacks": 33, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "Kestrel pan composition timeline": { + "timestampFrequency": 1000000000, + "traceStarted": 92557209178958, + "publications": [ + { + "Timestamp": 92557226522416, + "Revision": 8, + "ConsumedInputSequence": 639244585761879204, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557258831041, + "Revision": 9, + "ConsumedInputSequence": 639244585761879207, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557293358666, + "Revision": 10, + "ConsumedInputSequence": 639244585761879209, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557319510666, + "Revision": 11, + "ConsumedInputSequence": 639244585761879211, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557361643541, + "Revision": 12, + "ConsumedInputSequence": 639244585761879213, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557384180041, + "Revision": 13, + "ConsumedInputSequence": 639244585761879215, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557428886000, + "Revision": 14, + "ConsumedInputSequence": 639244585761879217, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557448427750, + "Revision": 15, + "ConsumedInputSequence": 639244585761879218, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557493330416, + "Revision": 16, + "ConsumedInputSequence": 639244585761879221, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557514834750, + "Revision": 17, + "ConsumedInputSequence": 639244585761879222, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557557897666, + "Revision": 18, + "ConsumedInputSequence": 639244585761879225, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557591532541, + "Revision": 19, + "ConsumedInputSequence": 639244585761879227, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557624251333, + "Revision": 20, + "ConsumedInputSequence": 639244585761879229, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557657479625, + "Revision": 21, + "ConsumedInputSequence": 639244585761879231, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557690373166, + "Revision": 22, + "ConsumedInputSequence": 639244585761879233, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557724187125, + "Revision": 23, + "ConsumedInputSequence": 639244585761879235, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557754871208, + "Revision": 24, + "ConsumedInputSequence": 639244585761879236, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557769932166, + "Revision": 25, + "ConsumedInputSequence": 639244585761879237, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557807100208, + "Revision": 26, + "ConsumedInputSequence": 639244585761879239, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557820731416, + "Revision": 27, + "ConsumedInputSequence": 639244585761879240, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557857425500, + "Revision": 28, + "ConsumedInputSequence": 639244585761879242, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557867735958, + "Revision": 29, + "ConsumedInputSequence": 639244585761879243, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557910432791, + "Revision": 30, + "ConsumedInputSequence": 639244585761879246, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557932437458, + "Revision": 31, + "ConsumedInputSequence": 639244585761879247, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557968309041, + "Revision": 32, + "ConsumedInputSequence": 639244585761879249, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92557981550125, + "Revision": 33, + "ConsumedInputSequence": 639244585761879250, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558023497500, + "Revision": 34, + "ConsumedInputSequence": 639244585761879252, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558030818250, + "Revision": 35, + "ConsumedInputSequence": 639244585761879253, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558066732875, + "Revision": 36, + "ConsumedInputSequence": 639244585761879255, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558081293166, + "Revision": 37, + "ConsumedInputSequence": 639244585761879256, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558117967500, + "Revision": 38, + "ConsumedInputSequence": 639244585761879258, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558131349250, + "Revision": 39, + "ConsumedInputSequence": 639244585761879259, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558166672833, + "Revision": 40, + "ConsumedInputSequence": 639244585761879261, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558182764666, + "Revision": 41, + "ConsumedInputSequence": 639244585761879262, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558223829083, + "Revision": 42, + "ConsumedInputSequence": 639244585761879264, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558244464000, + "Revision": 43, + "ConsumedInputSequence": 639244585761879265, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558252086083, + "Revision": 44, + "ConsumedInputSequence": 639244585761879266, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558292082250, + "Revision": 45, + "ConsumedInputSequence": 639244585761879268, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558317690833, + "Revision": 46, + "ConsumedInputSequence": 639244585761879270, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558350810375, + "Revision": 47, + "ConsumedInputSequence": 639244585761879272, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558390782875, + "Revision": 48, + "ConsumedInputSequence": 639244585761879274, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558425038333, + "Revision": 49, + "ConsumedInputSequence": 639244585761879276, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558458286083, + "Revision": 50, + "ConsumedInputSequence": 639244585761879278, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558490300541, + "Revision": 51, + "ConsumedInputSequence": 639244585761879280, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558524004791, + "Revision": 52, + "ConsumedInputSequence": 639244585761879282, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558557647625, + "Revision": 53, + "ConsumedInputSequence": 639244585761879284, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558568789666, + "Revision": 54, + "ConsumedInputSequence": 639244585761879285, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92558596868208, + "Revision": 55, + "ConsumedInputSequence": 639244585761879285, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 92557234165208, + "Revision": 8, + "ConsumedInputSequence": 639244585761879204, + "AcceptedTimestamp": 92557226682333 + }, + { + "Timestamp": 92557282959958, + "Revision": 9, + "ConsumedInputSequence": 639244585761879207, + "AcceptedTimestamp": 92557279440125 + }, + { + "Timestamp": 92557354054500, + "Revision": 11, + "ConsumedInputSequence": 639244585761879211, + "AcceptedTimestamp": 92557350922666 + }, + { + "Timestamp": 92557417709833, + "Revision": 13, + "ConsumedInputSequence": 639244585761879215, + "AcceptedTimestamp": 92557415008750 + }, + { + "Timestamp": 92557486050958, + "Revision": 15, + "ConsumedInputSequence": 639244585761879218, + "AcceptedTimestamp": 92557483549583 + }, + { + "Timestamp": 92557550874458, + "Revision": 17, + "ConsumedInputSequence": 639244585761879222, + "AcceptedTimestamp": 92557548297208 + }, + { + "Timestamp": 92557583812208, + "Revision": 18, + "ConsumedInputSequence": 639244585761879225, + "AcceptedTimestamp": 92557581324500 + }, + { + "Timestamp": 92557614437875, + "Revision": 19, + "ConsumedInputSequence": 639244585761879227, + "AcceptedTimestamp": 92557612099125 + }, + { + "Timestamp": 92557646661916, + "Revision": 20, + "ConsumedInputSequence": 639244585761879229, + "AcceptedTimestamp": 92557644253541 + }, + { + "Timestamp": 92557679553250, + "Revision": 21, + "ConsumedInputSequence": 639244585761879231, + "AcceptedTimestamp": 92557677243791 + }, + { + "Timestamp": 92557714138166, + "Revision": 22, + "ConsumedInputSequence": 639244585761879233, + "AcceptedTimestamp": 92557711816125 + }, + { + "Timestamp": 92557745942250, + "Revision": 23, + "ConsumedInputSequence": 639244585761879235, + "AcceptedTimestamp": 92557743607125 + }, + { + "Timestamp": 92557797018458, + "Revision": 25, + "ConsumedInputSequence": 639244585761879237, + "AcceptedTimestamp": 92557794589333 + }, + { + "Timestamp": 92557847193208, + "Revision": 27, + "ConsumedInputSequence": 639244585761879240, + "AcceptedTimestamp": 92557844540416 + }, + { + "Timestamp": 92557898060041, + "Revision": 29, + "ConsumedInputSequence": 639244585761879243, + "AcceptedTimestamp": 92557895455583 + }, + { + "Timestamp": 92557960244833, + "Revision": 31, + "ConsumedInputSequence": 639244585761879247, + "AcceptedTimestamp": 92557957486416 + }, + { + "Timestamp": 92558013408500, + "Revision": 33, + "ConsumedInputSequence": 639244585761879250, + "AcceptedTimestamp": 92558010791250 + }, + { + "Timestamp": 92558060259583, + "Revision": 35, + "ConsumedInputSequence": 639244585761879253, + "AcceptedTimestamp": 92558057652083 + }, + { + "Timestamp": 92558111592833, + "Revision": 37, + "ConsumedInputSequence": 639244585761879256, + "AcceptedTimestamp": 92558109155583 + }, + { + "Timestamp": 92558160238541, + "Revision": 39, + "ConsumedInputSequence": 639244585761879259, + "AcceptedTimestamp": 92558157363375 + }, + { + "Timestamp": 92558213599541, + "Revision": 41, + "ConsumedInputSequence": 639244585761879262, + "AcceptedTimestamp": 92558211199875 + }, + { + "Timestamp": 92558245419583, + "Revision": 42, + "ConsumedInputSequence": 639244585761879264, + "AcceptedTimestamp": 92558242750041 + }, + { + "Timestamp": 92558282027250, + "Revision": 44, + "ConsumedInputSequence": 639244585761879266, + "AcceptedTimestamp": 92558279631666 + }, + { + "Timestamp": 92558311284458, + "Revision": 45, + "ConsumedInputSequence": 639244585761879268, + "AcceptedTimestamp": 92558308934750 + }, + { + "Timestamp": 92558344368125, + "Revision": 46, + "ConsumedInputSequence": 639244585761879270, + "AcceptedTimestamp": 92558341979083 + }, + { + "Timestamp": 92558379655625, + "Revision": 47, + "ConsumedInputSequence": 639244585761879272, + "AcceptedTimestamp": 92558377197833 + }, + { + "Timestamp": 92558411330166, + "Revision": 48, + "ConsumedInputSequence": 639244585761879274, + "AcceptedTimestamp": 92558408932333 + }, + { + "Timestamp": 92558444506541, + "Revision": 49, + "ConsumedInputSequence": 639244585761879276, + "AcceptedTimestamp": 92558442134625 + }, + { + "Timestamp": 92558477723041, + "Revision": 50, + "ConsumedInputSequence": 639244585761879278, + "AcceptedTimestamp": 92558475347583 + }, + { + "Timestamp": 92558512458833, + "Revision": 51, + "ConsumedInputSequence": 639244585761879280, + "AcceptedTimestamp": 92558510105541 + }, + { + "Timestamp": 92558544623041, + "Revision": 52, + "ConsumedInputSequence": 639244585761879282, + "AcceptedTimestamp": 92558542198000 + }, + { + "Timestamp": 92558592948458, + "Revision": 54, + "ConsumedInputSequence": 639244585761879285, + "AcceptedTimestamp": 92558590276125 + }, + { + "Timestamp": 92558628240708, + "Revision": 55, + "ConsumedInputSequence": 639244585761879285, + "AcceptedTimestamp": 92558625831458 + } + ], + "drawCallbackCompletions": [ + 92557233799958, + 92557282955791, + 92557354051791, + 92557417706208, + 92557486048000, + 92557550870625, + 92557583807875, + 92557614435666, + 92557646659666, + 92557679550916, + 92557714136916, + 92557745939791, + 92557797015791, + 92557847187416, + 92557898054333, + 92557960239250, + 92558013402500, + 92558060254750, + 92558111587958, + 92558160231916, + 92558213593791, + 92558245415250, + 92558282018416, + 92558311280291, + 92558344364500, + 92558379652083, + 92558411326000, + 92558444502875, + 92558477719250, + 92558512453583, + 92558544620916, + 92558592945875, + 92558628239666 + ], + "physicalPresentationVerified": false + } + } +} diff --git a/docs/graphics/evidence/kestrel/consumer-retirement-after-draw.json b/docs/graphics/evidence/kestrel/consumer-retirement-after-draw.json new file mode 100644 index 000000000..d8a48a99e --- /dev/null +++ b/docs/graphics/evidence/kestrel/consumer-retirement-after-draw.json @@ -0,0 +1,2350 @@ +{ + "originalSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "change": "Recheck retired GPU image groups after recording the current frame, under the existing Skia graphics lease. The current image group remains drawable; only retiring groups are polled. Fence waits retain zero timeout.", + "rejectedExperiment": "Polling through EnsureCurrent before producer-frame submission reduced collection age but processed fewer frames/moves. Removed before final implementation.", + "runs": { + "fence-trace": { + "deliveredMoves": 60, + "coalescedMoves": 20, + "callbacks": 30, + "renderedScenes": 44, + "elapsedMilliseconds": 1862.1027, + "errors": 0, + "fenceCollectionMedianMilliseconds": 20.396, + "fencePolls": [ + { + "ageMilliseconds": 0.466, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 15.689, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.08, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.097, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 16.351, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 15.679, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.119, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.157, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 17.897, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 16.633, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.084, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.115, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 16.86, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 16.509, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.106, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.109, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 16.597, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 15.666, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.071, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.148, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 18.082, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 16.503, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.096, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.214, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 16.477, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 37.688, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.069, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.087, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.716, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 7.94, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.088, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 24.631, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 16.66, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.104, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 17.66, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.085, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.1, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 16.792, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 16.416, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 4.193, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 16.397, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.263, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 39.949, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.079, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.101, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 23.878, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 38.786, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.062, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.119, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 39.455, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 26.42, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.102, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.089, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 43.046, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 39.558, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.071, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.087, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 22.84, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 38.955, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.075, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.078, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 38.343, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 19.381, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.063, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.08, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 35.844, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 37.583, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.074, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.088, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 19.989, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 36.771, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.072, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.081, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 35.332, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 20.803, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.089, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.092, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 33.966, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 33.811, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.065, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.096, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 18.749, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 36.343, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.072, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.639, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.089, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 33.228, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.064, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.134, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 18.819, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 33.982, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.063, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.072, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 33.57, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 19.19, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.063, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.077, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 35.06, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 4.79, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.086, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 34.001, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.08, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.074, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 18.036, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 34.926, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.081, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.086, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 34.116, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 19.838, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.105, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.07, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 36.246, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 33.248, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.109, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.067, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 19.682, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 32.328, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.06, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.611, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.077, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 33.58, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.065, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.091, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 18.509, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 33.675, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.084, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.6, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.07, + "poll": 1, + "glStatus": "911A" + } + ], + "delta": { + "Elapsed": "00:00:01.8642221", + "EnqueuedInputs": 125, + "DroppedInputs": 0, + "ConsumedInputs": 125, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 92, + "AppliedAnimationFrames": 43, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 89, + "BlockedPublications": 0, + "PublishedScenes": 44, + "AcquiredScenes": 44, + "AcknowledgedScenes": 44, + "RenderedScenes": 44, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 30, + "AnimationFramesInvoked": 30, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 172, + "WorkerWaits": 192, + "WorkerSignalledWakes": 163, + "WorkerTimeoutWakes": 28, + "SceneBuilds": 44, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 75, + "CompositionRenders": 44, + "CompositionAppliedDiffs": 44, + "CompositionInvalidations": 49, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 43, + "CompositionSkippedEmptyAnimationFrames": 32, + "CompositionRenderCallbacks": 49, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "early-retirement-pan": { + "deliveredMoves": 41, + "coalescedMoves": 39, + "callbacks": 28, + "renderedScenes": 32, + "elapsedMilliseconds": 1900.3174, + "errors": 0, + "fenceCollectionMedianMilliseconds": 4.961, + "fencePolls": [ + { + "ageMilliseconds": 0.472, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 5.361, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 2.693, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 15.045, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.081, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 5.122, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.089, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.649, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.126, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.602, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.151, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.138, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.131, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.09, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.228, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 8.12, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.1, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.831, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.101, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.885, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.169, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 19.023, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.153, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 9.174, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.096, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 5.029, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.106, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.767, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 38.341, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.09, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 5.41, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.086, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 6.314, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.232, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 5.387, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.152, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.485, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.096, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.965, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.117, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.893, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.097, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.963, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.087, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 5.488, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.224, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 6.117, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.1, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.159, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.099, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.697, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.093, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.624, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.163, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.704, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 42.703, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.098, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.973, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.095, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.304, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 43.569, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.095, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.739, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.097, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 35.555, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 4.012, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.094, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.33, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 38.514, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.084, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.295, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.058, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.62, + "poll": 2, + "glStatus": "911A" + } + ], + "delta": { + "Elapsed": "00:00:01.9023536", + "EnqueuedInputs": 113, + "DroppedInputs": 0, + "ConsumedInputs": 113, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 71, + "AppliedAnimationFrames": 31, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 79, + "BlockedPublications": 0, + "PublishedScenes": 32, + "AcquiredScenes": 32, + "AcknowledgedScenes": 32, + "RenderedScenes": 32, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 28, + "AnimationFramesInvoked": 28, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 132, + "WorkerWaits": 182, + "WorkerSignalledWakes": 147, + "WorkerTimeoutWakes": 34, + "SceneBuilds": 32, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 58, + "CompositionRenders": 32, + "CompositionAppliedDiffs": 32, + "CompositionInvalidations": 32, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 31, + "CompositionSkippedEmptyAnimationFrames": 27, + "CompositionRenderCallbacks": 32, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "after-draw-retirement-pan": { + "deliveredMoves": 65, + "coalescedMoves": 15, + "callbacks": 41, + "renderedScenes": 53, + "elapsedMilliseconds": 1855.9072, + "errors": 0, + "fenceCollectionMedianMilliseconds": 3.569, + "fencePolls": [ + { + "ageMilliseconds": 0.365, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.727, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 25.504, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.073, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.005, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.086, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.743, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 29.654, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.096, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.197, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.073, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.974, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.086, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.211, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 25.934, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.077, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.24, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.072, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.434, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.102, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.392, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.295, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.323, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.087, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.085, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.089, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.049, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.103, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.394, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.098, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.027, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.202, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.249, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.189, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.737, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.11, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.603, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.116, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.305, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.138, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.52, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.117, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.667, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.088, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.374, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.15, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.358, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.128, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.456, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.113, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.95, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.075, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.561, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 28.188, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.079, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.825, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.081, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.654, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.077, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.432, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 31.883, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.097, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.811, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.078, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 22.05, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 3.618, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.07, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.45, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 5.458, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.211, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.415, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.128, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.255, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.152, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.637, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.151, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.878, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.128, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.731, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 2.286, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 8.109, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.097, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.536, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.108, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.836, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 25.529, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.084, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.495, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.105, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 21.844, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 2.849, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.104, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.394, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 27.25, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.151, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.296, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.117, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.803, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.091, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.096, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.108, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.895, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.079, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.386, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 25.651, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.122, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.094, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.09, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.725, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.106, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.066, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.097, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.725, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.137, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.866, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 26.488, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.069, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.583, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.072, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 19.413, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 2.516, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.098, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.899, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.078, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.173, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 24.36, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.105, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.383, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.105, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.342, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 27.91, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.102, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.978, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.117, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.488, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 27.455, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.093, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.319, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.075, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 19.652, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 2.688, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.209, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.636, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.211, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.003, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 32.106, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.127, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.166, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.194, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 23.921, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 4.542, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.135, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.205, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 33.724, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.138, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.266, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.088, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 24.968, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 2.226, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 23.075, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.099, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.297, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.089, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 24.755, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 2.717, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.068, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.147, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 22.102, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.102, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.126, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.067, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.589, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.146, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.872, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.142, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 5.305, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.073, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.7, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.08, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.496, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.179, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.144, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.088, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.025, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.104, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.283, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.231, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.014, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 41.51, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.214, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.866, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.146, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 4.26, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.122, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.241, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.083, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.653, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 32.926, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.07, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.734, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 0.072, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 27.177, + "poll": 3, + "glStatus": "911A" + }, + { + "ageMilliseconds": 2.866, + "poll": 2, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.073, + "poll": 1, + "glStatus": "911B" + }, + { + "ageMilliseconds": 2.425, + "poll": 2, + "glStatus": "911B" + }, + { + "ageMilliseconds": 3.589, + "poll": 3, + "glStatus": "911B" + }, + { + "ageMilliseconds": 6.08, + "poll": 4, + "glStatus": "911A" + }, + { + "ageMilliseconds": 0.083, + "poll": 1, + "glStatus": "911A" + } + ], + "delta": { + "Elapsed": "00:00:01.8595648", + "EnqueuedInputs": 134, + "DroppedInputs": 0, + "ConsumedInputs": 134, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 108, + "AppliedAnimationFrames": 52, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 103, + "BlockedPublications": 1, + "PublishedScenes": 53, + "AcquiredScenes": 53, + "AcknowledgedScenes": 53, + "RenderedScenes": 53, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 41, + "AnimationFramesInvoked": 41, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 193, + "WorkerWaits": 211, + "WorkerSignalledWakes": 183, + "WorkerTimeoutWakes": 27, + "SceneBuilds": 53, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 78, + "CompositionRenders": 53, + "CompositionAppliedDiffs": 53, + "CompositionInvalidations": 54, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 52, + "CompositionSkippedEmptyAnimationFrames": 26, + "CompositionRenderCallbacks": 54, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "after-draw-final-pan": { + "deliveredMoves": 71, + "coalescedMoves": 9, + "callbacks": 52, + "renderedScenes": 61, + "elapsedMilliseconds": 1854.8945, + "errors": 0, + "fenceCollectionMedianMilliseconds": null, + "fencePolls": [], + "delta": { + "Elapsed": "00:00:01.8569473", + "EnqueuedInputs": 141, + "DroppedInputs": 0, + "ConsumedInputs": 141, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 125, + "AppliedAnimationFrames": 59, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 151, + "BlockedPublications": 0, + "PublishedScenes": 61, + "AcquiredScenes": 61, + "AcknowledgedScenes": 61, + "RenderedScenes": 61, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 52, + "AnimationFramesInvoked": 52, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 216, + "WorkerWaits": 252, + "WorkerSignalledWakes": 223, + "WorkerTimeoutWakes": 28, + "SceneBuilds": 61, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 89, + "CompositionRenders": 61, + "CompositionAppliedDiffs": 61, + "CompositionInvalidations": 61, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 59, + "CompositionSkippedEmptyAnimationFrames": 30, + "CompositionRenderCallbacks": 61, + "CompositionUnchangedRenderCallbacks": 0 + } + } + }, + "validation": { + "managedInteropTests": { + "net8Passed": 11, + "net10Passed": 11, + "skipped": 0 + }, + "ganesh": { + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "detachedBeforeRetirement": false + } + }, + "limitations": [ + "Short individual runs; no statistical or browser performance qualification.", + "Fence age measures collection time, not exact GPU completion time.", + "Traced and untraced runs have different instrumentation overhead; do not infer a speedup ratio.", + "Physical display smoothness, resize timing and all epic acceptance gates remain unqualified." + ] +} diff --git a/docs/graphics/evidence/kestrel/continuous-resize-native-counters.json b/docs/graphics/evidence/kestrel/continuous-resize-native-counters.json new file mode 100644 index 000000000..c562b4e6a --- /dev/null +++ b/docs/graphics/evidence/kestrel/continuous-resize-native-counters.json @@ -0,0 +1,54 @@ +{ + "validated": true, + "exitCode": 0, + "scriptedUpdates": 80, + "baselineResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "afterResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 44, + "AppliedPairs": 44, + "PublishedPairs": 44, + "TotalQueueNanoseconds": 4890623, + "LastQueueNanoseconds": 9459, + "MaximumQueueNanoseconds": 2927292, + "TotalDispatchNanoseconds": 87236626, + "LastDispatchNanoseconds": 1532708, + "MaximumDispatchNanoseconds": 7826083, + "AnimationFrameCallbacks": 44, + "TotalAnimationFrameBatchNanoseconds": 158392208, + "LastAnimationFrameBatchNanoseconds": 3774250, + "MaximumAnimationFrameBatchNanoseconds": 8730167, + "TotalToPublicationNanoseconds": 337478418, + "LastToPublicationNanoseconds": 7035834, + "MaximumToPublicationNanoseconds": 23807042 + }, + "engineDelta": { + "PublishedScenes": 44, + "LayoutPasses": 132, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 44, + "AppliedAnimationFrames": 44 + }, + "physicalPresentationVerified": false, + "nativeUserDragVerified": false +} diff --git a/docs/graphics/evidence/kestrel/continuous-window-resize.json b/docs/graphics/evidence/kestrel/continuous-window-resize.json new file mode 100644 index 000000000..4aee7e2f1 --- /dev/null +++ b/docs/graphics/evidence/kestrel/continuous-window-resize.json @@ -0,0 +1,101 @@ +{ + "validated": true, + "exitCode": 0, + "submittedUpdates": 80, + "activeUpdateSpanMilliseconds": 3436.036125, + "publishedScenes": 103, + "drawnScenes": 56, + "distinctPublishedViewportSizes": 41, + "maximumDrawCallbackGapMilliseconds": 88.04025, + "finalGeometry": { + "window": [ + 1280, + 800 + ], + "canvas": [ + 1612, + 926 + ], + "css": [ + 806, + 463 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 806, + 463 + ], + "height": "463px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1280, + 463 + ], + "height": "463px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + "physicalPresentationVerified": false, + "nativeUserDragVerified": false, + "limitations": [ + "Scripted window dimensions, not a native user drag.", + "Requested 16ms delays did not produce 16ms update cadence; actual update span is recorded.", + "Earlier development runs failed because monitoring was disabled and then required ancestor diagnostic data was missing; excluded from qualification." + ] +} diff --git a/docs/graphics/evidence/kestrel/cpu-scene-application.json b/docs/graphics/evidence/kestrel/cpu-scene-application.json new file mode 100644 index 000000000..06d12fa7c --- /dev/null +++ b/docs/graphics/evidence/kestrel/cpu-scene-application.json @@ -0,0 +1,152 @@ +{ + "runs": { + "baseline": { + "validated": true, + "stageCounts": { + "frame": 100, + "acquire:Success": 51, + "apply:images-retained": 51, + "apply:cpu-applied": 51, + "apply:replaced": 51, + "apply:Applied": 51, + "acquire:Empty": 2 + }, + "stageDurationsMilliseconds": { + "apply:images-retained": { + "count": 51, + "median": 0.009583, + "maximum": 0.041291 + }, + "apply:cpu-applied": { + "count": 51, + "median": 16.916167, + "maximum": 21.166542 + }, + "apply:replaced": { + "count": 51, + "median": 0.002666, + "maximum": 0.019417 + }, + "apply:Applied": { + "count": 51, + "median": 0.007083, + "maximum": 0.028125 + } + }, + "analysis": { + "physicalPresentationVerified": false, + "publicationToAcceptance": { + "count": 35, + "medianMilliseconds": 26.514083, + "p95Milliseconds": 31.56925, + "maximumMilliseconds": 34.658875 + }, + "acceptanceToDrawCallbackEnd": { + "count": 35, + "medianMilliseconds": 0.795042, + "p95Milliseconds": 1.106959, + "maximumMilliseconds": 1.733125 + }, + "publicationToDrawCallbackEnd": { + "count": 35, + "medianMilliseconds": 27.293625, + "p95Milliseconds": 32.469375, + "maximumMilliseconds": 35.604375 + }, + "inputToPublishedConsumptionWatermark": { + "count": 80, + "medianMilliseconds": 14.965792, + "p95Milliseconds": 32.548792, + "maximumMilliseconds": 38.463416 + }, + "unmatchedInputCount": 0, + "limitations": [ + "Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived." + ] + } + }, + "sharedShapers": { + "validated": true, + "stageCounts": { + "frame": 109, + "acquire:Success": 53, + "apply:images-retained": 53, + "apply:cpu-applied": 53, + "apply:replaced": 53, + "apply:Applied": 53, + "acquire:Empty": 1 + }, + "stageDurationsMilliseconds": { + "apply:images-retained": { + "count": 53, + "median": 0.011583, + "maximum": 0.033416 + }, + "apply:cpu-applied": { + "count": 53, + "median": 15.413291, + "maximum": 19.584917 + }, + "apply:replaced": { + "count": 53, + "median": 0.002167, + "maximum": 0.004958 + }, + "apply:Applied": { + "count": 53, + "median": 0.006, + "maximum": 0.044333 + } + }, + "analysis": { + "physicalPresentationVerified": false, + "publicationToAcceptance": { + "count": 33, + "medianMilliseconds": 25.524625, + "p95Milliseconds": 30.575458, + "maximumMilliseconds": 31.203333 + }, + "acceptanceToDrawCallbackEnd": { + "count": 33, + "medianMilliseconds": 0.769167, + "p95Milliseconds": 1.116334, + "maximumMilliseconds": 1.632625 + }, + "publicationToDrawCallbackEnd": { + "count": 33, + "medianMilliseconds": 26.274166, + "p95Milliseconds": 31.554666, + "maximumMilliseconds": 31.92725 + }, + "inputToPublishedConsumptionWatermark": { + "count": 80, + "medianMilliseconds": 14.3288125, + "p95Milliseconds": 30.780584, + "maximumMilliseconds": 38.169583 + }, + "unmatchedInputCount": 0, + "limitations": [ + "Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived." + ] + } + } + }, + "tests": { + "net8.0": { + "passed": 16, + "failed": 0 + }, + "net10.0": { + "passed": 16, + "failed": 0 + } + }, + "limitations": [ + "One validated run per variant; not a statistically qualified performance improvement.", + "Physical presentation and sustained 60 FPS remain unverified." + ] +} diff --git a/docs/graphics/evidence/kestrel/current-graphics-disabled-verification.json b/docs/graphics/evidence/kestrel/current-graphics-disabled-verification.json new file mode 100644 index 000000000..b68beb40b --- /dev/null +++ b/docs/graphics/evidence/kestrel/current-graphics-disabled-verification.json @@ -0,0 +1,30 @@ +{ + "revision": "62ec2336497463d99846f1bc42ff243de0c78ed7", + "rid": "osx-arm64", + "configuration": { + "buildType": "Release", + "v8": true, + "graphics": false + }, + "librarySha256": "8c639d87185632fa16a78ec9ac8efd85c44bab26a1d946118302ee05af7ae9c6", + "localUncommittedInputs": [ + "experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_css_cascade.inc", + "experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_runtime_tests.cpp" + ], + "buildExitCode": 0, + "nativeTestExitCode": 0, + "nativeTestsPassed": 1, + "wptCandidateExitCode": 0, + "wptCandidateOutput": "RUN contracts/animation-frame-batch-boundary.html ... PASS (123 ms)\nWPT subset: 1/1 documents passed; 5/5 subtests passed.\nResults: /Volumes/SSD/repos/worktrees/aa5a/HtmlML/artifacts/wpt-animation-frame-batch-graphics-disabled/results.json\n", + "dynamicDependencies": [ + "\t@rpath/libwebscene_native_engine.dylib (compatibility version 0.0.0, current version 0.0.0)", + "\t/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 5026.5.4)", + "\t/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)", + "\t/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 5026.5.4)", + "\t/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 61901.120.67)", + "\t/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.12)", + "\t/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 2100.43.0)", + "\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1356.0.0)" + ], + "scope": "Incremental current-worktree macOS graphics-disabled V8 verification; not a clean three-RID package qualification or performance result" +} diff --git a/docs/graphics/evidence/kestrel/current-pan-handoff-latency.json b/docs/graphics/evidence/kestrel/current-pan-handoff-latency.json new file mode 100644 index 000000000..f066cc03d --- /dev/null +++ b/docs/graphics/evidence/kestrel/current-pan-handoff-latency.json @@ -0,0 +1,33 @@ +{ + "physicalPresentationVerified": false, + "publicationToAcceptance": { + "count": 35, + "medianMilliseconds": 23.542541, + "p95Milliseconds": 32.272916, + "maximumMilliseconds": 34.768667 + }, + "acceptanceToDrawCallbackEnd": { + "count": 35, + "medianMilliseconds": 2.442334, + "p95Milliseconds": 3.355375, + "maximumMilliseconds": 9.497166 + }, + "publicationToDrawCallbackEnd": { + "count": 35, + "medianMilliseconds": 27.248666, + "p95Milliseconds": 34.689208, + "maximumMilliseconds": 37.257042 + }, + "inputToPublishedConsumptionWatermark": { + "count": 80, + "medianMilliseconds": 17.371916499999998, + "p95Milliseconds": 33.479667, + "maximumMilliseconds": 47.785375 + }, + "unmatchedInputCount": 0, + "limitations": [ + "Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived." + ] +} diff --git a/docs/graphics/evidence/kestrel/dawn-iosurface-metal-import.json b/docs/graphics/evidence/kestrel/dawn-iosurface-metal-import.json new file mode 100644 index 000000000..a76261148 --- /dev/null +++ b/docs/graphics/evidence/kestrel/dawn-iosurface-metal-import.json @@ -0,0 +1,13 @@ +{ + "host": "Avalonia.Native.MetalDevice", + "metalDeviceAvailable": true, + "metalQueueAvailable": true, + "skiaGpuContextAvailable": true, + "metalTextureWrapperVerified": true, + "dawnIOSurfaceImportVerified": true, + "producerInteropVerified": false, + "physicalPresentationVerified": false, + "scope": "Dawn completion-certified fixture IOSurface imported on leased Avalonia Metal device and wrapped as valid Skia backend texture; no image sampling", + "explicitTransportCopies": 0, + "consumerReadSubmitted": false +} diff --git a/docs/graphics/evidence/kestrel/dawn-metal-fence-bridge.json b/docs/graphics/evidence/kestrel/dawn-metal-fence-bridge.json new file mode 100644 index 000000000..bc7380399 --- /dev/null +++ b/docs/graphics/evidence/kestrel/dawn-metal-fence-bridge.json @@ -0,0 +1,15 @@ +{ + "test": "webscene_graphics_metal_event_handoff_tests", + "result": "passed", + "durationSeconds": 0.44, + "scope": "Two controlled Metal shared events imported into Dawn SharedFence objects, exported by the bridge and waited on by a separate Metal consumer queue", + "checks": [ + "reject mismatched fence/value count before queue submission", + "export every fence as MTLSharedEvent", + "consumer remains held by second dependency after first completes", + "diagnostic buffer bytes match after both dependencies complete" + ], + "dawnProducedTextureConsumed": false, + "productionPresenterIntegrated": false, + "physicalPresentationVerified": false +} diff --git a/docs/graphics/evidence/kestrel/dawn-metal-skia-sampling.json b/docs/graphics/evidence/kestrel/dawn-metal-skia-sampling.json new file mode 100644 index 000000000..b25ed8605 --- /dev/null +++ b/docs/graphics/evidence/kestrel/dawn-metal-skia-sampling.json @@ -0,0 +1,23 @@ +{ + "host": "Avalonia.Native.MetalDevice", + "metalDeviceAvailable": true, + "metalQueueAvailable": true, + "skiaGpuContextAvailable": true, + "metalTextureWrapperVerified": true, + "dawnIOSurfaceImportVerified": true, + "metalSampledPixelsVerified": true, + "diagnosticReadbacks": 1, + "producerInteropVerified": false, + "physicalPresentationVerified": false, + "scope": "Original Dawn completion-certified fixture sampled by Skia into a GPU diagnostic surface using active Avalonia Metal context", + "expectedRGBA": [ + 51, + 102, + 153, + 255 + ], + "rgbTolerance": 1, + "checkedEveryPixel": true, + "cleanup": "Diagnostic synchronous GRContext.Flush before native consumer release", + "productionPresenterIntegrated": false +} diff --git a/docs/graphics/evidence/kestrel/deadline-paced-window-resize.json b/docs/graphics/evidence/kestrel/deadline-paced-window-resize.json new file mode 100644 index 000000000..705b0a0fa --- /dev/null +++ b/docs/graphics/evidence/kestrel/deadline-paced-window-resize.json @@ -0,0 +1,33 @@ +{ + "validated": true, + "exitCode": 0, + "activeSpanMilliseconds": 1353.228292, + "activeCadence": { + "updates": { + "count": 80, + "medianIntervalMilliseconds": 15.73125, + "maximumIntervalMilliseconds": 66.180083 + }, + "compositorTicks": { + "count": 94, + "medianIntervalMilliseconds": 16.240334, + "maximumIntervalMilliseconds": 25.734875 + }, + "drawCallbacks": { + "count": 54, + "medianIntervalMilliseconds": 30.004791, + "maximumIntervalMilliseconds": 51.366583 + }, + "setters": { + "medianMilliseconds": 0.020104, + "maximumMilliseconds": 0.626709 + } + }, + "physicalPresentationVerified": false, + "nativeUserDragVerified": false, + "limitations": [ + "Single scripted run; callbacks are not physical presentation.", + "Deadline pacing changes the input workload, not production rendering.", + "Long input outliers remain; no fixed 60Hz delivery guarantee." + ] +} diff --git a/docs/graphics/evidence/kestrel/demand-order-experiment.json b/docs/graphics/evidence/kestrel/demand-order-experiment.json new file mode 100644 index 000000000..7a2747e6e --- /dev/null +++ b/docs/graphics/evidence/kestrel/demand-order-experiment.json @@ -0,0 +1,93 @@ +{ + "experiment": "Sample animation demand before observing compositor frame", + "disposition": "Reverted; no measured improvement", + "runs": { + "baseline": { + "elapsedMilliseconds": 1919.3152, + "counters": { + "CompositionAnimationFrames": 107, + "CompositionSubmittedAnimationFrames": 53, + "CompositionSkippedEmptyAnimationFrames": 54, + "RenderedScenes": 33, + "PublishedScenes": 54 + }, + "timing": { + "physicalPresentationVerified": false, + "publicationToAcceptance": { + "count": 33, + "medianMilliseconds": 26.548125, + "p95Milliseconds": 34.276625, + "maximumMilliseconds": 35.23975 + }, + "acceptanceToDrawCallbackEnd": { + "count": 33, + "medianMilliseconds": 0.795709, + "p95Milliseconds": 1.179542, + "maximumMilliseconds": 2.415625 + }, + "publicationToDrawCallbackEnd": { + "count": 33, + "medianMilliseconds": 27.5425, + "p95Milliseconds": 35.215417, + "maximumMilliseconds": 36.144 + }, + "inputToPublishedConsumptionWatermark": { + "count": 80, + "medianMilliseconds": 14.6361875, + "p95Milliseconds": 32.557125, + "maximumMilliseconds": 44.848708 + }, + "unmatchedInputCount": 0, + "limitations": [ + "Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived." + ] + } + }, + "experiment": { + "elapsedMilliseconds": 1917.7333, + "counters": { + "CompositionAnimationFrames": 102, + "CompositionSubmittedAnimationFrames": 53, + "CompositionSkippedEmptyAnimationFrames": 49, + "RenderedScenes": 33, + "PublishedScenes": 55 + }, + "timing": { + "physicalPresentationVerified": false, + "publicationToAcceptance": { + "count": 33, + "medianMilliseconds": 27.74775, + "p95Milliseconds": 33.9705, + "maximumMilliseconds": 35.260834 + }, + "acceptanceToDrawCallbackEnd": { + "count": 33, + "medianMilliseconds": 0.82875, + "p95Milliseconds": 1.236916, + "maximumMilliseconds": 2.069917 + }, + "publicationToDrawCallbackEnd": { + "count": 33, + "medianMilliseconds": 28.471333, + "p95Milliseconds": 34.786584, + "maximumMilliseconds": 36.177875 + }, + "inputToPublishedConsumptionWatermark": { + "count": 80, + "medianMilliseconds": 15.711333, + "p95Milliseconds": 33.017709, + "maximumMilliseconds": 45.134584 + }, + "unmatchedInputCount": 0, + "limitations": [ + "Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived." + ] + } + } + }, + "nextInvestigation": "Capture per-compositor-tick mailbox availability and scene acquisition/retirement backpressure; aggregate demand counts cannot establish why individual frames miss." +} diff --git a/docs/graphics/evidence/kestrel/device-lost-startup.json b/docs/graphics/evidence/kestrel/device-lost-startup.json new file mode 100644 index 000000000..fd61c9688 --- /dev/null +++ b/docs/graphics/evidence/kestrel/device-lost-startup.json @@ -0,0 +1,13 @@ +{ + "date": "2026-09-08", + "originalDocumentSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "status": { + "ready": "true", + "backend": "WebGPU \u00b7 GPU pipeline", + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands.Renderthis.device.queue.writeBuffer is not a function", + "errors": 1, + "gpu": true + }, + "exitCode": 1, + "acceptance": "failed: initial render requires GPUQueue.writeBuffer" +} diff --git a/docs/graphics/evidence/kestrel/dom-canvas-compilation-split.json b/docs/graphics/evidence/kestrel/dom-canvas-compilation-split.json new file mode 100644 index 000000000..26133c934 --- /dev/null +++ b/docs/graphics/evidence/kestrel/dom-canvas-compilation-split.json @@ -0,0 +1,30 @@ +{ + "validated": true, + "temporaryTimingHooksRemoved": true, + "stageDurationsMilliseconds": { + "cpu:dom-compiled": { + "count": 52, + "median": 12.098333, + "maximum": 19.496292 + }, + "cpu:layers-compiled": { + "count": 52, + "median": 2.4543755000000003, + "maximum": 4.426375 + } + }, + "physicalPresentationVerified": false, + "limitations": [ + "Single validated Kestrel workload; no command-kind attribution or physical FPS qualification." + ], + "orderedTextRegression": { + "net8.0": { + "passed": 5, + "failed": 0 + }, + "net10.0": { + "passed": 5, + "failed": 0 + } + } +} diff --git a/docs/graphics/evidence/kestrel/dom-text-command-profile.json b/docs/graphics/evidence/kestrel/dom-text-command-profile.json new file mode 100644 index 000000000..ee5f01d9b --- /dev/null +++ b/docs/graphics/evidence/kestrel/dom-text-command-profile.json @@ -0,0 +1,58 @@ +{ + "runs": { + "commands": { + "validated": true, + "compilations": 45, + "buckets": { + "text": { + "totalMilliseconds": 617.165347, + "medianMillisecondsPerCompilation": 13.09659 + }, + "svg": { + "totalMilliseconds": 9.603327, + "medianMillisecondsPerCompilation": 0.152826 + } + } + }, + "textRoutes": { + "validated": true, + "compilations": 41, + "buckets": { + "ordinaryText": { + "totalMilliseconds": 438.980528, + "medianMillisecondsPerCompilation": 10.018664 + }, + "spacedText": { + "totalMilliseconds": 156.592934, + "medianMillisecondsPerCompilation": 3.517793 + } + } + }, + "drawing": { + "validated": true, + "compilations": 43, + "buckets": { + "ordinaryTextTotal": { + "totalMilliseconds": 442.346195, + "medianMillisecondsPerCompilation": 9.59738 + }, + "ordinaryTextDrawSubset": { + "totalMilliseconds": 17.392991, + "medianMillisecondsPerCompilation": 0.274666 + }, + "spacedText": { + "totalMilliseconds": 158.53589, + "medianMillisecondsPerCompilation": 3.333041 + } + } + } + }, + "temporaryInstrumentationRemoved": true, + "physicalPresentationVerified": false, + "limitations": [ + "Includes startup and interaction compilations; not an active-only FPS measure.", + "Diagnostic timers/logging may perturb timings.", + "Draw subset overlaps ordinary-text total; do not sum these buckets.", + "Preparation includes parsing, font resolution, fallback checking, shaping, positioning and metrics; finer attribution remains." + ] +} diff --git a/docs/graphics/evidence/kestrel/early-gpu-publication.json b/docs/graphics/evidence/kestrel/early-gpu-publication.json new file mode 100644 index 000000000..aef365730 --- /dev/null +++ b/docs/graphics/evidence/kestrel/early-gpu-publication.json @@ -0,0 +1,55 @@ +{ + "implementation": "Validated early GPU image publication for consumers advertising producer GPU waits", + "nativeTests": { + "passed": 3, + "failed": 0, + "suites": [ + "webscene_graphics_completion_tests", + "webscene_native_engine_tests", + "webscene_graphics_v8_runtime_tests" + ] + }, + "originalKestrel": { + "startupPassed": true, + "panWorkloadValidated": true, + "analysis": { + "physicalPresentationVerified": false, + "publicationToAcceptance": { + "count": 33, + "medianMilliseconds": 26.548125, + "p95Milliseconds": 34.276625, + "maximumMilliseconds": 35.23975 + }, + "acceptanceToDrawCallbackEnd": { + "count": 33, + "medianMilliseconds": 0.795709, + "p95Milliseconds": 1.179542, + "maximumMilliseconds": 2.415625 + }, + "publicationToDrawCallbackEnd": { + "count": 33, + "medianMilliseconds": 27.5425, + "p95Milliseconds": 35.215417, + "maximumMilliseconds": 36.144 + }, + "inputToPublishedConsumptionWatermark": { + "count": 80, + "medianMilliseconds": 14.6361875, + "p95Milliseconds": 32.557125, + "maximumMilliseconds": 44.848708 + }, + "unmatchedInputCount": 0, + "limitations": [ + "Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived." + ] + } + }, + "limitations": [ + "Controlled scene regression verifies capability and validation gates but does not submit GPU reads.", + "This run does not prove that a real Dawn image was consumed while its producer work remained pending.", + "No physical presentation or 60 FPS qualification; observed handoff latency remains.", + "Delayed real-Dawn end-to-end handoff, device loss and continuous resize stress remain required." + ] +} diff --git a/docs/graphics/evidence/kestrel/explicit-opengl-resize.json b/docs/graphics/evidence/kestrel/explicit-opengl-resize.json new file mode 100644 index 000000000..337006188 --- /dev/null +++ b/docs/graphics/evidence/kestrel/explicit-opengl-resize.json @@ -0,0 +1,349 @@ +{ + "host": "Explicit AvaloniaNativeRenderingMode.OpenGl only", + "producer": "Dawn WebGPU", + "exitCode": 0, + "resizeGeometries": [ + { + "window": [ + 980, + 680 + ], + "canvas": [ + 1610, + 750 + ], + "css": [ + 805, + 375 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 175, + 196, + 805, + 375 + ], + "height": "375px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 196, + 980, + 375 + ], + "height": "375px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1440, + 900 + ], + "canvas": [ + 1932, + 1126 + ], + "css": [ + 966, + 563 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 966, + 563 + ], + "height": "563px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1440, + 563 + ], + "height": "563px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1100, + 740 + ], + "canvas": [ + 1362, + 806 + ], + "css": [ + 681, + 403 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 195, + 205, + 681, + 403 + ], + "height": "403px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1100, + 403 + ], + "height": "403px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1280, + 800 + ], + "canvas": [ + 1612, + 926 + ], + "css": [ + 806, + 463 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 806, + 463 + ], + "height": "463px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1280, + 463 + ], + "height": "463px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } + ], + "physicalPresentationVerified": false, + "limitations": [ + "This verifies the OpenGL compositor host, not WebGL API support or fallback.", + "Explicit-host sidebar run failed gesture validation and is not counted as a pass.", + "No native user-drag or visual flicker qualification." + ] +} diff --git a/docs/graphics/evidence/kestrel/explicit-opengl-sidebar.json b/docs/graphics/evidence/kestrel/explicit-opengl-sidebar.json new file mode 100644 index 000000000..7bdd9d918 --- /dev/null +++ b/docs/graphics/evidence/kestrel/explicit-opengl-sidebar.json @@ -0,0 +1,4402 @@ +{ + "date": "2026-09-08", + "sourceCommit": "0db03fd98d165d8bda5ecc9d27819877c8c9f426", + "host": "Explicit AvaloniaNativeRenderingMode.OpenGl only", + "producer": "Dawn WebGPU", + "command": "WEBSCENE_TEST_NATIVE_LIBRARY=\"$PWD/artifacts/graphics-build/native-v8-enabled/libwebscene_native_engine.dylib\" dotnet run --no-build --project experiments/WebScene.GpuHost.Probe -- --webgpu-opengl --kestrel tests/GraphicsCompatibility/fixtures/Kestrel-CAD.zip --sidebar-kestrel --verify-kestrel", + "exitCode": 0, + "workloadValidated": true, + "logSha256": "63c3348a731890ff5e7dabdca6f394c4e037959a2c61b9e94837c6b1fe1b8638", + "timeline": { + "traceStarted": 105336880826333, + "timestampFrequency": 1000000000, + "originalWidth": 222, + "width": 342, + "initialGeometry": { + "x": 220.5, + "y": 436.5, + "width": 222, + "viewport": [ + 1280, + 800 + ], + "dpr": 2, + "canvas": [ + 1612, + 926 + ] + }, + "baseline": { + "ContextId": 1, + "Timestamp": 105336876104750, + "Engine": { + "EnqueuedInputs": 18, + "DroppedInputs": 0, + "ConsumedInputs": 18, + "PublishedScenes": 11, + "AcquiredScenes": 10, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3280543, + "InputEventsDispatched": 95, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 40750, + "LastScenePublicationNanoseconds": 1410167, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 4, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 5, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 7, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 2243875, + "LastSceneBuildNanoseconds": 1119333, + "MaximumScenePublicationNanoseconds": 3074791 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 241458, + "MaximumDispatchNanoseconds": 597416, + "LastDispatchSequence": 639244713561672131, + "DispatchedInputs": 6, + "TotalDispatchNanoseconds": 2069374 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 7, + "TotalDispatchNanoseconds": 5209, + "LastDispatchNanoseconds": 1042, + "MaximumDispatchNanoseconds": 1333, + "LastTimestampMicroseconds": 105334927828 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 22, + "BlockedPublications": 0, + "AcknowledgedScenes": 10, + "TotalAcknowledgementNanoseconds": 376503832, + "LastAcknowledgementNanoseconds": 13644125, + "MaximumAcknowledgementNanoseconds": 107728625, + "AcknowledgedRevision": 11 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 2877660, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1282048, + "LatestSceneBytes": 127076, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 992, + "NativeDomInlineBytes": 1042592, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 480, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1079296, + "NativeDomNodePoolPeakBytes": 1079296, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 441696, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920268, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196928, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 131296, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1294, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1294, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 7, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 1, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 1, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 105338400222583, + "Engine": { + "EnqueuedInputs": 139, + "DroppedInputs": 0, + "ConsumedInputs": 139, + "PublishedScenes": 66, + "AcquiredScenes": 65, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1365, + "LayoutPasses": 165, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3379334, + "InputEventsDispatched": 243, + "InputCallbacksInvoked": 172, + "BusiestCanvasWidthMilli": 686000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 40750, + "LastScenePublicationNanoseconds": 5750, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 5, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 64, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 66, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 1535667, + "LastSceneBuildNanoseconds": 423584, + "MaximumScenePublicationNanoseconds": 3914791 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 124792, + "MaximumDispatchNanoseconds": 12222209, + "LastDispatchSequence": 639244713561672193, + "DispatchedInputs": 67, + "TotalDispatchNanoseconds": 362341834 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 66, + "TotalDispatchNanoseconds": 111919, + "LastDispatchNanoseconds": 20500, + "MaximumDispatchNanoseconds": 20500, + "LastTimestampMicroseconds": 105337910918 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 92, + "BlockedPublications": 0, + "AcknowledgedScenes": 65, + "TotalAcknowledgementNanoseconds": 856195832, + "LastAcknowledgementNanoseconds": 11793958, + "MaximumAcknowledgementNanoseconds": 107728625, + "AcknowledgedRevision": 71 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 49, + "AnimationFramesInvoked": 49, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 247, + "WorkerWaits": 189, + "WorkerSignalledWakes": 163, + "WorkerTimeoutWakes": 25, + "SceneBuilds": 60, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 2877660, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1282048, + "LatestSceneBytes": 156952, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 992, + "NativeDomInlineBytes": 1042592, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 480, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1079296, + "NativeDomNodePoolPeakBytes": 1079296, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 441696, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920268, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196928, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 131296, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1294, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1294, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 483, + "StringCount": 384, + "StringBytes": 56410, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 60, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 92, + "Renders": 53, + "AppliedDiffs": 55, + "InvalidationCalls": 54, + "DamageRectangles": 65, + "ChangedLayers": 10, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 55, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 59, + "SkippedEmptyAnimationFrames": 33, + "RenderCallbacks": 54, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.5241178", + "EnqueuedInputs": 121, + "DroppedInputs": 0, + "ConsumedInputs": 121, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 157, + "AppliedAnimationFrames": 59, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 70, + "BlockedPublications": 0, + "PublishedScenes": 55, + "AcquiredScenes": 55, + "AcknowledgedScenes": 55, + "RenderedScenes": 53, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 1, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 49, + "AnimationFramesInvoked": 49, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 247, + "WorkerWaits": 189, + "WorkerSignalledWakes": 163, + "WorkerTimeoutWakes": 25, + "SceneBuilds": 60, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 91, + "CompositionRenders": 53, + "CompositionAppliedDiffs": 55, + "CompositionInvalidations": 54, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 59, + "CompositionSkippedEmptyAnimationFrames": 32, + "CompositionRenderCallbacks": 54, + "CompositionUnchangedRenderCallbacks": 0 + }, + "submittedMoves": [ + { + "sequence": 639244713561672133, + "submittedAt": 105336880955291, + "step": 1, + "x": 222.5, + "y": 436.5 + }, + { + "sequence": 639244713561672134, + "submittedAt": 105336898166958, + "step": 2, + "x": 224.5, + "y": 436.5 + }, + { + "sequence": 639244713561672135, + "submittedAt": 105336915419208, + "step": 3, + "x": 226.5, + "y": 436.5 + }, + { + "sequence": 639244713561672136, + "submittedAt": 105336932518541, + "step": 4, + "x": 228.5, + "y": 436.5 + }, + { + "sequence": 639244713561672137, + "submittedAt": 105336949672125, + "step": 5, + "x": 230.5, + "y": 436.5 + }, + { + "sequence": 639244713561672138, + "submittedAt": 105336966773250, + "step": 6, + "x": 232.5, + "y": 436.5 + }, + { + "sequence": 639244713561672139, + "submittedAt": 105336983287291, + "step": 7, + "x": 234.5, + "y": 436.5 + }, + { + "sequence": 639244713561672140, + "submittedAt": 105337000378958, + "step": 8, + "x": 236.5, + "y": 436.5 + }, + { + "sequence": 639244713561672141, + "submittedAt": 105337017483791, + "step": 9, + "x": 238.5, + "y": 436.5 + }, + { + "sequence": 639244713561672142, + "submittedAt": 105337034679250, + "step": 10, + "x": 240.5, + "y": 436.5 + }, + { + "sequence": 639244713561672143, + "submittedAt": 105337051598708, + "step": 11, + "x": 242.5, + "y": 436.5 + }, + { + "sequence": 639244713561672144, + "submittedAt": 105337068739083, + "step": 12, + "x": 244.5, + "y": 436.5 + }, + { + "sequence": 639244713561672145, + "submittedAt": 105337085840916, + "step": 13, + "x": 246.5, + "y": 436.5 + }, + { + "sequence": 639244713561672146, + "submittedAt": 105337102983875, + "step": 14, + "x": 248.5, + "y": 436.5 + }, + { + "sequence": 639244713561672147, + "submittedAt": 105337118442541, + "step": 15, + "x": 250.5, + "y": 436.5 + }, + { + "sequence": 639244713561672148, + "submittedAt": 105337135554375, + "step": 16, + "x": 252.5, + "y": 436.5 + }, + { + "sequence": 639244713561672149, + "submittedAt": 105337152664125, + "step": 17, + "x": 254.5, + "y": 436.5 + }, + { + "sequence": 639244713561672150, + "submittedAt": 105337169768958, + "step": 18, + "x": 256.5, + "y": 436.5 + }, + { + "sequence": 639244713561672151, + "submittedAt": 105337186853916, + "step": 19, + "x": 258.5, + "y": 436.5 + }, + { + "sequence": 639244713561672152, + "submittedAt": 105337203627083, + "step": 20, + "x": 260.5, + "y": 436.5 + }, + { + "sequence": 639244713561672153, + "submittedAt": 105337220170208, + "step": 21, + "x": 262.5, + "y": 436.5 + }, + { + "sequence": 639244713561672154, + "submittedAt": 105337237259250, + "step": 22, + "x": 264.5, + "y": 436.5 + }, + { + "sequence": 639244713561672155, + "submittedAt": 105337253618833, + "step": 23, + "x": 266.5, + "y": 436.5 + }, + { + "sequence": 639244713561672156, + "submittedAt": 105337270704916, + "step": 24, + "x": 268.5, + "y": 436.5 + }, + { + "sequence": 639244713561672157, + "submittedAt": 105337287942083, + "step": 25, + "x": 270.5, + "y": 436.5 + }, + { + "sequence": 639244713561672158, + "submittedAt": 105337304565791, + "step": 26, + "x": 272.5, + "y": 436.5 + }, + { + "sequence": 639244713561672159, + "submittedAt": 105337321687125, + "step": 27, + "x": 274.5, + "y": 436.5 + }, + { + "sequence": 639244713561672160, + "submittedAt": 105337338821583, + "step": 28, + "x": 276.5, + "y": 436.5 + }, + { + "sequence": 639244713561672161, + "submittedAt": 105337355904791, + "step": 29, + "x": 278.5, + "y": 436.5 + }, + { + "sequence": 639244713561672162, + "submittedAt": 105337373056041, + "step": 30, + "x": 280.5, + "y": 436.5 + }, + { + "sequence": 639244713561672163, + "submittedAt": 105337390162625, + "step": 31, + "x": 282.5, + "y": 436.5 + }, + { + "sequence": 639244713561672164, + "submittedAt": 105337406614541, + "step": 32, + "x": 284.5, + "y": 436.5 + }, + { + "sequence": 639244713561672165, + "submittedAt": 105337423679125, + "step": 33, + "x": 286.5, + "y": 436.5 + }, + { + "sequence": 639244713561672166, + "submittedAt": 105337440761208, + "step": 34, + "x": 288.5, + "y": 436.5 + }, + { + "sequence": 639244713561672167, + "submittedAt": 105337457834041, + "step": 35, + "x": 290.5, + "y": 436.5 + }, + { + "sequence": 639244713561672168, + "submittedAt": 105337474902500, + "step": 36, + "x": 292.5, + "y": 436.5 + }, + { + "sequence": 639244713561672169, + "submittedAt": 105337492099291, + "step": 37, + "x": 294.5, + "y": 436.5 + }, + { + "sequence": 639244713561672170, + "submittedAt": 105337508571041, + "step": 38, + "x": 296.5, + "y": 436.5 + }, + { + "sequence": 639244713561672171, + "submittedAt": 105337525633833, + "step": 39, + "x": 298.5, + "y": 436.5 + }, + { + "sequence": 639244713561672172, + "submittedAt": 105337542695666, + "step": 40, + "x": 300.5, + "y": 436.5 + }, + { + "sequence": 639244713561672173, + "submittedAt": 105337559748875, + "step": 41, + "x": 302.5, + "y": 436.5 + }, + { + "sequence": 639244713561672174, + "submittedAt": 105337576811500, + "step": 42, + "x": 304.5, + "y": 436.5 + }, + { + "sequence": 639244713561672175, + "submittedAt": 105337593800666, + "step": 43, + "x": 306.5, + "y": 436.5 + }, + { + "sequence": 639244713561672176, + "submittedAt": 105337610867083, + "step": 44, + "x": 308.5, + "y": 436.5 + }, + { + "sequence": 639244713561672177, + "submittedAt": 105337627924708, + "step": 45, + "x": 310.5, + "y": 436.5 + }, + { + "sequence": 639244713561672178, + "submittedAt": 105337644275333, + "step": 46, + "x": 312.5, + "y": 436.5 + }, + { + "sequence": 639244713561672179, + "submittedAt": 105337660565000, + "step": 47, + "x": 314.5, + "y": 436.5 + }, + { + "sequence": 639244713561672180, + "submittedAt": 105337677649791, + "step": 48, + "x": 316.5, + "y": 436.5 + }, + { + "sequence": 639244713561672181, + "submittedAt": 105337694694416, + "step": 49, + "x": 318.5, + "y": 436.5 + }, + { + "sequence": 639244713561672182, + "submittedAt": 105337711753000, + "step": 50, + "x": 320.5, + "y": 436.5 + }, + { + "sequence": 639244713561672183, + "submittedAt": 105337728814750, + "step": 51, + "x": 322.5, + "y": 436.5 + }, + { + "sequence": 639244713561672184, + "submittedAt": 105337745853041, + "step": 52, + "x": 324.5, + "y": 436.5 + }, + { + "sequence": 639244713561672185, + "submittedAt": 105337762890750, + "step": 53, + "x": 326.5, + "y": 436.5 + }, + { + "sequence": 639244713561672186, + "submittedAt": 105337779960583, + "step": 54, + "x": 328.5, + "y": 436.5 + }, + { + "sequence": 639244713561672187, + "submittedAt": 105337797000166, + "step": 55, + "x": 330.5, + "y": 436.5 + }, + { + "sequence": 639244713561672188, + "submittedAt": 105337814092541, + "step": 56, + "x": 332.5, + "y": 436.5 + }, + { + "sequence": 639244713561672189, + "submittedAt": 105337831150875, + "step": 57, + "x": 334.5, + "y": 436.5 + }, + { + "sequence": 639244713561672190, + "submittedAt": 105337848203166, + "step": 58, + "x": 336.5, + "y": 436.5 + }, + { + "sequence": 639244713561672191, + "submittedAt": 105337865074041, + "step": 59, + "x": 338.5, + "y": 436.5 + }, + { + "sequence": 639244713561672192, + "submittedAt": 105337881493583, + "step": 60, + "x": 340.5, + "y": 436.5 + } + ], + "publications": [ + { + "Timestamp": 105336882986000, + "Revision": 12, + "ConsumedInputSequence": 639244713561672132, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105336921224250, + "Revision": 13, + "ConsumedInputSequence": 639244713561672134, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105336950874500, + "Revision": 14, + "ConsumedInputSequence": 639244713561672136, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105336965774291, + "Revision": 15, + "ConsumedInputSequence": 639244713561672137, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105336995492083, + "Revision": 16, + "ConsumedInputSequence": 639244713561672139, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337023683083, + "Revision": 17, + "ConsumedInputSequence": 639244713561672140, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337037951125, + "Revision": 18, + "ConsumedInputSequence": 639244713561672141, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337056149000, + "Revision": 19, + "ConsumedInputSequence": 639244713561672142, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337073104791, + "Revision": 20, + "ConsumedInputSequence": 639244713561672143, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337089483041, + "Revision": 21, + "ConsumedInputSequence": 639244713561672144, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337107185500, + "Revision": 22, + "ConsumedInputSequence": 639244713561672145, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337123015541, + "Revision": 23, + "ConsumedInputSequence": 639244713561672146, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337140107833, + "Revision": 24, + "ConsumedInputSequence": 639244713561672147, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337158147791, + "Revision": 25, + "ConsumedInputSequence": 639244713561672148, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337173469041, + "Revision": 26, + "ConsumedInputSequence": 639244713561672149, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337188065250, + "Revision": 27, + "ConsumedInputSequence": 639244713561672150, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337205781458, + "Revision": 28, + "ConsumedInputSequence": 639244713561672151, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337220700416, + "Revision": 29, + "ConsumedInputSequence": 639244713561672152, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337237721875, + "Revision": 30, + "ConsumedInputSequence": 639244713561672153, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337256796166, + "Revision": 31, + "ConsumedInputSequence": 639244713561672154, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337274647291, + "Revision": 32, + "ConsumedInputSequence": 639244713561672155, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337290532875, + "Revision": 33, + "ConsumedInputSequence": 639244713561672156, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337306757583, + "Revision": 34, + "ConsumedInputSequence": 639244713561672157, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337323959000, + "Revision": 35, + "ConsumedInputSequence": 639244713561672158, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337340390583, + "Revision": 36, + "ConsumedInputSequence": 639244713561672159, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337358798875, + "Revision": 37, + "ConsumedInputSequence": 639244713561672160, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337374918166, + "Revision": 38, + "ConsumedInputSequence": 639244713561672161, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337387441375, + "Revision": 39, + "ConsumedInputSequence": 639244713561672162, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337404364541, + "Revision": 40, + "ConsumedInputSequence": 639244713561672163, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337422741041, + "Revision": 41, + "ConsumedInputSequence": 639244713561672164, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337441380458, + "Revision": 42, + "ConsumedInputSequence": 639244713561672165, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337457675500, + "Revision": 43, + "ConsumedInputSequence": 639244713561672166, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337471361458, + "Revision": 44, + "ConsumedInputSequence": 639244713561672167, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337487832208, + "Revision": 45, + "ConsumedInputSequence": 639244713561672168, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337504445000, + "Revision": 46, + "ConsumedInputSequence": 639244713561672169, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337523992166, + "Revision": 47, + "ConsumedInputSequence": 639244713561672170, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337539819875, + "Revision": 48, + "ConsumedInputSequence": 639244713561672171, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337554167333, + "Revision": 49, + "ConsumedInputSequence": 639244713561672172, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337571343958, + "Revision": 50, + "ConsumedInputSequence": 639244713561672173, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337587705666, + "Revision": 51, + "ConsumedInputSequence": 639244713561672174, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337604108666, + "Revision": 52, + "ConsumedInputSequence": 639244713561672174, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337618337083, + "Revision": 53, + "ConsumedInputSequence": 639244713561672176, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337633985416, + "Revision": 54, + "ConsumedInputSequence": 639244713561672176, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337657758125, + "Revision": 55, + "ConsumedInputSequence": 639244713561672178, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337671001750, + "Revision": 56, + "ConsumedInputSequence": 639244713561672179, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337682721166, + "Revision": 57, + "ConsumedInputSequence": 639244713561672179, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337705660333, + "Revision": 58, + "ConsumedInputSequence": 639244713561672181, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337739393125, + "Revision": 60, + "ConsumedInputSequence": 639244713561672183, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337753696375, + "Revision": 61, + "ConsumedInputSequence": 639244713561672183, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337773230666, + "Revision": 62, + "ConsumedInputSequence": 639244713561672185, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337805819375, + "Revision": 64, + "ConsumedInputSequence": 639244713561672187, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337838744541, + "Revision": 66, + "ConsumedInputSequence": 639244713561672189, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337873118125, + "Revision": 68, + "ConsumedInputSequence": 639244713561672191, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337900386000, + "Revision": 70, + "ConsumedInputSequence": 639244713561672193, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 105337921598083, + "Revision": 71, + "ConsumedInputSequence": 639244713561672193, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 105336916103583, + "Revision": 12, + "ConsumedInputSequence": 639244713561672132, + "AcceptedTimestamp": 105336902851541 + }, + { + "Timestamp": 105336940790041, + "Revision": 13, + "ConsumedInputSequence": 639244713561672134, + "AcceptedTimestamp": 105336933798125 + }, + { + "Timestamp": 105336975329208, + "Revision": 15, + "ConsumedInputSequence": 639244713561672137, + "AcceptedTimestamp": 105336969749000 + }, + { + "Timestamp": 105337021322500, + "Revision": 16, + "ConsumedInputSequence": 639244713561672139, + "AcceptedTimestamp": 105337014228958 + }, + { + "Timestamp": 105337038254666, + "Revision": 17, + "ConsumedInputSequence": 639244713561672140, + "AcceptedTimestamp": 105337030029500 + }, + { + "Timestamp": 105337056127708, + "Revision": 18, + "ConsumedInputSequence": 639244713561672141, + "AcceptedTimestamp": 105337048610041 + }, + { + "Timestamp": 105337067628125, + "Revision": 19, + "ConsumedInputSequence": 639244713561672142, + "AcceptedTimestamp": 105337064445333 + }, + { + "Timestamp": 105337088918541, + "Revision": 20, + "ConsumedInputSequence": 639244713561672143, + "AcceptedTimestamp": 105337081344750 + }, + { + "Timestamp": 105337105788791, + "Revision": 21, + "ConsumedInputSequence": 639244713561672144, + "AcceptedTimestamp": 105337097735458 + }, + { + "Timestamp": 105337117377125, + "Revision": 22, + "ConsumedInputSequence": 639244713561672145, + "AcceptedTimestamp": 105337114095083 + }, + { + "Timestamp": 105337135965000, + "Revision": 23, + "ConsumedInputSequence": 639244713561672146, + "AcceptedTimestamp": 105337131795833 + }, + { + "Timestamp": 105337156106083, + "Revision": 24, + "ConsumedInputSequence": 639244713561672147, + "AcceptedTimestamp": 105337147487875 + }, + { + "Timestamp": 105337166686916, + "Revision": 25, + "ConsumedInputSequence": 639244713561672148, + "AcceptedTimestamp": 105337163452833 + }, + { + "Timestamp": 105337183674083, + "Revision": 26, + "ConsumedInputSequence": 639244713561672149, + "AcceptedTimestamp": 105337179669958 + }, + { + "Timestamp": 105337202178500, + "Revision": 27, + "ConsumedInputSequence": 639244713561672150, + "AcceptedTimestamp": 105337198064541 + }, + { + "Timestamp": 105337221546500, + "Revision": 28, + "ConsumedInputSequence": 639244713561672151, + "AcceptedTimestamp": 105337213618916 + }, + { + "Timestamp": 105337232828625, + "Revision": 29, + "ConsumedInputSequence": 639244713561672152, + "AcceptedTimestamp": 105337230107625 + }, + { + "Timestamp": 105337249580375, + "Revision": 30, + "ConsumedInputSequence": 639244713561672153, + "AcceptedTimestamp": 105337246576666 + }, + { + "Timestamp": 105337271077166, + "Revision": 31, + "ConsumedInputSequence": 639244713561672154, + "AcceptedTimestamp": 105337263459916 + }, + { + "Timestamp": 105337289028208, + "Revision": 32, + "ConsumedInputSequence": 639244713561672155, + "AcceptedTimestamp": 105337280940625 + }, + { + "Timestamp": 105337300276041, + "Revision": 33, + "ConsumedInputSequence": 639244713561672156, + "AcceptedTimestamp": 105337296988625 + }, + { + "Timestamp": 105337316196208, + "Revision": 34, + "ConsumedInputSequence": 639244713561672157, + "AcceptedTimestamp": 105337312749208 + }, + { + "Timestamp": 105337334172625, + "Revision": 35, + "ConsumedInputSequence": 639244713561672158, + "AcceptedTimestamp": 105337330643666 + }, + { + "Timestamp": 105337351313791, + "Revision": 36, + "ConsumedInputSequence": 639244713561672159, + "AcceptedTimestamp": 105337347632875 + }, + { + "Timestamp": 105337370787833, + "Revision": 37, + "ConsumedInputSequence": 639244713561672160, + "AcceptedTimestamp": 105337364558625 + }, + { + "Timestamp": 105337383463291, + "Revision": 38, + "ConsumedInputSequence": 639244713561672161, + "AcceptedTimestamp": 105337380007166 + }, + { + "Timestamp": 105337400132916, + "Revision": 39, + "ConsumedInputSequence": 639244713561672162, + "AcceptedTimestamp": 105337396776875 + }, + { + "Timestamp": 105337416824333, + "Revision": 40, + "ConsumedInputSequence": 639244713561672163, + "AcceptedTimestamp": 105337413512125 + }, + { + "Timestamp": 105337434034208, + "Revision": 41, + "ConsumedInputSequence": 639244713561672164, + "AcceptedTimestamp": 105337430336958 + }, + { + "Timestamp": 105337451351750, + "Revision": 42, + "ConsumedInputSequence": 639244713561672165, + "AcceptedTimestamp": 105337447887875 + }, + { + "Timestamp": 105337467118000, + "Revision": 43, + "ConsumedInputSequence": 639244713561672166, + "AcceptedTimestamp": 105337463733791 + }, + { + "Timestamp": 105337483508000, + "Revision": 44, + "ConsumedInputSequence": 639244713561672167, + "AcceptedTimestamp": 105337480145625 + }, + { + "Timestamp": 105337500309000, + "Revision": 45, + "ConsumedInputSequence": 639244713561672168, + "AcceptedTimestamp": 105337496876458 + }, + { + "Timestamp": 105337516458041, + "Revision": 46, + "ConsumedInputSequence": 639244713561672169, + "AcceptedTimestamp": 105337513344958 + }, + { + "Timestamp": 105337534249875, + "Revision": 47, + "ConsumedInputSequence": 639244713561672170, + "AcceptedTimestamp": 105337531105875 + }, + { + "Timestamp": 105337549693375, + "Revision": 48, + "ConsumedInputSequence": 639244713561672171, + "AcceptedTimestamp": 105337546727000 + }, + { + "Timestamp": 105337566938791, + "Revision": 49, + "ConsumedInputSequence": 639244713561672172, + "AcceptedTimestamp": 105337563727625 + }, + { + "Timestamp": 105337583467958, + "Revision": 50, + "ConsumedInputSequence": 639244713561672173, + "AcceptedTimestamp": 105337580265000 + }, + { + "Timestamp": 105337600565375, + "Revision": 51, + "ConsumedInputSequence": 639244713561672174, + "AcceptedTimestamp": 105337596283750 + }, + { + "Timestamp": 105337618047458, + "Revision": 52, + "ConsumedInputSequence": 639244713561672174, + "AcceptedTimestamp": 105337614948041 + }, + { + "Timestamp": 105337633344250, + "Revision": 53, + "ConsumedInputSequence": 639244713561672176, + "AcceptedTimestamp": 105337630482250 + }, + { + "Timestamp": 105337650580375, + "Revision": 54, + "ConsumedInputSequence": 639244713561672176, + "AcceptedTimestamp": 105337647567083 + }, + { + "Timestamp": 105337666601875, + "Revision": 55, + "ConsumedInputSequence": 639244713561672178, + "AcceptedTimestamp": 105337663474500 + }, + { + "Timestamp": 105337689377458, + "Revision": 57, + "ConsumedInputSequence": 639244713561672179, + "AcceptedTimestamp": 105337685857250 + }, + { + "Timestamp": 105337717032166, + "Revision": 58, + "ConsumedInputSequence": 639244713561672181, + "AcceptedTimestamp": 105337713760708 + }, + { + "Timestamp": 105337750741500, + "Revision": 60, + "ConsumedInputSequence": 639244713561672183, + "AcceptedTimestamp": 105337747049458 + }, + { + "Timestamp": 105337766433458, + "Revision": 61, + "ConsumedInputSequence": 639244713561672183, + "AcceptedTimestamp": 105337764008666 + }, + { + "Timestamp": 105337784091041, + "Revision": 62, + "ConsumedInputSequence": 639244713561672185, + "AcceptedTimestamp": 105337781287583 + }, + { + "Timestamp": 105337816719875, + "Revision": 64, + "ConsumedInputSequence": 639244713561672187, + "AcceptedTimestamp": 105337813711708 + }, + { + "Timestamp": 105337850277500, + "Revision": 66, + "ConsumedInputSequence": 639244713561672189, + "AcceptedTimestamp": 105337847452666 + }, + { + "Timestamp": 105337884418000, + "Revision": 68, + "ConsumedInputSequence": 639244713561672191, + "AcceptedTimestamp": 105337880042458 + }, + { + "Timestamp": 105337918801375, + "Revision": 70, + "ConsumedInputSequence": 639244713561672193, + "AcceptedTimestamp": 105337913905541 + }, + { + "Timestamp": 105337936661833, + "Revision": 71, + "ConsumedInputSequence": 639244713561672193, + "AcceptedTimestamp": 105337933405625 + } + ], + "scheduling": [ + { + "Timestamp": 105336894249625, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 11, + "PendingRetirements": false + }, + { + "Timestamp": 105336894334875, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 11, + "PendingRetirements": false + }, + { + "Timestamp": 105336894359833, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 12, + "PendingRetirements": false + }, + { + "Timestamp": 105336902793333, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 12, + "PendingRetirements": false + }, + { + "Timestamp": 105336902798166, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 12, + "PendingRetirements": true + }, + { + "Timestamp": 105336902835750, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 12, + "PendingRetirements": true + }, + { + "Timestamp": 105336920358000, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 12, + "PendingRetirements": false + }, + { + "Timestamp": 105336927599916, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 12, + "PendingRetirements": false + }, + { + "Timestamp": 105336927645250, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 12, + "PendingRetirements": false + }, + { + "Timestamp": 105336927659375, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 13, + "PendingRetirements": false + }, + { + "Timestamp": 105336933749166, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 13, + "PendingRetirements": false + }, + { + "Timestamp": 105336933753125, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 13, + "PendingRetirements": true + }, + { + "Timestamp": 105336933786625, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 13, + "PendingRetirements": true + }, + { + "Timestamp": 105336944248333, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 13, + "PendingRetirements": false + }, + { + "Timestamp": 105336960911041, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 13, + "PendingRetirements": false + }, + { + "Timestamp": 105336960946000, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 13, + "PendingRetirements": false + }, + { + "Timestamp": 105336960964041, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 14, + "PendingRetirements": false + }, + { + "Timestamp": 105336966219416, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 14, + "PendingRetirements": false + }, + { + "Timestamp": 105336966222250, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 14, + "PendingRetirements": true + }, + { + "Timestamp": 105336966227958, + "Stage": "apply:Applied", + "PendingPublications": 2, + "Revision": 14, + "PendingRetirements": true + }, + { + "Timestamp": 105336966244791, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 14, + "PendingRetirements": true + }, + { + "Timestamp": 105336966252500, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 15, + "PendingRetirements": true + }, + { + "Timestamp": 105336969732041, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 15, + "PendingRetirements": true + }, + { + "Timestamp": 105336969737833, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 15, + "PendingRetirements": true + }, + { + "Timestamp": 105336969741125, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 15, + "PendingRetirements": true + }, + { + "Timestamp": 105336977573708, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 15, + "PendingRetirements": false + }, + { + "Timestamp": 105336994230708, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 15, + "PendingRetirements": false + }, + { + "Timestamp": 105337010943041, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 15, + "PendingRetirements": false + }, + { + "Timestamp": 105337010958916, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 15, + "PendingRetirements": false + }, + { + "Timestamp": 105337010971375, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 16, + "PendingRetirements": false + }, + { + "Timestamp": 105337014216708, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 16, + "PendingRetirements": false + }, + { + "Timestamp": 105337014219250, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 16, + "PendingRetirements": true + }, + { + "Timestamp": 105337014223666, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 16, + "PendingRetirements": true + }, + { + "Timestamp": 105337026581166, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 16, + "PendingRetirements": false + }, + { + "Timestamp": 105337026591666, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 16, + "PendingRetirements": false + }, + { + "Timestamp": 105337026600708, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 17, + "PendingRetirements": false + }, + { + "Timestamp": 105337030010083, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 17, + "PendingRetirements": false + }, + { + "Timestamp": 105337030012250, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 17, + "PendingRetirements": true + }, + { + "Timestamp": 105337030022583, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 17, + "PendingRetirements": true + }, + { + "Timestamp": 105337044236750, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 17, + "PendingRetirements": false + }, + { + "Timestamp": 105337044292458, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 17, + "PendingRetirements": false + }, + { + "Timestamp": 105337044305458, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 18, + "PendingRetirements": false + }, + { + "Timestamp": 105337048582791, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 18, + "PendingRetirements": false + }, + { + "Timestamp": 105337048585166, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 18, + "PendingRetirements": true + }, + { + "Timestamp": 105337048604541, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 18, + "PendingRetirements": true + }, + { + "Timestamp": 105337060950166, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 18, + "PendingRetirements": false + }, + { + "Timestamp": 105337060981625, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 18, + "PendingRetirements": false + }, + { + "Timestamp": 105337060996083, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 19, + "PendingRetirements": false + }, + { + "Timestamp": 105337064433000, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 19, + "PendingRetirements": false + }, + { + "Timestamp": 105337064435500, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 19, + "PendingRetirements": true + }, + { + "Timestamp": 105337064440083, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 19, + "PendingRetirements": true + }, + { + "Timestamp": 105337077622458, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 19, + "PendingRetirements": false + }, + { + "Timestamp": 105337077634166, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 19, + "PendingRetirements": false + }, + { + "Timestamp": 105337077645458, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 20, + "PendingRetirements": false + }, + { + "Timestamp": 105337081333000, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 20, + "PendingRetirements": false + }, + { + "Timestamp": 105337081335333, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 20, + "PendingRetirements": true + }, + { + "Timestamp": 105337081340833, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 20, + "PendingRetirements": true + }, + { + "Timestamp": 105337094238875, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 20, + "PendingRetirements": false + }, + { + "Timestamp": 105337094251541, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 20, + "PendingRetirements": false + }, + { + "Timestamp": 105337094260541, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 21, + "PendingRetirements": false + }, + { + "Timestamp": 105337097724458, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 21, + "PendingRetirements": false + }, + { + "Timestamp": 105337097727000, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 21, + "PendingRetirements": true + }, + { + "Timestamp": 105337097731791, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 21, + "PendingRetirements": true + }, + { + "Timestamp": 105337110895750, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 21, + "PendingRetirements": false + }, + { + "Timestamp": 105337110905666, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 21, + "PendingRetirements": false + }, + { + "Timestamp": 105337110914041, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 22, + "PendingRetirements": false + }, + { + "Timestamp": 105337114083916, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 22, + "PendingRetirements": false + }, + { + "Timestamp": 105337114086291, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 22, + "PendingRetirements": true + }, + { + "Timestamp": 105337114090833, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 22, + "PendingRetirements": true + }, + { + "Timestamp": 105337127601083, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 22, + "PendingRetirements": false + }, + { + "Timestamp": 105337127611916, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 22, + "PendingRetirements": false + }, + { + "Timestamp": 105337127621000, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 23, + "PendingRetirements": false + }, + { + "Timestamp": 105337131772583, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 23, + "PendingRetirements": false + }, + { + "Timestamp": 105337131775000, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 23, + "PendingRetirements": true + }, + { + "Timestamp": 105337131791833, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 23, + "PendingRetirements": true + }, + { + "Timestamp": 105337144261791, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 23, + "PendingRetirements": false + }, + { + "Timestamp": 105337144307958, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 23, + "PendingRetirements": false + }, + { + "Timestamp": 105337144319708, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 24, + "PendingRetirements": false + }, + { + "Timestamp": 105337147476458, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 24, + "PendingRetirements": false + }, + { + "Timestamp": 105337147478416, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 24, + "PendingRetirements": true + }, + { + "Timestamp": 105337147483416, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 24, + "PendingRetirements": true + }, + { + "Timestamp": 105337160934791, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 24, + "PendingRetirements": false + }, + { + "Timestamp": 105337160944458, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 24, + "PendingRetirements": false + }, + { + "Timestamp": 105337160954166, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 25, + "PendingRetirements": false + }, + { + "Timestamp": 105337163442875, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 25, + "PendingRetirements": false + }, + { + "Timestamp": 105337163445291, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 25, + "PendingRetirements": true + }, + { + "Timestamp": 105337163448916, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 25, + "PendingRetirements": true + }, + { + "Timestamp": 105337176668041, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 25, + "PendingRetirements": false + }, + { + "Timestamp": 105337176680875, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 25, + "PendingRetirements": false + }, + { + "Timestamp": 105337176706500, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 26, + "PendingRetirements": false + }, + { + "Timestamp": 105337179648791, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 26, + "PendingRetirements": false + }, + { + "Timestamp": 105337179650500, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 26, + "PendingRetirements": true + }, + { + "Timestamp": 105337179666125, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 26, + "PendingRetirements": true + }, + { + "Timestamp": 105337194237458, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 26, + "PendingRetirements": false + }, + { + "Timestamp": 105337194247958, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 26, + "PendingRetirements": false + }, + { + "Timestamp": 105337194256583, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 27, + "PendingRetirements": false + }, + { + "Timestamp": 105337198024125, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 27, + "PendingRetirements": false + }, + { + "Timestamp": 105337198026250, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 27, + "PendingRetirements": true + }, + { + "Timestamp": 105337198042833, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 27, + "PendingRetirements": true + }, + { + "Timestamp": 105337210900708, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 27, + "PendingRetirements": false + }, + { + "Timestamp": 105337210926125, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 27, + "PendingRetirements": false + }, + { + "Timestamp": 105337210937208, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 28, + "PendingRetirements": false + }, + { + "Timestamp": 105337213608708, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 28, + "PendingRetirements": false + }, + { + "Timestamp": 105337213611125, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 28, + "PendingRetirements": true + }, + { + "Timestamp": 105337213614583, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 28, + "PendingRetirements": true + }, + { + "Timestamp": 105337227559416, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 28, + "PendingRetirements": false + }, + { + "Timestamp": 105337227567875, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 28, + "PendingRetirements": false + }, + { + "Timestamp": 105337227573541, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 29, + "PendingRetirements": false + }, + { + "Timestamp": 105337230097375, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 29, + "PendingRetirements": false + }, + { + "Timestamp": 105337230099375, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 29, + "PendingRetirements": true + }, + { + "Timestamp": 105337230103000, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 29, + "PendingRetirements": true + }, + { + "Timestamp": 105337244231708, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 29, + "PendingRetirements": false + }, + { + "Timestamp": 105337244240916, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 29, + "PendingRetirements": false + }, + { + "Timestamp": 105337244248625, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 30, + "PendingRetirements": false + }, + { + "Timestamp": 105337246568416, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 30, + "PendingRetirements": false + }, + { + "Timestamp": 105337246570083, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 30, + "PendingRetirements": true + }, + { + "Timestamp": 105337246573166, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 30, + "PendingRetirements": true + }, + { + "Timestamp": 105337260909250, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 30, + "PendingRetirements": false + }, + { + "Timestamp": 105337260920666, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 30, + "PendingRetirements": false + }, + { + "Timestamp": 105337260929458, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 31, + "PendingRetirements": false + }, + { + "Timestamp": 105337263450875, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 31, + "PendingRetirements": false + }, + { + "Timestamp": 105337263453166, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 31, + "PendingRetirements": true + }, + { + "Timestamp": 105337263455791, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 31, + "PendingRetirements": true + }, + { + "Timestamp": 105337277562291, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 31, + "PendingRetirements": false + }, + { + "Timestamp": 105337277570708, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 31, + "PendingRetirements": false + }, + { + "Timestamp": 105337277576583, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 32, + "PendingRetirements": false + }, + { + "Timestamp": 105337280914333, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 32, + "PendingRetirements": false + }, + { + "Timestamp": 105337280916458, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 32, + "PendingRetirements": true + }, + { + "Timestamp": 105337280935416, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 32, + "PendingRetirements": true + }, + { + "Timestamp": 105337294282000, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 32, + "PendingRetirements": false + }, + { + "Timestamp": 105337294314791, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 32, + "PendingRetirements": false + }, + { + "Timestamp": 105337294326958, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 33, + "PendingRetirements": false + }, + { + "Timestamp": 105337296978666, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 33, + "PendingRetirements": false + }, + { + "Timestamp": 105337296980875, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 33, + "PendingRetirements": true + }, + { + "Timestamp": 105337296983958, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 33, + "PendingRetirements": true + }, + { + "Timestamp": 105337309943291, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 33, + "PendingRetirements": false + }, + { + "Timestamp": 105337309954958, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 33, + "PendingRetirements": false + }, + { + "Timestamp": 105337309962916, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 34, + "PendingRetirements": false + }, + { + "Timestamp": 105337312739916, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 34, + "PendingRetirements": false + }, + { + "Timestamp": 105337312742166, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 34, + "PendingRetirements": true + }, + { + "Timestamp": 105337312744750, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 34, + "PendingRetirements": true + }, + { + "Timestamp": 105337327568333, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 34, + "PendingRetirements": false + }, + { + "Timestamp": 105337327596041, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 34, + "PendingRetirements": false + }, + { + "Timestamp": 105337327606000, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 35, + "PendingRetirements": false + }, + { + "Timestamp": 105337330632416, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 35, + "PendingRetirements": false + }, + { + "Timestamp": 105337330635125, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 35, + "PendingRetirements": true + }, + { + "Timestamp": 105337330639375, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 35, + "PendingRetirements": true + }, + { + "Timestamp": 105337344261791, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 35, + "PendingRetirements": true + }, + { + "Timestamp": 105337344272041, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 35, + "PendingRetirements": true + }, + { + "Timestamp": 105337344279166, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 36, + "PendingRetirements": true + }, + { + "Timestamp": 105337347621916, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 36, + "PendingRetirements": true + }, + { + "Timestamp": 105337347624458, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 36, + "PendingRetirements": true + }, + { + "Timestamp": 105337347629500, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 36, + "PendingRetirements": true + }, + { + "Timestamp": 105337360910166, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 36, + "PendingRetirements": false + }, + { + "Timestamp": 105337360922375, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 36, + "PendingRetirements": false + }, + { + "Timestamp": 105337360929125, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 37, + "PendingRetirements": false + }, + { + "Timestamp": 105337364528708, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 37, + "PendingRetirements": false + }, + { + "Timestamp": 105337364533208, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 37, + "PendingRetirements": true + }, + { + "Timestamp": 105337364555208, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 37, + "PendingRetirements": true + }, + { + "Timestamp": 105337376913208, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 37, + "PendingRetirements": false + }, + { + "Timestamp": 105337376936125, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 37, + "PendingRetirements": false + }, + { + "Timestamp": 105337376947166, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 38, + "PendingRetirements": false + }, + { + "Timestamp": 105337379995166, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 38, + "PendingRetirements": false + }, + { + "Timestamp": 105337379999708, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 38, + "PendingRetirements": true + }, + { + "Timestamp": 105337380003708, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 38, + "PendingRetirements": true + }, + { + "Timestamp": 105337394233541, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 38, + "PendingRetirements": false + }, + { + "Timestamp": 105337394242958, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 38, + "PendingRetirements": false + }, + { + "Timestamp": 105337394250458, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 39, + "PendingRetirements": false + }, + { + "Timestamp": 105337396764166, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 39, + "PendingRetirements": false + }, + { + "Timestamp": 105337396770166, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 39, + "PendingRetirements": true + }, + { + "Timestamp": 105337396773541, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 39, + "PendingRetirements": true + }, + { + "Timestamp": 105337410933166, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 39, + "PendingRetirements": false + }, + { + "Timestamp": 105337410941791, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 39, + "PendingRetirements": false + }, + { + "Timestamp": 105337410948208, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 40, + "PendingRetirements": false + }, + { + "Timestamp": 105337413505041, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 40, + "PendingRetirements": false + }, + { + "Timestamp": 105337413506000, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 40, + "PendingRetirements": true + }, + { + "Timestamp": 105337413508875, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 40, + "PendingRetirements": true + }, + { + "Timestamp": 105337427614000, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 40, + "PendingRetirements": false + }, + { + "Timestamp": 105337427622708, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 40, + "PendingRetirements": false + }, + { + "Timestamp": 105337427630875, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 41, + "PendingRetirements": false + }, + { + "Timestamp": 105337430329166, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 41, + "PendingRetirements": false + }, + { + "Timestamp": 105337430330500, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 41, + "PendingRetirements": true + }, + { + "Timestamp": 105337430333791, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 41, + "PendingRetirements": true + }, + { + "Timestamp": 105337444243500, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 41, + "PendingRetirements": false + }, + { + "Timestamp": 105337444252458, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 41, + "PendingRetirements": false + }, + { + "Timestamp": 105337444261333, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 42, + "PendingRetirements": false + }, + { + "Timestamp": 105337447869000, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 42, + "PendingRetirements": false + }, + { + "Timestamp": 105337447870041, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 42, + "PendingRetirements": true + }, + { + "Timestamp": 105337447885000, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 42, + "PendingRetirements": true + }, + { + "Timestamp": 105337460898750, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 42, + "PendingRetirements": false + }, + { + "Timestamp": 105337460921958, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 42, + "PendingRetirements": false + }, + { + "Timestamp": 105337460932000, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 43, + "PendingRetirements": false + }, + { + "Timestamp": 105337463726666, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 43, + "PendingRetirements": false + }, + { + "Timestamp": 105337463727708, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 43, + "PendingRetirements": true + }, + { + "Timestamp": 105337463731000, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 43, + "PendingRetirements": true + }, + { + "Timestamp": 105337477563041, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 43, + "PendingRetirements": false + }, + { + "Timestamp": 105337477571625, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 43, + "PendingRetirements": false + }, + { + "Timestamp": 105337477579000, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 44, + "PendingRetirements": false + }, + { + "Timestamp": 105337480138083, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 44, + "PendingRetirements": false + }, + { + "Timestamp": 105337480139458, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 44, + "PendingRetirements": true + }, + { + "Timestamp": 105337480142750, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 44, + "PendingRetirements": true + }, + { + "Timestamp": 105337494227708, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 44, + "PendingRetirements": false + }, + { + "Timestamp": 105337494234916, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 44, + "PendingRetirements": false + }, + { + "Timestamp": 105337494242250, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 45, + "PendingRetirements": false + }, + { + "Timestamp": 105337496870083, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 45, + "PendingRetirements": false + }, + { + "Timestamp": 105337496871500, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 45, + "PendingRetirements": true + }, + { + "Timestamp": 105337496874083, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 45, + "PendingRetirements": true + }, + { + "Timestamp": 105337510896250, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 45, + "PendingRetirements": false + }, + { + "Timestamp": 105337510902666, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 45, + "PendingRetirements": false + }, + { + "Timestamp": 105337510908541, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 46, + "PendingRetirements": false + }, + { + "Timestamp": 105337513337500, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 46, + "PendingRetirements": false + }, + { + "Timestamp": 105337513338583, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 46, + "PendingRetirements": true + }, + { + "Timestamp": 105337513341833, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 46, + "PendingRetirements": true + }, + { + "Timestamp": 105337527566208, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 46, + "PendingRetirements": false + }, + { + "Timestamp": 105337527572208, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 46, + "PendingRetirements": false + }, + { + "Timestamp": 105337527578791, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 47, + "PendingRetirements": false + }, + { + "Timestamp": 105337531083375, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 47, + "PendingRetirements": false + }, + { + "Timestamp": 105337531084625, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 47, + "PendingRetirements": true + }, + { + "Timestamp": 105337531102416, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 47, + "PendingRetirements": true + }, + { + "Timestamp": 105337544230416, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 47, + "PendingRetirements": false + }, + { + "Timestamp": 105337544252125, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 47, + "PendingRetirements": false + }, + { + "Timestamp": 105337544264041, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 48, + "PendingRetirements": false + }, + { + "Timestamp": 105337546721208, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 48, + "PendingRetirements": false + }, + { + "Timestamp": 105337546722250, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 48, + "PendingRetirements": true + }, + { + "Timestamp": 105337546724333, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 48, + "PendingRetirements": true + }, + { + "Timestamp": 105337560897000, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 48, + "PendingRetirements": false + }, + { + "Timestamp": 105337560906708, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 48, + "PendingRetirements": false + }, + { + "Timestamp": 105337560913500, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 49, + "PendingRetirements": false + }, + { + "Timestamp": 105337563719416, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 49, + "PendingRetirements": false + }, + { + "Timestamp": 105337563720666, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 49, + "PendingRetirements": true + }, + { + "Timestamp": 105337563723833, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 49, + "PendingRetirements": true + }, + { + "Timestamp": 105337577564416, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 49, + "PendingRetirements": false + }, + { + "Timestamp": 105337577571625, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 49, + "PendingRetirements": false + }, + { + "Timestamp": 105337577577375, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 50, + "PendingRetirements": false + }, + { + "Timestamp": 105337580257708, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 50, + "PendingRetirements": false + }, + { + "Timestamp": 105337580259083, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 50, + "PendingRetirements": true + }, + { + "Timestamp": 105337580262041, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 50, + "PendingRetirements": true + }, + { + "Timestamp": 105337593775708, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 50, + "PendingRetirements": false + }, + { + "Timestamp": 105337593785625, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 50, + "PendingRetirements": false + }, + { + "Timestamp": 105337593795916, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 51, + "PendingRetirements": false + }, + { + "Timestamp": 105337596275500, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 51, + "PendingRetirements": false + }, + { + "Timestamp": 105337596277083, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 51, + "PendingRetirements": true + }, + { + "Timestamp": 105337596280166, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 51, + "PendingRetirements": true + }, + { + "Timestamp": 105337610897666, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 51, + "PendingRetirements": false + }, + { + "Timestamp": 105337610905750, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 51, + "PendingRetirements": false + }, + { + "Timestamp": 105337610914583, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 52, + "PendingRetirements": false + }, + { + "Timestamp": 105337614928583, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 52, + "PendingRetirements": false + }, + { + "Timestamp": 105337614929875, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 52, + "PendingRetirements": true + }, + { + "Timestamp": 105337614944916, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 52, + "PendingRetirements": true + }, + { + "Timestamp": 105337627566625, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 52, + "PendingRetirements": false + }, + { + "Timestamp": 105337627588958, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 52, + "PendingRetirements": false + }, + { + "Timestamp": 105337627601291, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 53, + "PendingRetirements": false + }, + { + "Timestamp": 105337630473875, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 53, + "PendingRetirements": false + }, + { + "Timestamp": 105337630475458, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 53, + "PendingRetirements": true + }, + { + "Timestamp": 105337630478875, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 53, + "PendingRetirements": true + }, + { + "Timestamp": 105337644244083, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 53, + "PendingRetirements": false + }, + { + "Timestamp": 105337644254333, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 53, + "PendingRetirements": false + }, + { + "Timestamp": 105337644263333, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 54, + "PendingRetirements": false + }, + { + "Timestamp": 105337647558833, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 54, + "PendingRetirements": false + }, + { + "Timestamp": 105337647560041, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 54, + "PendingRetirements": true + }, + { + "Timestamp": 105337647564333, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 54, + "PendingRetirements": true + }, + { + "Timestamp": 105337660901583, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 54, + "PendingRetirements": true + }, + { + "Timestamp": 105337660908375, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 54, + "PendingRetirements": true + }, + { + "Timestamp": 105337660915291, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 55, + "PendingRetirements": true + }, + { + "Timestamp": 105337663468041, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 55, + "PendingRetirements": true + }, + { + "Timestamp": 105337663469083, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 55, + "PendingRetirements": true + }, + { + "Timestamp": 105337663471583, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 55, + "PendingRetirements": true + }, + { + "Timestamp": 105337677568958, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 55, + "PendingRetirements": false + }, + { + "Timestamp": 105337677577791, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 55, + "PendingRetirements": false + }, + { + "Timestamp": 105337677585750, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 56, + "PendingRetirements": false + }, + { + "Timestamp": 105337682839750, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 56, + "PendingRetirements": false + }, + { + "Timestamp": 105337682841458, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 56, + "PendingRetirements": true + }, + { + "Timestamp": 105337682857791, + "Stage": "apply:Applied", + "PendingPublications": 2, + "Revision": 56, + "PendingRetirements": true + }, + { + "Timestamp": 105337682866166, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 56, + "PendingRetirements": true + }, + { + "Timestamp": 105337682879708, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 57, + "PendingRetirements": true + }, + { + "Timestamp": 105337685851083, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 57, + "PendingRetirements": true + }, + { + "Timestamp": 105337685852708, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 57, + "PendingRetirements": true + }, + { + "Timestamp": 105337685855333, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 57, + "PendingRetirements": true + }, + { + "Timestamp": 105337694237291, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 57, + "PendingRetirements": false + }, + { + "Timestamp": 105337710901125, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 57, + "PendingRetirements": false + }, + { + "Timestamp": 105337710908958, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 57, + "PendingRetirements": false + }, + { + "Timestamp": 105337710916541, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 58, + "PendingRetirements": false + }, + { + "Timestamp": 105337713748375, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 58, + "PendingRetirements": false + }, + { + "Timestamp": 105337713750958, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 58, + "PendingRetirements": true + }, + { + "Timestamp": 105337713755833, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 58, + "PendingRetirements": true + }, + { + "Timestamp": 105337727566375, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 58, + "PendingRetirements": false + }, + { + "Timestamp": 105337744238750, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 58, + "PendingRetirements": false + }, + { + "Timestamp": 105337744246666, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 58, + "PendingRetirements": false + }, + { + "Timestamp": 105337744257041, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 60, + "PendingRetirements": false + }, + { + "Timestamp": 105337747038208, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 60, + "PendingRetirements": false + }, + { + "Timestamp": 105337747039375, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 60, + "PendingRetirements": true + }, + { + "Timestamp": 105337747042458, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 60, + "PendingRetirements": true + }, + { + "Timestamp": 105337760900541, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 60, + "PendingRetirements": false + }, + { + "Timestamp": 105337760909041, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 60, + "PendingRetirements": false + }, + { + "Timestamp": 105337760917125, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 61, + "PendingRetirements": false + }, + { + "Timestamp": 105337764001666, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 61, + "PendingRetirements": false + }, + { + "Timestamp": 105337764002750, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 61, + "PendingRetirements": true + }, + { + "Timestamp": 105337764005833, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 61, + "PendingRetirements": true + }, + { + "Timestamp": 105337777565833, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 61, + "PendingRetirements": false + }, + { + "Timestamp": 105337777573666, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 61, + "PendingRetirements": false + }, + { + "Timestamp": 105337777579708, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 62, + "PendingRetirements": false + }, + { + "Timestamp": 105337781261708, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 62, + "PendingRetirements": false + }, + { + "Timestamp": 105337781263333, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 62, + "PendingRetirements": true + }, + { + "Timestamp": 105337781281208, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 62, + "PendingRetirements": true + }, + { + "Timestamp": 105337794231875, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 62, + "PendingRetirements": false + }, + { + "Timestamp": 105337810901500, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 62, + "PendingRetirements": false + }, + { + "Timestamp": 105337810935750, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 62, + "PendingRetirements": false + }, + { + "Timestamp": 105337810952750, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 64, + "PendingRetirements": false + }, + { + "Timestamp": 105337813701416, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 64, + "PendingRetirements": false + }, + { + "Timestamp": 105337813703208, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 64, + "PendingRetirements": true + }, + { + "Timestamp": 105337813706833, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 64, + "PendingRetirements": true + }, + { + "Timestamp": 105337827573708, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 64, + "PendingRetirements": false + }, + { + "Timestamp": 105337844232458, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 64, + "PendingRetirements": false + }, + { + "Timestamp": 105337844239375, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 64, + "PendingRetirements": false + }, + { + "Timestamp": 105337844245750, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 66, + "PendingRetirements": false + }, + { + "Timestamp": 105337847444791, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 66, + "PendingRetirements": false + }, + { + "Timestamp": 105337847446750, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 66, + "PendingRetirements": true + }, + { + "Timestamp": 105337847449833, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 66, + "PendingRetirements": true + }, + { + "Timestamp": 105337860898250, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 66, + "PendingRetirements": false + }, + { + "Timestamp": 105337876587958, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 66, + "PendingRetirements": false + }, + { + "Timestamp": 105337876598208, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 66, + "PendingRetirements": false + }, + { + "Timestamp": 105337876607000, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 68, + "PendingRetirements": false + }, + { + "Timestamp": 105337880027625, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 68, + "PendingRetirements": false + }, + { + "Timestamp": 105337880030500, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 68, + "PendingRetirements": true + }, + { + "Timestamp": 105337880037958, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 68, + "PendingRetirements": true + }, + { + "Timestamp": 105337893295458, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 68, + "PendingRetirements": false + }, + { + "Timestamp": 105337910917958, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 68, + "PendingRetirements": false + }, + { + "Timestamp": 105337910963208, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 68, + "PendingRetirements": false + }, + { + "Timestamp": 105337910976125, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 70, + "PendingRetirements": false + }, + { + "Timestamp": 105337913895500, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 70, + "PendingRetirements": false + }, + { + "Timestamp": 105337913897458, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 70, + "PendingRetirements": true + }, + { + "Timestamp": 105337913901500, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 70, + "PendingRetirements": true + }, + { + "Timestamp": 105337927666750, + "Stage": "frame", + "PendingPublications": 1, + "Revision": 70, + "PendingRetirements": false + }, + { + "Timestamp": 105337927677708, + "Stage": "acquire:Success", + "PendingPublications": 1, + "Revision": 70, + "PendingRetirements": false + }, + { + "Timestamp": 105337927686500, + "Stage": "apply:images-retained", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105337933380250, + "Stage": "apply:cpu-applied", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105337933382541, + "Stage": "apply:replaced", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": true + }, + { + "Timestamp": 105337933402250, + "Stage": "apply:Applied", + "PendingPublications": 1, + "Revision": 71, + "PendingRetirements": true + }, + { + "Timestamp": 105337944237750, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": true + }, + { + "Timestamp": 105337944265000, + "Stage": "acquire:Empty", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": true + }, + { + "Timestamp": 105337959966500, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105337977573583, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105337993361041, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338010068375, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338027596916, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338044443333, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338060921916, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338076599083, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338094269083, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338110929875, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338127585083, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338144105250, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338160108916, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338176983500, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338193635291, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338210936791, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338226575750, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338243875166, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338259907708, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338277092041, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338294261416, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338310919125, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338327584541, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338344235333, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338360915708, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338377623750, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + }, + { + "Timestamp": 105338394299708, + "Stage": "frame", + "PendingPublications": 0, + "Revision": 71, + "PendingRetirements": false + } + ], + "physicalPresentationVerified": false + }, + "limitations": [ + "Single scripted sidebar run; not physical FPS or native user-drag qualification.", + "OpenGL compositor host with WebGPU producer; does not verify ANGLE WebGL API fallback.", + "Earlier failed gesture run remains excluded." + ] +} diff --git a/docs/graphics/evidence/kestrel/first-render-and-resize.json b/docs/graphics/evidence/kestrel/first-render-and-resize.json new file mode 100644 index 000000000..8a623b43b --- /dev/null +++ b/docs/graphics/evidence/kestrel/first-render-and-resize.json @@ -0,0 +1,82 @@ +{ + "date": "2026-09-08", + "originalDocumentSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "startup": { + "ready": "true", + "backend": "WebGPU \u00b7 GPU pipeline", + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands.", + "errors": 0, + "gpu": true + }, + "resizes": [ + { + "window": [ + 980, + 680 + ], + "canvas": [ + 1110, + 1006 + ], + "css": [ + 555, + 503 + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1440, + 900 + ], + "canvas": [ + 1432, + 1228 + ], + "css": [ + 716, + 614 + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1100, + 740 + ], + "canvas": [ + 862, + 1228 + ], + "css": [ + 431, + 614 + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1280, + 800 + ], + "canvas": [ + 1112, + 1228 + ], + "css": [ + 556, + 614 + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } + ], + "qualification": "Initial WebGPU floor-plan rendering and four sampled resizes observed. UI layout/clipping defects, interaction, export, live drag performance, and fallback qualification remain." +} diff --git a/docs/graphics/evidence/kestrel/font-cache-continuous-resize.json b/docs/graphics/evidence/kestrel/font-cache-continuous-resize.json new file mode 100644 index 000000000..39bc3a5a6 --- /dev/null +++ b/docs/graphics/evidence/kestrel/font-cache-continuous-resize.json @@ -0,0 +1,102 @@ +{ + "validated": true, + "exitCode": 0, + "publishedScenes": 106, + "drawnScenes": 101, + "activeUpdateSpanMilliseconds": 2812.973292, + "drawCallbackGapMilliseconds": { + "median": 33.14197849999999, + "maximum": 50.558834 + }, + "finalGeometry": { + "window": [ + 1280, + 800 + ], + "canvas": [ + 1612, + 926 + ], + "css": [ + 806, + 463 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 806, + 463 + ], + "height": "463px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1280, + 463 + ], + "height": "463px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + "physicalPresentationVerified": false, + "nativeUserDragVerified": false, + "limitations": [ + "Single scripted run, no statistical comparison or physical FPS qualification.", + "Includes settling in callback gaps; update loop requests 16ms delays but actual cadence differs.", + "Intermediate scene checks establish progress, not the absence of visual flicker." + ] +} diff --git a/docs/graphics/evidence/kestrel/fractional-grid-resize-fix.json b/docs/graphics/evidence/kestrel/fractional-grid-resize-fix.json new file mode 100644 index 000000000..63b35295f --- /dev/null +++ b/docs/graphics/evidence/kestrel/fractional-grid-resize-fix.json @@ -0,0 +1,776 @@ +{ + "originalSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "problem": "A flexible track minimum was subtracted before calculating the entire fractional size, leaving 250 CSS pixels unused in Kestrel.", + "implementation": "Allocate fractions from available space minus non-flexible tracks and gaps; freeze tracks below their minimum and recompute, keeping the denominator at least one. Share the allocator between axes and route explicit single-column/row templates through track layout.", + "specification": "https://drafts.csswg.org/css-grid-2/#algo-find-fr-size", + "regressionBefore": { + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "d640b7825f53b56437622cc238f78569a4a0a4c0f15f08937909338064bb471a", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=78cb8160904fc5a35997770383cabda2542ef619f8ac93197d726532fa62c6d0", + "chromiumIdentity": null, + "startedAt": "2026-09-08T09:08:41.061542+00:00", + "duration": "00:00:00.2337178", + "selection": "candidate", + "summary": { + "tests": 1, + "passed": 0, + "failed": 1, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 6, + "subtestsPassed": 1, + "subtestsFailed": 5 + }, + "results": [ + { + "path": "contracts/css-grid-flex-track-minimum.html", + "type": "testharness", + "status": "FAIL", + "duration": "00:00:00.2309316", + "message": "diagnostic: activeElement=", + "subtests": [ + { + "name": "Flexible minimum is part of the final fraction, with fixed tracks and gaps", + "status": "FAIL", + "message": "assert_approx_equals: track 1 expected 380 +/- 0.01 but got 230", + "stack": "Error\n at get_stack (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4802:21)\n at new AssertionError (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4795:22)\n at assert (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4779:19)\n at assert_approx_equals (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1812:13)\n at assert_wrapper (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1518:30)\n at check (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:7:42)\n at Test. (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:10:10)\n at Test.step (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:2869:25)\n at test (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:633:30)\n at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:10:1" + }, + { + "name": "A frozen minimum redistributes remaining space", + "status": "FAIL", + "message": "assert_approx_equals: track 1 expected 100 +/- 0.01 but got 40", + "stack": "Error\n at get_stack (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4802:21)\n at new AssertionError (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4795:22)\n at assert (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4779:19)\n at assert_approx_equals (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1812:13)\n at assert_wrapper (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1518:30)\n at check (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:7:42)\n at Test. (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:11:10)\n at Test.step (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:2869:25)\n at test (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:633:30)\n at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:11:1" + }, + { + "name": "Unequal fractions recompute after minimum freezing", + "status": "FAIL", + "message": "assert_approx_equals: track 1 expected 300 +/- 0.01 but got 200", + "stack": "Error\n at get_stack (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4802:21)\n at new AssertionError (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4795:22)\n at assert (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4779:19)\n at assert_approx_equals (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1812:13)\n at assert_wrapper (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1518:30)\n at check (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:7:42)\n at Test. (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:12:10)\n at Test.step (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:2869:25)\n at test (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:633:30)\n at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:12:1" + }, + { + "name": "Fractions below one leave their unrequested share unused", + "status": "FAIL", + "message": "assert_approx_equals: track 0 expected 225 +/- 0.01 but got 800", + "stack": "Error\n at get_stack (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4802:21)\n at new AssertionError (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4795:22)\n at assert (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4779:19)\n at assert_approx_equals (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1812:13)\n at assert_wrapper (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1518:30)\n at check (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:7:42)\n at Test. (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:13:10)\n at Test.step (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:2869:25)\n at test (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:633:30)\n at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:13:1" + }, + { + "name": "Minimum sizes overflow instead of becoming negative", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Block-axis fractional tracks use the same allocation rule", + "status": "FAIL", + "message": "assert_approx_equals: track 0 expected 250 +/- 0.01 but got 300", + "stack": "Error\n at get_stack (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4802:21)\n at new AssertionError (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4795:22)\n at assert (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4779:19)\n at assert_approx_equals (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1812:13)\n at assert_wrapper (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1518:30)\n at check (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:7:42)\n at Test. (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:15:10)\n at Test.step (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:2869:25)\n at test (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:633:30)\n at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:15:1" + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] + }, + "candidateGridAfter": { + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "d640b7825f53b56437622cc238f78569a4a0a4c0f15f08937909338064bb471a", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=c6f3428fc890b23c6df4e0af9f0a15f26d2d6ad3aa083765ecb4376488cc4064", + "chromiumIdentity": null, + "startedAt": "2026-09-08T09:10:02.582383+00:00", + "duration": "00:00:02.2631426", + "selection": "candidate", + "summary": { + "tests": 7, + "passed": 7, + "failed": 0, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 21, + "subtestsPassed": 21, + "subtestsFailed": 0 + }, + "results": [ + { + "path": "contracts/css-grid-flex-track-minimum.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.6930496", + "message": null, + "subtests": [ + { + "name": "Flexible minimum is part of the final fraction, with fixed tracks and gaps", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "A frozen minimum redistributes remaining space", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Unequal fractions recompute after minimum freezing", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Fractions below one leave their unrequested share unused", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Minimum sizes overflow instead of becoming negative", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Block-axis fractional tracks use the same allocation rule", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/css-grid-auto-row-shrink.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.1125121", + "message": null, + "subtests": [ + { + "name": "Scrollable auto row grows and shrinks with definite grid height", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Explicit zero minimum permits row shrinking without losing authored minimum", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Visible overflow retains content-based automatic minimum", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/css-grid-form-layout.html", + "type": "contract", + "status": "PASS", + "duration": "00:00:00.1144459", + "message": null, + "subtests": [ + { + "name": "one automatic middle track leaves equal fractional form tracks", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "minmax fractional CRUD columns share the available width", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "explicit row spans and column placement retain the authored cells", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/css-grid-column-subgrid-form-row.html", + "type": "contract", + "status": "PASS", + "duration": "00:00:00.1058056", + "message": null, + "subtests": [ + { + "name": "a spanning column subgrid inherits the parent form tracks", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/css-grid-display-contents-items.html", + "type": "contract", + "status": "PASS", + "duration": "00:00:00.0987575", + "message": null, + "subtests": [ + { + "name": "display contents rows do not generate grid item boxes", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "a definite grid end line aligns the header with flattened row cells", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "the row descendants occupy one compact parent-grid row", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "all flattened cells remain within the five-track grid", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/grid-auto-row-stretch-dir-radius.html", + "type": "reftest", + "status": "PASS", + "duration": "00:00:01.0399840", + "message": null, + "subtests": [], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/implicit-grid-and-compact-flex-controls.html", + "type": "contract", + "status": "PASS", + "duration": "00:00:00.0948501", + "message": null, + "subtests": [ + { + "name": "column auto-flow creates three equal implicit fractional tracks", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "an inline flex item is blockified and stretches in the cross axis", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "a three-column fractional grid preserves a nested nonshrinking suffix", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "a spaced flex summary keeps its numeric value on one line", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] + }, + "requiredGridAfter": { + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "d640b7825f53b56437622cc238f78569a4a0a4c0f15f08937909338064bb471a", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=c6f3428fc890b23c6df4e0af9f0a15f26d2d6ad3aa083765ecb4376488cc4064", + "chromiumIdentity": null, + "startedAt": "2026-09-08T09:10:22.576568+00:00", + "duration": "00:00:01.4257227", + "selection": "required", + "summary": { + "tests": 4, + "passed": 4, + "failed": 0, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 9, + "subtestsPassed": 9, + "subtestsFailed": 0 + }, + "results": [ + { + "path": "contracts/css-grid-gap-alias-flex.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.1495392", + "message": null, + "subtests": [ + { + "name": "Grid row/column gap aliases preserve flex spacing and wrapping", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "CSSOM camel-case gridColumnGap sets the column-gap alias", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/responsive-settings-property-grid.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.1219079", + "message": null, + "subtests": [ + { + "name": "compact 1fr/min-content settings rows use post-track wrapped block contributions", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "css/css-grid/alignment/grid-gutters-001.html", + "type": "reftest", + "status": "PASS", + "duration": "00:00:01.0514904", + "message": null, + "subtests": [], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/css-grid-placement-cssom.html", + "type": "contract", + "status": "PASS", + "duration": "00:00:00.1000779", + "message": null, + "subtests": [ + { + "name": "a one-line grid-area expands to four computed placement longhands", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "a four-line stylesheet grid-area preserves placement order", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "grid-row and grid-column expand their omitted ends to auto", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "grid placement longhands retain unitless integer values", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "an explicit placement longhand overrides its shorthand component", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "removing a grid placement shorthand restores auto longhands", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] + }, + "nativeTests": { + "engineSeconds": 11.03, + "gpuRuntimeSeconds": 1.79, + "passed": true + }, + "kestrelResize": [ + { + "window": [ + 980, + 680 + ], + "canvas": [ + 1610, + 750 + ], + "css": [ + 805, + 375 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 175, + 196, + 805, + 375 + ], + "height": "375px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 196, + 980, + 375 + ], + "height": "375px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1440, + 900 + ], + "canvas": [ + 1932, + 1126 + ], + "css": [ + 966, + 563 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 966, + 563 + ], + "height": "563px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1440, + 563 + ], + "height": "563px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1100, + 740 + ], + "canvas": [ + 1362, + 806 + ], + "css": [ + 681, + 403 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 195, + 205, + 681, + 403 + ], + "height": "403px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1100, + 403 + ], + "height": "403px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1280, + 800 + ], + "canvas": [ + 1612, + 926 + ], + "css": [ + 806, + 463 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 806, + 463 + ], + "height": "463px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1280, + 463 + ], + "height": "463px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } + ], + "screenshot": "macos-kestrel-grid-resize-fixed.png", + "limitations": [ + "Stable resized geometry and screenshot verified; live resize timing and pan flicker are not certified by a still image.", + "No full grid conformance or cross-platform qualification claimed. New contract remains candidate.", + "Dynamic CSSOM setup using cssText/property assignment did not establish the intended fixture; the regression uses style attributes. CSSOM coverage remains separate." + ] +} diff --git a/docs/graphics/evidence/kestrel/frame-admission-ownership.json b/docs/graphics/evidence/kestrel/frame-admission-ownership.json new file mode 100644 index 000000000..ef0da4a4f --- /dev/null +++ b/docs/graphics/evidence/kestrel/frame-admission-ownership.json @@ -0,0 +1,300 @@ +{ + "method": "Temporary frame-admission stderr trace plus thread-safe image-pool ownership snapshot, filtered to first/last observed pointer events. Temporary tracing removed after collection.", + "originalSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "observedMoves": 66, + "coalescedMoves": 14, + "admittedSignals": 37, + "blockedSignals": 17, + "records": [ + { + "timestampMilliseconds": 90240805.16, + "admitted": true + }, + { + "timestampMilliseconds": 90240821.819, + "admitted": true + }, + { + "timestampMilliseconds": 90240847.113, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90240874.474, + "admitted": true + }, + { + "timestampMilliseconds": 90240895.914, + "admitted": true + }, + { + "timestampMilliseconds": 90240918.151, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90240941.784, + "admitted": true + }, + { + "timestampMilliseconds": 90240961.937, + "admitted": true + }, + { + "timestampMilliseconds": 90240981.343, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241000.475, + "admitted": true + }, + { + "timestampMilliseconds": 90241019.002, + "admitted": true + }, + { + "timestampMilliseconds": 90241042.961, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241063.635, + "admitted": true + }, + { + "timestampMilliseconds": 90241080.831, + "admitted": true + }, + { + "timestampMilliseconds": 90241099.749, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241125.651, + "admitted": true + }, + { + "timestampMilliseconds": 90241143.273, + "admitted": true + }, + { + "timestampMilliseconds": 90241163.134, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241183.875, + "admitted": true + }, + { + "timestampMilliseconds": 90241200.415, + "admitted": true + }, + { + "timestampMilliseconds": 90241219.966, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241240.299, + "admitted": true + }, + { + "timestampMilliseconds": 90241259.864, + "admitted": true + }, + { + "timestampMilliseconds": 90241298.512, + "admitted": true + }, + { + "timestampMilliseconds": 90241321.887, + "admitted": true + }, + { + "timestampMilliseconds": 90241342.078, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241365.034, + "admitted": true + }, + { + "timestampMilliseconds": 90241407.298, + "admitted": true + }, + { + "timestampMilliseconds": 90241438.459, + "admitted": true + }, + { + "timestampMilliseconds": 90241464.127, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241491.669, + "admitted": true + }, + { + "timestampMilliseconds": 90241514.117, + "admitted": true + }, + { + "timestampMilliseconds": 90241533.987, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241558.163, + "admitted": true + }, + { + "timestampMilliseconds": 90241576.888, + "admitted": true + }, + { + "timestampMilliseconds": 90241597.447, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241616.882, + "admitted": true + }, + { + "timestampMilliseconds": 90241636.353, + "admitted": true + }, + { + "timestampMilliseconds": 90241657.531, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241678.292, + "admitted": true + }, + { + "timestampMilliseconds": 90241696.431, + "admitted": true + }, + { + "timestampMilliseconds": 90241720.793, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241836.288, + "admitted": true + }, + { + "timestampMilliseconds": 90241880.628, + "admitted": true + }, + { + "timestampMilliseconds": 90241914.149, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90241983.864, + "admitted": true + }, + { + "timestampMilliseconds": 90242011.812, + "admitted": true + }, + { + "timestampMilliseconds": 90242034.231, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90242057.777, + "admitted": true + }, + { + "timestampMilliseconds": 90242081.524, + "admitted": true + }, + { + "timestampMilliseconds": 90242106.791, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + }, + { + "timestampMilliseconds": 90242131.798, + "admitted": true + }, + { + "timestampMilliseconds": 90242155.417, + "admitted": true + }, + { + "timestampMilliseconds": 90242179.749, + "admitted": false, + "busyImages": 3, + "producerPendingImages": 0, + "retainedImages": 2, + "consumerPendingImages": 2 + } + ], + "interpretation": "All blocked signals had three occupied images, zero unfinished producers, two images with retained references and two with outstanding consumer leases. Consumer GPU completion versus delayed fence polling is not distinguished by lease state.", + "limitations": [ + "Instrumented single run; existing interactive demo remained open.", + "Not a performance gate, browser comparison, GPU timestamp measurement or physical presentation qualification.", + "Earlier admission-only run included extra manual input and is not used for controlled counts." + ] +} diff --git a/docs/graphics/evidence/kestrel/gpu-completion-ticket-fixture.json b/docs/graphics/evidence/kestrel/gpu-completion-ticket-fixture.json new file mode 100644 index 000000000..260e39420 --- /dev/null +++ b/docs/graphics/evidence/kestrel/gpu-completion-ticket-fixture.json @@ -0,0 +1,12 @@ +{ + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "detachedBeforeRetirement": false, + "scope": "Submission snapshot retention and existing Ganesh fixture; scene publisher integration remains pending", + "nativeRuntimeRegression": "webscene_graphics_v8_runtime_tests passed (0.58 seconds)" +} diff --git a/docs/graphics/evidence/kestrel/gpu-provider-ticket-fixture.json b/docs/graphics/evidence/kestrel/gpu-provider-ticket-fixture.json new file mode 100644 index 000000000..6dd6f85e4 --- /dev/null +++ b/docs/graphics/evidence/kestrel/gpu-provider-ticket-fixture.json @@ -0,0 +1,12 @@ +{ + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "detachedBeforeRetirement": false, + "scope": "Provider capture survives ready-queue drain; discarded work has no capture. Runtime scene integration is pending.", + "nativeRuntimeRegression": "webscene_graphics_v8_runtime_tests passed (0.61 seconds)" +} diff --git a/docs/graphics/evidence/kestrel/grid-resize-fix.json b/docs/graphics/evidence/kestrel/grid-resize-fix.json new file mode 100644 index 000000000..3c02b87a0 --- /dev/null +++ b/docs/graphics/evidence/kestrel/grid-resize-fix.json @@ -0,0 +1,1048 @@ +{ + "originalSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "problem": "Auto grid row held max-content height after its definite container shrank; grid item arrange also expanded the resolved row height as if indefinite.", + "specification": "https://drafts.csswg.org/css-grid-2/#algo-track-sizing", + "scope": "Single implicit or explicit auto row separates minimum contribution from max-content growth; resolved grid item stretch height is definite. Multi-row intrinsic sizing and full grid conformance remain outside this fix.", + "before": [ + { + "window": [ + 980, + 680 + ], + "canvas": [ + 1110, + 880 + ], + "css": [ + 555, + 440 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 175, + 196, + 555, + 440 + ], + "height": "440px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 196, + 980, + 375 + ], + "height": "375px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1440, + 900 + ], + "canvas": [ + 1432, + 1228 + ], + "css": [ + 716, + 614 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 716, + 614 + ], + "height": "614px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1440, + 563 + ], + "height": "563px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1100, + 740 + ], + "canvas": [ + 862, + 1228 + ], + "css": [ + 431, + 614 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 195, + 205, + 431, + 614 + ], + "height": "614px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1100, + 403 + ], + "height": "403px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1280, + 800 + ], + "canvas": [ + 1112, + 1228 + ], + "css": [ + 556, + 614 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 556, + 614 + ], + "height": "614px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1280, + 463 + ], + "height": "463px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } + ], + "after": [ + { + "window": [ + 980, + 680 + ], + "canvas": [ + 1110, + 750 + ], + "css": [ + 555, + 375 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 175, + 196, + 555, + 375 + ], + "height": "375px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 196, + 980, + 375 + ], + "height": "375px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1440, + 900 + ], + "canvas": [ + 1432, + 1126 + ], + "css": [ + 716, + 563 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 716, + 563 + ], + "height": "563px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1440, + 563 + ], + "height": "563px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1100, + 740 + ], + "canvas": [ + 862, + 806 + ], + "css": [ + 431, + 403 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 195, + 205, + 431, + 403 + ], + "height": "403px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1100, + 403 + ], + "height": "403px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1280, + 800 + ], + "canvas": [ + 1112, + 926 + ], + "css": [ + 556, + 463 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 556, + 463 + ], + "height": "463px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1280, + 463 + ], + "height": "463px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } + ], + "regressionBefore": { + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "eae039ff0f49b160b9eef5dd1d1c179462901da60f90f8d8516ec1911f37f826", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=54407f543df10e7a6ec92dcc1d8f0cc8355dba0a73a5cdfb18b5f1eca87a43b5", + "chromiumIdentity": null, + "startedAt": "2026-09-08T09:02:36.488117+00:00", + "duration": "00:00:00.2895067", + "selection": "candidate", + "summary": { + "tests": 1, + "passed": 0, + "failed": 1, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 3, + "subtestsPassed": 1, + "subtestsFailed": 2 + }, + "results": [ + { + "path": "contracts/css-grid-auto-row-shrink.html", + "type": "testharness", + "status": "FAIL", + "duration": "00:00:00.2855252", + "message": "diagnostic: DIV#scroll-grid style=height: 160px;\ndiagnostic: DIV#zero-grid style=height: 160px;\ndiagnostic: activeElement=", + "subtests": [ + { + "name": "Scrollable auto row grows and shrinks with definite grid height", + "status": "FAIL", + "message": "assert_equals: scroll row at 160 expected 160 but got 400", + "stack": "Error\n at get_stack (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4802:21)\n at new AssertionError (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4795:22)\n at assert (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4779:19)\n at assert_equals (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1598:9)\n at assert_wrapper (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1518:30)\n at Test. (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:4:73)\n at Test.step (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:2869:25)\n at test (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:633:30)\n at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:2:1" + }, + { + "name": "Explicit zero minimum permits row shrinking without losing authored minimum", + "status": "FAIL", + "message": "assert_equals: zero minimum row at 160 expected 160 but got 400", + "stack": "Error\n at get_stack (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4802:21)\n at new AssertionError (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4795:22)\n at assert (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4779:19)\n at assert_equals (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1598:9)\n at assert_wrapper (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1518:30)\n at Test. (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:8:73)\n at Test.step (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:2869:25)\n at test (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:633:30)\n at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-3.js:6:1" + }, + { + "name": "Visible overflow retains content-based automatic minimum", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] + }, + "candidateGridAfter": { + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "eae039ff0f49b160b9eef5dd1d1c179462901da60f90f8d8516ec1911f37f826", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=78cb8160904fc5a35997770383cabda2542ef619f8ac93197d726532fa62c6d0", + "chromiumIdentity": null, + "startedAt": "2026-09-08T09:05:44.771851+00:00", + "duration": "00:00:02.0495650", + "selection": "candidate", + "summary": { + "tests": 6, + "passed": 6, + "failed": 0, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 15, + "subtestsPassed": 15, + "subtestsFailed": 0 + }, + "results": [ + { + "path": "contracts/css-grid-auto-row-shrink.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.6671127", + "message": null, + "subtests": [ + { + "name": "Scrollable auto row grows and shrinks with definite grid height", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Explicit zero minimum permits row shrinking without losing authored minimum", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Visible overflow retains content-based automatic minimum", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/css-grid-form-layout.html", + "type": "contract", + "status": "PASS", + "duration": "00:00:00.1114907", + "message": null, + "subtests": [ + { + "name": "one automatic middle track leaves equal fractional form tracks", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "minmax fractional CRUD columns share the available width", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "explicit row spans and column placement retain the authored cells", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/css-grid-column-subgrid-form-row.html", + "type": "contract", + "status": "PASS", + "duration": "00:00:00.0969582", + "message": null, + "subtests": [ + { + "name": "a spanning column subgrid inherits the parent form tracks", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/css-grid-display-contents-items.html", + "type": "contract", + "status": "PASS", + "duration": "00:00:00.1007485", + "message": null, + "subtests": [ + { + "name": "display contents rows do not generate grid item boxes", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "a definite grid end line aligns the header with flattened row cells", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "the row descendants occupy one compact parent-grid row", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "all flattened cells remain within the five-track grid", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/grid-auto-row-stretch-dir-radius.html", + "type": "reftest", + "status": "PASS", + "duration": "00:00:00.9727320", + "message": null, + "subtests": [], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/implicit-grid-and-compact-flex-controls.html", + "type": "contract", + "status": "PASS", + "duration": "00:00:00.0972089", + "message": null, + "subtests": [ + { + "name": "column auto-flow creates three equal implicit fractional tracks", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "an inline flex item is blockified and stretches in the cross axis", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "a three-column fractional grid preserves a nested nonshrinking suffix", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "a spaced flex summary keeps its numeric value on one line", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] + }, + "requiredGridAfter": { + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "eae039ff0f49b160b9eef5dd1d1c179462901da60f90f8d8516ec1911f37f826", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=78cb8160904fc5a35997770383cabda2542ef619f8ac93197d726532fa62c6d0", + "chromiumIdentity": null, + "startedAt": "2026-09-08T09:06:04.631373+00:00", + "duration": "00:00:01.3713778", + "selection": "required", + "summary": { + "tests": 4, + "passed": 4, + "failed": 0, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 9, + "subtestsPassed": 9, + "subtestsFailed": 0 + }, + "results": [ + { + "path": "contracts/css-grid-gap-alias-flex.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.1560796", + "message": null, + "subtests": [ + { + "name": "Grid row/column gap aliases preserve flex spacing and wrapping", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "CSSOM camel-case gridColumnGap sets the column-gap alias", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/responsive-settings-property-grid.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.1160675", + "message": null, + "subtests": [ + { + "name": "compact 1fr/min-content settings rows use post-track wrapped block contributions", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "css/css-grid/alignment/grid-gutters-001.html", + "type": "reftest", + "status": "PASS", + "duration": "00:00:00.9997002", + "message": null, + "subtests": [], + "artifacts": null, + "chromiumOracle": null + }, + { + "path": "contracts/css-grid-placement-cssom.html", + "type": "contract", + "status": "PASS", + "duration": "00:00:00.0968105", + "message": null, + "subtests": [ + { + "name": "a one-line grid-area expands to four computed placement longhands", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "a four-line stylesheet grid-area preserves placement order", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "grid-row and grid-column expand their omitted ends to auto", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "grid placement longhands retain unitless integer values", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "an explicit placement longhand overrides its shorthand component", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "removing a grid placement shorthand restores auto longhands", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] + }, + "nativeTests": { + "engineSeconds": 10.93, + "gpuRuntimeSeconds": 1.81, + "passed": true + }, + "limitations": [ + "CSS/bitmap geometry and zero application errors verified, not physical frame-by-frame resize smoothness.", + "New contract remains candidate; no cross-RID qualification or new upstream WPT import claimed.", + "Grid fractional column sizing still leaves unused horizontal space; requires a separate regression and fix." + ] +} diff --git a/docs/graphics/evidence/kestrel/headless-render-clock-observed.json b/docs/graphics/evidence/kestrel/headless-render-clock-observed.json new file mode 100644 index 000000000..a03c83372 --- /dev/null +++ b/docs/graphics/evidence/kestrel/headless-render-clock-observed.json @@ -0,0 +1,114 @@ +{ + "exitCode": 0, + "qualification": "CPU baseline diagnostic only; physical presentation unqualified", + "command": "WEBSCENE_NATIVE_ENGINE_PATH=\"$PWD/artifacts/graphics-build/native-v8-enabled/libwebscene_native_engine.dylib\" dotnet run --project benchmarks/WebScene.NativeEngine.Benchmarks -c Release --no-build -- probe native-resize-cadence --seconds 5 --warmup-seconds 2 --hz 60 --composition", + "frameworkSource": "https://raw.githubusercontent.com/AvaloniaUI/Avalonia/11.3.4/src/Headless/Avalonia.Headless/AvaloniaHeadlessPlatform.cs", + "measurement": { + "schema": "webscene-native-resize-cadence-v2", + "measurementScope": "headless-cpu-draw-callback", + "physicalPresentationVerified": false, + "headlessRenderTimer": { + "available": true, + "tickCount": 150, + "ticksPerSecond": 29.999894400371712, + "intervalMilliseconds": { + "count": 149, + "average": 33.32357634228188, + "p50": 34.057917, + "p95": 34.789709, + "maximum": 37.345333 + } + }, + "sourceKind": "deterministic-fixture", + "composition": true, + "certificationTelemetryEnabled": false, + "requestedHz": 60, + "warmupSeconds": 2, + "requestedSeconds": 5, + "elapsedMilliseconds": 5000.0176, + "submitted": 300, + "acceptedSubmissions": 300, + "appliedPairs": 300, + "publishedPairs": 150, + "coalescedPairs": 0, + "renderedFrames": 150, + "drawCallbackCompletions": 150, + "renderedFramesPerSecond": 29.999894400371712, + "drawCallbackCompletionsPerSecond": 30.002529827410196, + "layoutPasses": 900, + "layoutPassesPerAppliedResize": 3, + "publishedScenes": 150, + "publicationAttempts": 302, + "blockedPublications": 152, + "fullInvalidations": 150, + "unchangedRenderCallbacks": 0, + "droppedInputs": 0, + "processCpuMilliseconds": 2431.793, + "normalizedProcessCpuPercent": 48.63568880237541, + "lastCompositionMilliseconds": { + "diffApply": 2.782125, + "retainedDraw": 0.292167, + "skiaSubmit": 0.387209, + "callback": 0.387792 + }, + "lastResizeStageMilliseconds": { + "outerListeners": 0, + "frameListeners": 0, + "finalLayout": 0, + "observers": 0, + "totalDispatch": 2.921459, + "scenePublication": 0.649875 + }, + "queueMilliseconds": { + "average": 0.008550266666666667, + "maximum": 0.066083 + }, + "dispatchMilliseconds": { + "average": 2.4751538933333337, + "maximum": 5.132834 + }, + "publicationLatencyMilliseconds": { + "count": 299, + "average": 12.695980217391305, + "p50": 8.443625, + "p95": 21.530667, + "maximum": 37.233458 + }, + "publicationToRenderLatencyMilliseconds": { + "count": 148, + "average": 65.95909599324321, + "p50": 66.026208, + "p95": 66.971208, + "maximum": 70.996625 + }, + "renderLatencyMilliseconds": { + "count": 295, + "average": 78.600604379661, + "p50": 72.146209, + "p95": 87.997666, + "maximum": 92.374791 + }, + "renderIntervalMilliseconds": { + "count": 149, + "average": 33.32958976510068, + "p50": 34.140584, + "p95": 34.784875, + "maximum": 36.909541 + }, + "drawCallbackIntervalMilliseconds": { + "count": 149, + "average": 33.330522651006696, + "p50": 34.140208, + "p95": 34.784875, + "maximum": 36.909584 + }, + "cpuCadenceGate": { + "maximumP95LatencyMilliseconds": 16.7, + "minimumFramesPerSecond": 58, + "maximumConsecutiveMissIntervalMilliseconds": 33.4, + "passed": false + }, + "chromeReferenceComparison": null, + "certificationDiagnostics": "certification telemetry disabled at compile time" + } +} diff --git a/docs/graphics/evidence/kestrel/host-frame-gate-experiment.json b/docs/graphics/evidence/kestrel/host-frame-gate-experiment.json new file mode 100644 index 000000000..75141b6bc --- /dev/null +++ b/docs/graphics/evidence/kestrel/host-frame-gate-experiment.json @@ -0,0 +1,1236 @@ +{ + "retained": false, + "change": "Ordinary host_frame_applied bypassed publication phase; fallback deadline reset after host-frame publication.", + "result": "Workload validation and startup passed, but single-run counters did not support improvement. Scheduler restored.", + "limitations": [ + "Single run; no statistical comparison.", + "No physical presentation timestamps.", + "A bypass limited to the same worker iteration may miss RAF completion in later task batches." + ], + "records": { + "Kestrel pan performance": { + "elapsedMilliseconds": 1857.3856, + "baseline": { + "ContextId": 1, + "Timestamp": 91570270882208, + "Engine": { + "EnqueuedInputs": 29, + "DroppedInputs": 0, + "ConsumedInputs": 29, + "PublishedScenes": 16, + "AcquiredScenes": 14, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3156084, + "InputEventsDispatched": 111, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 98250, + "LastScenePublicationNanoseconds": 682584, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 6, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 10, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 11, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 1542000, + "LastSceneBuildNanoseconds": 537541, + "MaximumScenePublicationNanoseconds": 1993708 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 217250, + "MaximumDispatchNanoseconds": 724708, + "LastDispatchSequence": 639244575877657368, + "DispatchedInputs": 11, + "TotalDispatchNanoseconds": 3860083 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 11, + "TotalDispatchNanoseconds": 10832, + "LastDispatchNanoseconds": 1416, + "MaximumDispatchNanoseconds": 1500, + "LastTimestampMicroseconds": 91568801335 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 26, + "BlockedPublications": 10, + "AcknowledgedScenes": 14, + "TotalAcknowledgementNanoseconds": 748769000, + "LastAcknowledgementNanoseconds": 16078834, + "MaximumAcknowledgementNanoseconds": 246399083, + "AcknowledgedRevision": 16 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5963776, + "V8UsedHeapBytes": 2764308, + "V8ExecutableHeapBytes": 786432, + "V8PhysicalHeapBytes": 5963776, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 2404352, + "LatestSceneBytes": 127052, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 328168, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920340, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196960, + "V8CodeSpacePhysicalBytes": 786432, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 131368, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 14, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 91572130433958, + "Engine": { + "EnqueuedInputs": 141, + "DroppedInputs": 0, + "ConsumedInputs": 141, + "PublishedScenes": 48, + "AcquiredScenes": 46, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1389, + "LayoutPasses": 81, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3156084, + "InputEventsDispatched": 219, + "InputCallbacksInvoked": 92, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 98250, + "LastScenePublicationNanoseconds": 1930459, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 43, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 53, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 41, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 1411333, + "LastSceneBuildNanoseconds": 386000, + "MaximumScenePublicationNanoseconds": 3338875 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 3377084, + "MaximumDispatchNanoseconds": 12128083, + "LastDispatchSequence": 639244575877657450, + "DispatchedInputs": 56, + "TotalDispatchNanoseconds": 239660423 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 41, + "TotalDispatchNanoseconds": 46580, + "LastDispatchNanoseconds": 2250, + "MaximumDispatchNanoseconds": 3958, + "LastTimestampMicroseconds": 91571683469 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 87, + "BlockedPublications": 10, + "AcknowledgedScenes": 46, + "TotalAcknowledgementNanoseconds": 3098991920, + "LastAcknowledgementNanoseconds": 47512708, + "MaximumAcknowledgementNanoseconds": 246399083, + "AcknowledgedRevision": 48 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 28, + "AnimationFramesInvoked": 28, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 136, + "WorkerWaits": 173, + "WorkerSignalledWakes": 139, + "WorkerTimeoutWakes": 33, + "SceneBuilds": 32, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5963776, + "V8UsedHeapBytes": 2764308, + "V8ExecutableHeapBytes": 786432, + "V8PhysicalHeapBytes": 5963776, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 2404352, + "LatestSceneBytes": 159344, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 328168, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920340, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196960, + "V8CodeSpacePhysicalBytes": 786432, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 131368, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5970848, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 1497, + "StringBytes": 100000, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 46, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 56, + "Renders": 32, + "AppliedDiffs": 32, + "InvalidationCalls": 32, + "DamageRectangles": 87, + "ChangedLayers": 28, + "EmptyDamageDiffs": 1, + "PartialDamageDiffs": 31, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 30, + "SkippedEmptyAnimationFrames": 26, + "RenderCallbacks": 32, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.8595517", + "EnqueuedInputs": 112, + "DroppedInputs": 0, + "ConsumedInputs": 112, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 73, + "AppliedAnimationFrames": 30, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 61, + "BlockedPublications": 0, + "PublishedScenes": 32, + "AcquiredScenes": 32, + "AcknowledgedScenes": 32, + "RenderedScenes": 32, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 28, + "AnimationFramesInvoked": 28, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 136, + "WorkerWaits": 173, + "WorkerSignalledWakes": 139, + "WorkerTimeoutWakes": 33, + "SceneBuilds": 32, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 56, + "CompositionRenders": 32, + "CompositionAppliedDiffs": 32, + "CompositionInvalidations": 32, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 30, + "CompositionSkippedEmptyAnimationFrames": 26, + "CompositionRenderCallbacks": 32, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "Kestrel pan diagnostics": { + "events": [ + { + "type": "pointerdown", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 91570281.619416, + "panning": false + }, + { + "type": "pointermove", + "x": 629, + "y": 437.5, + "button": 2, + "buttons": 2, + "time": 91570285.840708, + "panning": true + }, + { + "type": "pointermove", + "x": 633, + "y": 438.5, + "button": 2, + "buttons": 2, + "time": 91570307.005375, + "panning": true + }, + { + "type": "pointermove", + "x": 637, + "y": 439.5, + "button": 2, + "buttons": 2, + "time": 91570311.933, + "panning": true + }, + { + "type": "pointermove", + "x": 641, + "y": 440.5, + "button": 2, + "buttons": 2, + "time": 91570345.238291, + "panning": true + }, + { + "type": "pointermove", + "x": 645, + "y": 441.5, + "button": 2, + "buttons": 2, + "time": 91570349.300875, + "panning": true + }, + { + "type": "pointermove", + "x": 653, + "y": 443.5, + "button": 2, + "buttons": 2, + "time": 91570384.742541, + "panning": true + }, + { + "type": "pointermove", + "x": 665, + "y": 446.5, + "button": 2, + "buttons": 2, + "time": 91570440.247875, + "panning": true + }, + { + "type": "pointermove", + "x": 673, + "y": 448.5, + "button": 2, + "buttons": 2, + "time": 91570476.459833, + "panning": true + }, + { + "type": "pointermove", + "x": 681, + "y": 450.5, + "button": 2, + "buttons": 2, + "time": 91570512.731916, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 91570515.630916, + "panning": true + }, + { + "type": "pointermove", + "x": 689, + "y": 452.5, + "button": 2, + "buttons": 2, + "time": 91570551.246083, + "panning": true + }, + { + "type": "pointermove", + "x": 693, + "y": 453.5, + "button": 2, + "buttons": 2, + "time": 91570554.479541, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 91570588.71975, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 91570625.614541, + "panning": true + }, + { + "type": "pointermove", + "x": 713, + "y": 458.5, + "button": 2, + "buttons": 2, + "time": 91570653.416666, + "panning": true + }, + { + "type": "pointermove", + "x": 717, + "y": 459.5, + "button": 2, + "buttons": 2, + "time": 91570658.352666, + "panning": true + }, + { + "type": "pointermove", + "x": 725, + "y": 461.5, + "button": 2, + "buttons": 2, + "time": 91570700.786333, + "panning": true + }, + { + "type": "pointermove", + "x": 729, + "y": 462.5, + "button": 2, + "buttons": 2, + "time": 91570705.250958, + "panning": true + }, + { + "type": "pointermove", + "x": 737, + "y": 464.5, + "button": 2, + "buttons": 2, + "time": 91570737.554958, + "panning": true + }, + { + "type": "pointermove", + "x": 745, + "y": 466.5, + "button": 2, + "buttons": 2, + "time": 91570774.911708, + "panning": true + }, + { + "type": "pointermove", + "x": 753, + "y": 468.5, + "button": 2, + "buttons": 2, + "time": 91570813.3285, + "panning": true + }, + { + "type": "pointermove", + "x": 761, + "y": 470.5, + "button": 2, + "buttons": 2, + "time": 91570855.120916, + "panning": true + }, + { + "type": "pointermove", + "x": 765, + "y": 471.5, + "button": 2, + "buttons": 2, + "time": 91570858.554791, + "panning": true + }, + { + "type": "pointermove", + "x": 777, + "y": 474.5, + "button": 2, + "buttons": 2, + "time": 91570914.231333, + "panning": true + }, + { + "type": "pointermove", + "x": 773, + "y": 473.5, + "button": 2, + "buttons": 2, + "time": 91570996.140333, + "panning": true + }, + { + "type": "pointermove", + "x": 757, + "y": 469.5, + "button": 2, + "buttons": 2, + "time": 91571066.476666, + "panning": true + }, + { + "type": "pointermove", + "x": 745, + "y": 466.5, + "button": 2, + "buttons": 2, + "time": 91571124.982125, + "panning": true + }, + { + "type": "pointermove", + "x": 741, + "y": 465.5, + "button": 2, + "buttons": 2, + "time": 91571129.585291, + "panning": true + }, + { + "type": "pointermove", + "x": 725, + "y": 461.5, + "button": 2, + "buttons": 2, + "time": 91571204.519708, + "panning": true + }, + { + "type": "pointermove", + "x": 721, + "y": 460.5, + "button": 2, + "buttons": 2, + "time": 91571211.651, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 91571269.631916, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 91571307.612416, + "panning": true + }, + { + "type": "pointermove", + "x": 697, + "y": 454.5, + "button": 2, + "buttons": 2, + "time": 91571314.535791, + "panning": true + }, + { + "type": "pointermove", + "x": 689, + "y": 452.5, + "button": 2, + "buttons": 2, + "time": 91571361.245666, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 91571364.943291, + "panning": true + }, + { + "type": "pointermove", + "x": 673, + "y": 448.5, + "button": 2, + "buttons": 2, + "time": 91571417.962541, + "panning": true + }, + { + "type": "pointermove", + "x": 657, + "y": 444.5, + "button": 2, + "buttons": 2, + "time": 91571483.0635, + "panning": true + }, + { + "type": "pointermove", + "x": 649, + "y": 442.5, + "button": 2, + "buttons": 2, + "time": 91571527.787041, + "panning": true + }, + { + "type": "pointermove", + "x": 641, + "y": 440.5, + "button": 2, + "buttons": 2, + "time": 91571563.572916, + "panning": true + }, + { + "type": "pointermove", + "x": 637, + "y": 439.5, + "button": 2, + "buttons": 2, + "time": 91571566.115125, + "panning": true + }, + { + "type": "pointermove", + "x": 633, + "y": 438.5, + "button": 2, + "buttons": 2, + "time": 91571597.707333, + "panning": true + }, + { + "type": "pointermove", + "x": 629, + "y": 437.5, + "button": 2, + "buttons": 2, + "time": 91571600.361625, + "panning": true + }, + { + "type": "pointermove", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 91571638.562583, + "panning": true + }, + { + "type": "pointerup", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 0, + "time": 91571639.571875, + "panning": false + } + ], + "captures": [], + "frames": [ + { + "timestamp": 91570284.992, + "start": 91570290.301791, + "duration": 1.623999997973442 + }, + { + "timestamp": 91570301.658416, + "start": 91570312.888, + "duration": 1.5056250095367432 + }, + { + "timestamp": 91570341.11425, + "start": 91570350.32225, + "duration": 1.5991660058498383 + }, + { + "timestamp": 91570378.391791, + "start": 91570386.725708, + "duration": 0.9732920080423355 + }, + { + "timestamp": 91570434.73437501, + "start": 91570441.788916, + "duration": 1.3052499890327454 + }, + { + "timestamp": 91570472.97925, + "start": 91570477.289041, + "duration": 1.2230419963598251 + }, + { + "timestamp": 91570510.021458, + "start": 91570516.295416, + "duration": 0.8215000033378601 + }, + { + "timestamp": 91570547.862791, + "start": 91570555.226333, + "duration": 1.4732919931411743 + }, + { + "timestamp": 91570622.306916, + "start": 91570626.345, + "duration": 0.7012909948825836 + }, + { + "timestamp": 91570649.329208, + "start": 91570660.091416, + "duration": 0.9324170053005219 + }, + { + "timestamp": 91570696.57633299, + "start": 91570706.19075, + "duration": 0.8111660033464432 + }, + { + "timestamp": 91570734.605416, + "start": 91570738.27975, + "duration": 0.8913749903440475 + }, + { + "timestamp": 91570772.056458, + "start": 91570775.67, + "duration": 0.5934579968452454 + }, + { + "timestamp": 91570810.140458, + "start": 91570814.24075, + "duration": 0.9056250005960464 + }, + { + "timestamp": 91570851.578625, + "start": 91570859.162375, + "duration": 0.6864999979734421 + }, + { + "timestamp": 91570910.188625, + "start": 91570914.925166, + "duration": 0.6580840051174164 + }, + { + "timestamp": 91570990.013625, + "start": 91570999.274208, + "duration": 1.6163749992847443 + }, + { + "timestamp": 91571062.588458, + "start": 91571069.00175, + "duration": 1.288457989692688 + }, + { + "timestamp": 91571119.578958, + "start": 91571131.450291, + "duration": 2.838584005832672 + }, + { + "timestamp": 91571264.694166, + "start": 91571271.01325, + "duration": 1.4030410051345825 + }, + { + "timestamp": 91571296.959833, + "start": 91571317.784333, + "duration": 0.7829999923706055 + }, + { + "timestamp": 91571356.83987501, + "start": 91571365.86875, + "duration": 1.4549999982118607 + }, + { + "timestamp": 91571410.42479101, + "start": 91571419.344541, + "duration": 1.4750000089406967 + }, + { + "timestamp": 91571478.655333, + "start": 91571484.385083, + "duration": 1.359499990940094 + }, + { + "timestamp": 91571525.02525, + "start": 91571528.486916, + "duration": 0.5990419983863831 + }, + { + "timestamp": 91571560.82804099, + "start": 91571566.7345, + "duration": 0.7391249984502792 + }, + { + "timestamp": 91571595.048541, + "start": 91571601.034875, + "duration": 0.6689160019159317 + }, + { + "timestamp": 91571683.469916, + "start": 91571683.53575, + "duration": 2.059208005666733 + } + ], + "panning": false, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } + } +} diff --git a/docs/graphics/evidence/kestrel/host-resize-event-boundaries.json b/docs/graphics/evidence/kestrel/host-resize-event-boundaries.json new file mode 100644 index 000000000..be34571d0 --- /dev/null +++ b/docs/graphics/evidence/kestrel/host-resize-event-boundaries.json @@ -0,0 +1,22 @@ +{ + "validated": true, + "exitCode": 0, + "counts": { + "submittedSizes": 80, + "nativeWindowResizes": 38, + "surfaceSizeChanges": 38, + "nativeSubmissions": 38, + "publications": 51, + "renderedScenes": 46 + }, + "windowResizeReasons": { + "Layout": 38 + }, + "physicalPresentationVerified": false, + "nativeUserDragVerified": false, + "limitations": [ + "One scripted property-update run.", + "Window notifications are all Layout; this does not qualify native user resize behavior.", + "Matching event counts locate reduction before native engine submission but do not identify each property/layout scheduling decision." + ] +} diff --git a/docs/graphics/evidence/kestrel/inert-style-harness-blocked.json b/docs/graphics/evidence/kestrel/inert-style-harness-blocked.json new file mode 100644 index 000000000..18e58ccd3 --- /dev/null +++ b/docs/graphics/evidence/kestrel/inert-style-harness-blocked.json @@ -0,0 +1,42 @@ +{ + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "dadfc76517839844f8e553a19ba5a7e4d18b1cb31424110d9ae3ea1f3a09af0c", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=cda82f39c6460bf9031162c37217e852d0d2203132ba7c366d77ecc1567d884f", + "chromiumIdentity": null, + "startedAt": "2026-09-08T07:58:54.758821+00:00", + "duration": "00:00:00.4534177", + "selection": "candidate", + "summary": { + "tests": 1, + "passed": 0, + "failed": 1, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 1, + "subtestsPassed": 0, + "subtestsFailed": 1 + }, + "results": [ + { + "path": "contracts/html-script-style-text-is-inert.html", + "type": "testharness", + "status": "FAIL", + "duration": "00:00:00.4489942", + "message": "diagnostic: activeElement=", + "subtests": [ + { + "name": "Script strings, comments and template contents do not activate stylesheets", + "status": "FAIL", + "message": "assert_equals: expected \u0022rgb(10, 20, 30)\u0022 but got \u0022rgb(0, 0, 255)\u0022", + "stack": "Error\n at get_stack (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4802:21)\n at new AssertionError (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4795:22)\n at assert (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:4779:19)\n at assert_equals (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1598:9)\n at assert_wrapper (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:1518:30)\n at Test.\u003Canonymous\u003E (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-4.js:3:2)\n at Test.step (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:2869:25)\n at test (/Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-1.js:633:30)\n at /Volumes/SSD/repos/worktrees/aa5a/HtmlML/tests/WebPlatformSubset/upstream/webscene-wpt-inline-4.js:2:1" + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] +} \ No newline at end of file diff --git a/docs/graphics/evidence/kestrel/inert-style-native-navigation.json b/docs/graphics/evidence/kestrel/inert-style-native-navigation.json new file mode 100644 index 000000000..c8bae5d7d --- /dev/null +++ b/docs/graphics/evidence/kestrel/inert-style-native-navigation.json @@ -0,0 +1,42 @@ +{ + "schema": "webscene-wpt-subset-result-v3", + "profile": "native-navigation-style-check", + "profileSha256": "fad42122ab4669a1d7c05540b8cdf9b010a77850fdda5e1ee4ad1932b736314f", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=091e320d69afe0c8bb9dcdb1f2aa7258fa704d308ec61c396b3f0a863bc87808", + "chromiumIdentity": null, + "startedAt": "2026-09-08T11:22:13.324792+00:00", + "duration": "00:00:00.2906659", + "selection": "candidate", + "summary": { + "tests": 1, + "passed": 1, + "failed": 0, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 1, + "subtestsPassed": 1, + "subtestsFailed": 0 + }, + "results": [ + { + "path": "contracts/html-script-style-text-is-inert.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.2880304", + "message": null, + "subtests": [ + { + "name": "Script strings, comments and template contents do not activate stylesheets", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] +} \ No newline at end of file diff --git a/docs/graphics/evidence/kestrel/inert-style-navigation-graphics-disabled.json b/docs/graphics/evidence/kestrel/inert-style-navigation-graphics-disabled.json new file mode 100644 index 000000000..379efce5e --- /dev/null +++ b/docs/graphics/evidence/kestrel/inert-style-navigation-graphics-disabled.json @@ -0,0 +1,42 @@ +{ + "schema": "webscene-wpt-subset-result-v3", + "profile": "native-navigation-style-check", + "profileSha256": "fad42122ab4669a1d7c05540b8cdf9b010a77850fdda5e1ee4ad1932b736314f", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=c9cef64d648d9e7c452b1fdd8e25f7b489177f1a5bf749252b78163f2499ba86", + "chromiumIdentity": null, + "startedAt": "2026-09-08T11:25:04.537731+00:00", + "duration": "00:00:01.7196702", + "selection": "candidate", + "summary": { + "tests": 1, + "passed": 1, + "failed": 0, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 1, + "subtestsPassed": 1, + "subtestsFailed": 0 + }, + "results": [ + { + "path": "contracts/html-script-style-text-is-inert.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:01.7171964", + "message": null, + "subtests": [ + { + "name": "Script strings, comments and template contents do not activate stylesheets", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] +} \ No newline at end of file diff --git a/docs/graphics/evidence/kestrel/input-sequence-trace-validation.json b/docs/graphics/evidence/kestrel/input-sequence-trace-validation.json new file mode 100644 index 000000000..cd6829cf7 --- /dev/null +++ b/docs/graphics/evidence/kestrel/input-sequence-trace-validation.json @@ -0,0 +1,2665 @@ +{ + "physicalPresentationVerified": false, + "latencyComparisonValid": false, + "runs": [ + { + "run": "webscene-input-timing-pan", + "workloadValidated": false, + "error": "System.InvalidOperationException: Invalid Kestrel pan workload: missing gesture boundary or application error.", + "submittedMoveCount": 80, + "sequenceAndTimestampOrderVerified": true, + "timeline": { + "timestampFrequency": 1000000000, + "traceStarted": 92657889599041, + "submittedMoves": [ + { + "sequence": 639244586769224940, + "submittedAt": 92657889704000, + "step": 1, + "x": 629, + "y": 437.5 + }, + { + "sequence": 639244586769224941, + "submittedAt": 92657906873250, + "step": 2, + "x": 633, + "y": 438.5 + }, + { + "sequence": 639244586769224942, + "submittedAt": 92657923972250, + "step": 3, + "x": 637, + "y": 439.5 + }, + { + "sequence": 639244586769224943, + "submittedAt": 92657941061708, + "step": 4, + "x": 641, + "y": 440.5 + }, + { + "sequence": 639244586769224944, + "submittedAt": 92657958177916, + "step": 5, + "x": 645, + "y": 441.5 + }, + { + "sequence": 639244586769224945, + "submittedAt": 92657975315250, + "step": 6, + "x": 649, + "y": 442.5 + }, + { + "sequence": 639244586769224946, + "submittedAt": 92657992428666, + "step": 7, + "x": 653, + "y": 443.5 + }, + { + "sequence": 639244586769224947, + "submittedAt": 92658009524500, + "step": 8, + "x": 657, + "y": 444.5 + }, + { + "sequence": 639244586769224948, + "submittedAt": 92658026740291, + "step": 9, + "x": 661, + "y": 445.5 + }, + { + "sequence": 639244586769224949, + "submittedAt": 92658043828500, + "step": 10, + "x": 665, + "y": 446.5 + }, + { + "sequence": 639244586769224950, + "submittedAt": 92658060627625, + "step": 11, + "x": 669, + "y": 447.5 + }, + { + "sequence": 639244586769224951, + "submittedAt": 92658077725041, + "step": 12, + "x": 673, + "y": 448.5 + }, + { + "sequence": 639244586769224952, + "submittedAt": 92658094811458, + "step": 13, + "x": 677, + "y": 449.5 + }, + { + "sequence": 639244586769224953, + "submittedAt": 92658112373833, + "step": 14, + "x": 681, + "y": 450.5 + }, + { + "sequence": 639244586769224954, + "submittedAt": 92658129432208, + "step": 15, + "x": 685, + "y": 451.5 + }, + { + "sequence": 639244586769224955, + "submittedAt": 92658146482583, + "step": 16, + "x": 689, + "y": 452.5 + }, + { + "sequence": 639244586769224956, + "submittedAt": 92658163524083, + "step": 17, + "x": 693, + "y": 453.5 + }, + { + "sequence": 639244586769224957, + "submittedAt": 92658180594541, + "step": 18, + "x": 697, + "y": 454.5 + }, + { + "sequence": 639244586769224958, + "submittedAt": 92658197644416, + "step": 19, + "x": 701, + "y": 455.5 + }, + { + "sequence": 639244586769224959, + "submittedAt": 92658214697750, + "step": 20, + "x": 705, + "y": 456.5 + }, + { + "sequence": 639244586769224960, + "submittedAt": 92658231754833, + "step": 21, + "x": 709, + "y": 457.5 + }, + { + "sequence": 639244586769224961, + "submittedAt": 92658248816166, + "step": 22, + "x": 713, + "y": 458.5 + }, + { + "sequence": 639244586769224962, + "submittedAt": 92658265260750, + "step": 23, + "x": 717, + "y": 459.5 + }, + { + "sequence": 639244586769224963, + "submittedAt": 92658282323375, + "step": 24, + "x": 721, + "y": 460.5 + }, + { + "sequence": 639244586769224964, + "submittedAt": 92658299421625, + "step": 25, + "x": 725, + "y": 461.5 + }, + { + "sequence": 639244586769224965, + "submittedAt": 92658316491375, + "step": 26, + "x": 729, + "y": 462.5 + }, + { + "sequence": 639244586769224966, + "submittedAt": 92658333532541, + "step": 27, + "x": 733, + "y": 463.5 + }, + { + "sequence": 639244586769224967, + "submittedAt": 92658350601208, + "step": 28, + "x": 737, + "y": 464.5 + }, + { + "sequence": 639244586769224968, + "submittedAt": 92658367653125, + "step": 29, + "x": 741, + "y": 465.5 + }, + { + "sequence": 639244586769224969, + "submittedAt": 92658384723166, + "step": 30, + "x": 745, + "y": 466.5 + }, + { + "sequence": 639244586769224970, + "submittedAt": 92658400937625, + "step": 31, + "x": 749, + "y": 467.5 + }, + { + "sequence": 639244586769224971, + "submittedAt": 92658417192416, + "step": 32, + "x": 753, + "y": 468.5 + }, + { + "sequence": 639244586769224972, + "submittedAt": 92658434230791, + "step": 33, + "x": 757, + "y": 469.5 + }, + { + "sequence": 639244586769224973, + "submittedAt": 92658451305416, + "step": 34, + "x": 761, + "y": 470.5 + }, + { + "sequence": 639244586769224974, + "submittedAt": 92658468351083, + "step": 35, + "x": 765, + "y": 471.5 + }, + { + "sequence": 639244586769224975, + "submittedAt": 92658484558916, + "step": 36, + "x": 769, + "y": 472.5 + }, + { + "sequence": 639244586769224976, + "submittedAt": 92658501763625, + "step": 37, + "x": 773, + "y": 473.5 + }, + { + "sequence": 639244586769224977, + "submittedAt": 92658519553791, + "step": 38, + "x": 777, + "y": 474.5 + }, + { + "sequence": 639244586769224978, + "submittedAt": 92658536035500, + "step": 39, + "x": 781, + "y": 475.5 + }, + { + "sequence": 639244586769224979, + "submittedAt": 92658553156250, + "step": 40, + "x": 785, + "y": 476.5 + }, + { + "sequence": 639244586769224980, + "submittedAt": 92658569640916, + "step": 41, + "x": 781, + "y": 475.5 + }, + { + "sequence": 639244586769224981, + "submittedAt": 92658586687458, + "step": 42, + "x": 777, + "y": 474.5 + }, + { + "sequence": 639244586769224982, + "submittedAt": 92658602918541, + "step": 43, + "x": 773, + "y": 473.5 + }, + { + "sequence": 639244586769224983, + "submittedAt": 92658619965125, + "step": 44, + "x": 769, + "y": 472.5 + }, + { + "sequence": 639244586769224984, + "submittedAt": 92658637020333, + "step": 45, + "x": 765, + "y": 471.5 + }, + { + "sequence": 639244586769224985, + "submittedAt": 92658653291083, + "step": 46, + "x": 761, + "y": 470.5 + }, + { + "sequence": 639244586769224986, + "submittedAt": 92658670417208, + "step": 47, + "x": 757, + "y": 469.5 + }, + { + "sequence": 639244586769224987, + "submittedAt": 92658688704125, + "step": 48, + "x": 753, + "y": 468.5 + }, + { + "sequence": 639244586769224988, + "submittedAt": 92658705396583, + "step": 49, + "x": 749, + "y": 467.5 + }, + { + "sequence": 639244586769224989, + "submittedAt": 92658722336416, + "step": 50, + "x": 745, + "y": 466.5 + }, + { + "sequence": 639244586769224990, + "submittedAt": 92658739394750, + "step": 51, + "x": 741, + "y": 465.5 + }, + { + "sequence": 639244586769224991, + "submittedAt": 92658756563291, + "step": 52, + "x": 737, + "y": 464.5 + }, + { + "sequence": 639244586769224992, + "submittedAt": 92658773622666, + "step": 53, + "x": 733, + "y": 463.5 + }, + { + "sequence": 639244586769224993, + "submittedAt": 92658790683208, + "step": 54, + "x": 729, + "y": 462.5 + }, + { + "sequence": 639244586769224994, + "submittedAt": 92658807793375, + "step": 55, + "x": 725, + "y": 461.5 + }, + { + "sequence": 639244586769224995, + "submittedAt": 92658824877166, + "step": 56, + "x": 721, + "y": 460.5 + }, + { + "sequence": 639244586769224996, + "submittedAt": 92658841989625, + "step": 57, + "x": 717, + "y": 459.5 + }, + { + "sequence": 639244586769224997, + "submittedAt": 92658858449375, + "step": 58, + "x": 713, + "y": 458.5 + }, + { + "sequence": 639244586769224998, + "submittedAt": 92658875567250, + "step": 59, + "x": 709, + "y": 457.5 + }, + { + "sequence": 639244586769224999, + "submittedAt": 92658891815208, + "step": 60, + "x": 705, + "y": 456.5 + }, + { + "sequence": 639244586769225000, + "submittedAt": 92658908135875, + "step": 61, + "x": 701, + "y": 455.5 + }, + { + "sequence": 639244586769225001, + "submittedAt": 92658925188583, + "step": 62, + "x": 697, + "y": 454.5 + }, + { + "sequence": 639244586769225002, + "submittedAt": 92658942260000, + "step": 63, + "x": 693, + "y": 453.5 + }, + { + "sequence": 639244586769225003, + "submittedAt": 92658958620583, + "step": 64, + "x": 689, + "y": 452.5 + }, + { + "sequence": 639244586769225004, + "submittedAt": 92658975148666, + "step": 65, + "x": 685, + "y": 451.5 + }, + { + "sequence": 639244586769225005, + "submittedAt": 92658991838833, + "step": 66, + "x": 681, + "y": 450.5 + }, + { + "sequence": 639244586769225006, + "submittedAt": 92659008912500, + "step": 67, + "x": 677, + "y": 449.5 + }, + { + "sequence": 639244586769225007, + "submittedAt": 92659025992666, + "step": 68, + "x": 673, + "y": 448.5 + }, + { + "sequence": 639244586769225008, + "submittedAt": 92659042086791, + "step": 69, + "x": 669, + "y": 447.5 + }, + { + "sequence": 639244586769225009, + "submittedAt": 92659058377833, + "step": 70, + "x": 665, + "y": 446.5 + }, + { + "sequence": 639244586769225010, + "submittedAt": 92659075498791, + "step": 71, + "x": 661, + "y": 445.5 + }, + { + "sequence": 639244586769225011, + "submittedAt": 92659092548250, + "step": 72, + "x": 657, + "y": 444.5 + }, + { + "sequence": 639244586769225012, + "submittedAt": 92659109602583, + "step": 73, + "x": 653, + "y": 443.5 + }, + { + "sequence": 639244586769225013, + "submittedAt": 92659127086125, + "step": 74, + "x": 649, + "y": 442.5 + }, + { + "sequence": 639244586769225014, + "submittedAt": 92659143953083, + "step": 75, + "x": 645, + "y": 441.5 + }, + { + "sequence": 639244586769225015, + "submittedAt": 92659160992958, + "step": 76, + "x": 641, + "y": 440.5 + }, + { + "sequence": 639244586769225016, + "submittedAt": 92659178041833, + "step": 77, + "x": 637, + "y": 439.5 + }, + { + "sequence": 639244586769225017, + "submittedAt": 92659195121625, + "step": 78, + "x": 633, + "y": 438.5 + }, + { + "sequence": 639244586769225018, + "submittedAt": 92659212124666, + "step": 79, + "x": 629, + "y": 437.5 + }, + { + "sequence": 639244586769225019, + "submittedAt": 92659229185833, + "step": 80, + "x": 625, + "y": 436.5 + } + ], + "publications": [ + { + "Timestamp": 92657890849666, + "Revision": 70, + "ConsumedInputSequence": 639244586769224939, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92657902291833, + "Revision": 71, + "ConsumedInputSequence": 639244586769224940, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92657917114875, + "Revision": 72, + "ConsumedInputSequence": 639244586769224941, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92657960841500, + "Revision": 73, + "ConsumedInputSequence": 639244586769224943, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92657981366791, + "Revision": 74, + "ConsumedInputSequence": 639244586769224945, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658022861333, + "Revision": 75, + "ConsumedInputSequence": 639244586769224947, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658032045833, + "Revision": 76, + "ConsumedInputSequence": 639244586769224948, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658067115208, + "Revision": 77, + "ConsumedInputSequence": 639244586769224950, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658101744916, + "Revision": 78, + "ConsumedInputSequence": 639244586769224952, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658136133708, + "Revision": 79, + "ConsumedInputSequence": 639244586769224954, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658170052000, + "Revision": 80, + "ConsumedInputSequence": 639244586769224956, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658203350833, + "Revision": 81, + "ConsumedInputSequence": 639244586769224958, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658241564166, + "Revision": 82, + "ConsumedInputSequence": 639244586769224960, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658271139458, + "Revision": 83, + "ConsumedInputSequence": 639244586769224962, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658301248458, + "Revision": 84, + "ConsumedInputSequence": 639244586769224963, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658317391958, + "Revision": 85, + "ConsumedInputSequence": 639244586769224964, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658350218000, + "Revision": 86, + "ConsumedInputSequence": 639244586769224966, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658365240916, + "Revision": 87, + "ConsumedInputSequence": 639244586769224967, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658399528750, + "Revision": 88, + "ConsumedInputSequence": 639244586769224969, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658415204166, + "Revision": 89, + "ConsumedInputSequence": 639244586769224970, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658450911250, + "Revision": 90, + "ConsumedInputSequence": 639244586769224972, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658465108791, + "Revision": 91, + "ConsumedInputSequence": 639244586769224973, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658498735291, + "Revision": 92, + "ConsumedInputSequence": 639244586769224975, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658515011541, + "Revision": 93, + "ConsumedInputSequence": 639244586769224976, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658550446458, + "Revision": 94, + "ConsumedInputSequence": 639244586769224978, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658565407625, + "Revision": 95, + "ConsumedInputSequence": 639244586769224979, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658602873958, + "Revision": 96, + "ConsumedInputSequence": 639244586769224981, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658621102708, + "Revision": 97, + "ConsumedInputSequence": 639244586769224982, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658651833458, + "Revision": 98, + "ConsumedInputSequence": 639244586769224984, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658671517500, + "Revision": 99, + "ConsumedInputSequence": 639244586769224985, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658702121958, + "Revision": 100, + "ConsumedInputSequence": 639244586769224987, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658715927666, + "Revision": 101, + "ConsumedInputSequence": 639244586769224988, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658749790333, + "Revision": 102, + "ConsumedInputSequence": 639244586769224990, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658765025541, + "Revision": 103, + "ConsumedInputSequence": 639244586769224991, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658801142916, + "Revision": 104, + "ConsumedInputSequence": 639244586769224993, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658816227708, + "Revision": 105, + "ConsumedInputSequence": 639244586769224994, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658850143708, + "Revision": 106, + "ConsumedInputSequence": 639244586769224996, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658865236208, + "Revision": 107, + "ConsumedInputSequence": 639244586769224997, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658902254958, + "Revision": 108, + "ConsumedInputSequence": 639244586769224999, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658915129458, + "Revision": 109, + "ConsumedInputSequence": 639244586769225000, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658949735625, + "Revision": 110, + "ConsumedInputSequence": 639244586769225002, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92658964806750, + "Revision": 111, + "ConsumedInputSequence": 639244586769225003, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659003477125, + "Revision": 112, + "ConsumedInputSequence": 639244586769225005, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659027183958, + "Revision": 113, + "ConsumedInputSequence": 639244586769225006, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659036017083, + "Revision": 114, + "ConsumedInputSequence": 639244586769225007, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659074304208, + "Revision": 115, + "ConsumedInputSequence": 639244586769225009, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659093590500, + "Revision": 116, + "ConsumedInputSequence": 639244586769225010, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659101070250, + "Revision": 117, + "ConsumedInputSequence": 639244586769225011, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659137566083, + "Revision": 118, + "ConsumedInputSequence": 639244586769225013, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659168384291, + "Revision": 119, + "ConsumedInputSequence": 639244586769225015, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659201031541, + "Revision": 120, + "ConsumedInputSequence": 639244586769225017, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659235930458, + "Revision": 121, + "ConsumedInputSequence": 639244586769225019, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659249581333, + "Revision": 122, + "ConsumedInputSequence": 639244586769225020, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659279888083, + "Revision": 123, + "ConsumedInputSequence": 639244586769225020, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92659332155916, + "Revision": 124, + "ConsumedInputSequence": 639244586769225021, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 92657898205375, + "Revision": 70, + "ConsumedInputSequence": 639244586769224939, + "AcceptedTimestamp": 92657894034666 + }, + { + "Timestamp": 92657951814375, + "Revision": 72, + "ConsumedInputSequence": 639244586769224941, + "AcceptedTimestamp": 92657948387166 + }, + { + "Timestamp": 92658013254375, + "Revision": 74, + "ConsumedInputSequence": 639244586769224945, + "AcceptedTimestamp": 92658009715500 + }, + { + "Timestamp": 92658060843916, + "Revision": 76, + "ConsumedInputSequence": 639244586769224948, + "AcceptedTimestamp": 92658057626958 + }, + { + "Timestamp": 92658095141375, + "Revision": 77, + "ConsumedInputSequence": 639244586769224950, + "AcceptedTimestamp": 92658092314166 + }, + { + "Timestamp": 92658129664500, + "Revision": 78, + "ConsumedInputSequence": 639244586769224952, + "AcceptedTimestamp": 92658127412333 + }, + { + "Timestamp": 92658161172500, + "Revision": 79, + "ConsumedInputSequence": 639244586769224954, + "AcceptedTimestamp": 92658158837666 + }, + { + "Timestamp": 92658194313958, + "Revision": 80, + "ConsumedInputSequence": 639244586769224956, + "AcceptedTimestamp": 92658192033500 + }, + { + "Timestamp": 92658229255458, + "Revision": 81, + "ConsumedInputSequence": 639244586769224958, + "AcceptedTimestamp": 92658227037041 + }, + { + "Timestamp": 92658261202500, + "Revision": 82, + "ConsumedInputSequence": 639244586769224960, + "AcceptedTimestamp": 92658258841708 + }, + { + "Timestamp": 92658294781875, + "Revision": 83, + "ConsumedInputSequence": 639244586769224962, + "AcceptedTimestamp": 92658292554041 + }, + { + "Timestamp": 92658343865000, + "Revision": 85, + "ConsumedInputSequence": 639244586769224964, + "AcceptedTimestamp": 92658341192208 + }, + { + "Timestamp": 92658392867166, + "Revision": 87, + "ConsumedInputSequence": 639244586769224967, + "AcceptedTimestamp": 92658390339500 + }, + { + "Timestamp": 92658444611083, + "Revision": 89, + "ConsumedInputSequence": 639244586769224970, + "AcceptedTimestamp": 92658442253416 + }, + { + "Timestamp": 92658492723666, + "Revision": 91, + "ConsumedInputSequence": 639244586769224973, + "AcceptedTimestamp": 92658490137333 + }, + { + "Timestamp": 92658544156291, + "Revision": 93, + "ConsumedInputSequence": 639244586769224976, + "AcceptedTimestamp": 92658541758000 + }, + { + "Timestamp": 92658594235583, + "Revision": 95, + "ConsumedInputSequence": 639244586769224979, + "AcceptedTimestamp": 92658590974750 + }, + { + "Timestamp": 92658645090166, + "Revision": 97, + "ConsumedInputSequence": 639244586769224982, + "AcceptedTimestamp": 92658642490416 + }, + { + "Timestamp": 92658695551125, + "Revision": 99, + "ConsumedInputSequence": 639244586769224985, + "AcceptedTimestamp": 92658693167458 + }, + { + "Timestamp": 92658743119875, + "Revision": 101, + "ConsumedInputSequence": 639244586769224988, + "AcceptedTimestamp": 92658740394083 + }, + { + "Timestamp": 92658794863708, + "Revision": 103, + "ConsumedInputSequence": 639244586769224991, + "AcceptedTimestamp": 92658792482125 + }, + { + "Timestamp": 92658843688958, + "Revision": 105, + "ConsumedInputSequence": 639244586769224994, + "AcceptedTimestamp": 92658840809583 + }, + { + "Timestamp": 92658895622333, + "Revision": 107, + "ConsumedInputSequence": 639244586769224997, + "AcceptedTimestamp": 92658893090708 + }, + { + "Timestamp": 92658943452875, + "Revision": 109, + "ConsumedInputSequence": 639244586769225000, + "AcceptedTimestamp": 92658940827541 + }, + { + "Timestamp": 92658996242958, + "Revision": 111, + "ConsumedInputSequence": 639244586769225003, + "AcceptedTimestamp": 92658993694291 + }, + { + "Timestamp": 92659029071833, + "Revision": 112, + "ConsumedInputSequence": 639244586769225005, + "AcceptedTimestamp": 92659026205208 + }, + { + "Timestamp": 92659065992208, + "Revision": 114, + "ConsumedInputSequence": 639244586769225007, + "AcceptedTimestamp": 92659063616833 + }, + { + "Timestamp": 92659094961625, + "Revision": 115, + "ConsumedInputSequence": 639244586769225009, + "AcceptedTimestamp": 92659092696458 + }, + { + "Timestamp": 92659131251791, + "Revision": 117, + "ConsumedInputSequence": 639244586769225011, + "AcceptedTimestamp": 92659128935000 + }, + { + "Timestamp": 92659162123791, + "Revision": 118, + "ConsumedInputSequence": 639244586769225013, + "AcceptedTimestamp": 92659159737083 + }, + { + "Timestamp": 92659194848625, + "Revision": 119, + "ConsumedInputSequence": 639244586769225015, + "AcceptedTimestamp": 92659192597125 + }, + { + "Timestamp": 92659227267708, + "Revision": 120, + "ConsumedInputSequence": 639244586769225017, + "AcceptedTimestamp": 92659224834500 + }, + { + "Timestamp": 92659276275375, + "Revision": 122, + "ConsumedInputSequence": 639244586769225020, + "AcceptedTimestamp": 92659273704916 + }, + { + "Timestamp": 92659311700291, + "Revision": 123, + "ConsumedInputSequence": 639244586769225020, + "AcceptedTimestamp": 92659309287083 + }, + { + "Timestamp": 92659361564375, + "Revision": 124, + "ConsumedInputSequence": 639244586769225021, + "AcceptedTimestamp": 92659359078375 + } + ], + "drawCallbackCompletions": [ + 92657897821250, + 92657951809125, + 92658013250000, + 92658060841333, + 92658095138583, + 92658129662791, + 92658161170833, + 92658194309916, + 92658229245875, + 92658261197291, + 92658294778541, + 92658343859833, + 92658392862666, + 92658444606208, + 92658492718250, + 92658544151958, + 92658594233625, + 92658645084041, + 92658695537000, + 92658743113625, + 92658794859125, + 92658843681666, + 92658895617291, + 92658943447166, + 92658996237125, + 92659029066500, + 92659065990458, + 92659094957208, + 92659131246208, + 92659162109708, + 92659194845750, + 92659227262291, + 92659276263875, + 92659311690458, + 92659361559458 + ], + "physicalPresentationVerified": false + } + }, + { + "run": "webscene-input-timing-pan-retry", + "workloadValidated": false, + "error": "System.InvalidOperationException: Invalid Kestrel pan workload: missing gesture boundary or application error.", + "submittedMoveCount": 80, + "sequenceAndTimestampOrderVerified": true, + "timeline": { + "timestampFrequency": 1000000000, + "traceStarted": 92690186500125, + "submittedMoves": [ + { + "sequence": 639244587092247793, + "submittedAt": 92690186649041, + "step": 1, + "x": 629, + "y": 437.5 + }, + { + "sequence": 639244587092247794, + "submittedAt": 92690203902500, + "step": 2, + "x": 633, + "y": 438.5 + }, + { + "sequence": 639244587092247795, + "submittedAt": 92690221020166, + "step": 3, + "x": 637, + "y": 439.5 + }, + { + "sequence": 639244587092247796, + "submittedAt": 92690238120208, + "step": 4, + "x": 641, + "y": 440.5 + }, + { + "sequence": 639244587092247797, + "submittedAt": 92690255208416, + "step": 5, + "x": 645, + "y": 441.5 + }, + { + "sequence": 639244587092247798, + "submittedAt": 92690272303666, + "step": 6, + "x": 649, + "y": 442.5 + }, + { + "sequence": 639244587092247799, + "submittedAt": 92690289446458, + "step": 7, + "x": 653, + "y": 443.5 + }, + { + "sequence": 639244587092247800, + "submittedAt": 92690306648333, + "step": 8, + "x": 657, + "y": 444.5 + }, + { + "sequence": 639244587092247801, + "submittedAt": 92690323512875, + "step": 9, + "x": 661, + "y": 445.5 + }, + { + "sequence": 639244587092247802, + "submittedAt": 92690340651708, + "step": 10, + "x": 665, + "y": 446.5 + }, + { + "sequence": 639244587092247803, + "submittedAt": 92690357737958, + "step": 11, + "x": 669, + "y": 447.5 + }, + { + "sequence": 639244587092247804, + "submittedAt": 92690374286708, + "step": 12, + "x": 673, + "y": 448.5 + }, + { + "sequence": 639244587092247805, + "submittedAt": 92690391393375, + "step": 13, + "x": 677, + "y": 449.5 + }, + { + "sequence": 639244587092247806, + "submittedAt": 92690408659541, + "step": 14, + "x": 681, + "y": 450.5 + }, + { + "sequence": 639244587092247807, + "submittedAt": 92690425770833, + "step": 15, + "x": 685, + "y": 451.5 + }, + { + "sequence": 639244587092247808, + "submittedAt": 92690442880958, + "step": 16, + "x": 689, + "y": 452.5 + }, + { + "sequence": 639244587092247809, + "submittedAt": 92690459723416, + "step": 17, + "x": 693, + "y": 453.5 + }, + { + "sequence": 639244587092247810, + "submittedAt": 92690476821375, + "step": 18, + "x": 697, + "y": 454.5 + }, + { + "sequence": 639244587092247811, + "submittedAt": 92690493346958, + "step": 19, + "x": 701, + "y": 455.5 + }, + { + "sequence": 639244587092247812, + "submittedAt": 92690510470708, + "step": 20, + "x": 705, + "y": 456.5 + }, + { + "sequence": 639244587092247813, + "submittedAt": 92690527460291, + "step": 21, + "x": 709, + "y": 457.5 + }, + { + "sequence": 639244587092247814, + "submittedAt": 92690544190125, + "step": 22, + "x": 713, + "y": 458.5 + }, + { + "sequence": 639244587092247815, + "submittedAt": 92690561371666, + "step": 23, + "x": 717, + "y": 459.5 + }, + { + "sequence": 639244587092247816, + "submittedAt": 92690578498291, + "step": 24, + "x": 721, + "y": 460.5 + }, + { + "sequence": 639244587092247817, + "submittedAt": 92690595650250, + "step": 25, + "x": 725, + "y": 461.5 + }, + { + "sequence": 639244587092247818, + "submittedAt": 92690612735041, + "step": 26, + "x": 729, + "y": 462.5 + }, + { + "sequence": 639244587092247819, + "submittedAt": 92690629697041, + "step": 27, + "x": 733, + "y": 463.5 + }, + { + "sequence": 639244587092247820, + "submittedAt": 92690646806291, + "step": 28, + "x": 737, + "y": 464.5 + }, + { + "sequence": 639244587092247821, + "submittedAt": 92690663886500, + "step": 29, + "x": 741, + "y": 465.5 + }, + { + "sequence": 639244587092247822, + "submittedAt": 92690681029083, + "step": 30, + "x": 745, + "y": 466.5 + }, + { + "sequence": 639244587092247823, + "submittedAt": 92690697295458, + "step": 31, + "x": 749, + "y": 467.5 + }, + { + "sequence": 639244587092247824, + "submittedAt": 92690714365583, + "step": 32, + "x": 753, + "y": 468.5 + }, + { + "sequence": 639244587092247825, + "submittedAt": 92690731514958, + "step": 33, + "x": 757, + "y": 469.5 + }, + { + "sequence": 639244587092247826, + "submittedAt": 92690748587833, + "step": 34, + "x": 761, + "y": 470.5 + }, + { + "sequence": 639244587092247827, + "submittedAt": 92690765669000, + "step": 35, + "x": 765, + "y": 471.5 + }, + { + "sequence": 639244587092247828, + "submittedAt": 92690782744791, + "step": 36, + "x": 769, + "y": 472.5 + }, + { + "sequence": 639244587092247829, + "submittedAt": 92690799829083, + "step": 37, + "x": 773, + "y": 473.5 + }, + { + "sequence": 639244587092247830, + "submittedAt": 92690816902875, + "step": 38, + "x": 777, + "y": 474.5 + }, + { + "sequence": 639244587092247831, + "submittedAt": 92690833940625, + "step": 39, + "x": 781, + "y": 475.5 + }, + { + "sequence": 639244587092247832, + "submittedAt": 92690850985791, + "step": 40, + "x": 785, + "y": 476.5 + }, + { + "sequence": 639244587092247833, + "submittedAt": 92690868038041, + "step": 41, + "x": 781, + "y": 475.5 + }, + { + "sequence": 639244587092247834, + "submittedAt": 92690885105666, + "step": 42, + "x": 777, + "y": 474.5 + }, + { + "sequence": 639244587092247835, + "submittedAt": 92690901153250, + "step": 43, + "x": 773, + "y": 473.5 + }, + { + "sequence": 639244587092247836, + "submittedAt": 92690918227291, + "step": 44, + "x": 769, + "y": 472.5 + }, + { + "sequence": 639244587092247837, + "submittedAt": 92690935361208, + "step": 45, + "x": 765, + "y": 471.5 + }, + { + "sequence": 639244587092247838, + "submittedAt": 92690952409000, + "step": 46, + "x": 761, + "y": 470.5 + }, + { + "sequence": 639244587092247839, + "submittedAt": 92690969449375, + "step": 47, + "x": 757, + "y": 469.5 + }, + { + "sequence": 639244587092247840, + "submittedAt": 92690986587375, + "step": 48, + "x": 753, + "y": 468.5 + }, + { + "sequence": 639244587092247841, + "submittedAt": 92691003640083, + "step": 49, + "x": 749, + "y": 467.5 + }, + { + "sequence": 639244587092247842, + "submittedAt": 92691020678791, + "step": 50, + "x": 745, + "y": 466.5 + }, + { + "sequence": 639244587092247843, + "submittedAt": 92691037383375, + "step": 51, + "x": 741, + "y": 465.5 + }, + { + "sequence": 639244587092247844, + "submittedAt": 92691053828958, + "step": 52, + "x": 737, + "y": 464.5 + }, + { + "sequence": 639244587092247845, + "submittedAt": 92691070886416, + "step": 53, + "x": 733, + "y": 463.5 + }, + { + "sequence": 639244587092247846, + "submittedAt": 92691087932666, + "step": 54, + "x": 729, + "y": 462.5 + }, + { + "sequence": 639244587092247847, + "submittedAt": 92691104128833, + "step": 55, + "x": 725, + "y": 461.5 + }, + { + "sequence": 639244587092247848, + "submittedAt": 92691121176916, + "step": 56, + "x": 721, + "y": 460.5 + }, + { + "sequence": 639244587092247849, + "submittedAt": 92691138297958, + "step": 57, + "x": 717, + "y": 459.5 + }, + { + "sequence": 639244587092247850, + "submittedAt": 92691155354208, + "step": 58, + "x": 713, + "y": 458.5 + }, + { + "sequence": 639244587092247851, + "submittedAt": 92691172405041, + "step": 59, + "x": 709, + "y": 457.5 + }, + { + "sequence": 639244587092247852, + "submittedAt": 92691189563708, + "step": 60, + "x": 705, + "y": 456.5 + }, + { + "sequence": 639244587092247853, + "submittedAt": 92691207279916, + "step": 61, + "x": 701, + "y": 455.5 + }, + { + "sequence": 639244587092247854, + "submittedAt": 92691224355208, + "step": 62, + "x": 697, + "y": 454.5 + }, + { + "sequence": 639244587092247855, + "submittedAt": 92691241488625, + "step": 63, + "x": 693, + "y": 453.5 + }, + { + "sequence": 639244587092247856, + "submittedAt": 92691258537583, + "step": 64, + "x": 689, + "y": 452.5 + }, + { + "sequence": 639244587092247857, + "submittedAt": 92691275350250, + "step": 65, + "x": 685, + "y": 451.5 + }, + { + "sequence": 639244587092247858, + "submittedAt": 92691292407916, + "step": 66, + "x": 681, + "y": 450.5 + }, + { + "sequence": 639244587092247859, + "submittedAt": 92691309484083, + "step": 67, + "x": 677, + "y": 449.5 + }, + { + "sequence": 639244587092247860, + "submittedAt": 92691326531000, + "step": 68, + "x": 673, + "y": 448.5 + }, + { + "sequence": 639244587092247861, + "submittedAt": 92691343613166, + "step": 69, + "x": 669, + "y": 447.5 + }, + { + "sequence": 639244587092247862, + "submittedAt": 92691360773833, + "step": 70, + "x": 665, + "y": 446.5 + }, + { + "sequence": 639244587092247863, + "submittedAt": 92691377849833, + "step": 71, + "x": 661, + "y": 445.5 + }, + { + "sequence": 639244587092247864, + "submittedAt": 92691394154625, + "step": 72, + "x": 657, + "y": 444.5 + }, + { + "sequence": 639244587092247865, + "submittedAt": 92691410451875, + "step": 73, + "x": 653, + "y": 443.5 + }, + { + "sequence": 639244587092247866, + "submittedAt": 92691427544875, + "step": 74, + "x": 649, + "y": 442.5 + }, + { + "sequence": 639244587092247867, + "submittedAt": 92691444587541, + "step": 75, + "x": 645, + "y": 441.5 + }, + { + "sequence": 639244587092247868, + "submittedAt": 92691461018666, + "step": 76, + "x": 641, + "y": 440.5 + }, + { + "sequence": 639244587092247869, + "submittedAt": 92691478081875, + "step": 77, + "x": 637, + "y": 439.5 + }, + { + "sequence": 639244587092247870, + "submittedAt": 92691495123708, + "step": 78, + "x": 633, + "y": 438.5 + }, + { + "sequence": 639244587092247872, + "submittedAt": 92691512188166, + "step": 79, + "x": 629, + "y": 437.5 + }, + { + "sequence": 639244587092247876, + "submittedAt": 92691529243791, + "step": 80, + "x": 625, + "y": 436.5 + } + ], + "publications": [ + { + "Timestamp": 92690196078625, + "Revision": 34, + "ConsumedInputSequence": 639244587092247792, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690221553708, + "Revision": 35, + "ConsumedInputSequence": 639244587092247794, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690241847458, + "Revision": 36, + "ConsumedInputSequence": 639244587092247795, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690282607958, + "Revision": 37, + "ConsumedInputSequence": 639244587092247798, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690299508666, + "Revision": 38, + "ConsumedInputSequence": 639244587092247799, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690336718541, + "Revision": 39, + "ConsumedInputSequence": 639244587092247801, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690348925875, + "Revision": 40, + "ConsumedInputSequence": 639244587092247802, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690384966750, + "Revision": 41, + "ConsumedInputSequence": 639244587092247804, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690399134333, + "Revision": 42, + "ConsumedInputSequence": 639244587092247805, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690441740750, + "Revision": 43, + "ConsumedInputSequence": 639244587092247807, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690460682583, + "Revision": 44, + "ConsumedInputSequence": 639244587092247808, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690468648041, + "Revision": 45, + "ConsumedInputSequence": 639244587092247809, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690505811583, + "Revision": 46, + "ConsumedInputSequence": 639244587092247811, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690533930208, + "Revision": 47, + "ConsumedInputSequence": 639244587092247813, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690568827625, + "Revision": 48, + "ConsumedInputSequence": 639244587092247815, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690606880333, + "Revision": 49, + "ConsumedInputSequence": 639244587092247817, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690636003208, + "Revision": 50, + "ConsumedInputSequence": 639244587092247819, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690671313125, + "Revision": 51, + "ConsumedInputSequence": 639244587092247821, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690704298791, + "Revision": 52, + "ConsumedInputSequence": 639244587092247823, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690741802250, + "Revision": 53, + "ConsumedInputSequence": 639244587092247825, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690770762500, + "Revision": 54, + "ConsumedInputSequence": 639244587092247827, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690804614833, + "Revision": 55, + "ConsumedInputSequence": 639244587092247829, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690835303250, + "Revision": 56, + "ConsumedInputSequence": 639244587092247830, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690851877791, + "Revision": 57, + "ConsumedInputSequence": 639244587092247831, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690886408750, + "Revision": 58, + "ConsumedInputSequence": 639244587092247833, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690899796125, + "Revision": 59, + "ConsumedInputSequence": 639244587092247834, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690934530166, + "Revision": 60, + "ConsumedInputSequence": 639244587092247836, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690948421166, + "Revision": 61, + "ConsumedInputSequence": 639244587092247837, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690982488208, + "Revision": 62, + "ConsumedInputSequence": 639244587092247839, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92690998825583, + "Revision": 63, + "ConsumedInputSequence": 639244587092247840, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691035370916, + "Revision": 64, + "ConsumedInputSequence": 639244587092247842, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691048754875, + "Revision": 65, + "ConsumedInputSequence": 639244587092247843, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691083734041, + "Revision": 66, + "ConsumedInputSequence": 639244587092247845, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691098722250, + "Revision": 67, + "ConsumedInputSequence": 639244587092247846, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691134632083, + "Revision": 68, + "ConsumedInputSequence": 639244587092247848, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691148710208, + "Revision": 69, + "ConsumedInputSequence": 639244587092247849, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691186271708, + "Revision": 70, + "ConsumedInputSequence": 639244587092247851, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691208269375, + "Revision": 71, + "ConsumedInputSequence": 639244587092247852, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691235763666, + "Revision": 72, + "ConsumedInputSequence": 639244587092247854, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691259715875, + "Revision": 73, + "ConsumedInputSequence": 639244587092247855, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691268168291, + "Revision": 74, + "ConsumedInputSequence": 639244587092247856, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691303545166, + "Revision": 75, + "ConsumedInputSequence": 639244587092247858, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691316140125, + "Revision": 76, + "ConsumedInputSequence": 639244587092247859, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691349914625, + "Revision": 77, + "ConsumedInputSequence": 639244587092247861, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691384799666, + "Revision": 78, + "ConsumedInputSequence": 639244587092247863, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691419688291, + "Revision": 79, + "ConsumedInputSequence": 639244587092247865, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691452659750, + "Revision": 80, + "ConsumedInputSequence": 639244587092247867, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691486202708, + "Revision": 81, + "ConsumedInputSequence": 639244587092247869, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691510245083, + "Revision": 82, + "ConsumedInputSequence": 639244587092247870, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691536651625, + "Revision": 83, + "ConsumedInputSequence": 639244587092247875, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691542820375, + "Revision": 84, + "ConsumedInputSequence": 639244587092247876, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691561268416, + "Revision": 85, + "ConsumedInputSequence": 639244587092247879, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691584830125, + "Revision": 86, + "ConsumedInputSequence": 639244587092247883, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691618122541, + "Revision": 87, + "ConsumedInputSequence": 639244587092247887, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691641945375, + "Revision": 88, + "ConsumedInputSequence": 639244587092247888, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691665810708, + "Revision": 89, + "ConsumedInputSequence": 639244587092247892, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691678086958, + "Revision": 90, + "ConsumedInputSequence": 639244587092247894, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691697545000, + "Revision": 91, + "ConsumedInputSequence": 639244587092247896, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691711441125, + "Revision": 92, + "ConsumedInputSequence": 639244587092247898, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691728045458, + "Revision": 93, + "ConsumedInputSequence": 639244587092247900, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691744862708, + "Revision": 94, + "ConsumedInputSequence": 639244587092247902, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691761556333, + "Revision": 95, + "ConsumedInputSequence": 639244587092247904, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691778117125, + "Revision": 96, + "ConsumedInputSequence": 639244587092247907, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691794780250, + "Revision": 97, + "ConsumedInputSequence": 639244587092247909, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691811388291, + "Revision": 98, + "ConsumedInputSequence": 639244587092247911, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691828055583, + "Revision": 99, + "ConsumedInputSequence": 639244587092247913, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691844703916, + "Revision": 100, + "ConsumedInputSequence": 639244587092247915, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691861425708, + "Revision": 101, + "ConsumedInputSequence": 639244587092247917, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691878121166, + "Revision": 102, + "ConsumedInputSequence": 639244587092247919, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691894769291, + "Revision": 103, + "ConsumedInputSequence": 639244587092247921, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691911762625, + "Revision": 104, + "ConsumedInputSequence": 639244587092247923, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92691934505750, + "Revision": 105, + "ConsumedInputSequence": 639244587092247924, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 92690216026000, + "Revision": 34, + "ConsumedInputSequence": 639244587092247792, + "AcceptedTimestamp": 92690210891583 + }, + { + "Timestamp": 92690272599833, + "Revision": 36, + "ConsumedInputSequence": 639244587092247795, + "AcceptedTimestamp": 92690269609916 + }, + { + "Timestamp": 92690329878916, + "Revision": 38, + "ConsumedInputSequence": 639244587092247799, + "AcceptedTimestamp": 92690326714166 + }, + { + "Timestamp": 92690378216291, + "Revision": 40, + "ConsumedInputSequence": 639244587092247802, + "AcceptedTimestamp": 92690374933833 + }, + { + "Timestamp": 92690432330041, + "Revision": 42, + "ConsumedInputSequence": 639244587092247805, + "AcceptedTimestamp": 92690429539583 + }, + { + "Timestamp": 92690462272083, + "Revision": 43, + "ConsumedInputSequence": 639244587092247807, + "AcceptedTimestamp": 92690459368166 + }, + { + "Timestamp": 92690498436416, + "Revision": 45, + "ConsumedInputSequence": 639244587092247809, + "AcceptedTimestamp": 92690496141541 + }, + { + "Timestamp": 92690527526458, + "Revision": 46, + "ConsumedInputSequence": 639244587092247811, + "AcceptedTimestamp": 92690525062375 + }, + { + "Timestamp": 92690562048458, + "Revision": 47, + "ConsumedInputSequence": 639244587092247813, + "AcceptedTimestamp": 92690559488833 + }, + { + "Timestamp": 92690597592916, + "Revision": 48, + "ConsumedInputSequence": 639244587092247815, + "AcceptedTimestamp": 92690595029916 + }, + { + "Timestamp": 92690628919375, + "Revision": 49, + "ConsumedInputSequence": 639244587092247817, + "AcceptedTimestamp": 92690626286458 + }, + { + "Timestamp": 92690662223625, + "Revision": 50, + "ConsumedInputSequence": 639244587092247819, + "AcceptedTimestamp": 92690659777208 + }, + { + "Timestamp": 92690695386083, + "Revision": 51, + "ConsumedInputSequence": 639244587092247821, + "AcceptedTimestamp": 92690692960166 + }, + { + "Timestamp": 92690729875541, + "Revision": 52, + "ConsumedInputSequence": 639244587092247823, + "AcceptedTimestamp": 92690727564333 + }, + { + "Timestamp": 92690761935833, + "Revision": 53, + "ConsumedInputSequence": 639244587092247825, + "AcceptedTimestamp": 92690759548166 + }, + { + "Timestamp": 92690795620083, + "Revision": 54, + "ConsumedInputSequence": 639244587092247827, + "AcceptedTimestamp": 92690793028208 + }, + { + "Timestamp": 92690828889541, + "Revision": 55, + "ConsumedInputSequence": 639244587092247829, + "AcceptedTimestamp": 92690826364125 + }, + { + "Timestamp": 92690879492708, + "Revision": 57, + "ConsumedInputSequence": 639244587092247831, + "AcceptedTimestamp": 92690877048833 + }, + { + "Timestamp": 92690926641208, + "Revision": 59, + "ConsumedInputSequence": 639244587092247834, + "AcceptedTimestamp": 92690923685958 + }, + { + "Timestamp": 92690976218375, + "Revision": 61, + "ConsumedInputSequence": 639244587092247837, + "AcceptedTimestamp": 92690973672500 + }, + { + "Timestamp": 92691029059416, + "Revision": 63, + "ConsumedInputSequence": 639244587092247840, + "AcceptedTimestamp": 92691026522208 + }, + { + "Timestamp": 92691077181875, + "Revision": 65, + "ConsumedInputSequence": 639244587092247843, + "AcceptedTimestamp": 92691074379083 + }, + { + "Timestamp": 92691128289083, + "Revision": 67, + "ConsumedInputSequence": 639244587092247846, + "AcceptedTimestamp": 92691125867750 + }, + { + "Timestamp": 92691177270208, + "Revision": 69, + "ConsumedInputSequence": 639244587092247849, + "AcceptedTimestamp": 92691174297666 + }, + { + "Timestamp": 92691229085208, + "Revision": 71, + "ConsumedInputSequence": 639244587092247852, + "AcceptedTimestamp": 92691226712375 + }, + { + "Timestamp": 92691261827208, + "Revision": 72, + "ConsumedInputSequence": 639244587092247854, + "AcceptedTimestamp": 92691258555791 + }, + { + "Timestamp": 92691297238166, + "Revision": 74, + "ConsumedInputSequence": 639244587092247856, + "AcceptedTimestamp": 92691294893125 + }, + { + "Timestamp": 92691343409166, + "Revision": 76, + "ConsumedInputSequence": 639244587092247859, + "AcceptedTimestamp": 92691340773750 + }, + { + "Timestamp": 92691378424416, + "Revision": 77, + "ConsumedInputSequence": 639244587092247861, + "AcceptedTimestamp": 92691376057083 + }, + { + "Timestamp": 92691413002416, + "Revision": 78, + "ConsumedInputSequence": 639244587092247863, + "AcceptedTimestamp": 92691410635208 + }, + { + "Timestamp": 92691444706666, + "Revision": 79, + "ConsumedInputSequence": 639244587092247865, + "AcceptedTimestamp": 92691442384250 + }, + { + "Timestamp": 92691479026833, + "Revision": 80, + "ConsumedInputSequence": 639244587092247867, + "AcceptedTimestamp": 92691476432041 + }, + { + "Timestamp": 92691526621208, + "Revision": 82, + "ConsumedInputSequence": 639244587092247870, + "AcceptedTimestamp": 92691523785250 + }, + { + "Timestamp": 92691575165333, + "Revision": 84, + "ConsumedInputSequence": 639244587092247876, + "AcceptedTimestamp": 92691571948291 + }, + { + "Timestamp": 92691613211458, + "Revision": 86, + "ConsumedInputSequence": 639244587092247883, + "AcceptedTimestamp": 92691610433416 + }, + { + "Timestamp": 92691661955166, + "Revision": 88, + "ConsumedInputSequence": 639244587092247888, + "AcceptedTimestamp": 92691659440500 + }, + { + "Timestamp": 92691695188041, + "Revision": 90, + "ConsumedInputSequence": 639244587092247894, + "AcceptedTimestamp": 92691692951041 + }, + { + "Timestamp": 92691713113541, + "Revision": 91, + "ConsumedInputSequence": 639244587092247896, + "AcceptedTimestamp": 92691710788916 + }, + { + "Timestamp": 92691729576791, + "Revision": 92, + "ConsumedInputSequence": 639244587092247898, + "AcceptedTimestamp": 92691727482791 + }, + { + "Timestamp": 92691746371291, + "Revision": 93, + "ConsumedInputSequence": 639244587092247900, + "AcceptedTimestamp": 92691744131208 + }, + { + "Timestamp": 92691762941375, + "Revision": 94, + "ConsumedInputSequence": 639244587092247902, + "AcceptedTimestamp": 92691760863791 + }, + { + "Timestamp": 92691779501416, + "Revision": 95, + "ConsumedInputSequence": 639244587092247904, + "AcceptedTimestamp": 92691777484791 + }, + { + "Timestamp": 92691796122041, + "Revision": 96, + "ConsumedInputSequence": 639244587092247907, + "AcceptedTimestamp": 92691794120791 + }, + { + "Timestamp": 92691812678375, + "Revision": 97, + "ConsumedInputSequence": 639244587092247909, + "AcceptedTimestamp": 92691810786833 + }, + { + "Timestamp": 92691829638666, + "Revision": 98, + "ConsumedInputSequence": 639244587092247911, + "AcceptedTimestamp": 92691827509416 + }, + { + "Timestamp": 92691846264500, + "Revision": 99, + "ConsumedInputSequence": 639244587092247913, + "AcceptedTimestamp": 92691844117375 + }, + { + "Timestamp": 92691863000333, + "Revision": 100, + "ConsumedInputSequence": 639244587092247915, + "AcceptedTimestamp": 92691860816791 + }, + { + "Timestamp": 92691879926666, + "Revision": 101, + "ConsumedInputSequence": 639244587092247917, + "AcceptedTimestamp": 92691877499333 + }, + { + "Timestamp": 92691896565541, + "Revision": 102, + "ConsumedInputSequence": 639244587092247919, + "AcceptedTimestamp": 92691894128000 + }, + { + "Timestamp": 92691913505041, + "Revision": 103, + "ConsumedInputSequence": 639244587092247921, + "AcceptedTimestamp": 92691910827291 + }, + { + "Timestamp": 92691930271083, + "Revision": 104, + "ConsumedInputSequence": 639244587092247923, + "AcceptedTimestamp": 92691927513458 + }, + { + "Timestamp": 92691947278000, + "Revision": 105, + "ConsumedInputSequence": 639244587092247924, + "AcceptedTimestamp": 92691944191000 + } + ], + "drawCallbackCompletions": [ + 92690215526750, + 92690272598291, + 92690275164916, + 92690329875125, + 92690378207416, + 92690432325916, + 92690462268750, + 92690498435291, + 92690527521666, + 92690562043083, + 92690597588541, + 92690628916208, + 92690662218291, + 92690695382625, + 92690729871916, + 92690761931500, + 92690795617041, + 92690828886875, + 92690879491500, + 92690926624750, + 92690976217166, + 92691029054500, + 92691077180333, + 92691128284958, + 92691177266041, + 92691229081791, + 92691261823625, + 92691297234666, + 92691343404791, + 92691378423250, + 92691412989000, + 92691444702666, + 92691479022791, + 92691526617791, + 92691575158208, + 92691613207166, + 92691661952791, + 92691695185833, + 92691713112375, + 92691729576083, + 92691746370416, + 92691762940583, + 92691779500541, + 92691796121416, + 92691812677708, + 92691829637916, + 92691846264041, + 92691862999750, + 92691879926041, + 92691896564708, + 92691913504458, + 92691930270458, + 92691947276208 + ], + "physicalPresentationVerified": false + } + } + ] +} diff --git a/docs/graphics/evidence/kestrel/linux-dom-footprint-fix.json b/docs/graphics/evidence/kestrel/linux-dom-footprint-fix.json new file mode 100644 index 000000000..2c273e668 --- /dev/null +++ b/docs/graphics/evidence/kestrel/linux-dom-footprint-fix.json @@ -0,0 +1,16 @@ +{ + "scope": "Linux x64 GCC/libstdc++ header compilation under container emulation; not hardware or full package qualification", + "compiler": "Ubuntu GCC 13.3.0", + "container": "ubuntu:24.04 linux/amd64", + "containerManifestDigest": "sha256:33ceb71981b602c1a7443a53469e4dba065f7503eab3078a2d7a57a2ab987517", + "before": "Original header fails unchanged 1024-byte static assertion", + "afterDomNodeBytes": 1024, + "afterCompileAndRunExitCode": 0, + "output": "g++ (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0\nCopyright (C) 2023 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions. There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\nIn file included from /probe/before.cpp:1:\n/probe/before.h:2052:24: error: static assertion failed: dom_node exceeded its cross-library 64-bit footprint budget\n 2052 | sizeof(void*) != 8 || sizeof(dom_node) <= 1024,\n | ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~\n1024\n", + "macOSVerification": { + "build": "passed", + "nativeEngineTests": "passed", + "graphicsV8RuntimeTests": "passed" + }, + "remainingCIProblem": "CandidateProfileContainsTheEstablishedDiscoveryDenominator rejects the documented inert-style harnessBlocked case" +} diff --git a/docs/graphics/evidence/kestrel/macos-first-webgpu.png b/docs/graphics/evidence/kestrel/macos-first-webgpu.png new file mode 100644 index 000000000..5fcfac615 Binary files /dev/null and b/docs/graphics/evidence/kestrel/macos-first-webgpu.png differ diff --git a/docs/graphics/evidence/kestrel/macos-isometric-webgpu.png b/docs/graphics/evidence/kestrel/macos-isometric-webgpu.png new file mode 100644 index 000000000..8810c50cd Binary files /dev/null and b/docs/graphics/evidence/kestrel/macos-isometric-webgpu.png differ diff --git a/docs/graphics/evidence/kestrel/macos-kestrel-grid-resize-fixed.png b/docs/graphics/evidence/kestrel/macos-kestrel-grid-resize-fixed.png new file mode 100644 index 000000000..3a18ed546 Binary files /dev/null and b/docs/graphics/evidence/kestrel/macos-kestrel-grid-resize-fixed.png differ diff --git a/docs/graphics/evidence/kestrel/macos-layer-list-border-fixed.png b/docs/graphics/evidence/kestrel/macos-layer-list-border-fixed.png new file mode 100644 index 000000000..52b8d7064 Binary files /dev/null and b/docs/graphics/evidence/kestrel/macos-layer-list-border-fixed.png differ diff --git a/docs/graphics/evidence/kestrel/macos-print-style-leak-fixed.png b/docs/graphics/evidence/kestrel/macos-print-style-leak-fixed.png new file mode 100644 index 000000000..4af51b198 Binary files /dev/null and b/docs/graphics/evidence/kestrel/macos-print-style-leak-fixed.png differ diff --git a/docs/graphics/evidence/kestrel/macos-resized-webgpu.png b/docs/graphics/evidence/kestrel/macos-resized-webgpu.png new file mode 100644 index 000000000..ada5a4caa Binary files /dev/null and b/docs/graphics/evidence/kestrel/macos-resized-webgpu.png differ diff --git a/docs/graphics/evidence/kestrel/mailbox-resize-verification.json b/docs/graphics/evidence/kestrel/mailbox-resize-verification.json new file mode 100644 index 000000000..f37d9ff4d --- /dev/null +++ b/docs/graphics/evidence/kestrel/mailbox-resize-verification.json @@ -0,0 +1,358 @@ +{ + "gpuFixture": { + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "detachedBeforeRetirement": false + }, + "resizeGeometryAssertionsPassed": true, + "resizeCheckpoints": [ + { + "window": [ + 980, + 680 + ], + "canvas": [ + 1610, + 750 + ], + "css": [ + 805, + 375 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 175, + 196, + 805, + 375 + ], + "height": "375px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 196, + 980, + 375 + ], + "height": "375px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1440, + 900 + ], + "canvas": [ + 1932, + 1126 + ], + "css": [ + 966, + 563 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 966, + 563 + ], + "height": "563px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1440, + 563 + ], + "height": "563px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1100, + 740 + ], + "canvas": [ + 1362, + 806 + ], + "css": [ + 681, + 403 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 195, + 205, + 681, + 403 + ], + "height": "403px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1100, + 403 + ], + "height": "403px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1280, + 800 + ], + "canvas": [ + 1612, + 926 + ], + "css": [ + 806, + 463 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 806, + 463 + ], + "height": "463px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1280, + 463 + ], + "height": "463px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } + ], + "repeatPanWorkloadValidated": true, + "repeatPanPublicationToDrawMedianMilliseconds": 34.059666, + "limitations": [ + "Repeat latency does not establish a speedup over the earlier baseline.", + "Resize assertions verify settled geometry, not visual smoothness.", + "Neither fixture nor pan trace certifies physical presentation." + ] +} diff --git a/docs/graphics/evidence/kestrel/managed-metal-producer-waits.json b/docs/graphics/evidence/kestrel/managed-metal-producer-waits.json new file mode 100644 index 000000000..45532e9ed --- /dev/null +++ b/docs/graphics/evidence/kestrel/managed-metal-producer-waits.json @@ -0,0 +1,39 @@ +{ + "physicalPresentationVerified": false, + "publicationToAcceptance": { + "count": 32, + "medianMilliseconds": 26.233604, + "p95Milliseconds": 33.912584, + "maximumMilliseconds": 34.374666 + }, + "acceptanceToDrawCallbackEnd": { + "count": 32, + "medianMilliseconds": 0.789792, + "p95Milliseconds": 1.279542, + "maximumMilliseconds": 2.492625 + }, + "publicationToDrawCallbackEnd": { + "count": 32, + "medianMilliseconds": 27.1083955, + "p95Milliseconds": 34.754084, + "maximumMilliseconds": 35.232083 + }, + "inputToPublishedConsumptionWatermark": { + "count": 80, + "medianMilliseconds": 16.128500000000003, + "p95Milliseconds": 34.445833, + "maximumMilliseconds": 44.239667 + }, + "unmatchedInputCount": 0, + "limitations": [ + "Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived." + ], + "scope": "Original Kestrel Metal pan with managed native-event borrowing and wait submission enabled; producer publication still completion-certified", + "borrowTests": { + "net8.0": "1 passed", + "net10.0": "1 passed" + }, + "earlyPublicationEnabled": false +} diff --git a/docs/graphics/evidence/kestrel/mesh-creation-blockers.json b/docs/graphics/evidence/kestrel/mesh-creation-blockers.json new file mode 100644 index 000000000..3b9da0031 --- /dev/null +++ b/docs/graphics/evidence/kestrel/mesh-creation-blockers.json @@ -0,0 +1,20 @@ +{ + "date": "2026-09-08", + "originalDocumentSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "command": "BOX", + "dimensions": { + "width": 2000, + "depth": 1500, + "height": 2500 + }, + "expectedObjects": 266, + "observed": { + "objects": 265, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 1, + "modalError": "Constructing FormData from a form is not yet supported", + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands.CommandBOXError$(...).showModal is not a function" + }, + "exitCode": 1, + "qualification": "Failed before geometry creation: HTMLDialogElement.showModal and FormData(form) are missing. This does not qualify or disprove GPU mesh rendering." +} diff --git a/docs/graphics/evidence/kestrel/metal-backend-texture-wrapper.json b/docs/graphics/evidence/kestrel/metal-backend-texture-wrapper.json new file mode 100644 index 000000000..2d0d7e224 --- /dev/null +++ b/docs/graphics/evidence/kestrel/metal-backend-texture-wrapper.json @@ -0,0 +1,9 @@ +{ + "host": "Avalonia.Native.MetalDevice", + "metalDeviceAvailable": true, + "metalQueueAvailable": true, + "skiaGpuContextAvailable": true, + "metalTextureWrapperVerified": true, + "producerInteropVerified": false, + "physicalPresentationVerified": false +} diff --git a/docs/graphics/evidence/kestrel/metal-consumer-retirement.json b/docs/graphics/evidence/kestrel/metal-consumer-retirement.json new file mode 100644 index 000000000..2d479c259 --- /dev/null +++ b/docs/graphics/evidence/kestrel/metal-consumer-retirement.json @@ -0,0 +1,18 @@ +{ + "host": "Avalonia.Native.MetalDevice", + "metalDeviceAvailable": true, + "metalQueueAvailable": true, + "skiaGpuContextAvailable": true, + "metalTextureWrapperVerified": true, + "dawnIOSurfaceImportVerified": true, + "metalSampledPixelsVerified": true, + "metalConsumerFenceVerified": true, + "diagnosticReadbacks": 1, + "producerInteropVerified": false, + "physicalPresentationVerified": false, + "delayedQueueCheckPassed": true, + "completionPollNonblocking": true, + "completedPollIdempotent": true, + "diagnosticPollingOnly": true, + "productionPresenterIntegrated": false +} diff --git a/docs/graphics/evidence/kestrel/metal-event-handoff.json b/docs/graphics/evidence/kestrel/metal-event-handoff.json new file mode 100644 index 000000000..d70628024 --- /dev/null +++ b/docs/graphics/evidence/kestrel/metal-event-handoff.json @@ -0,0 +1,16 @@ +{ + "test": "webscene_graphics_metal_event_handoff_tests", + "result": "passed", + "durationSeconds": 0.43, + "hardware": "local Metal device", + "checks": [ + "consumer submission returns while producer gated", + "consumer does not complete before shared event signal", + "cross-queue dependent read receives all 4096 expected bytes" + ], + "diagnosticCopy": true, + "productionPixelTransportChanged": false, + "dawnExportVerified": false, + "avaloniaQueueVerifiedByThisTest": false, + "physicalPresentationVerified": false +} diff --git a/docs/graphics/evidence/kestrel/metal-host-lease.json b/docs/graphics/evidence/kestrel/metal-host-lease.json new file mode 100644 index 000000000..6dadc3943 --- /dev/null +++ b/docs/graphics/evidence/kestrel/metal-host-lease.json @@ -0,0 +1,11 @@ +{ + "host": "Avalonia.Native.MetalDevice", + "metalDeviceAvailable": true, + "metalQueueAvailable": true, + "skiaGpuContextAvailable": true, + "producerInteropVerified": false, + "physicalPresentationVerified": false, + "command": "dotnet run --no-build --project experiments/WebScene.GpuHost.Probe -- --metal-host", + "exitCode": 0, + "scope": "Native-window active drawing lease, Avalonia 11.3.4; handles inspected while platform lease held, released before Skia draw" +} diff --git a/docs/graphics/evidence/kestrel/metal-multiple-dependencies.json b/docs/graphics/evidence/kestrel/metal-multiple-dependencies.json new file mode 100644 index 000000000..ac0d7a11e --- /dev/null +++ b/docs/graphics/evidence/kestrel/metal-multiple-dependencies.json @@ -0,0 +1,14 @@ +{ + "test": "webscene_graphics_metal_event_handoff_tests", + "result": "passed", + "durationSeconds": 0.39, + "checks": [ + "reject null dependency before encoding", + "submit waits without CPU completion wait", + "first producer completion insufficient while second event unsignaled", + "subsequent consumer command buffer reads expected data after both waits" + ], + "productionConsumerIntegrated": false, + "physicalPresentationVerified": false, + "initialFailure": "Overstrict event.device identity check rejected valid MTLSharedEvent; removed because shared events support cross-device synchronization" +} diff --git a/docs/graphics/evidence/kestrel/metal-retirement-owner-fix.json b/docs/graphics/evidence/kestrel/metal-retirement-owner-fix.json new file mode 100644 index 000000000..75f247c58 --- /dev/null +++ b/docs/graphics/evidence/kestrel/metal-retirement-owner-fix.json @@ -0,0 +1,23 @@ +{ + "change": "Initiate detached retirement on composition Stop owner; Metal background polls do not flush Skia", + "runs": [ + { + "name": "owner", + "exitCode": 0, + "geometryChecks": 4, + "startupPassed": true + }, + { + "name": "owner-repeat", + "exitCode": 0, + "geometryChecks": 4, + "startupPassed": true + } + ], + "testSummaries": [ + "Passed! - Failed: 0, Passed: 7, Skipped: 0, Total: 7, Duration: 144 ms - WebScene.Backend.Avalonia.Tests.dll (net8.0)", + "Passed! - Failed: 0, Passed: 7, Skipped: 0, Total: 7, Duration: 110 ms - WebScene.Backend.Avalonia.Tests.dll (net10.0)" + ], + "physicalPresentationVerified": false, + "continuousResizeQualified": false +} diff --git a/docs/graphics/evidence/kestrel/native-command-edits.json b/docs/graphics/evidence/kestrel/native-command-edits.json new file mode 100644 index 000000000..7812b2417 --- /dev/null +++ b/docs/graphics/evidence/kestrel/native-command-edits.json @@ -0,0 +1,40 @@ +{ + "date": "2026-09-08", + "originalDocumentSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "input": "Focus command input via DOM; NativeSceneSurface.SubmitText followed by native keydown/keyup Enter.", + "commands": [ + "LINE 0,0 1000,1000 ENTER", + "UNDO", + "REDO", + "UNDO" + ], + "baselineObjects": 265, + "steps": [ + { + "objects": 266, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0, + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands.CommandLINE 0,0 1000,1000 ENTERLINESpecify first pointLineCreated Line." + }, + { + "objects": 265, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0, + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands.CommandLINE 0,0 1000,1000 ENTERLINESpecify first pointLineCreated Line.CommandUNDOUndoLine" + }, + { + "objects": 266, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0, + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands.CommandLINE 0,0 1000,1000 ENTERLINESpecify first pointLineCreated Line.CommandUNDOUndoLineCommandREDORedoLine" + }, + { + "objects": 265, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0, + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands.CommandLINE 0,0 1000,1000 ENTERLINESpecify first pointLineCreated Line.CommandUNDOUndoLineCommandREDORedoLineCommandUNDOUndoLine" + } + ], + "exitCode": 0, + "qualification": "Native host-input document editing smoke test. OS hardware events, pixel comparison after editing, export and other tools remain unqualified." +} diff --git a/docs/graphics/evidence/kestrel/native-initial-style-diagnostics.json b/docs/graphics/evidence/kestrel/native-initial-style-diagnostics.json new file mode 100644 index 000000000..7d0473261 --- /dev/null +++ b/docs/graphics/evidence/kestrel/native-initial-style-diagnostics.json @@ -0,0 +1,69 @@ +{ + "phase": "initial startup, before interactions", + "observations": { + "theme": "dark", + "nodes": [ + { + "id": "explorer-list", + "background": "rgba(0, 0, 0, 0)", + "color": "rgb(220, 228, 237)", + "display": "block", + "rect": [ + 0, + 348, + 221, + 360 + ] + }, + { + "id": "viewport", + "background": "rgb(18, 28, 41)", + "color": "rgb(220, 228, 237)", + "display": "block", + "rect": [ + 222, + 205, + 556, + 614 + ] + }, + { + "id": "scene", + "background": "rgba(0, 0, 0, 0)", + "color": "rgb(220, 228, 237)", + "display": "block", + "rect": [ + 222, + 205, + 556, + 614 + ] + }, + { + "id": "layers-tab", + "background": "rgba(0, 0, 0, 0)", + "color": "rgb(220, 228, 237)", + "display": "flex", + "rect": [ + -1, + 253, + 53.50365447998047, + 34 + ] + }, + { + "id": "objects-tab", + "background": "rgba(0, 0, 0, 0)", + "color": "rgb(139, 154, 171)", + "display": "flex", + "rect": [ + 67.50365447998047, + 253, + 71.0402603149414, + 34 + ] + } + ] + }, + "limitation": "Computed styles do not certify painted pixels or interaction states." +} diff --git a/docs/graphics/evidence/kestrel/native-pan-callback-timing.json b/docs/graphics/evidence/kestrel/native-pan-callback-timing.json new file mode 100644 index 000000000..c7b4f8c2e --- /dev/null +++ b/docs/graphics/evidence/kestrel/native-pan-callback-timing.json @@ -0,0 +1,1342 @@ +{ + "originalSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "method": "Wrap requestAnimationFrame during native right-button probe, preserving request IDs/callback receiver and restoring the original function during cleanup. Measure callback wall time with performance.now; no changes to original HTML.", + "callbackTiming": { + "samples": 31, + "medianMilliseconds": 0.8262919932603836, + "p95Milliseconds": 5.304583013057709, + "maximumMilliseconds": 5.978791996836662, + "totalMilliseconds": 35.98129095137119 + }, + "performance": { + "elapsedMilliseconds": 1855.7641, + "baseline": { + "ContextId": 1, + "Timestamp": 89905035815208, + "Engine": { + "EnqueuedInputs": 168, + "DroppedInputs": 0, + "ConsumedInputs": 168, + "PublishedScenes": 41, + "AcquiredScenes": 41, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1372, + "LayoutPasses": 64, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 4924293, + "InputEventsDispatched": 204, + "InputCallbacksInvoked": 36, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 758208, + "LastScenePublicationNanoseconds": 1511333, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 89, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 40, + "AppliedWheelInputs": 1, + "AppliedAnimationFrames": 36, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 1028583, + "LastSceneBuildNanoseconds": 356958, + "MaximumScenePublicationNanoseconds": 4704708 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 374000, + "MaximumDispatchNanoseconds": 55118208, + "LastDispatchSequence": 639244559237902742, + "DispatchedInputs": 42, + "TotalDispatchNanoseconds": 138530791 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 36, + "TotalDispatchNanoseconds": 47167, + "LastDispatchNanoseconds": 2125, + "MaximumDispatchNanoseconds": 4500, + "LastTimestampMicroseconds": 89904818682 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 69, + "BlockedPublications": 11, + "AcknowledgedScenes": 41, + "TotalAcknowledgementNanoseconds": 2638451169, + "LastAcknowledgementNanoseconds": 35345792, + "MaximumAcknowledgementNanoseconds": 300953667, + "AcknowledgedRevision": 41 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3335404, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1880064, + "LatestSceneBytes": 158460, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 910828, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1919184, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 190720, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127200, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 41, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 89906893451666, + "Engine": { + "EnqueuedInputs": 293, + "DroppedInputs": 0, + "ConsumedInputs": 293, + "PublishedScenes": 85, + "AcquiredScenes": 85, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1456, + "LayoutPasses": 148, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 4924293, + "InputEventsDispatched": 317, + "InputCallbacksInvoked": 146, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 758208, + "LastScenePublicationNanoseconds": 482250, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 117, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 92, + "AppliedWheelInputs": 2, + "AppliedAnimationFrames": 78, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 562416, + "LastSceneBuildNanoseconds": 359208, + "MaximumScenePublicationNanoseconds": 4704708 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 3013958, + "MaximumDispatchNanoseconds": 55118208, + "LastDispatchSequence": 639244559237902825, + "DispatchedInputs": 97, + "TotalDispatchNanoseconds": 356862749 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 78, + "TotalDispatchNanoseconds": 98876, + "LastDispatchNanoseconds": 625, + "MaximumDispatchNanoseconds": 4584, + "LastTimestampMicroseconds": 89906397096 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 156, + "BlockedPublications": 12, + "AcknowledgedScenes": 85, + "TotalAcknowledgementNanoseconds": 4746675709, + "LastAcknowledgementNanoseconds": 42354791, + "MaximumAcknowledgementNanoseconds": 300953667, + "AcknowledgedRevision": 85 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 30, + "AnimationFramesInvoked": 30, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 143, + "WorkerWaits": 209, + "WorkerSignalledWakes": 172, + "WorkerTimeoutWakes": 36, + "SceneBuilds": 44, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3335404, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1880064, + "LatestSceneBytes": 163868, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 910828, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1919184, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 190720, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127200, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 327, + "LogicalBitmapBytes": 5970848, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 3361, + "StringBytes": 175822, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 85, + "RoutedInputEvents": 1, + "AcceptedInputEvents": 1, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 74, + "Renders": 44, + "AppliedDiffs": 44, + "InvalidationCalls": 49, + "DamageRectangles": 101, + "ChangedLayers": 29, + "EmptyDamageDiffs": 1, + "PartialDamageDiffs": 43, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 42, + "SkippedEmptyAnimationFrames": 32, + "RenderCallbacks": 49, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.8576364", + "EnqueuedInputs": 125, + "DroppedInputs": 0, + "ConsumedInputs": 125, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 84, + "AppliedAnimationFrames": 42, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 87, + "BlockedPublications": 1, + "PublishedScenes": 44, + "AcquiredScenes": 44, + "AcknowledgedScenes": 44, + "RenderedScenes": 44, + "CompositionUiWakes": 0, + "RoutedInputEvents": 1, + "AcceptedInputEvents": 1, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 30, + "AnimationFramesInvoked": 30, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 143, + "WorkerWaits": 209, + "WorkerSignalledWakes": 172, + "WorkerTimeoutWakes": 36, + "SceneBuilds": 44, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 74, + "CompositionRenders": 44, + "CompositionAppliedDiffs": 44, + "CompositionInvalidations": 49, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 42, + "CompositionSkippedEmptyAnimationFrames": 32, + "CompositionRenderCallbacks": 49, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "diagnostics": { + "events": [ + { + "type": "pointerdown", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 89905042.9935, + "panning": false + }, + { + "type": "pointermove", + "x": 629, + "y": 437.5, + "button": 2, + "buttons": 2, + "time": 89905052.654083, + "panning": true + }, + { + "type": "pointermove", + "x": 633, + "y": 438.5, + "button": 2, + "buttons": 2, + "time": 89905061.462833, + "panning": true + }, + { + "type": "pointermove", + "x": 641, + "y": 440.5, + "button": 2, + "buttons": 2, + "time": 89905107.089666, + "panning": true + }, + { + "type": "pointermove", + "x": 645, + "y": 441.5, + "button": 2, + "buttons": 2, + "time": 89905112.558625, + "panning": true + }, + { + "type": "pointermove", + "x": 653, + "y": 443.5, + "button": 2, + "buttons": 2, + "time": 89905156.552416, + "panning": true + }, + { + "type": "pointermove", + "x": 657, + "y": 444.5, + "button": 2, + "buttons": 2, + "time": 89905160.638583, + "panning": true + }, + { + "type": "pointermove", + "x": 665, + "y": 446.5, + "button": 2, + "buttons": 2, + "time": 89905210.670083, + "panning": true + }, + { + "type": "pointermove", + "x": 669, + "y": 447.5, + "button": 2, + "buttons": 2, + "time": 89905214.686166, + "panning": true + }, + { + "type": "pointermove", + "x": 673, + "y": 448.5, + "button": 2, + "buttons": 2, + "time": 89905244.39625, + "panning": true + }, + { + "type": "pointermove", + "x": 677, + "y": 449.5, + "button": 2, + "buttons": 2, + "time": 89905249.217125, + "panning": true + }, + { + "type": "pointermove", + "x": 681, + "y": 450.5, + "button": 2, + "buttons": 2, + "time": 89905271.510916, + "panning": true + }, + { + "type": "pointermove", + "x": 689, + "y": 452.5, + "button": 2, + "buttons": 2, + "time": 89905311.21025, + "panning": true + }, + { + "type": "pointermove", + "x": 693, + "y": 453.5, + "button": 2, + "buttons": 2, + "time": 89905314.60675, + "panning": true + }, + { + "type": "pointermove", + "x": 697, + "y": 454.5, + "button": 2, + "buttons": 2, + "time": 89905347.866958, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 89905351.289541, + "panning": true + }, + { + "type": "pointermove", + "x": 705, + "y": 456.5, + "button": 2, + "buttons": 2, + "time": 89905365.786083, + "panning": true + }, + { + "type": "pointermove", + "x": 713, + "y": 458.5, + "button": 2, + "buttons": 2, + "time": 89905401.42375, + "panning": true + }, + { + "type": "pointermove", + "x": 725, + "y": 461.5, + "button": 2, + "buttons": 2, + "time": 89905448.944708, + "panning": true + }, + { + "type": "pointermove", + "x": 729, + "y": 462.5, + "button": 2, + "buttons": 2, + "time": 89905470.0915, + "panning": true + }, + { + "type": "pointermove", + "x": 737, + "y": 464.5, + "button": 2, + "buttons": 2, + "time": 89905504.231125, + "panning": true + }, + { + "type": "pointermove", + "x": 745, + "y": 466.5, + "button": 2, + "buttons": 2, + "time": 89905537.753041, + "panning": true + }, + { + "type": "pointermove", + "x": 749, + "y": 467.5, + "button": 2, + "buttons": 2, + "time": 89905559.045708, + "panning": true + }, + { + "type": "pointermove", + "x": 757, + "y": 469.5, + "button": 2, + "buttons": 2, + "time": 89905595.438083, + "panning": true + }, + { + "type": "pointermove", + "x": 765, + "y": 471.5, + "button": 2, + "buttons": 2, + "time": 89905629.351041, + "panning": true + }, + { + "type": "pointermove", + "x": 769, + "y": 472.5, + "button": 2, + "buttons": 2, + "time": 89905650.625375, + "panning": true + }, + { + "type": "pointermove", + "x": 773, + "y": 473.5, + "button": 2, + "buttons": 2, + "time": 89905654.358125, + "panning": true + }, + { + "type": "pointermove", + "x": 781, + "y": 475.5, + "button": 2, + "buttons": 2, + "time": 89905687.936833, + "panning": true + }, + { + "type": "pointermove", + "x": 781, + "y": 475.5, + "button": 2, + "buttons": 2, + "time": 89905727.149375, + "panning": true + }, + { + "type": "pointermove", + "x": 777, + "y": 474.5, + "button": 2, + "buttons": 2, + "time": 89905745.231083, + "panning": true + }, + { + "type": "pointermove", + "x": 769, + "y": 472.5, + "button": 2, + "buttons": 2, + "time": 89905785.280291, + "panning": true + }, + { + "type": "pointermove", + "x": 765, + "y": 471.5, + "button": 2, + "buttons": 2, + "time": 89905789.391083, + "panning": true + }, + { + "type": "pointermove", + "x": 761, + "y": 470.5, + "button": 2, + "buttons": 2, + "time": 89905814.761958, + "panning": true + }, + { + "type": "pointermove", + "x": 757, + "y": 469.5, + "button": 2, + "buttons": 2, + "time": 89905831.934958, + "panning": true + }, + { + "type": "pointermove", + "x": 749, + "y": 467.5, + "button": 2, + "buttons": 2, + "time": 89905869.449875, + "panning": true + }, + { + "type": "pointermove", + "x": 745, + "y": 466.5, + "button": 2, + "buttons": 2, + "time": 89905872.757541, + "panning": true + }, + { + "type": "pointermove", + "x": 737, + "y": 464.5, + "button": 2, + "buttons": 2, + "time": 89905905.393041, + "panning": true + }, + { + "type": "pointermove", + "x": 733, + "y": 463.5, + "button": 2, + "buttons": 2, + "time": 89905925.613166, + "panning": true + }, + { + "type": "pointermove", + "x": 721, + "y": 460.5, + "button": 2, + "buttons": 2, + "time": 89905982.083541, + "panning": true + }, + { + "type": "pointermove", + "x": 713, + "y": 458.5, + "button": 2, + "buttons": 2, + "time": 89906017.277708, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 89906040.010458, + "panning": true + }, + { + "type": "pointermove", + "x": 705, + "y": 456.5, + "button": 2, + "buttons": 2, + "time": 89906043.014458, + "panning": true + }, + { + "type": "pointermove", + "x": 697, + "y": 454.5, + "button": 2, + "buttons": 2, + "time": 89906076.1705, + "panning": true + }, + { + "type": "pointermove", + "x": 689, + "y": 452.5, + "button": 2, + "buttons": 2, + "time": 89906109.978041, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 89906129.775916, + "panning": true + }, + { + "type": "pointermove", + "x": 677, + "y": 449.5, + "button": 2, + "buttons": 2, + "time": 89906170.859208, + "panning": true + }, + { + "type": "pointermove", + "x": 665, + "y": 446.5, + "button": 2, + "buttons": 2, + "time": 89906212.441833, + "panning": true + }, + { + "type": "pointermove", + "x": 661, + "y": 445.5, + "button": 2, + "buttons": 2, + "time": 89906231.6105, + "panning": true + }, + { + "type": "pointermove", + "x": 653, + "y": 443.5, + "button": 2, + "buttons": 2, + "time": 89906267.271333, + "panning": true + }, + { + "type": "pointermove", + "x": 645, + "y": 441.5, + "button": 2, + "buttons": 2, + "time": 89906301.146291, + "panning": true + }, + { + "type": "pointermove", + "x": 641, + "y": 440.5, + "button": 2, + "buttons": 2, + "time": 89906317.513958, + "panning": true + }, + { + "type": "pointermove", + "x": 633, + "y": 438.5, + "button": 2, + "buttons": 2, + "time": 89906349.265208, + "panning": true + }, + { + "type": "pointermove", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 89906382.692041, + "panning": true + }, + { + "type": "pointerup", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 0, + "time": 89906393.388583, + "panning": false + } + ], + "captures": [], + "frames": [ + { + "timestamp": 89904818.68275, + "start": 89904818.819541, + "duration": 5.978791996836662 + }, + { + "timestamp": 89905051.974541, + "start": 89905062.144541, + "duration": 0.8075840026140213 + }, + { + "timestamp": 89905103.944791, + "start": 89905113.253875, + "duration": 0.7653329968452454 + }, + { + "timestamp": 89905153.66275, + "start": 89905161.295458, + "duration": 0.6672499924898148 + }, + { + "timestamp": 89905218.65825, + "start": 89905218.734333, + "duration": 5.304583013057709 + }, + { + "timestamp": 89905240.10808301, + "start": 89905250.230708, + "duration": 0.8892499953508377 + }, + { + "timestamp": 89905268.64612499, + "start": 89905272.8835, + "duration": 1.170625001192093 + }, + { + "timestamp": 89905344.092166, + "start": 89905352.193791, + "duration": 0.692124992609024 + }, + { + "timestamp": 89905362.584791, + "start": 89905366.50675, + "duration": 1.0562909990549088 + }, + { + "timestamp": 89905445.650833, + "start": 89905450.6805, + "duration": 0.9740830063819885 + }, + { + "timestamp": 89905467.297208, + "start": 89905471.350583, + "duration": 0.8425419926643372 + }, + { + "timestamp": 89905534.798916, + "start": 89905538.505541, + "duration": 0.7145000100135803 + }, + { + "timestamp": 89905556.16783299, + "start": 89905559.992208, + "duration": 0.5463749915361404 + }, + { + "timestamp": 89905626.58108301, + "start": 89905630.15325, + "duration": 0.5642910003662109 + }, + { + "timestamp": 89905645.43825, + "start": 89905655.124166, + "duration": 0.982792004942894 + }, + { + "timestamp": 89905724.484541, + "start": 89905728.456583, + "duration": 0.6894580125808716 + }, + { + "timestamp": 89905742.607916, + "start": 89905746.440208, + "duration": 0.5582499951124191 + }, + { + "timestamp": 89905812.492375, + "start": 89905815.489291, + "duration": 1.0652920007705688 + }, + { + "timestamp": 89905829.739791, + "start": 89905832.835125, + "duration": 1.6031659990549088 + }, + { + "timestamp": 89905902.16354099, + "start": 89905906.222416, + "duration": 1.152208998799324 + }, + { + "timestamp": 89905922.17229101, + "start": 89905926.480166, + "duration": 0.6157499998807907 + }, + { + "timestamp": 89906014.02533299, + "start": 89906018.217166, + "duration": 1.1793749928474426 + }, + { + "timestamp": 89906036.646208, + "start": 89906043.5935, + "duration": 0.6894159913063049 + }, + { + "timestamp": 89906107.283, + "start": 89906110.654833, + "duration": 0.8262919932603836 + }, + { + "timestamp": 89906127.028, + "start": 89906130.509375, + "duration": 0.5888329893350601 + }, + { + "timestamp": 89906209.793208, + "start": 89906213.133208, + "duration": 1.1744999885559082 + }, + { + "timestamp": 89906229.17608301, + "start": 89906232.438291, + "duration": 1.1855420023202896 + }, + { + "timestamp": 89906297.63125, + "start": 89906302.614208, + "duration": 0.6832920014858246 + }, + { + "timestamp": 89906314.45787501, + "start": 89906318.243166, + "duration": 0.583749994635582 + }, + { + "timestamp": 89906380.36679101, + "start": 89906383.514583, + "duration": 0.9305000007152557 + }, + { + "timestamp": 89906397.0965, + "start": 89906397.113583, + "duration": 0.49924999475479126 + } + ], + "panning": false, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + "nativeTests": { + "engineSeconds": 12.89, + "gpuRuntimeSeconds": 1.87, + "passed": true + }, + "limitations": [ + "Callbacks measured include setup/settle; 31 wrapper samples versus 30 callbacks within native counter interval.", + "Timing includes instrumentation overhead and callback work, not GPU execution or physical presentation.", + "One short run; no browser timing baseline or statistical performance qualification.", + "Existing interactive demo remained open; this is investigative evidence, not an isolated benchmark." + ] +} diff --git a/docs/graphics/evidence/kestrel/native-right-button-pan.json b/docs/graphics/evidence/kestrel/native-right-button-pan.json new file mode 100644 index 000000000..d1c050894 --- /dev/null +++ b/docs/graphics/evidence/kestrel/native-right-button-pan.json @@ -0,0 +1,1388 @@ +{ + "originalSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "method": "Native host queue: right button down, 80 moves at requested 16ms intervals (160px out and back), right button up, 500ms settle; unchanged Kestrel. Not OS input injection.", + "summary": { + "moveEvents": 74, + "coalescedMoveInputs": 6, + "allMovesInApplicationPanState": true, + "endedPanState": false, + "pointerCaptureEvents": [], + "dispatchIntervalMilliseconds": { + "median": 20.52845799922943, + "maximum": 37.04091700911522 + }, + "applicationAnimationCallbacks": 43, + "renderedScenes": 66, + "layoutPasses": 119, + "blockedPublications": 9, + "droppedInputs": 0, + "errors": 0 + }, + "limitations": [ + "No browser timing baseline or physical frame capture.", + "Pointer capture events were absent; capture event conformance is not passed.", + "Dispatch intervals include probe scheduling and input coalescing; they are not GPU timings.", + "No claim of fixed flicker, smoothness, or complete epic qualification." + ], + "performance": { + "elapsedMilliseconds": 1859.9141, + "baseline": { + "ContextId": 1, + "Timestamp": 88390155275125, + "Engine": { + "EnqueuedInputs": 16, + "DroppedInputs": 0, + "ConsumedInputs": 16, + "PublishedScenes": 11, + "AcquiredScenes": 9, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3266294, + "InputEventsDispatched": 35, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 556000, + "BusiestCanvasHeightMilli": 614000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 42750, + "LastScenePublicationNanoseconds": 641750, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 4, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 5, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 5, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 1667417, + "LastSceneBuildNanoseconds": 490667, + "MaximumScenePublicationNanoseconds": 2690667 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 385375, + "MaximumDispatchNanoseconds": 510417, + "LastDispatchSequence": 639244544092045191, + "DispatchedInputs": 6, + "TotalDispatchNanoseconds": 1789292 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 5, + "TotalDispatchNanoseconds": 4124, + "LastDispatchNanoseconds": 958, + "MaximumDispatchNanoseconds": 1000, + "LastTimestampMicroseconds": 88388836753 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 16, + "BlockedPublications": 5, + "AcknowledgedScenes": 9, + "TotalAcknowledgementNanoseconds": 532799999, + "LastAcknowledgementNanoseconds": 26746959, + "MaximumAcknowledgementNanoseconds": 180795833, + "AcknowledgedRevision": 11 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3070588, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1323008, + "LatestSceneBytes": 127052, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 635676, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920380, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196288, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 130772, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 9, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 88392017421791, + "Engine": { + "EnqueuedInputs": 162, + "DroppedInputs": 0, + "ConsumedInputs": 162, + "PublishedScenes": 77, + "AcquiredScenes": 75, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1435, + "LayoutPasses": 127, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3266294, + "InputEventsDispatched": 205, + "InputCallbacksInvoked": 154, + "BusiestCanvasWidthMilli": 556000, + "BusiestCanvasHeightMilli": 614000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 42750, + "LastScenePublicationNanoseconds": 657709, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 10, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 79, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 69, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 647583, + "LastSceneBuildNanoseconds": 519750, + "MaximumScenePublicationNanoseconds": 2690667 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 4238208, + "MaximumDispatchNanoseconds": 8231208, + "LastDispatchSequence": 639244544092045273, + "DispatchedInputs": 82, + "TotalDispatchNanoseconds": 305297833 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 69, + "TotalDispatchNanoseconds": 82037, + "LastDispatchNanoseconds": 1250, + "MaximumDispatchNanoseconds": 4000, + "LastTimestampMicroseconds": 88391536199 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 141, + "BlockedPublications": 14, + "AcknowledgedScenes": 75, + "TotalAcknowledgementNanoseconds": 2516951086, + "LastAcknowledgementNanoseconds": 22174416, + "MaximumAcknowledgementNanoseconds": 180795833, + "AcknowledgedRevision": 77 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 43, + "AnimationFramesInvoked": 43, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 213, + "WorkerWaits": 242, + "WorkerSignalledWakes": 213, + "WorkerTimeoutWakes": 28, + "SceneBuilds": 66, + "NoDamageSceneBuilds": 6, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3070588, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1323008, + "LatestSceneBytes": 133240, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 635676, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920380, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196288, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 130772, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5462144, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 480, + "StringCount": 2267, + "StringBytes": 132204, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 75, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 94, + "Renders": 66, + "AppliedDiffs": 66, + "InvalidationCalls": 70, + "DamageRectangles": 142, + "ChangedLayers": 41, + "EmptyDamageDiffs": 6, + "PartialDamageDiffs": 60, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 64, + "SkippedEmptyAnimationFrames": 30, + "RenderCallbacks": 70, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.8621466", + "EnqueuedInputs": 146, + "DroppedInputs": 0, + "ConsumedInputs": 146, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 119, + "AppliedAnimationFrames": 64, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 125, + "BlockedPublications": 9, + "PublishedScenes": 66, + "AcquiredScenes": 66, + "AcknowledgedScenes": 66, + "RenderedScenes": 66, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 43, + "AnimationFramesInvoked": 43, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 213, + "WorkerWaits": 242, + "WorkerSignalledWakes": 213, + "WorkerTimeoutWakes": 28, + "SceneBuilds": 66, + "NoDamageSceneBuilds": 6, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 94, + "CompositionRenders": 66, + "CompositionAppliedDiffs": 66, + "CompositionInvalidations": 70, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 64, + "CompositionSkippedEmptyAnimationFrames": 30, + "CompositionRenderCallbacks": 70, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "diagnostics": { + "events": [ + { + "type": "pointerdown", + "x": 500, + "y": 512, + "button": 2, + "buttons": 2, + "time": 88390165.691416, + "panning": false + }, + { + "type": "pointermove", + "x": 504, + "y": 513, + "button": 2, + "buttons": 2, + "time": 88390170.984958, + "panning": true + }, + { + "type": "pointermove", + "x": 508, + "y": 514, + "button": 2, + "buttons": 2, + "time": 88390191.531041, + "panning": true + }, + { + "type": "pointermove", + "x": 512, + "y": 515, + "button": 2, + "buttons": 2, + "time": 88390196.399208, + "panning": true + }, + { + "type": "pointermove", + "x": 516, + "y": 516, + "button": 2, + "buttons": 2, + "time": 88390215.831333, + "panning": true + }, + { + "type": "pointermove", + "x": 520, + "y": 517, + "button": 2, + "buttons": 2, + "time": 88390238.312291, + "panning": true + }, + { + "type": "pointermove", + "x": 524, + "y": 518, + "button": 2, + "buttons": 2, + "time": 88390260.879333, + "panning": true + }, + { + "type": "pointermove", + "x": 528, + "y": 519, + "button": 2, + "buttons": 2, + "time": 88390263.596083, + "panning": true + }, + { + "type": "pointermove", + "x": 532, + "y": 520, + "button": 2, + "buttons": 2, + "time": 88390283.165291, + "panning": true + }, + { + "type": "pointermove", + "x": 536, + "y": 521, + "button": 2, + "buttons": 2, + "time": 88390305.860041, + "panning": true + }, + { + "type": "pointermove", + "x": 540, + "y": 522, + "button": 2, + "buttons": 2, + "time": 88390326.591291, + "panning": true + }, + { + "type": "pointermove", + "x": 544, + "y": 523, + "button": 2, + "buttons": 2, + "time": 88390329.478, + "panning": true + }, + { + "type": "pointermove", + "x": 548, + "y": 524, + "button": 2, + "buttons": 2, + "time": 88390350.532333, + "panning": true + }, + { + "type": "pointermove", + "x": 552, + "y": 525, + "button": 2, + "buttons": 2, + "time": 88390373.868875, + "panning": true + }, + { + "type": "pointermove", + "x": 556, + "y": 526, + "button": 2, + "buttons": 2, + "time": 88390394.678125, + "panning": true + }, + { + "type": "pointermove", + "x": 560, + "y": 527, + "button": 2, + "buttons": 2, + "time": 88390397.661041, + "panning": true + }, + { + "type": "pointermove", + "x": 564, + "y": 528, + "button": 2, + "buttons": 2, + "time": 88390420.731291, + "panning": true + }, + { + "type": "pointermove", + "x": 568, + "y": 529, + "button": 2, + "buttons": 2, + "time": 88390443.504916, + "panning": true + }, + { + "type": "pointermove", + "x": 572, + "y": 530, + "button": 2, + "buttons": 2, + "time": 88390464.050333, + "panning": true + }, + { + "type": "pointermove", + "x": 576, + "y": 531, + "button": 2, + "buttons": 2, + "time": 88390467.546625, + "panning": true + }, + { + "type": "pointermove", + "x": 580, + "y": 532, + "button": 2, + "buttons": 2, + "time": 88390487.061625, + "panning": true + }, + { + "type": "pointermove", + "x": 584, + "y": 533, + "button": 2, + "buttons": 2, + "time": 88390516.079333, + "panning": true + }, + { + "type": "pointermove", + "x": 588, + "y": 534, + "button": 2, + "buttons": 2, + "time": 88390521.024333, + "panning": true + }, + { + "type": "pointermove", + "x": 592, + "y": 535, + "button": 2, + "buttons": 2, + "time": 88390541.563083, + "panning": true + }, + { + "type": "pointermove", + "x": 596, + "y": 536, + "button": 2, + "buttons": 2, + "time": 88390564.9, + "panning": true + }, + { + "type": "pointermove", + "x": 604, + "y": 538, + "button": 2, + "buttons": 2, + "time": 88390586.649375, + "panning": true + }, + { + "type": "pointermove", + "x": 608, + "y": 539, + "button": 2, + "buttons": 2, + "time": 88390608.557083, + "panning": true + }, + { + "type": "pointermove", + "x": 612, + "y": 540, + "button": 2, + "buttons": 2, + "time": 88390631.185333, + "panning": true + }, + { + "type": "pointermove", + "x": 616, + "y": 541, + "button": 2, + "buttons": 2, + "time": 88390651.368583, + "panning": true + }, + { + "type": "pointermove", + "x": 620, + "y": 542, + "button": 2, + "buttons": 2, + "time": 88390655.176625, + "panning": true + }, + { + "type": "pointermove", + "x": 624, + "y": 543, + "button": 2, + "buttons": 2, + "time": 88390670.126041, + "panning": true + }, + { + "type": "pointermove", + "x": 628, + "y": 544, + "button": 2, + "buttons": 2, + "time": 88390696.701625, + "panning": true + }, + { + "type": "pointermove", + "x": 632, + "y": 545, + "button": 2, + "buttons": 2, + "time": 88390708.103, + "panning": true + }, + { + "type": "pointermove", + "x": 636, + "y": 546, + "button": 2, + "buttons": 2, + "time": 88390722.1395, + "panning": true + }, + { + "type": "pointermove", + "x": 640, + "y": 547, + "button": 2, + "buttons": 2, + "time": 88390742.823958, + "panning": true + }, + { + "type": "pointermove", + "x": 644, + "y": 548, + "button": 2, + "buttons": 2, + "time": 88390762.562708, + "panning": true + }, + { + "type": "pointermove", + "x": 648, + "y": 549, + "button": 2, + "buttons": 2, + "time": 88390785.568833, + "panning": true + }, + { + "type": "pointermove", + "x": 652, + "y": 550, + "button": 2, + "buttons": 2, + "time": 88390805.811625, + "panning": true + }, + { + "type": "pointermove", + "x": 656, + "y": 551, + "button": 2, + "buttons": 2, + "time": 88390809.059583, + "panning": true + }, + { + "type": "pointermove", + "x": 656, + "y": 551, + "button": 2, + "buttons": 2, + "time": 88390846.1005, + "panning": true + }, + { + "type": "pointermove", + "x": 652, + "y": 550, + "button": 2, + "buttons": 2, + "time": 88390872.711541, + "panning": true + }, + { + "type": "pointermove", + "x": 648, + "y": 549, + "button": 2, + "buttons": 2, + "time": 88390876.728291, + "panning": true + }, + { + "type": "pointermove", + "x": 644, + "y": 548, + "button": 2, + "buttons": 2, + "time": 88390894.738333, + "panning": true + }, + { + "type": "pointermove", + "x": 640, + "y": 547, + "button": 2, + "buttons": 2, + "time": 88390915.266791, + "panning": true + }, + { + "type": "pointermove", + "x": 636, + "y": 546, + "button": 2, + "buttons": 2, + "time": 88390932.036125, + "panning": true + }, + { + "type": "pointermove", + "x": 632, + "y": 545, + "button": 2, + "buttons": 2, + "time": 88390953.044458, + "panning": true + }, + { + "type": "pointermove", + "x": 628, + "y": 544, + "button": 2, + "buttons": 2, + "time": 88390976.576333, + "panning": true + }, + { + "type": "pointermove", + "x": 624, + "y": 543, + "button": 2, + "buttons": 2, + "time": 88390979.204458, + "panning": true + }, + { + "type": "pointermove", + "x": 620, + "y": 542, + "button": 2, + "buttons": 2, + "time": 88390996.143, + "panning": true + }, + { + "type": "pointermove", + "x": 616, + "y": 541, + "button": 2, + "buttons": 2, + "time": 88391016.15575, + "panning": true + }, + { + "type": "pointermove", + "x": 612, + "y": 540, + "button": 2, + "buttons": 2, + "time": 88391036.273541, + "panning": true + }, + { + "type": "pointermove", + "x": 608, + "y": 539, + "button": 2, + "buttons": 2, + "time": 88391056.203708, + "panning": true + }, + { + "type": "pointermove", + "x": 604, + "y": 538, + "button": 2, + "buttons": 2, + "time": 88391077.4955, + "panning": true + }, + { + "type": "pointermove", + "x": 600, + "y": 537, + "button": 2, + "buttons": 2, + "time": 88391081.815583, + "panning": true + }, + { + "type": "pointermove", + "x": 596, + "y": 536, + "button": 2, + "buttons": 2, + "time": 88391099.227125, + "panning": true + }, + { + "type": "pointermove", + "x": 592, + "y": 535, + "button": 2, + "buttons": 2, + "time": 88391115.867208, + "panning": true + }, + { + "type": "pointermove", + "x": 588, + "y": 534, + "button": 2, + "buttons": 2, + "time": 88391137.203583, + "panning": true + }, + { + "type": "pointermove", + "x": 584, + "y": 533, + "button": 2, + "buttons": 2, + "time": 88391157.495125, + "panning": true + }, + { + "type": "pointermove", + "x": 580, + "y": 532, + "button": 2, + "buttons": 2, + "time": 88391174.764125, + "panning": true + }, + { + "type": "pointermove", + "x": 572, + "y": 530, + "button": 2, + "buttons": 2, + "time": 88391200.468958, + "panning": true + }, + { + "type": "pointermove", + "x": 568, + "y": 529, + "button": 2, + "buttons": 2, + "time": 88391220.738625, + "panning": true + }, + { + "type": "pointermove", + "x": 564, + "y": 528, + "button": 2, + "buttons": 2, + "time": 88391238.214833, + "panning": true + }, + { + "type": "pointermove", + "x": 560, + "y": 527, + "button": 2, + "buttons": 2, + "time": 88391260.452041, + "panning": true + }, + { + "type": "pointermove", + "x": 552, + "y": 525, + "button": 2, + "buttons": 2, + "time": 88391282.634041, + "panning": true + }, + { + "type": "pointermove", + "x": 548, + "y": 524, + "button": 2, + "buttons": 2, + "time": 88391301.837833, + "panning": true + }, + { + "type": "pointermove", + "x": 544, + "y": 523, + "button": 2, + "buttons": 2, + "time": 88391326.557208, + "panning": true + }, + { + "type": "pointermove", + "x": 536, + "y": 521, + "button": 2, + "buttons": 2, + "time": 88391353.140916, + "panning": true + }, + { + "type": "pointermove", + "x": 532, + "y": 520, + "button": 2, + "buttons": 2, + "time": 88391380.901041, + "panning": true + }, + { + "type": "pointermove", + "x": 528, + "y": 519, + "button": 2, + "buttons": 2, + "time": 88391385.153, + "panning": true + }, + { + "type": "pointermove", + "x": 524, + "y": 518, + "button": 2, + "buttons": 2, + "time": 88391407.129625, + "panning": true + }, + { + "type": "pointermove", + "x": 520, + "y": 517, + "button": 2, + "buttons": 2, + "time": 88391429.113958, + "panning": true + }, + { + "type": "pointermove", + "x": 516, + "y": 516, + "button": 2, + "buttons": 2, + "time": 88391448.238291, + "panning": true + }, + { + "type": "pointermove", + "x": 508, + "y": 514, + "button": 2, + "buttons": 2, + "time": 88391471.19775, + "panning": true + }, + { + "type": "pointermove", + "x": 504, + "y": 513, + "button": 2, + "buttons": 2, + "time": 88391494.162625, + "panning": true + }, + { + "type": "pointermove", + "x": 500, + "y": 512, + "button": 2, + "buttons": 2, + "time": 88391505.980666, + "panning": true + }, + { + "type": "pointerup", + "x": 500, + "y": 512, + "button": 2, + "buttons": 0, + "time": 88391518.196875, + "panning": false + } + ], + "captures": [], + "panning": false, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } +} diff --git a/docs/graphics/evidence/kestrel/native-wheel-zoom-diagnostics.json b/docs/graphics/evidence/kestrel/native-wheel-zoom-diagnostics.json new file mode 100644 index 000000000..44f3e7f17 --- /dev/null +++ b/docs/graphics/evidence/kestrel/native-wheel-zoom-diagnostics.json @@ -0,0 +1,36 @@ +{ + "platform": "macOS arm64", + "documentSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "input": { + "route": "NativeSceneSurface.SubmitWheel", + "count": 40, + "deltaY": [ + -25, + 25 + ], + "stepsPerDirection": 20, + "requestedSpacingMs": 30 + }, + "observations": { + "wheelEvents": 40, + "handledWheelEvents": 40, + "resizes": [ + [ + 556, + 614 + ] + ], + "bitmapMutations": [], + "canvas": [ + 1112, + 1228 + ], + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + "limitations": [ + "No presented-frame capture or smoothness measurement.", + "Handled events do not independently prove the expected camera transformation.", + "Does not resolve white UI, flickering, or resize layout." + ] +} diff --git a/docs/graphics/evidence/kestrel/native-wheel-zoom-performance.json b/docs/graphics/evidence/kestrel/native-wheel-zoom-performance.json new file mode 100644 index 000000000..71dc2a97d --- /dev/null +++ b/docs/graphics/evidence/kestrel/native-wheel-zoom-performance.json @@ -0,0 +1,668 @@ +{ + "baseline": { + "ContextId": 1, + "Timestamp": 85583976781583, + "Engine": { + "EnqueuedInputs": 4, + "DroppedInputs": 0, + "ConsumedInputs": 4, + "PublishedScenes": 7, + "AcquiredScenes": 5, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3342623, + "InputEventsDispatched": 31, + "InputCallbacksInvoked": 2, + "BusiestCanvasWidthMilli": 556000, + "BusiestCanvasHeightMilli": 614000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 50958, + "LastScenePublicationNanoseconds": 493708, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 1, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 1, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 4963916, + "LastSceneBuildNanoseconds": 375500, + "MaximumScenePublicationNanoseconds": 6159208 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 266416, + "MaximumDispatchNanoseconds": 266416, + "LastDispatchSequence": 639244516030805293, + "DispatchedInputs": 2, + "TotalDispatchNanoseconds": 461082 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 1, + "TotalDispatchNanoseconds": 625, + "LastDispatchNanoseconds": 625, + "MaximumDispatchNanoseconds": 625, + "LastTimestampMicroseconds": 85580717048 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 15, + "BlockedPublications": 8, + "AcknowledgedScenes": 5, + "TotalAcknowledgementNanoseconds": 495214293, + "LastAcknowledgementNanoseconds": 11511209, + "MaximumAcknowledgementNanoseconds": 185740209, + "AcknowledgedRevision": 7 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5963776, + "V8UsedHeapBytes": 2930396, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5963776, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1552384, + "LatestSceneBytes": 126908, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 416, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 492440, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920268, + "V8OldSpacePhysicalBytes": 2359296, + "V8CodeSpaceUsedBytes": 198368, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 131848, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 5, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 85585720075125, + "Engine": { + "EnqueuedInputs": 94, + "DroppedInputs": 0, + "ConsumedInputs": 94, + "PublishedScenes": 70, + "AcquiredScenes": 69, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1351, + "LayoutPasses": 43, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3342623, + "InputEventsDispatched": 87, + "InputCallbacksInvoked": 83, + "BusiestCanvasWidthMilli": 556000, + "BusiestCanvasHeightMilli": 614000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 50958, + "LastScenePublicationNanoseconds": 1140417, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 2, + "AppliedWheelInputs": 40, + "AppliedAnimationFrames": 50, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 690666, + "LastSceneBuildNanoseconds": 315459, + "MaximumScenePublicationNanoseconds": 6159208 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 201625, + "MaximumDispatchNanoseconds": 6944916, + "LastDispatchSequence": 639244516030805334, + "DispatchedInputs": 43, + "TotalDispatchNanoseconds": 19299623 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 50, + "TotalDispatchNanoseconds": 118169, + "LastDispatchNanoseconds": 2917, + "MaximumDispatchNanoseconds": 7208, + "LastTimestampMicroseconds": 85585190041 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 96, + "BlockedPublications": 26, + "AcknowledgedScenes": 68, + "TotalAcknowledgementNanoseconds": 2477817670, + "LastAcknowledgementNanoseconds": 20612125, + "MaximumAcknowledgementNanoseconds": 185740209, + "AcknowledgedRevision": 70 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 34, + "AnimationFramesInvoked": 34, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 90, + "WorkerWaits": 177, + "WorkerSignalledWakes": 123, + "WorkerTimeoutWakes": 53, + "SceneBuilds": 63, + "NoDamageSceneBuilds": 19, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5963776, + "V8UsedHeapBytes": 2930396, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5963776, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1552384, + "LatestSceneBytes": 157224, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 416, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 492440, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920268, + "V8OldSpacePhysicalBytes": 2359296, + "V8CodeSpaceUsedBytes": 198368, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 131848, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 329, + "LogicalBitmapBytes": 5462144, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 480, + "StringCount": 2302, + "StringBytes": 133188, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 68, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 96, + "Renders": 63, + "AppliedDiffs": 63, + "InvalidationCalls": 66, + "DamageRectangles": 112, + "ChangedLayers": 34, + "EmptyDamageDiffs": 19, + "PartialDamageDiffs": 44, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 49, + "SkippedEmptyAnimationFrames": 47, + "RenderCallbacks": 66, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.7432935", + "EnqueuedInputs": 90, + "DroppedInputs": 0, + "ConsumedInputs": 90, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 35, + "AppliedAnimationFrames": 49, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 81, + "BlockedPublications": 18, + "PublishedScenes": 63, + "AcquiredScenes": 64, + "AcknowledgedScenes": 63, + "RenderedScenes": 63, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 34, + "AnimationFramesInvoked": 34, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 90, + "WorkerWaits": 177, + "WorkerSignalledWakes": 123, + "WorkerTimeoutWakes": 53, + "SceneBuilds": 63, + "NoDamageSceneBuilds": 19, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 96, + "CompositionRenders": 63, + "CompositionAppliedDiffs": 63, + "CompositionInvalidations": 66, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 49, + "CompositionSkippedEmptyAnimationFrames": 47, + "CompositionRenderCallbacks": 66, + "CompositionUnchangedRenderCallbacks": 0 + } +} diff --git a/docs/graphics/evidence/kestrel/non-gpu-headless-resize-failed.json b/docs/graphics/evidence/kestrel/non-gpu-headless-resize-failed.json new file mode 100644 index 000000000..228290cb2 --- /dev/null +++ b/docs/graphics/evidence/kestrel/non-gpu-headless-resize-failed.json @@ -0,0 +1,105 @@ +{ + "qualification": "failed", + "exitCode": 134, + "failure": "Post-report shutdown aborted: std::system_error: mutex lock failed: Invalid argument", + "nativeRevision": "88e48dca9ae877bd0a0a18f867021c8794ef05c2", + "nativeLibrarySha256": "c956fa1ba3210db331af13cd04aa726201557c7e4ab4e3b40b5412d1035cda44", + "command": "WEBSCENE_NATIVE_ENGINE_PATH=\"$PWD/artifacts/graphics-build/native-v8-enabled/libwebscene_native_engine.dylib\" dotnet run --project benchmarks/WebScene.NativeEngine.Benchmarks -c Release --no-build -- probe native-resize-cadence --seconds 5 --warmup-seconds 2 --hz 60 --composition", + "measurement": { + "schema": "webscene-native-resize-cadence-v2", + "measurementScope": "headless-cpu-draw-callback", + "physicalPresentationVerified": false, + "sourceKind": "deterministic-fixture", + "composition": true, + "certificationTelemetryEnabled": false, + "requestedHz": 60, + "warmupSeconds": 2, + "requestedSeconds": 5, + "elapsedMilliseconds": 5000.0402, + "submitted": 300, + "acceptedSubmissions": 300, + "appliedPairs": 300, + "publishedPairs": 150, + "coalescedPairs": 0, + "renderedFrames": 150, + "drawCallbackCompletions": 150, + "renderedFramesPerSecond": 29.999758801939233, + "drawCallbackCompletionsPerSecond": 30.0077325314807, + "layoutPasses": 897, + "layoutPassesPerAppliedResize": 2.99, + "publishedScenes": 150, + "publicationAttempts": 301, + "blockedPublications": 151, + "fullInvalidations": 150, + "unchangedRenderCallbacks": 0, + "droppedInputs": 0, + "processCpuMilliseconds": 2329.377, + "normalizedProcessCpuPercent": 46.58716543918987, + "lastCompositionMilliseconds": { + "diffApply": 1.957084, + "retainedDraw": 0.298375, + "skiaSubmit": 0.373667, + "callback": 0.374625 + }, + "lastResizeStageMilliseconds": { + "outerListeners": 0, + "frameListeners": 0, + "finalLayout": 0, + "observers": 0, + "totalDispatch": 1.946666, + "scenePublication": 0.459708 + }, + "queueMilliseconds": { + "average": 0.008671253333333333, + "maximum": 0.020167 + }, + "dispatchMilliseconds": { + "average": 2.2276397400000003, + "maximum": 4.06125 + }, + "publicationLatencyMilliseconds": { + "count": 299, + "average": 12.289368193979932, + "p50": 5.769167, + "p95": 21.440958, + "maximum": 22.447667 + }, + "publicationToRenderLatencyMilliseconds": { + "count": 148, + "average": 66.24723166216216, + "p50": 66.249833, + "p95": 67.320875, + "maximum": 68.003 + }, + "renderLatencyMilliseconds": { + "count": 295, + "average": 78.5424598779661, + "p50": 72.409292, + "p95": 88.043, + "maximum": 89.090875 + }, + "renderIntervalMilliseconds": { + "count": 149, + "average": 33.32384395973155, + "p50": 33.160416, + "p95": 34.786291, + "maximum": 35.796333 + }, + "drawCallbackIntervalMilliseconds": { + "count": 149, + "average": 33.3247438456376, + "p50": 33.160291, + "p95": 34.786417, + "maximum": 35.795625 + }, + "cpuCadenceGate": { + "maximumP95LatencyMilliseconds": 16.7, + "minimumFramesPerSecond": 58, + "maximumConsecutiveMissIntervalMilliseconds": 33.4, + "passed": false + }, + "chromeReferenceComparison": null, + "certificationDiagnostics": "certification telemetry disabled at compile time" + }, + "physicalPresentationVerified": false +} diff --git a/docs/graphics/evidence/kestrel/non-gpu-resize-cleanup-fixed.json b/docs/graphics/evidence/kestrel/non-gpu-resize-cleanup-fixed.json new file mode 100644 index 000000000..1790a034d --- /dev/null +++ b/docs/graphics/evidence/kestrel/non-gpu-resize-cleanup-fixed.json @@ -0,0 +1,108 @@ +{ + "shutdownCheck": "passed", + "exitCode": 0, + "physicalPresentationVerified": false, + "performanceQualification": "not passed", + "measurement": { + "schema": "webscene-native-resize-cadence-v2", + "measurementScope": "headless-cpu-draw-callback", + "physicalPresentationVerified": false, + "sourceKind": "deterministic-fixture", + "composition": true, + "certificationTelemetryEnabled": false, + "requestedHz": 60, + "warmupSeconds": 2, + "requestedSeconds": 5, + "elapsedMilliseconds": 5000.0296, + "submitted": 300, + "acceptedSubmissions": 300, + "appliedPairs": 300, + "publishedPairs": 150, + "coalescedPairs": 0, + "renderedFrames": 150, + "drawCallbackCompletions": 150, + "renderedFramesPerSecond": 29.999822401051382, + "drawCallbackCompletionsPerSecond": 30.00758094003664, + "layoutPasses": 897, + "layoutPassesPerAppliedResize": 2.99, + "publishedScenes": 150, + "publicationAttempts": 301, + "blockedPublications": 151, + "fullInvalidations": 150, + "unchangedRenderCallbacks": 0, + "droppedInputs": 0, + "processCpuMilliseconds": 2420.718, + "normalizedProcessCpuPercent": 48.41407338868554, + "lastCompositionMilliseconds": { + "diffApply": 2.576333, + "retainedDraw": 0.269375, + "skiaSubmit": 0.355208, + "callback": 0.355292 + }, + "lastResizeStageMilliseconds": { + "outerListeners": 0, + "frameListeners": 0, + "finalLayout": 0, + "observers": 0, + "totalDispatch": 2.446584, + "scenePublication": 0.601167 + }, + "queueMilliseconds": { + "average": 0.008569023333333333, + "maximum": 0.0855 + }, + "dispatchMilliseconds": { + "average": 2.50976627, + "maximum": 7.221375 + }, + "publicationLatencyMilliseconds": { + "count": 299, + "average": 12.70474201672241, + "p50": 7.074458, + "p95": 21.458667, + "maximum": 23.746291 + }, + "publicationToRenderLatencyMilliseconds": { + "count": 148, + "average": 66.06984041891891, + "p50": 66.001375, + "p95": 67.127083, + "maximum": 70.4875 + }, + "renderLatencyMilliseconds": { + "count": 295, + "average": 78.77105805423724, + "p50": 73.838792, + "p95": 88.123166, + "maximum": 90.509625 + }, + "renderIntervalMilliseconds": { + "count": 149, + "average": 33.323867167785245, + "p50": 32.808042, + "p95": 34.887917, + "maximum": 37.98525 + }, + "drawCallbackIntervalMilliseconds": { + "count": 149, + "average": 33.324912194630855, + "p50": 32.807792, + "p95": 34.894292, + "maximum": 37.985042 + }, + "cpuCadenceGate": { + "maximumP95LatencyMilliseconds": 16.7, + "minimumFramesPerSecond": 58, + "maximumConsecutiveMissIntervalMilliseconds": 33.4, + "passed": false + }, + "chromeReferenceComparison": null, + "certificationDiagnostics": "certification telemetry disabled at compile time" + }, + "limitations": [ + "Headless CPU draw callbacks, not scanout", + "Run overlapped managed regression test execution; not an isolated performance comparison" + ], + "rootCauseEvidence": "macOS crash report: std::mutex::lock -> webscene_engine_acquire_next_scene during cleanup after asynchronous compositor stop", + "debuggerLimitations": "Initial LLDB startup crashed in the SOS init plugin; no-init launch did not progress and owned debugger/child processes were terminated. Root-cause evidence comes from the OS crash report and the code path, not a live debugger stack." +} diff --git a/docs/graphics/evidence/kestrel/original-kestrel-metal-pan.json b/docs/graphics/evidence/kestrel/original-kestrel-metal-pan.json new file mode 100644 index 000000000..1e480c70f --- /dev/null +++ b/docs/graphics/evidence/kestrel/original-kestrel-metal-pan.json @@ -0,0 +1,33 @@ +{ + "physicalPresentationVerified": false, + "publicationToAcceptance": { + "count": 33, + "medianMilliseconds": 25.9775, + "p95Milliseconds": 29.699083, + "maximumMilliseconds": 30.231875 + }, + "acceptanceToDrawCallbackEnd": { + "count": 33, + "medianMilliseconds": 0.792584, + "p95Milliseconds": 1.556083, + "maximumMilliseconds": 2.148791 + }, + "publicationToDrawCallbackEnd": { + "count": 33, + "medianMilliseconds": 27.034791, + "p95Milliseconds": 31.00825, + "maximumMilliseconds": 31.255166 + }, + "inputToPublishedConsumptionWatermark": { + "count": 80, + "medianMilliseconds": 16.8208535, + "p95Milliseconds": 35.697167, + "maximumMilliseconds": 43.287167 + }, + "unmatchedInputCount": 0, + "limitations": [ + "Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived." + ] +} diff --git a/docs/graphics/evidence/kestrel/original-kestrel-metal-resize-failure.json b/docs/graphics/evidence/kestrel/original-kestrel-metal-resize-failure.json new file mode 100644 index 000000000..1bf91bf30 --- /dev/null +++ b/docs/graphics/evidence/kestrel/original-kestrel-metal-resize-failure.json @@ -0,0 +1,346 @@ +{ + "exitCode": 134, + "status": "failed", + "resizeGeometry": [ + { + "window": [ + 980, + 680 + ], + "canvas": [ + 1610, + 750 + ], + "css": [ + 805, + 375 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 175, + 196, + 805, + 375 + ], + "height": "375px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 196, + 980, + 375 + ], + "height": "375px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1440, + 900 + ], + "canvas": [ + 1932, + 1126 + ], + "css": [ + 966, + 563 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 966, + 563 + ], + "height": "563px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1440, + 563 + ], + "height": "563px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1100, + 740 + ], + "canvas": [ + 1362, + 806 + ], + "css": [ + 681, + 403 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 195, + 205, + 681, + 403 + ], + "height": "403px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1100, + 403 + ], + "height": "403px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1280, + 800 + ], + "canvas": [ + 1612, + 926 + ], + "css": [ + 806, + 463 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 806, + 463 + ], + "height": "463px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1280, + 463 + ], + "height": "463px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } + ], + "failure": "Metal command buffer commit with uncommitted encoder", + "crashStack": "gr_direct_context_flush_and_submit -> AGX command buffer commit -> IOGPUMetalCommandBuffer validate -> abort", + "crashReport": "/Users/danw/Library/Logs/DiagnosticReports/WebScene.GpuHost.Probe-2026-09-08-130820.ips", + "physicalPresentationVerified": false +} diff --git a/docs/graphics/evidence/kestrel/pan-composition-timeline.json b/docs/graphics/evidence/kestrel/pan-composition-timeline.json new file mode 100644 index 000000000..1d3344638 --- /dev/null +++ b/docs/graphics/evidence/kestrel/pan-composition-timeline.json @@ -0,0 +1,2481 @@ +{ + "workloadValidated": true, + "physicalPresentationVerified": false, + "publicationToDrawMedianMilliseconds": 33.3722295, + "records": { + "Kestrel pan performance": { + "elapsedMilliseconds": 1857.0006, + "baseline": { + "ContextId": 1, + "Timestamp": 91760001607666, + "Engine": { + "EnqueuedInputs": 3, + "DroppedInputs": 0, + "ConsumedInputs": 3, + "PublishedScenes": 6, + "AcquiredScenes": 4, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3418207, + "InputEventsDispatched": 1, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 91750, + "LastScenePublicationNanoseconds": 1935459, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 0, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 2, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 1490917, + "LastSceneBuildNanoseconds": 328167, + "MaximumScenePublicationNanoseconds": 1935459 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "LastDispatchSequence": 0, + "DispatchedInputs": 0, + "TotalDispatchNanoseconds": 0 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 2, + "TotalDispatchNanoseconds": 1750, + "LastDispatchNanoseconds": 1416, + "MaximumDispatchNanoseconds": 1416, + "LastTimestampMicroseconds": 91756932908 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 7, + "BlockedPublications": 0, + "AcknowledgedScenes": 4, + "TotalAcknowledgementNanoseconds": 393463332, + "LastAcknowledgementNanoseconds": 22471291, + "MaximumAcknowledgementNanoseconds": 175062791, + "AcknowledgedRevision": 6 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3096480, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1484800, + "LatestSceneBytes": 127068, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 660600, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920184, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196928, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 131296, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 4, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 1, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 1, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 91761865174875, + "Engine": { + "EnqueuedInputs": 147, + "DroppedInputs": 0, + "ConsumedInputs": 147, + "PublishedScenes": 70, + "AcquiredScenes": 68, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1447, + "LayoutPasses": 139, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3418207, + "InputEventsDispatched": 167, + "InputCallbacksInvoked": 150, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 91750, + "LastScenePublicationNanoseconds": 1002833, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 8, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 72, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 64, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 574750, + "LastSceneBuildNanoseconds": 308792, + "MaximumScenePublicationNanoseconds": 1935459 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 2961292, + "MaximumDispatchNanoseconds": 18233708, + "LastDispatchSequence": 639244577775456243, + "DispatchedInputs": 74, + "TotalDispatchNanoseconds": 279052165 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 64, + "TotalDispatchNanoseconds": 51375, + "LastDispatchNanoseconds": 917, + "MaximumDispatchNanoseconds": 3209, + "LastTimestampMicroseconds": 91761371068 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 158, + "BlockedPublications": 0, + "AcknowledgedScenes": 68, + "TotalAcknowledgementNanoseconds": 2387493378, + "LastAcknowledgementNanoseconds": 34422208, + "MaximumAcknowledgementNanoseconds": 175062791, + "AcknowledgedRevision": 70 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 57, + "AnimationFramesInvoked": 57, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 223, + "WorkerWaits": 270, + "WorkerSignalledWakes": 243, + "WorkerTimeoutWakes": 26, + "SceneBuilds": 64, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3096480, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1484800, + "LatestSceneBytes": 162360, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 660600, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920184, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196928, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 131296, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5970848, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 3283, + "StringBytes": 170674, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 68, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 97, + "Renders": 64, + "AppliedDiffs": 64, + "InvalidationCalls": 65, + "DamageRectangles": 177, + "ChangedLayers": 57, + "EmptyDamageDiffs": 1, + "PartialDamageDiffs": 63, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 62, + "SkippedEmptyAnimationFrames": 35, + "RenderCallbacks": 65, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.8635672", + "EnqueuedInputs": 144, + "DroppedInputs": 0, + "ConsumedInputs": 144, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 131, + "AppliedAnimationFrames": 62, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 151, + "BlockedPublications": 0, + "PublishedScenes": 64, + "AcquiredScenes": 64, + "AcknowledgedScenes": 64, + "RenderedScenes": 64, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 57, + "AnimationFramesInvoked": 57, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 223, + "WorkerWaits": 270, + "WorkerSignalledWakes": 243, + "WorkerTimeoutWakes": 26, + "SceneBuilds": 64, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 96, + "CompositionRenders": 64, + "CompositionAppliedDiffs": 64, + "CompositionInvalidations": 65, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 62, + "CompositionSkippedEmptyAnimationFrames": 34, + "CompositionRenderCallbacks": 65, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "Kestrel pan diagnostics": { + "events": [ + { + "type": "pointerdown", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 91760026.392125, + "panning": false + }, + { + "type": "pointermove", + "x": 633, + "y": 438.5, + "button": 2, + "buttons": 2, + "time": 91760036.787958, + "panning": true + }, + { + "type": "pointermove", + "x": 637, + "y": 439.5, + "button": 2, + "buttons": 2, + "time": 91760058.650541, + "panning": true + }, + { + "type": "pointermove", + "x": 641, + "y": 440.5, + "button": 2, + "buttons": 2, + "time": 91760063.603833, + "panning": true + }, + { + "type": "pointermove", + "x": 645, + "y": 441.5, + "button": 2, + "buttons": 2, + "time": 91760082.749916, + "panning": true + }, + { + "type": "pointermove", + "x": 649, + "y": 442.5, + "button": 2, + "buttons": 2, + "time": 91760104.884208, + "panning": true + }, + { + "type": "pointermove", + "x": 653, + "y": 443.5, + "button": 2, + "buttons": 2, + "time": 91760127.979, + "panning": true + }, + { + "type": "pointermove", + "x": 657, + "y": 444.5, + "button": 2, + "buttons": 2, + "time": 91760130.74025, + "panning": true + }, + { + "type": "pointermove", + "x": 661, + "y": 445.5, + "button": 2, + "buttons": 2, + "time": 91760148.710458, + "panning": true + }, + { + "type": "pointermove", + "x": 665, + "y": 446.5, + "button": 2, + "buttons": 2, + "time": 91760169.396125, + "panning": true + }, + { + "type": "pointermove", + "x": 669, + "y": 447.5, + "button": 2, + "buttons": 2, + "time": 91760191.75025, + "panning": true + }, + { + "type": "pointermove", + "x": 673, + "y": 448.5, + "button": 2, + "buttons": 2, + "time": 91760212.110583, + "panning": true + }, + { + "type": "pointermove", + "x": 677, + "y": 449.5, + "button": 2, + "buttons": 2, + "time": 91760214.771208, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 91760252.659625, + "panning": true + }, + { + "type": "pointermove", + "x": 689, + "y": 452.5, + "button": 2, + "buttons": 2, + "time": 91760271.96525, + "panning": true + }, + { + "type": "pointermove", + "x": 693, + "y": 453.5, + "button": 2, + "buttons": 2, + "time": 91760295.125416, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 91760320.41025, + "panning": true + }, + { + "type": "pointermove", + "x": 705, + "y": 456.5, + "button": 2, + "buttons": 2, + "time": 91760340.350208, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 91760362.098833, + "panning": true + }, + { + "type": "pointermove", + "x": 713, + "y": 458.5, + "button": 2, + "buttons": 2, + "time": 91760384.547333, + "panning": true + }, + { + "type": "pointermove", + "x": 717, + "y": 459.5, + "button": 2, + "buttons": 2, + "time": 91760387.540583, + "panning": true + }, + { + "type": "pointermove", + "x": 721, + "y": 460.5, + "button": 2, + "buttons": 2, + "time": 91760407.373083, + "panning": true + }, + { + "type": "pointermove", + "x": 725, + "y": 461.5, + "button": 2, + "buttons": 2, + "time": 91760427.394458, + "panning": true + }, + { + "type": "pointermove", + "x": 729, + "y": 462.5, + "button": 2, + "buttons": 2, + "time": 91760450.800375, + "panning": true + }, + { + "type": "pointermove", + "x": 733, + "y": 463.5, + "button": 2, + "buttons": 2, + "time": 91760453.688875, + "panning": true + }, + { + "type": "pointermove", + "x": 737, + "y": 464.5, + "button": 2, + "buttons": 2, + "time": 91760471.811708, + "panning": true + }, + { + "type": "pointermove", + "x": 741, + "y": 465.5, + "button": 2, + "buttons": 2, + "time": 91760494.208125, + "panning": true + }, + { + "type": "pointermove", + "x": 745, + "y": 466.5, + "button": 2, + "buttons": 2, + "time": 91760516.126208, + "panning": true + }, + { + "type": "pointermove", + "x": 753, + "y": 468.5, + "button": 2, + "buttons": 2, + "time": 91760539.91125, + "panning": true + }, + { + "type": "pointermove", + "x": 757, + "y": 469.5, + "button": 2, + "buttons": 2, + "time": 91760560.227375, + "panning": true + }, + { + "type": "pointermove", + "x": 761, + "y": 470.5, + "button": 2, + "buttons": 2, + "time": 91760580.112666, + "panning": true + }, + { + "type": "pointermove", + "x": 765, + "y": 471.5, + "button": 2, + "buttons": 2, + "time": 91760600.838708, + "panning": true + }, + { + "type": "pointermove", + "x": 773, + "y": 473.5, + "button": 2, + "buttons": 2, + "time": 91760621.665083, + "panning": true + }, + { + "type": "pointermove", + "x": 777, + "y": 474.5, + "button": 2, + "buttons": 2, + "time": 91760640.685416, + "panning": true + }, + { + "type": "pointermove", + "x": 781, + "y": 475.5, + "button": 2, + "buttons": 2, + "time": 91760660.508541, + "panning": true + }, + { + "type": "pointermove", + "x": 785, + "y": 476.5, + "button": 2, + "buttons": 2, + "time": 91760681.631916, + "panning": true + }, + { + "type": "pointermove", + "x": 781, + "y": 475.5, + "button": 2, + "buttons": 2, + "time": 91760700.943916, + "panning": true + }, + { + "type": "pointermove", + "x": 777, + "y": 474.5, + "button": 2, + "buttons": 2, + "time": 91760720.427166, + "panning": true + }, + { + "type": "pointermove", + "x": 773, + "y": 473.5, + "button": 2, + "buttons": 2, + "time": 91760739.016791, + "panning": true + }, + { + "type": "pointermove", + "x": 769, + "y": 472.5, + "button": 2, + "buttons": 2, + "time": 91760742.175791, + "panning": true + }, + { + "type": "pointermove", + "x": 765, + "y": 471.5, + "button": 2, + "buttons": 2, + "time": 91760766.561916, + "panning": true + }, + { + "type": "pointermove", + "x": 761, + "y": 470.5, + "button": 2, + "buttons": 2, + "time": 91760788.857375, + "panning": true + }, + { + "type": "pointermove", + "x": 757, + "y": 469.5, + "button": 2, + "buttons": 2, + "time": 91760810.13425, + "panning": true + }, + { + "type": "pointermove", + "x": 753, + "y": 468.5, + "button": 2, + "buttons": 2, + "time": 91760813.048, + "panning": true + }, + { + "type": "pointermove", + "x": 749, + "y": 467.5, + "button": 2, + "buttons": 2, + "time": 91760829.682375, + "panning": true + }, + { + "type": "pointermove", + "x": 745, + "y": 466.5, + "button": 2, + "buttons": 2, + "time": 91760852.278958, + "panning": true + }, + { + "type": "pointermove", + "x": 741, + "y": 465.5, + "button": 2, + "buttons": 2, + "time": 91760872.160416, + "panning": true + }, + { + "type": "pointermove", + "x": 737, + "y": 464.5, + "button": 2, + "buttons": 2, + "time": 91760892.989375, + "panning": true + }, + { + "type": "pointermove", + "x": 733, + "y": 463.5, + "button": 2, + "buttons": 2, + "time": 91760895.51825, + "panning": true + }, + { + "type": "pointermove", + "x": 729, + "y": 462.5, + "button": 2, + "buttons": 2, + "time": 91760914.307208, + "panning": true + }, + { + "type": "pointermove", + "x": 725, + "y": 461.5, + "button": 2, + "buttons": 2, + "time": 91760933.458166, + "panning": true + }, + { + "type": "pointermove", + "x": 721, + "y": 460.5, + "button": 2, + "buttons": 2, + "time": 91760956.0035, + "panning": true + }, + { + "type": "pointermove", + "x": 717, + "y": 459.5, + "button": 2, + "buttons": 2, + "time": 91760972.711666, + "panning": true + }, + { + "type": "pointermove", + "x": 713, + "y": 458.5, + "button": 2, + "buttons": 2, + "time": 91760994.292166, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 91760996.884208, + "panning": true + }, + { + "type": "pointermove", + "x": 705, + "y": 456.5, + "button": 2, + "buttons": 2, + "time": 91761014.466375, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 91761034.509833, + "panning": true + }, + { + "type": "pointermove", + "x": 697, + "y": 454.5, + "button": 2, + "buttons": 2, + "time": 91761053.768, + "panning": true + }, + { + "type": "pointermove", + "x": 693, + "y": 453.5, + "button": 2, + "buttons": 2, + "time": 91761071.822, + "panning": true + }, + { + "type": "pointermove", + "x": 689, + "y": 452.5, + "button": 2, + "buttons": 2, + "time": 91761091.253708, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 91761110.404333, + "panning": true + }, + { + "type": "pointermove", + "x": 677, + "y": 449.5, + "button": 2, + "buttons": 2, + "time": 91761132.013416, + "panning": true + }, + { + "type": "pointermove", + "x": 673, + "y": 448.5, + "button": 2, + "buttons": 2, + "time": 91761150.848875, + "panning": true + }, + { + "type": "pointermove", + "x": 669, + "y": 447.5, + "button": 2, + "buttons": 2, + "time": 91761169.94, + "panning": true + }, + { + "type": "pointermove", + "x": 665, + "y": 446.5, + "button": 2, + "buttons": 2, + "time": 91761186.776291, + "panning": true + }, + { + "type": "pointermove", + "x": 661, + "y": 445.5, + "button": 2, + "buttons": 2, + "time": 91761207.531166, + "panning": true + }, + { + "type": "pointermove", + "x": 657, + "y": 444.5, + "button": 2, + "buttons": 2, + "time": 91761226.545625, + "panning": true + }, + { + "type": "pointermove", + "x": 653, + "y": 443.5, + "button": 2, + "buttons": 2, + "time": 91761245.634, + "panning": true + }, + { + "type": "pointermove", + "x": 649, + "y": 442.5, + "button": 2, + "buttons": 2, + "time": 91761248.116333, + "panning": true + }, + { + "type": "pointermove", + "x": 637, + "y": 439.5, + "button": 2, + "buttons": 2, + "time": 91761307.664375, + "panning": true + }, + { + "type": "pointermove", + "x": 633, + "y": 438.5, + "button": 2, + "buttons": 2, + "time": 91761322.722, + "panning": true + }, + { + "type": "pointermove", + "x": 629, + "y": 437.5, + "button": 2, + "buttons": 2, + "time": 91761349.111291, + "panning": true + }, + { + "type": "pointermove", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 91761355.918125, + "panning": true + }, + { + "type": "pointerup", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 0, + "time": 91761365.035916, + "panning": false + } + ], + "captures": [], + "frames": [ + { + "timestamp": 91760035.777458, + "start": 91760042.126166, + "duration": 1.9514999985694885 + }, + { + "timestamp": 91760053.15404099, + "start": 91760064.429708, + "duration": 1.5311249941587448 + }, + { + "timestamp": 91760079.744333, + "start": 91760083.5595, + "duration": 0.6601250022649765 + }, + { + "timestamp": 91760102.07775, + "start": 91760105.554041, + "duration": 0.6067920029163361 + }, + { + "timestamp": 91760125.503458, + "start": 91760131.29625, + "duration": 0.5764999985694885 + }, + { + "timestamp": 91760146.078791, + "start": 91760149.442416, + "duration": 0.6803750097751617 + }, + { + "timestamp": 91760166.77633299, + "start": 91760170.079916, + "duration": 0.5417499989271164 + }, + { + "timestamp": 91760189.18075, + "start": 91760192.304916, + "duration": 0.5354590117931366 + }, + { + "timestamp": 91760209.607625, + "start": 91760215.272, + "duration": 0.5563330054283142 + }, + { + "timestamp": 91760249.97504099, + "start": 91760253.303875, + "duration": 0.532708004117012 + }, + { + "timestamp": 91760269.785583, + "start": 91760272.560083, + "duration": 0.7537920027971268 + }, + { + "timestamp": 91760292.583708, + "start": 91760295.913583, + "duration": 0.8098750114440918 + }, + { + "timestamp": 91760315.9505, + "start": 91760321.131291, + "duration": 0.658624991774559 + }, + { + "timestamp": 91760337.613208, + "start": 91760340.957666, + "duration": 0.528084009885788 + }, + { + "timestamp": 91760359.289125, + "start": 91760362.744291, + "duration": 0.5539590120315552 + }, + { + "timestamp": 91760403.69912499, + "start": 91760408.055416, + "duration": 0.6549170017242432 + }, + { + "timestamp": 91760423.265416, + "start": 91760428.288166, + "duration": 1.0339590013027191 + }, + { + "timestamp": 91760448.220625, + "start": 91760454.413666, + "duration": 0.5903750061988831 + }, + { + "timestamp": 91760469.01379101, + "start": 91760472.494375, + "duration": 0.8091249912977219 + }, + { + "timestamp": 91760491.297791, + "start": 91760495.054875, + "duration": 0.6380829960107803 + }, + { + "timestamp": 91760513.14325, + "start": 91760516.8075, + "duration": 0.8402500003576279 + }, + { + "timestamp": 91760536.980958, + "start": 91760540.596166, + "duration": 0.5729999989271164 + }, + { + "timestamp": 91760557.73829101, + "start": 91760560.88125, + "duration": 0.71458300948143 + }, + { + "timestamp": 91760577.54458301, + "start": 91760580.85075, + "duration": 0.5129159986972809 + }, + { + "timestamp": 91760597.173083, + "start": 91760602.36125, + "duration": 0.6947910040616989 + }, + { + "timestamp": 91760619.020625, + "start": 91760622.290208, + "duration": 0.5503749996423721 + }, + { + "timestamp": 91760638.378125, + "start": 91760641.313875, + "duration": 0.5725829899311066 + }, + { + "timestamp": 91760658.163125, + "start": 91760661.073708, + "duration": 0.5339580029249191 + }, + { + "timestamp": 91760679.310791, + "start": 91760682.264041, + "duration": 0.5538339912891388 + }, + { + "timestamp": 91760717.952833, + "start": 91760721.036458, + "duration": 0.6992499977350235 + }, + { + "timestamp": 91760735.703541, + "start": 91760742.831125, + "duration": 0.675040990114212 + }, + { + "timestamp": 91760761.581666, + "start": 91760767.861041, + "duration": 0.9136670082807541 + }, + { + "timestamp": 91760785.49308302, + "start": 91760790.8595, + "duration": 0.6487910002470016 + }, + { + "timestamp": 91760806.953583, + "start": 91760813.648541, + "duration": 0.9930419921875 + }, + { + "timestamp": 91760826.930125, + "start": 91760830.455083, + "duration": 0.6273750066757202 + }, + { + "timestamp": 91760849.731041, + "start": 91760852.914583, + "duration": 0.7527080029249191 + }, + { + "timestamp": 91760869.367916, + "start": 91760872.893041, + "duration": 0.6643339991569519 + }, + { + "timestamp": 91760890.495666, + "start": 91760896.064208, + "duration": 0.6480419933795929 + }, + { + "timestamp": 91760911.96379101, + "start": 91760914.881333, + "duration": 0.6356250047683716 + }, + { + "timestamp": 91760952.041458, + "start": 91760957.431416, + "duration": 0.6498750001192093 + }, + { + "timestamp": 91760969.957791, + "start": 91760973.371541, + "duration": 0.5867920070886612 + }, + { + "timestamp": 91760991.721666, + "start": 91760997.520458, + "duration": 0.5911670029163361 + }, + { + "timestamp": 91761012.152875, + "start": 91761015.1095, + "duration": 0.5790829956531525 + }, + { + "timestamp": 91761051.337166, + "start": 91761054.389958, + "duration": 0.7157920002937317 + }, + { + "timestamp": 91761069.475666, + "start": 91761072.472791, + "duration": 0.6372089982032776 + }, + { + "timestamp": 91761088.907166, + "start": 91761091.923333, + "duration": 0.5967079997062683 + }, + { + "timestamp": 91761108.090916, + "start": 91761111.005625, + "duration": 0.4493750035762787 + }, + { + "timestamp": 91761128.732625, + "start": 91761133.953541, + "duration": 0.6931670010089874 + }, + { + "timestamp": 91761167.45158301, + "start": 91761170.563333, + "duration": 0.668957993388176 + }, + { + "timestamp": 91761184.30258301, + "start": 91761187.439708, + "duration": 0.5696250051259995 + }, + { + "timestamp": 91761205.254625, + "start": 91761208.131166, + "duration": 0.5298340022563934 + }, + { + "timestamp": 91761224.28075, + "start": 91761227.124333, + "duration": 0.5235830098390579 + }, + { + "timestamp": 91761243.248458, + "start": 91761248.626791, + "duration": 0.6551250070333481 + }, + { + "timestamp": 91761301.90908301, + "start": 91761309.179416, + "duration": 1.6092090010643005 + }, + { + "timestamp": 91761319.784916, + "start": 91761324.026458, + "duration": 1.6230420023202896 + }, + { + "timestamp": 91761345.49983299, + "start": 91761358.6465, + "duration": 0.7779579907655716 + }, + { + "timestamp": 91761371.068, + "start": 91761371.094916, + "duration": 0.894666999578476 + } + ], + "panning": false, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + "Kestrel pan composition timeline": { + "timestampFrequency": 1000000000, + "traceStarted": 91760008262041, + "publications": [ + { + "Timestamp": 91760027686750, + "Revision": 7, + "ConsumedInputSequence": 639244577775456162, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760046859750, + "Revision": 8, + "ConsumedInputSequence": 639244577775456164, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760067639541, + "Revision": 9, + "ConsumedInputSequence": 639244577775456166, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760085442666, + "Revision": 10, + "ConsumedInputSequence": 639244577775456167, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760107624333, + "Revision": 11, + "ConsumedInputSequence": 639244577775456168, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760133223500, + "Revision": 12, + "ConsumedInputSequence": 639244577775456170, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760151254208, + "Revision": 13, + "ConsumedInputSequence": 639244577775456171, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760171951041, + "Revision": 14, + "ConsumedInputSequence": 639244577775456172, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760194138875, + "Revision": 15, + "ConsumedInputSequence": 639244577775456173, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760216983750, + "Revision": 16, + "ConsumedInputSequence": 639244577775456175, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760255374458, + "Revision": 17, + "ConsumedInputSequence": 639244577775456177, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760274506500, + "Revision": 18, + "ConsumedInputSequence": 639244577775456178, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760298055666, + "Revision": 19, + "ConsumedInputSequence": 639244577775456179, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760323012958, + "Revision": 20, + "ConsumedInputSequence": 639244577775456181, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760342692125, + "Revision": 21, + "ConsumedInputSequence": 639244577775456182, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760364693500, + "Revision": 22, + "ConsumedInputSequence": 639244577775456183, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760388638583, + "Revision": 23, + "ConsumedInputSequence": 639244577775456185, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760409984666, + "Revision": 24, + "ConsumedInputSequence": 639244577775456186, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760431721333, + "Revision": 25, + "ConsumedInputSequence": 639244577775456187, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760456432791, + "Revision": 26, + "ConsumedInputSequence": 639244577775456189, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760474433541, + "Revision": 27, + "ConsumedInputSequence": 639244577775456190, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760497906916, + "Revision": 28, + "ConsumedInputSequence": 639244577775456191, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760518677083, + "Revision": 29, + "ConsumedInputSequence": 639244577775456192, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760542504875, + "Revision": 30, + "ConsumedInputSequence": 639244577775456194, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760564382083, + "Revision": 31, + "ConsumedInputSequence": 639244577775456195, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760582538083, + "Revision": 32, + "ConsumedInputSequence": 639244577775456196, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760604383833, + "Revision": 33, + "ConsumedInputSequence": 639244577775456197, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760624129541, + "Revision": 34, + "ConsumedInputSequence": 639244577775456199, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760643283083, + "Revision": 35, + "ConsumedInputSequence": 639244577775456200, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760665004750, + "Revision": 36, + "ConsumedInputSequence": 639244577775456201, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760684029125, + "Revision": 37, + "ConsumedInputSequence": 639244577775456202, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760702666000, + "Revision": 38, + "ConsumedInputSequence": 639244577775456203, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760723005083, + "Revision": 39, + "ConsumedInputSequence": 639244577775456204, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760747283000, + "Revision": 40, + "ConsumedInputSequence": 639244577775456206, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760770058750, + "Revision": 41, + "ConsumedInputSequence": 639244577775456207, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760792757458, + "Revision": 42, + "ConsumedInputSequence": 639244577775456208, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760815621208, + "Revision": 43, + "ConsumedInputSequence": 639244577775456210, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760832373125, + "Revision": 44, + "ConsumedInputSequence": 639244577775456211, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760855073625, + "Revision": 45, + "ConsumedInputSequence": 639244577775456212, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760874881791, + "Revision": 46, + "ConsumedInputSequence": 639244577775456213, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760898134750, + "Revision": 47, + "ConsumedInputSequence": 639244577775456215, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760916904083, + "Revision": 48, + "ConsumedInputSequence": 639244577775456216, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760934609208, + "Revision": 49, + "ConsumedInputSequence": 639244577775456217, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760959328291, + "Revision": 50, + "ConsumedInputSequence": 639244577775456218, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760975227416, + "Revision": 51, + "ConsumedInputSequence": 639244577775456219, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91760999382541, + "Revision": 52, + "ConsumedInputSequence": 639244577775456221, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761017499250, + "Revision": 53, + "ConsumedInputSequence": 639244577775456222, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761035590708, + "Revision": 54, + "ConsumedInputSequence": 639244577775456223, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761056399625, + "Revision": 55, + "ConsumedInputSequence": 639244577775456224, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761074329583, + "Revision": 56, + "ConsumedInputSequence": 639244577775456225, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761093723250, + "Revision": 57, + "ConsumedInputSequence": 639244577775456226, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761115000625, + "Revision": 58, + "ConsumedInputSequence": 639244577775456227, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761135727000, + "Revision": 59, + "ConsumedInputSequence": 639244577775456229, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761151902333, + "Revision": 60, + "ConsumedInputSequence": 639244577775456230, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761172377583, + "Revision": 61, + "ConsumedInputSequence": 639244577775456231, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761189414791, + "Revision": 62, + "ConsumedInputSequence": 639244577775456232, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761210056708, + "Revision": 63, + "ConsumedInputSequence": 639244577775456233, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761231407458, + "Revision": 64, + "ConsumedInputSequence": 639244577775456234, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761250633125, + "Revision": 65, + "ConsumedInputSequence": 639244577775456236, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761314285333, + "Revision": 66, + "ConsumedInputSequence": 639244577775456239, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761327342458, + "Revision": 67, + "ConsumedInputSequence": 639244577775456240, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761360755208, + "Revision": 68, + "ConsumedInputSequence": 639244577775456242, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761368175166, + "Revision": 69, + "ConsumedInputSequence": 639244577775456243, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 91761394082916, + "Revision": 70, + "ConsumedInputSequence": 639244577775456243, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 91760042728666, + "Revision": 7, + "ConsumedInputSequence": 639244577775456162 + }, + { + "Timestamp": 91760078022541, + "Revision": 8, + "ConsumedInputSequence": 639244577775456164 + }, + { + "Timestamp": 91760100324250, + "Revision": 9, + "ConsumedInputSequence": 639244577775456166 + }, + { + "Timestamp": 91760123736416, + "Revision": 10, + "ConsumedInputSequence": 639244577775456167 + }, + { + "Timestamp": 91760145027375, + "Revision": 11, + "ConsumedInputSequence": 639244577775456168 + }, + { + "Timestamp": 91760165175958, + "Revision": 12, + "ConsumedInputSequence": 639244577775456170 + }, + { + "Timestamp": 91760187617333, + "Revision": 13, + "ConsumedInputSequence": 639244577775456171 + }, + { + "Timestamp": 91760208052916, + "Revision": 14, + "ConsumedInputSequence": 639244577775456172 + }, + { + "Timestamp": 91760228187291, + "Revision": 15, + "ConsumedInputSequence": 639244577775456173 + }, + { + "Timestamp": 91760248435791, + "Revision": 16, + "ConsumedInputSequence": 639244577775456175 + }, + { + "Timestamp": 91760290984375, + "Revision": 17, + "ConsumedInputSequence": 639244577775456177 + }, + { + "Timestamp": 91760314159750, + "Revision": 18, + "ConsumedInputSequence": 639244577775456178 + }, + { + "Timestamp": 91760335906500, + "Revision": 19, + "ConsumedInputSequence": 639244577775456179 + }, + { + "Timestamp": 91760357608625, + "Revision": 20, + "ConsumedInputSequence": 639244577775456181 + }, + { + "Timestamp": 91760380248000, + "Revision": 21, + "ConsumedInputSequence": 639244577775456182 + }, + { + "Timestamp": 91760401873708, + "Revision": 22, + "ConsumedInputSequence": 639244577775456183 + }, + { + "Timestamp": 91760421559416, + "Revision": 23, + "ConsumedInputSequence": 639244577775456185 + }, + { + "Timestamp": 91760446547875, + "Revision": 24, + "ConsumedInputSequence": 639244577775456186 + }, + { + "Timestamp": 91760467199958, + "Revision": 25, + "ConsumedInputSequence": 639244577775456187 + }, + { + "Timestamp": 91760489398416, + "Revision": 26, + "ConsumedInputSequence": 639244577775456189 + }, + { + "Timestamp": 91760511270458, + "Revision": 27, + "ConsumedInputSequence": 639244577775456190 + }, + { + "Timestamp": 91760534879416, + "Revision": 28, + "ConsumedInputSequence": 639244577775456191 + }, + { + "Timestamp": 91760556086166, + "Revision": 29, + "ConsumedInputSequence": 639244577775456192 + }, + { + "Timestamp": 91760575898500, + "Revision": 30, + "ConsumedInputSequence": 639244577775456194 + }, + { + "Timestamp": 91760596169416, + "Revision": 31, + "ConsumedInputSequence": 639244577775456195 + }, + { + "Timestamp": 91760617383041, + "Revision": 32, + "ConsumedInputSequence": 639244577775456196 + }, + { + "Timestamp": 91760636731666, + "Revision": 33, + "ConsumedInputSequence": 639244577775456197 + }, + { + "Timestamp": 91760656589750, + "Revision": 34, + "ConsumedInputSequence": 639244577775456199 + }, + { + "Timestamp": 91760677707166, + "Revision": 35, + "ConsumedInputSequence": 639244577775456200 + }, + { + "Timestamp": 91760697001625, + "Revision": 36, + "ConsumedInputSequence": 639244577775456201 + }, + { + "Timestamp": 91760716372833, + "Revision": 37, + "ConsumedInputSequence": 639244577775456202 + }, + { + "Timestamp": 91760733769208, + "Revision": 38, + "ConsumedInputSequence": 639244577775456203 + }, + { + "Timestamp": 91760759758500, + "Revision": 39, + "ConsumedInputSequence": 639244577775456204 + }, + { + "Timestamp": 91760783110083, + "Revision": 40, + "ConsumedInputSequence": 639244577775456206 + }, + { + "Timestamp": 91760805279791, + "Revision": 41, + "ConsumedInputSequence": 639244577775456207 + }, + { + "Timestamp": 91760825305000, + "Revision": 42, + "ConsumedInputSequence": 639244577775456208 + }, + { + "Timestamp": 91760848098333, + "Revision": 43, + "ConsumedInputSequence": 639244577775456210 + }, + { + "Timestamp": 91760868016750, + "Revision": 44, + "ConsumedInputSequence": 639244577775456211 + }, + { + "Timestamp": 91760888871250, + "Revision": 45, + "ConsumedInputSequence": 639244577775456212 + }, + { + "Timestamp": 91760910373625, + "Revision": 46, + "ConsumedInputSequence": 639244577775456213 + }, + { + "Timestamp": 91760929571708, + "Revision": 47, + "ConsumedInputSequence": 639244577775456215 + }, + { + "Timestamp": 91760950226541, + "Revision": 48, + "ConsumedInputSequence": 639244577775456216 + }, + { + "Timestamp": 91760968078208, + "Revision": 49, + "ConsumedInputSequence": 639244577775456217 + }, + { + "Timestamp": 91760990150458, + "Revision": 50, + "ConsumedInputSequence": 639244577775456218 + }, + { + "Timestamp": 91761010531875, + "Revision": 51, + "ConsumedInputSequence": 639244577775456219 + }, + { + "Timestamp": 91761030356041, + "Revision": 52, + "ConsumedInputSequence": 639244577775456221 + }, + { + "Timestamp": 91761049502125, + "Revision": 53, + "ConsumedInputSequence": 639244577775456222 + }, + { + "Timestamp": 91761067861791, + "Revision": 54, + "ConsumedInputSequence": 639244577775456223 + }, + { + "Timestamp": 91761087322041, + "Revision": 55, + "ConsumedInputSequence": 639244577775456224 + }, + { + "Timestamp": 91761106524333, + "Revision": 56, + "ConsumedInputSequence": 639244577775456225 + }, + { + "Timestamp": 91761127149208, + "Revision": 57, + "ConsumedInputSequence": 639244577775456226 + }, + { + "Timestamp": 91761146616875, + "Revision": 58, + "ConsumedInputSequence": 639244577775456227 + }, + { + "Timestamp": 91761165902708, + "Revision": 59, + "ConsumedInputSequence": 639244577775456229 + }, + { + "Timestamp": 91761182789750, + "Revision": 60, + "ConsumedInputSequence": 639244577775456230 + }, + { + "Timestamp": 91761203657541, + "Revision": 61, + "ConsumedInputSequence": 639244577775456231 + }, + { + "Timestamp": 91761222765625, + "Revision": 62, + "ConsumedInputSequence": 639244577775456232 + }, + { + "Timestamp": 91761241617916, + "Revision": 63, + "ConsumedInputSequence": 639244577775456233 + }, + { + "Timestamp": 91761261188375, + "Revision": 64, + "ConsumedInputSequence": 639244577775456234 + }, + { + "Timestamp": 91761299004791, + "Revision": 65, + "ConsumedInputSequence": 639244577775456236 + }, + { + "Timestamp": 91761342955750, + "Revision": 66, + "ConsumedInputSequence": 639244577775456239 + }, + { + "Timestamp": 91761369306875, + "Revision": 67, + "ConsumedInputSequence": 639244577775456240 + }, + { + "Timestamp": 91761390889583, + "Revision": 68, + "ConsumedInputSequence": 639244577775456242 + }, + { + "Timestamp": 91761412227000, + "Revision": 69, + "ConsumedInputSequence": 639244577775456243 + }, + { + "Timestamp": 91761431076375, + "Revision": 70, + "ConsumedInputSequence": 639244577775456243 + } + ], + "drawCallbackCompletions": [ + 91760042358666, + 91760078018958, + 91760100321250, + 91760123730833, + 91760145025791, + 91760165174250, + 91760187611250, + 91760208050083, + 91760228184625, + 91760248434416, + 91760290980416, + 91760314155333, + 91760335902416, + 91760357604833, + 91760380246333, + 91760401872208, + 91760421557750, + 91760446543875, + 91760467194916, + 91760489392958, + 91760511265791, + 91760534876708, + 91760556080166, + 91760575894125, + 91760596168250, + 91760617375666, + 91760636718833, + 91760656584041, + 91760677704125, + 91760697000541, + 91760716370625, + 91760733767625, + 91760759753916, + 91760783105416, + 91760805274916, + 91760825301791, + 91760848095083, + 91760868014125, + 91760888866875, + 91760910369916, + 91760929569875, + 91760950222583, + 91760968077458, + 91760990142250, + 91761010528000, + 91761030355083, + 91761049499416, + 91761067860666, + 91761087319375, + 91761106522208, + 91761127146875, + 91761146616333, + 91761165902083, + 91761182788666, + 91761203655833, + 91761222763666, + 91761241615208, + 91761261181125, + 91761299000791, + 91761342952375, + 91761369304916, + 91761390885625, + 91761412225041, + 91761431075000, + 91761433666625 + ], + "physicalPresentationVerified": false + } + } +} diff --git a/docs/graphics/evidence/kestrel/producer-completion-gate.json b/docs/graphics/evidence/kestrel/producer-completion-gate.json new file mode 100644 index 000000000..56f1ce435 --- /dev/null +++ b/docs/graphics/evidence/kestrel/producer-completion-gate.json @@ -0,0 +1,15 @@ +{ + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "detachedBeforeRetirement": false, + "scope": "Production two-phase completion gate tested with controlled phase ordering; delayed whole-scene capture remains pending.", + "tests": [ + "webscene_graphics_completion_tests passed (0.31s)", + "webscene_graphics_v8_runtime_tests passed (0.60s)" + ] +} diff --git a/docs/graphics/evidence/kestrel/rejected-pre-raf-resize-layout.json b/docs/graphics/evidence/kestrel/rejected-pre-raf-resize-layout.json new file mode 100644 index 000000000..aba158f76 --- /dev/null +++ b/docs/graphics/evidence/kestrel/rejected-pre-raf-resize-layout.json @@ -0,0 +1,68 @@ +{ + "experiment": "Layout and ResizeObserver delivery before admitted GPU RAF batch", + "retained": false, + "reason": "Changes observable ResizeObserver/RAF ordering. More redraws do not justify this compatibility change.", + "delta": { + "Elapsed": "00:00:01.5769215", + "EnqueuedInputs": 122, + "DroppedInputs": 0, + "ConsumedInputs": 122, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 129, + "AppliedAnimationFrames": 59, + "CoalescedAnimationFrames": 1, + "PublicationAttempts": 137, + "BlockedPublications": 0, + "PublishedScenes": 26, + "AcquiredScenes": 26, + "AcknowledgedScenes": 26, + "RenderedScenes": 26, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 1, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 27, + "AnimationFramesInvoked": 27, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 214, + "WorkerWaits": 171, + "WorkerSignalledWakes": 147, + "WorkerTimeoutWakes": 23, + "SceneBuilds": 28, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 93, + "CompositionRenders": 26, + "CompositionAppliedDiffs": 26, + "CompositionInvalidations": 26, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 60, + "CompositionSkippedEmptyAnimationFrames": 33, + "CompositionRenderCallbacks": 26, + "CompositionUnchangedRenderCallbacks": 0 + }, + "originalWidth": 222, + "finalWidth": 342, + "physicalPresentationVerified": false, + "tests": "native engine and graphics V8 runtime suites passed; these do not establish rendering phase conformance", + "source": "https://drafts.csswg.org/resize-observer/#html-event-loop" +} diff --git a/docs/graphics/evidence/kestrel/rejected-publication-wake-pan.json b/docs/graphics/evidence/kestrel/rejected-publication-wake-pan.json new file mode 100644 index 000000000..250b6898d --- /dev/null +++ b/docs/graphics/evidence/kestrel/rejected-publication-wake-pan.json @@ -0,0 +1,33 @@ +{ + "physicalPresentationVerified": false, + "publicationToAcceptance": { + "count": 37, + "medianMilliseconds": 24.120417, + "p95Milliseconds": 31.066, + "maximumMilliseconds": 33.98925 + }, + "acceptanceToDrawCallbackEnd": { + "count": 37, + "medianMilliseconds": 2.555125, + "p95Milliseconds": 3.826541, + "maximumMilliseconds": 8.019375 + }, + "publicationToDrawCallbackEnd": { + "count": 37, + "medianMilliseconds": 26.675542, + "p95Milliseconds": 34.892541, + "maximumMilliseconds": 37.107875 + }, + "inputToPublishedConsumptionWatermark": { + "count": 80, + "medianMilliseconds": 17.0362915, + "p95Milliseconds": 35.039958, + "maximumMilliseconds": 47.333125 + }, + "unmatchedInputCount": 0, + "limitations": [ + "Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived." + ] +} diff --git a/docs/graphics/evidence/kestrel/resize-redraw-boundary.json b/docs/graphics/evidence/kestrel/resize-redraw-boundary.json new file mode 100644 index 000000000..a407803d6 --- /dev/null +++ b/docs/graphics/evidence/kestrel/resize-redraw-boundary.json @@ -0,0 +1,351 @@ +{ + "sourceRecording": { + "filename": "Screen Recording 2026-09-08 at 11.06.24.mov", + "durationSeconds": 8.266667, + "captureRate": 60, + "observation": "Canvas drawing and grid disappear while surrounding HTML remains during resizing." + }, + "fix": "Defer blank bitmap-reset scene while a redraw RAF awaits its next rendering opportunity; then release the hold even if no GPU drawing occurs.", + "runtimeRegressionPassed": true, + "kestrelResizeGeometryPassed": true, + "physicalFlickerFixVerified": false, + "physical60FpsVerified": false, + "resizes": [ + { + "window": [ + 980, + 680 + ], + "canvas": [ + 1610, + 750 + ], + "css": [ + 805, + 375 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 175, + 196, + 805, + 375 + ], + "height": "375px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 196, + 980, + 375 + ], + "height": "375px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 980, + 680 + ], + "height": "680px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1440, + 900 + ], + "canvas": [ + 1932, + 1126 + ], + "css": [ + 966, + 563 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 966, + 563 + ], + "height": "563px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1440, + 563 + ], + "height": "563px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1440, + 900 + ], + "height": "900px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1100, + 740 + ], + "canvas": [ + 1362, + 806 + ], + "css": [ + 681, + 403 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 195, + 205, + 681, + 403 + ], + "height": "403px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1100, + 403 + ], + "height": "403px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1100, + 740 + ], + "height": "740px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + }, + { + "window": [ + 1280, + 800 + ], + "canvas": [ + 1612, + 926 + ], + "css": [ + 806, + 463 + ], + "ancestors": [ + { + "id": "viewport", + "tag": "SECTION", + "rect": [ + 222, + 205, + 806, + 463 + ], + "height": "463px", + "minHeight": "", + "display": "block" + }, + { + "id": "workbench", + "tag": "MAIN", + "rect": [ + 0, + 205, + 1280, + 463 + ], + "height": "463px", + "minHeight": "140px", + "display": "grid" + }, + { + "id": "shell", + "tag": "DIV", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "flex" + }, + { + "id": "", + "tag": "BODY", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + }, + { + "id": "", + "tag": "HTML", + "rect": [ + 0, + 0, + 1280, + 800 + ], + "height": "800px", + "minHeight": "", + "display": "block" + } + ], + "dpr": 2, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } + ] +} diff --git a/docs/graphics/evidence/kestrel/resize-redraw-check.mp4 b/docs/graphics/evidence/kestrel/resize-redraw-check.mp4 new file mode 100644 index 000000000..57adafc6e Binary files /dev/null and b/docs/graphics/evidence/kestrel/resize-redraw-check.mp4 differ diff --git a/docs/graphics/evidence/kestrel/retained-dawn-handoff.json b/docs/graphics/evidence/kestrel/retained-dawn-handoff.json new file mode 100644 index 000000000..9c57fbb25 --- /dev/null +++ b/docs/graphics/evidence/kestrel/retained-dawn-handoff.json @@ -0,0 +1,18 @@ +{ + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "detachedBeforeRetirement": false, + "retainedEndAccessChecks": [ + "initialized output", + "nonempty fence array", + "matching fence/value counts", + "each retained fence exports MTLSharedEvent with nonnull event after publishing function returns" + ], + "nativeSuitesPassed": 2, + "asynchronousConsumerEnabled": false +} diff --git a/docs/graphics/evidence/kestrel/retained-image-sidebar-resize.json b/docs/graphics/evidence/kestrel/retained-image-sidebar-resize.json new file mode 100644 index 000000000..494c3a5c4 --- /dev/null +++ b/docs/graphics/evidence/kestrel/retained-image-sidebar-resize.json @@ -0,0 +1,23 @@ +{ + "validated": true, + "finalWidth": 342, + "publishedScenes": 49, + "drawnScenes": 28, + "maximumDrawCallbackGapMilliseconds": 56.618209, + "nativeSuites": { + "passed": 2, + "failed": 0, + "seconds": 11.66 + }, + "steppedWindowResize": { + "passedGeometries": 4, + "exitCode": 0 + }, + "graphicsDisabledBuildPassed": true, + "physicalPresentationVerified": false, + "limitations": [ + "One sidebar comparison; no sustained physical FPS qualification.", + "Sidebar/window probe preceded the configuration-generation guard; final guard covered by native runtime regression.", + "Continuous visual resize and device-loss behavior still require qualification." + ] +} diff --git a/docs/graphics/evidence/kestrel/retained-metal-presenter.json b/docs/graphics/evidence/kestrel/retained-metal-presenter.json new file mode 100644 index 000000000..24030fbea --- /dev/null +++ b/docs/graphics/evidence/kestrel/retained-metal-presenter.json @@ -0,0 +1,33 @@ +{ + "metal": { + "route": "Dawn-IOSurface-Metal-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "detachedBeforeRetirement": false + }, + "metalDetached": { + "route": "Dawn-IOSurface-Metal-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "detachedBeforeRetirement": true + }, + "cglRegression": { + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "detachedBeforeRetirement": false + }, + "earlyProducerAdmissionEnabled": false +} diff --git a/docs/graphics/evidence/kestrel/runtime-output-snapshot.json b/docs/graphics/evidence/kestrel/runtime-output-snapshot.json new file mode 100644 index 000000000..f96c9c755 --- /dev/null +++ b/docs/graphics/evidence/kestrel/runtime-output-snapshot.json @@ -0,0 +1,19 @@ +{ + "wheelEvents": 40, + "handledWheelEvents": 40, + "resizes": [ + [ + 556, + 614 + ] + ], + "bitmapMutations": [], + "canvas": [ + 1112, + 1228 + ], + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0, + "scope": "Runtime output capture enabled; coherent scene publication is not yet integrated.", + "nativeSuites": "native engine and GPU V8 tests passed (12.44 seconds)" +} diff --git a/docs/graphics/evidence/kestrel/scheduling-trace-validation.json b/docs/graphics/evidence/kestrel/scheduling-trace-validation.json new file mode 100644 index 000000000..8db81d873 --- /dev/null +++ b/docs/graphics/evidence/kestrel/scheduling-trace-validation.json @@ -0,0 +1,37 @@ +{ + "tests": { + "net8.0": { + "passed": 2, + "failed": 0 + }, + "net10.0": { + "passed": 2, + "failed": 0 + } + }, + "runs": [ + { + "validated": false, + "reason": "Injected gesture contaminated by additional pointer events; validator rejected workload", + "stageCounts": { + "frame": 95, + "acquire:Success": 75, + "apply:Applied": 75, + "acquire:Empty": 4 + }, + "physicalPresentationVerified": false + }, + { + "validated": false, + "reason": "Injected gesture contaminated by additional pointer events; validator rejected workload", + "stageCounts": { + "frame": 104, + "acquire:Success": 67, + "apply:Applied": 67, + "acquire:Empty": 4 + }, + "physicalPresentationVerified": false + } + ], + "performanceComparisonQualified": false +} diff --git a/docs/graphics/evidence/kestrel/sidebar-bitmap-reset-gate.json b/docs/graphics/evidence/kestrel/sidebar-bitmap-reset-gate.json new file mode 100644 index 000000000..809a118b8 --- /dev/null +++ b/docs/graphics/evidence/kestrel/sidebar-bitmap-reset-gate.json @@ -0,0 +1,16 @@ +{ + "validated": true, + "gateCounts": { + "bitmap-reset-awaiting-frame": 224 + }, + "finalWidth": 342, + "publishedScenes": 10, + "layoutPasses": 165, + "maximumDrawGapMilliseconds": 502.534, + "temporaryInstrumentationRemoved": true, + "physicalPresentationVerified": false, + "limitations": [ + "Diagnostic logging can perturb timing; use counts for gate attribution, not a performance comparison.", + "Only has_open_gpu_output reasons were traced; other publication deferral branches were not counted." + ] +} diff --git a/docs/graphics/evidence/kestrel/sidebar-drag-baseline.json b/docs/graphics/evidence/kestrel/sidebar-drag-baseline.json new file mode 100644 index 000000000..239a17193 --- /dev/null +++ b/docs/graphics/evidence/kestrel/sidebar-drag-baseline.json @@ -0,0 +1,826 @@ +{ + "geometryVerified": true, + "inputContaminationChecked": false, + "physicalPresentationVerified": false, + "publicationToDrawMedianMilliseconds": 32.622667, + "timeline": { + "traceStarted": 93453145574500, + "timestampFrequency": 1000000000, + "originalWidth": 222, + "width": 342, + "baseline": { + "ContextId": 1, + "Timestamp": 93453143695625, + "Engine": { + "EnqueuedInputs": 4, + "DroppedInputs": 0, + "ConsumedInputs": 4, + "PublishedScenes": 5, + "AcquiredScenes": 4, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 13, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3299831, + "InputEventsDispatched": 17, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 48708, + "LastScenePublicationNanoseconds": 7755709, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 1, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 2, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 6161417, + "LastSceneBuildNanoseconds": 1290958, + "MaximumScenePublicationNanoseconds": 7755709 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 284750, + "MaximumDispatchNanoseconds": 284750, + "LastDispatchSequence": 639244594724050992, + "DispatchedInputs": 1, + "TotalDispatchNanoseconds": 284750 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 2, + "TotalDispatchNanoseconds": 875, + "LastDispatchNanoseconds": 541, + "MaximumDispatchNanoseconds": 541, + "LastTimestampMicroseconds": 93450365455 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 6, + "BlockedPublications": 0, + "AcknowledgedScenes": 4, + "TotalAcknowledgementNanoseconds": 333835542, + "LastAcknowledgementNanoseconds": 44951458, + "MaximumAcknowledgementNanoseconds": 137943875, + "AcknowledgedRevision": 5 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 13, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5439488, + "V8UsedHeapBytes": 3240612, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5439488, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1290240, + "LatestSceneBytes": 127068, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 805280, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920180, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196544, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 131136, + "V8TrustedSpacePhysicalBytes": 524288, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 2, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 3, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 93454666508166, + "Engine": { + "EnqueuedInputs": 124, + "DroppedInputs": 0, + "ConsumedInputs": 124, + "PublishedScenes": 16, + "AcquiredScenes": 15, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1363, + "LayoutPasses": 162, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3633915, + "InputEventsDispatched": 165, + "InputCallbacksInvoked": 110, + "BusiestCanvasWidthMilli": 686000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 48708, + "LastScenePublicationNanoseconds": 5250, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 61, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 60, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 519833, + "LastSceneBuildNanoseconds": 301250, + "MaximumScenePublicationNanoseconds": 7755709 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 159042, + "MaximumDispatchNanoseconds": 5475708, + "LastDispatchSequence": 639244594724051054, + "DispatchedInputs": 63, + "TotalDispatchNanoseconds": 295528750 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 60, + "TotalDispatchNanoseconds": 34585, + "LastDispatchNanoseconds": 708, + "MaximumDispatchNanoseconds": 2167, + "LastTimestampMicroseconds": 93454166146 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 196, + "BlockedPublications": 0, + "AcknowledgedScenes": 15, + "TotalAcknowledgementNanoseconds": 633876583, + "LastAcknowledgementNanoseconds": 29277708, + "MaximumAcknowledgementNanoseconds": 137943875, + "AcknowledgedRevision": 17 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 47, + "AnimationFramesInvoked": 47, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 243, + "WorkerWaits": 218, + "WorkerSignalledWakes": 191, + "WorkerTimeoutWakes": 26, + "SceneBuilds": 12, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5439488, + "V8UsedHeapBytes": 3240612, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5439488, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1290240, + "LatestSceneBytes": 156872, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 805280, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920180, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196544, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 131136, + "V8TrustedSpacePhysicalBytes": 524288, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5081888, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 830, + "StringBytes": 73964, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 14, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 92, + "Renders": 11, + "AppliedDiffs": 11, + "InvalidationCalls": 11, + "DamageRectangles": 31, + "ChangedLayers": 10, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 11, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 58, + "SkippedEmptyAnimationFrames": 34, + "RenderCallbacks": 11, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.5228125", + "EnqueuedInputs": 120, + "DroppedInputs": 0, + "ConsumedInputs": 120, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 154, + "AppliedAnimationFrames": 58, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 190, + "BlockedPublications": 0, + "PublishedScenes": 11, + "AcquiredScenes": 11, + "AcknowledgedScenes": 11, + "RenderedScenes": 11, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 1, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 47, + "AnimationFramesInvoked": 47, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 243, + "WorkerWaits": 218, + "WorkerSignalledWakes": 191, + "WorkerTimeoutWakes": 26, + "SceneBuilds": 12, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 92, + "CompositionRenders": 11, + "CompositionAppliedDiffs": 11, + "CompositionInvalidations": 11, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 58, + "CompositionSkippedEmptyAnimationFrames": 34, + "CompositionRenderCallbacks": 11, + "CompositionUnchangedRenderCallbacks": 0 + }, + "publications": [ + { + "Timestamp": 93453146537750, + "Revision": 6, + "ConsumedInputSequence": 639244594724050993, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93453338438625, + "Revision": 8, + "ConsumedInputSequence": 639244594724051004, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93453420787041, + "Revision": 9, + "ConsumedInputSequence": 639244594724051009, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93453470503208, + "Revision": 10, + "ConsumedInputSequence": 639244594724051012, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93453520405916, + "Revision": 11, + "ConsumedInputSequence": 639244594724051015, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93453604406291, + "Revision": 12, + "ConsumedInputSequence": 639244594724051020, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93453655388583, + "Revision": 13, + "ConsumedInputSequence": 639244594724051023, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93453736854791, + "Revision": 14, + "ConsumedInputSequence": 639244594724051028, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93453786702833, + "Revision": 15, + "ConsumedInputSequence": 639244594724051031, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93453843302500, + "Revision": 16, + "ConsumedInputSequence": 639244594724051034, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93454170322416, + "Revision": 17, + "ConsumedInputSequence": 639244594724051054, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 93453172093458, + "Revision": 6, + "ConsumedInputSequence": 639244594724050993, + "AcceptedTimestamp": 93453164893333 + }, + { + "Timestamp": 93453371708333, + "Revision": 8, + "ConsumedInputSequence": 639244594724051004, + "AcceptedTimestamp": 93453366513458 + }, + { + "Timestamp": 93453454606208, + "Revision": 9, + "ConsumedInputSequence": 639244594724051009, + "AcceptedTimestamp": 93453449801291 + }, + { + "Timestamp": 93453504668791, + "Revision": 10, + "ConsumedInputSequence": 639244594724051012, + "AcceptedTimestamp": 93453501520541 + }, + { + "Timestamp": 93453552789500, + "Revision": 11, + "ConsumedInputSequence": 639244594724051015, + "AcceptedTimestamp": 93453549716916 + }, + { + "Timestamp": 93453637028958, + "Revision": 12, + "ConsumedInputSequence": 639244594724051020, + "AcceptedTimestamp": 93453632410791 + }, + { + "Timestamp": 93453688246500, + "Revision": 13, + "ConsumedInputSequence": 639244594724051023, + "AcceptedTimestamp": 93453683471500 + }, + { + "Timestamp": 93453768185083, + "Revision": 14, + "ConsumedInputSequence": 639244594724051028, + "AcceptedTimestamp": 93453765159458 + }, + { + "Timestamp": 93453818643375, + "Revision": 15, + "ConsumedInputSequence": 639244594724051031, + "AcceptedTimestamp": 93453815323583 + }, + { + "Timestamp": 93453869711166, + "Revision": 16, + "ConsumedInputSequence": 639244594724051034, + "AcceptedTimestamp": 93453865247625 + }, + { + "Timestamp": 93454203905291, + "Revision": 17, + "ConsumedInputSequence": 639244594724051054, + "AcceptedTimestamp": 93454199599083 + } + ], + "physicalPresentationVerified": false + } +} diff --git a/docs/graphics/evidence/kestrel/sidebar-publication-deferrals.json b/docs/graphics/evidence/kestrel/sidebar-publication-deferrals.json new file mode 100644 index 000000000..faa00575a --- /dev/null +++ b/docs/graphics/evidence/kestrel/sidebar-publication-deferrals.json @@ -0,0 +1,871 @@ +{ + "geometryVerified": true, + "inputContaminationChecked": false, + "physicalPresentationVerified": false, + "countsIncludingStartup": { + "PUBTRACE published": 19, + "OPENWHY resize": 137, + "PUBTRACE open-output": 137, + "PUBTRACE pending-output": 30 + }, + "conclusion": "Resize-awaiting-RAF hold dominates observed deferrals; no stale-binding rejections observed. Scheduling correction remains needed.", + "timeline": { + "traceStarted": 93621656297333, + "timestampFrequency": 1000000000, + "originalWidth": 222, + "width": 342, + "baseline": { + "ContextId": 1, + "Timestamp": 93621650028166, + "Engine": { + "EnqueuedInputs": 3, + "DroppedInputs": 0, + "ConsumedInputs": 3, + "PublishedScenes": 5, + "AcquiredScenes": 3, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 13, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3366460, + "InputEventsDispatched": 17, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 101000, + "LastScenePublicationNanoseconds": 6534833, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 1, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 1, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 5077875, + "LastSceneBuildNanoseconds": 1070959, + "MaximumScenePublicationNanoseconds": 6534833 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 249833, + "MaximumDispatchNanoseconds": 249833, + "LastDispatchSequence": 639244596393520972, + "DispatchedInputs": 1, + "TotalDispatchNanoseconds": 249833 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 1, + "TotalDispatchNanoseconds": 625, + "LastDispatchNanoseconds": 625, + "MaximumDispatchNanoseconds": 625, + "LastTimestampMicroseconds": 93618635289 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 6, + "BlockedPublications": 0, + "AcknowledgedScenes": 3, + "TotalAcknowledgementNanoseconds": 320447750, + "LastAcknowledgementNanoseconds": 52920375, + "MaximumAcknowledgementNanoseconds": 141775833, + "AcknowledgedRevision": 5 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 13, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3406740, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1585152, + "LatestSceneBytes": 127068, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 978960, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1919996, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 192032, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 128280, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 2, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 2, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 1, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 1, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 93623228226708, + "Engine": { + "EnqueuedInputs": 124, + "DroppedInputs": 0, + "ConsumedInputs": 124, + "PublishedScenes": 19, + "AcquiredScenes": 17, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1361, + "LayoutPasses": 157, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3461918, + "InputEventsDispatched": 163, + "InputCallbacksInvoked": 107, + "BusiestCanvasWidthMilli": 686000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 101000, + "LastScenePublicationNanoseconds": 12209, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 1, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 60, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 60, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 577542, + "LastSceneBuildNanoseconds": 339459, + "MaximumScenePublicationNanoseconds": 6534833 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 120583, + "MaximumDispatchNanoseconds": 11367667, + "LastDispatchSequence": 639244596393521034, + "DispatchedInputs": 62, + "TotalDispatchNanoseconds": 321023920 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 60, + "TotalDispatchNanoseconds": 39751, + "LastDispatchNanoseconds": 1625, + "MaximumDispatchNanoseconds": 1959, + "LastTimestampMicroseconds": 93622739824 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 186, + "BlockedPublications": 0, + "AcknowledgedScenes": 17, + "TotalAcknowledgementNanoseconds": 701441416, + "LastAcknowledgementNanoseconds": 21054500, + "MaximumAcknowledgementNanoseconds": 141775833, + "AcknowledgedRevision": 19 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 45, + "AnimationFramesInvoked": 45, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 237, + "WorkerWaits": 198, + "WorkerSignalledWakes": 173, + "WorkerTimeoutWakes": 24, + "SceneBuilds": 14, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3406740, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1585152, + "LatestSceneBytes": 156768, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 978960, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1919996, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 192032, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 128280, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5096704, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 986, + "StringBytes": 80522, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 16, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 94, + "Renders": 14, + "AppliedDiffs": 14, + "InvalidationCalls": 14, + "DamageRectangles": 40, + "ChangedLayers": 13, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 14, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 59, + "SkippedEmptyAnimationFrames": 35, + "RenderCallbacks": 14, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.5781985", + "EnqueuedInputs": 121, + "DroppedInputs": 0, + "ConsumedInputs": 121, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 149, + "AppliedAnimationFrames": 59, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 180, + "BlockedPublications": 0, + "PublishedScenes": 14, + "AcquiredScenes": 14, + "AcknowledgedScenes": 14, + "RenderedScenes": 14, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 1, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 45, + "AnimationFramesInvoked": 45, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 237, + "WorkerWaits": 198, + "WorkerSignalledWakes": 173, + "WorkerTimeoutWakes": 24, + "SceneBuilds": 14, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 93, + "CompositionRenders": 14, + "CompositionAppliedDiffs": 14, + "CompositionInvalidations": 14, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 59, + "CompositionSkippedEmptyAnimationFrames": 34, + "CompositionRenderCallbacks": 14, + "CompositionUnchangedRenderCallbacks": 0 + }, + "publications": [ + { + "Timestamp": 93621658905000, + "Revision": 6, + "ConsumedInputSequence": 639244596393520973, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93621731876291, + "Revision": 7, + "ConsumedInputSequence": 639244596393520977, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93621780032958, + "Revision": 8, + "ConsumedInputSequence": 639244596393520980, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93621857657250, + "Revision": 9, + "ConsumedInputSequence": 639244596393520984, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93622089771958, + "Revision": 10, + "ConsumedInputSequence": 639244596393520997, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93622173375208, + "Revision": 11, + "ConsumedInputSequence": 639244596393521002, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93622228904125, + "Revision": 12, + "ConsumedInputSequence": 639244596393521005, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93622323842625, + "Revision": 13, + "ConsumedInputSequence": 639244596393521010, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93622375805625, + "Revision": 14, + "ConsumedInputSequence": 639244596393521013, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93622425777208, + "Revision": 15, + "ConsumedInputSequence": 639244596393521016, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93622605999916, + "Revision": 16, + "ConsumedInputSequence": 639244596393521026, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93622656179125, + "Revision": 17, + "ConsumedInputSequence": 639244596393521029, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93622707786875, + "Revision": 18, + "ConsumedInputSequence": 639244596393521032, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93622748091875, + "Revision": 19, + "ConsumedInputSequence": 639244596393521034, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 93621704965375, + "Revision": 6, + "ConsumedInputSequence": 639244596393520973, + "AcceptedTimestamp": 93621694982208 + }, + { + "Timestamp": 93621758713666, + "Revision": 7, + "ConsumedInputSequence": 639244596393520977, + "AcceptedTimestamp": 93621752544666 + }, + { + "Timestamp": 93621808137000, + "Revision": 8, + "ConsumedInputSequence": 639244596393520980, + "AcceptedTimestamp": 93621802391666 + }, + { + "Timestamp": 93621891627583, + "Revision": 9, + "ConsumedInputSequence": 639244596393520984, + "AcceptedTimestamp": 93621887848125 + }, + { + "Timestamp": 93622124900125, + "Revision": 10, + "ConsumedInputSequence": 639244596393520997, + "AcceptedTimestamp": 93622119954250 + }, + { + "Timestamp": 93622206651958, + "Revision": 11, + "ConsumedInputSequence": 639244596393521002, + "AcceptedTimestamp": 93622201523833 + }, + { + "Timestamp": 93622256818250, + "Revision": 12, + "ConsumedInputSequence": 639244596393521005, + "AcceptedTimestamp": 93622252161458 + }, + { + "Timestamp": 93622355469000, + "Revision": 13, + "ConsumedInputSequence": 639244596393521010, + "AcceptedTimestamp": 93622351671500 + }, + { + "Timestamp": 93622411268875, + "Revision": 14, + "ConsumedInputSequence": 639244596393521013, + "AcceptedTimestamp": 93622406009250 + }, + { + "Timestamp": 93622454211500, + "Revision": 15, + "ConsumedInputSequence": 639244596393521016, + "AcceptedTimestamp": 93622451000333 + }, + { + "Timestamp": 93622641061208, + "Revision": 16, + "ConsumedInputSequence": 639244596393521026, + "AcceptedTimestamp": 93622636401916 + }, + { + "Timestamp": 93622688391625, + "Revision": 17, + "ConsumedInputSequence": 639244596393521029, + "AcceptedTimestamp": 93622684499916 + }, + { + "Timestamp": 93622738328291, + "Revision": 18, + "ConsumedInputSequence": 639244596393521032, + "AcceptedTimestamp": 93622734839708 + }, + { + "Timestamp": 93622772641875, + "Revision": 19, + "ConsumedInputSequence": 639244596393521034, + "AcceptedTimestamp": 93622769146250 + } + ], + "physicalPresentationVerified": false + } +} diff --git a/docs/graphics/evidence/kestrel/sidebar-publication-gate-diagnosis.json b/docs/graphics/evidence/kestrel/sidebar-publication-gate-diagnosis.json new file mode 100644 index 000000000..09a8469d5 --- /dev/null +++ b/docs/graphics/evidence/kestrel/sidebar-publication-gate-diagnosis.json @@ -0,0 +1,18 @@ +{ + "scope": "Instrumented original Kestrel native sidebar run including startup; diagnostic attempt counts, not timing or FPS", + "counts": { + "GPU_GATE resize": 206, + "SCENE_GATE 46": 206, + "SCENE_GATE 431": 34 + }, + "mapping": { + "SCENE_GATE 46": "runtime open GPU output", + "GPU_GATE resize": "bitmap reset awaiting a waiting RAF task", + "SCENE_GATE 431": "captured immutable GPU image not resolved" + }, + "workloadValidated": true, + "physicalPresentationVerified": false, + "logSha256": "fb1201f5eb188e0f53ac385dd82402d5179adbe5b72797fbb30a3db9b9111cba", + "instrumentation": "Temporary fprintf at publication defer/discard branches and runtime open-output reasons; removed after run", + "conclusion": "Resize redraw hold dominates attempt counts; no mailbox-full, invalidated binding, or failed producer branches observed. Logging perturbs timing; not a performance comparison." +} diff --git a/docs/graphics/evidence/kestrel/sidebar-shell-profile.json b/docs/graphics/evidence/kestrel/sidebar-shell-profile.json new file mode 100644 index 000000000..14b2834a3 --- /dev/null +++ b/docs/graphics/evidence/kestrel/sidebar-shell-profile.json @@ -0,0 +1,103 @@ +{ + "validated": true, + "originalWidth": 222, + "finalWidth": 342, + "activeMoveSpanMilliseconds": 1004.312584, + "maximumDrawGapMilliseconds": 512.820917, + "delta": { + "Elapsed": "00:00:01.5261225", + "EnqueuedInputs": 123, + "DroppedInputs": 0, + "ConsumedInputs": 123, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 157, + "AppliedAnimationFrames": 61, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 194, + "BlockedPublications": 0, + "PublishedScenes": 14, + "AcquiredScenes": 14, + "AcknowledgedScenes": 14, + "RenderedScenes": 14, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 1, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 49, + "AnimationFramesInvoked": 49, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 242, + "WorkerWaits": 223, + "WorkerSignalledWakes": 196, + "WorkerTimeoutWakes": 26, + "SceneBuilds": 14, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 91, + "CompositionRenders": 14, + "CompositionAppliedDiffs": 14, + "CompositionInvalidations": 15, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 61, + "CompositionSkippedEmptyAnimationFrames": 30, + "CompositionRenderCallbacks": 15, + "CompositionUnchangedRenderCallbacks": 0 + }, + "stages": { + "apply:images-retained": { + "count": 14, + "medianMilliseconds": 0.0159585, + "maximumMilliseconds": 0.036375 + }, + "apply:cpu-applied": { + "count": 14, + "medianMilliseconds": 15.572416500000001, + "maximumMilliseconds": 22.157375 + }, + "apply:replaced": { + "count": 14, + "medianMilliseconds": 0.002687, + "maximumMilliseconds": 0.005959 + }, + "apply:Applied": { + "count": 14, + "medianMilliseconds": 0.0083545, + "maximumMilliseconds": 0.030167 + } + }, + "stageCounts": { + "frame": 92, + "acquire:Success": 14, + "apply:images-retained": 14, + "apply:cpu-applied": 14, + "apply:replaced": 14, + "apply:Applied": 14, + "acquire:Empty": 1 + }, + "physicalPresentationVerified": false, + "limitations": [ + "Single automated sidebar gesture, no physical presentation qualification.", + "Native publication deferral reasons are not captured by this trace.", + "Earlier failed run ended at width 390 instead of 342; excluded from timing comparison.", + "Five-second OS sample mostly captured waiting threads; not active-resize cost evidence." + ] +} diff --git a/docs/graphics/evidence/kestrel/split-gpu-overlay-publications.json b/docs/graphics/evidence/kestrel/split-gpu-overlay-publications.json new file mode 100644 index 000000000..b3d938747 --- /dev/null +++ b/docs/graphics/evidence/kestrel/split-gpu-overlay-publications.json @@ -0,0 +1,978 @@ +{ + "method": "Temporary native publish_scene trace during unchanged Kestrel native wheel probe", + "limitation": "Producer publication trace; not a captured display frame or timing benchmark. GPU serial and canvas generation are independent counters.", + "overlayAdvancedWithSameGpuImage": [ + [ + 15, + 16 + ], + [ + 39, + 40 + ], + [ + 54, + 55 + ], + [ + 60, + 61 + ], + [ + 64, + 65 + ] + ], + "publications": [ + { + "revision": 1, + "gpu": [], + "layers": [] + }, + { + "revision": 2, + "gpu": [], + "layers": [] + }, + { + "revision": 3, + "gpu": [], + "layers": [] + }, + { + "revision": 4, + "gpu": [], + "layers": [] + }, + { + "revision": 5, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 6 + ] + ] + }, + { + "revision": 6, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 6 + ] + ] + }, + { + "revision": 7, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 6 + ] + ] + }, + { + "revision": 8, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 6 + ] + ] + }, + { + "revision": 9, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 6 + ] + ] + }, + { + "revision": 10, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 6 + ] + ] + }, + { + "revision": 11, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 6 + ] + ] + }, + { + "revision": 12, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 6 + ] + ] + }, + { + "revision": 13, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 6 + ] + ] + }, + { + "revision": 14, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 6 + ] + ] + }, + { + "revision": 15, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 6 + ] + ] + }, + { + "revision": 16, + "gpu": [ + [ + 9, + 6 + ] + ], + "layers": [ + [ + 163, + 7 + ] + ] + }, + { + "revision": 17, + "gpu": [ + [ + 9, + 7 + ] + ], + "layers": [ + [ + 163, + 8 + ] + ] + }, + { + "revision": 18, + "gpu": [ + [ + 9, + 8 + ] + ], + "layers": [ + [ + 163, + 8 + ] + ] + }, + { + "revision": 19, + "gpu": [ + [ + 9, + 8 + ] + ], + "layers": [ + [ + 163, + 8 + ] + ] + }, + { + "revision": 20, + "gpu": [ + [ + 9, + 9 + ] + ], + "layers": [ + [ + 163, + 9 + ] + ] + }, + { + "revision": 21, + "gpu": [ + [ + 9, + 9 + ] + ], + "layers": [ + [ + 163, + 9 + ] + ] + }, + { + "revision": 22, + "gpu": [ + [ + 9, + 10 + ] + ], + "layers": [ + [ + 163, + 10 + ] + ] + }, + { + "revision": 23, + "gpu": [ + [ + 9, + 11 + ] + ], + "layers": [ + [ + 163, + 11 + ] + ] + }, + { + "revision": 24, + "gpu": [ + [ + 9, + 11 + ] + ], + "layers": [ + [ + 163, + 11 + ] + ] + }, + { + "revision": 25, + "gpu": [ + [ + 9, + 11 + ] + ], + "layers": [ + [ + 163, + 11 + ] + ] + }, + { + "revision": 26, + "gpu": [ + [ + 9, + 13 + ] + ], + "layers": [ + [ + 163, + 13 + ] + ] + }, + { + "revision": 27, + "gpu": [ + [ + 9, + 13 + ] + ], + "layers": [ + [ + 163, + 13 + ] + ] + }, + { + "revision": 28, + "gpu": [ + [ + 9, + 13 + ] + ], + "layers": [ + [ + 163, + 13 + ] + ] + }, + { + "revision": 29, + "gpu": [ + [ + 9, + 14 + ] + ], + "layers": [ + [ + 163, + 14 + ] + ] + }, + { + "revision": 30, + "gpu": [ + [ + 9, + 15 + ] + ], + "layers": [ + [ + 163, + 15 + ] + ] + }, + { + "revision": 31, + "gpu": [ + [ + 9, + 15 + ] + ], + "layers": [ + [ + 163, + 15 + ] + ] + }, + { + "revision": 32, + "gpu": [ + [ + 9, + 16 + ] + ], + "layers": [ + [ + 163, + 16 + ] + ] + }, + { + "revision": 33, + "gpu": [ + [ + 9, + 17 + ] + ], + "layers": [ + [ + 163, + 17 + ] + ] + }, + { + "revision": 34, + "gpu": [ + [ + 9, + 17 + ] + ], + "layers": [ + [ + 163, + 17 + ] + ] + }, + { + "revision": 35, + "gpu": [ + [ + 9, + 18 + ] + ], + "layers": [ + [ + 163, + 18 + ] + ] + }, + { + "revision": 36, + "gpu": [ + [ + 9, + 18 + ] + ], + "layers": [ + [ + 163, + 18 + ] + ] + }, + { + "revision": 37, + "gpu": [ + [ + 9, + 19 + ] + ], + "layers": [ + [ + 163, + 19 + ] + ] + }, + { + "revision": 38, + "gpu": [ + [ + 9, + 20 + ] + ], + "layers": [ + [ + 163, + 20 + ] + ] + }, + { + "revision": 39, + "gpu": [ + [ + 9, + 20 + ] + ], + "layers": [ + [ + 163, + 20 + ] + ] + }, + { + "revision": 40, + "gpu": [ + [ + 9, + 20 + ] + ], + "layers": [ + [ + 163, + 21 + ] + ] + }, + { + "revision": 41, + "gpu": [ + [ + 9, + 21 + ] + ], + "layers": [ + [ + 163, + 21 + ] + ] + }, + { + "revision": 42, + "gpu": [ + [ + 9, + 22 + ] + ], + "layers": [ + [ + 163, + 22 + ] + ] + }, + { + "revision": 43, + "gpu": [ + [ + 9, + 22 + ] + ], + "layers": [ + [ + 163, + 22 + ] + ] + }, + { + "revision": 44, + "gpu": [ + [ + 9, + 23 + ] + ], + "layers": [ + [ + 163, + 23 + ] + ] + }, + { + "revision": 45, + "gpu": [ + [ + 9, + 23 + ] + ], + "layers": [ + [ + 163, + 23 + ] + ] + }, + { + "revision": 46, + "gpu": [ + [ + 9, + 23 + ] + ], + "layers": [ + [ + 163, + 23 + ] + ] + }, + { + "revision": 47, + "gpu": [ + [ + 9, + 24 + ] + ], + "layers": [ + [ + 163, + 25 + ] + ] + }, + { + "revision": 48, + "gpu": [ + [ + 9, + 25 + ] + ], + "layers": [ + [ + 163, + 25 + ] + ] + }, + { + "revision": 49, + "gpu": [ + [ + 9, + 25 + ] + ], + "layers": [ + [ + 163, + 25 + ] + ] + }, + { + "revision": 50, + "gpu": [ + [ + 9, + 26 + ] + ], + "layers": [ + [ + 163, + 26 + ] + ] + }, + { + "revision": 51, + "gpu": [ + [ + 9, + 26 + ] + ], + "layers": [ + [ + 163, + 26 + ] + ] + }, + { + "revision": 52, + "gpu": [ + [ + 9, + 27 + ] + ], + "layers": [ + [ + 163, + 27 + ] + ] + }, + { + "revision": 53, + "gpu": [ + [ + 9, + 27 + ] + ], + "layers": [ + [ + 163, + 27 + ] + ] + }, + { + "revision": 54, + "gpu": [ + [ + 9, + 28 + ] + ], + "layers": [ + [ + 163, + 28 + ] + ] + }, + { + "revision": 55, + "gpu": [ + [ + 9, + 28 + ] + ], + "layers": [ + [ + 163, + 29 + ] + ] + }, + { + "revision": 56, + "gpu": [ + [ + 9, + 29 + ] + ], + "layers": [ + [ + 163, + 29 + ] + ] + }, + { + "revision": 57, + "gpu": [ + [ + 9, + 29 + ] + ], + "layers": [ + [ + 163, + 29 + ] + ] + }, + { + "revision": 58, + "gpu": [ + [ + 9, + 30 + ] + ], + "layers": [ + [ + 163, + 30 + ] + ] + }, + { + "revision": 59, + "gpu": [ + [ + 9, + 31 + ] + ], + "layers": [ + [ + 163, + 31 + ] + ] + }, + { + "revision": 60, + "gpu": [ + [ + 9, + 31 + ] + ], + "layers": [ + [ + 163, + 31 + ] + ] + }, + { + "revision": 61, + "gpu": [ + [ + 9, + 31 + ] + ], + "layers": [ + [ + 163, + 32 + ] + ] + }, + { + "revision": 62, + "gpu": [ + [ + 9, + 32 + ] + ], + "layers": [ + [ + 163, + 33 + ] + ] + }, + { + "revision": 63, + "gpu": [ + [ + 9, + 33 + ] + ], + "layers": [ + [ + 163, + 33 + ] + ] + }, + { + "revision": 64, + "gpu": [ + [ + 9, + 33 + ] + ], + "layers": [ + [ + 163, + 33 + ] + ] + }, + { + "revision": 65, + "gpu": [ + [ + 9, + 33 + ] + ], + "layers": [ + [ + 163, + 34 + ] + ] + }, + { + "revision": 66, + "gpu": [ + [ + 9, + 34 + ] + ], + "layers": [ + [ + 163, + 34 + ] + ] + } + ] +} diff --git a/docs/graphics/evidence/kestrel/startup.json b/docs/graphics/evidence/kestrel/startup.json new file mode 100644 index 000000000..a34fdd86c --- /dev/null +++ b/docs/graphics/evidence/kestrel/startup.json @@ -0,0 +1,13 @@ +{ + "application": "Unmodified Kestrel-CAD/Kestrel-CAD.html", + "documentSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "host": "macOS NativeWebSceneView", + "navigatorGpuExposed": true, + "applicationReady": true, + "renderer": "Canvas 2D · Compatibility", + "webGpuStartupPassed": false, + "exitCode": 1, + "fallbackReason": "d.pushErrorScope is not a function", + "interactionQualified": false, + "resizeQualified": false +} diff --git a/docs/graphics/evidence/kestrel/system-font-probe-cache.json b/docs/graphics/evidence/kestrel/system-font-probe-cache.json new file mode 100644 index 000000000..c7c5eff54 --- /dev/null +++ b/docs/graphics/evidence/kestrel/system-font-probe-cache.json @@ -0,0 +1,60 @@ +{ + "change": "Bound system font-name existence probes, preserving live web-font registry precedence", + "sidebar": { + "validated": true, + "cpuApplicationMedianMilliseconds": 2.926958, + "cpuApplicationMaximumMilliseconds": 12.344041, + "drawnScenes": 61, + "maximumDrawCallbackGapMilliseconds": 39.091083 + }, + "pan": { + "physicalPresentationVerified": false, + "publicationToAcceptance": { + "count": 64, + "medianMilliseconds": 14.408978999999999, + "p95Milliseconds": 16.598875, + "maximumMilliseconds": 17.79075 + }, + "acceptanceToDrawCallbackEnd": { + "count": 64, + "medianMilliseconds": 0.9060625, + "p95Milliseconds": 1.524167, + "maximumMilliseconds": 3.365 + }, + "publicationToDrawCallbackEnd": { + "count": 64, + "medianMilliseconds": 15.206479, + "p95Milliseconds": 17.412167, + "maximumMilliseconds": 18.852459 + }, + "inputToPublishedConsumptionWatermark": { + "count": 80, + "medianMilliseconds": 12.6670835, + "p95Milliseconds": 24.210875, + "maximumMilliseconds": 30.659 + }, + "unmatchedInputCount": 0, + "limitations": [ + "Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived." + ] + }, + "tests": { + "net8.0": { + "regularPassed": 68, + "nativeIntegrationPassed": 2, + "failed": 0 + }, + "net10.0": { + "regularPassed": 68, + "nativeIntegrationPassed": 2, + "failed": 0 + } + }, + "limitations": [ + "One validated pan and sidebar run; no statistical or physical 60 FPS qualification.", + "Cache follows existing process system-font resolution policy; live OS font-install notifications are not introduced.", + "Initial native integration skip was rerun with the native library and both cases passed on each framework." + ] +} diff --git a/docs/graphics/evidence/kestrel/theme-recascade-subset.json b/docs/graphics/evidence/kestrel/theme-recascade-subset.json new file mode 100644 index 000000000..d406a6425 --- /dev/null +++ b/docs/graphics/evidence/kestrel/theme-recascade-subset.json @@ -0,0 +1,48 @@ +{ + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "52e16847a409688d6229b2ffd5b99ed14b505b99fd8e56861ffae187aacff57c", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=3d61bda1b7b46c42159bbeb392e3d9f465e2cf334c971ee63f19a8d418768eef", + "chromiumIdentity": null, + "startedAt": "2026-09-08T07:46:41.353528+00:00", + "duration": "00:00:00.1863388", + "selection": "candidate", + "summary": { + "tests": 1, + "passed": 1, + "failed": 0, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 2, + "subtestsPassed": 2, + "subtestsFailed": 0 + }, + "results": [ + { + "path": "contracts/css-root-theme-interaction-recascade.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.1841078", + "message": null, + "subtests": [ + { + "name": "Conditional root custom properties track theme and descendant state changes", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Pointer hover resolves the active root theme", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] +} \ No newline at end of file diff --git a/docs/graphics/evidence/kestrel/two-canvas-publication.json b/docs/graphics/evidence/kestrel/two-canvas-publication.json new file mode 100644 index 000000000..6835de46f --- /dev/null +++ b/docs/graphics/evidence/kestrel/two-canvas-publication.json @@ -0,0 +1,27 @@ +{ + "test": "test_native_gpu_scene_leases", + "coverage": [ + "Production capture/commit with a first-ever pending second canvas", + "Second producer completes before first: no partial publication", + "First producer completes before second: no partial publication", + "Published GPU paint indices map to exact canvas identities/content serials", + "Failed second producer rejected while first remains pending", + "Same-size bitmap reset of second dependency rejected while first remains pending", + "Previous complete scene remains intact" + ], + "before": { + "failure": "Later failed producer not inspected while first dependency pending", + "line": 208, + "gpuRuntimePassed": false + }, + "after": { + "nativeEnginePassed": true, + "nativeEngineSeconds": 12.55, + "gpuRuntimePassed": true, + "gpuRuntimeSeconds": 0.67 + }, + "limitations": [ + "Controlled native snapshots; no physical hardware queue stall.", + "No physical presentation or browser performance qualification." + ] +} diff --git a/docs/graphics/evidence/kestrel/unimported-scene-release.json b/docs/graphics/evidence/kestrel/unimported-scene-release.json new file mode 100644 index 000000000..48cf2703d --- /dev/null +++ b/docs/graphics/evidence/kestrel/unimported-scene-release.json @@ -0,0 +1,1024 @@ +{ + "workloadValidated": true, + "physicalPresentationVerified": false, + "publicationToDrawMedianMilliseconds": 63.4059995, + "gpuFixture": { + "route": "Dawn-IOSurface-CGL-Ganesh", + "renderedFrames": 32, + "imports": 2, + "gpuRetirementCompleted": true, + "explicitTransportCopies": 0, + "diagnosticReadbacks": 8, + "physicalPresentationVerified": false, + "detachedBeforeRetirement": false + }, + "focusedTests": { + "net8": 23, + "net10": 23, + "skipped": 0 + }, + "records": { + "Kestrel pan performance": { + "elapsedMilliseconds": 1853.9381, + "baseline": { + "ContextId": 1, + "Timestamp": 92327801846666, + "Engine": { + "EnqueuedInputs": 33, + "DroppedInputs": 0, + "ConsumedInputs": 33, + "PublishedScenes": 12, + "AcquiredScenes": 12, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1320, + "LayoutPasses": 12, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 6262377, + "InputEventsDispatched": 147, + "InputCallbacksInvoked": 4, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 718084, + "LastScenePublicationNanoseconds": 643042, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 10, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 11, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 10, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 893375, + "LastSceneBuildNanoseconds": 472375, + "MaximumScenePublicationNanoseconds": 1830375 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 170542, + "MaximumDispatchNanoseconds": 9741958, + "LastDispatchSequence": 639244583466073983, + "DispatchedInputs": 12, + "TotalDispatchNanoseconds": 14427957 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 10, + "TotalDispatchNanoseconds": 8000, + "LastDispatchNanoseconds": 583, + "MaximumDispatchNanoseconds": 1458, + "LastTimestampMicroseconds": 92325256795 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 16, + "BlockedPublications": 2, + "AcknowledgedScenes": 12, + "TotalAcknowledgementNanoseconds": 645156501, + "LastAcknowledgementNanoseconds": 50017417, + "MaximumAcknowledgementNanoseconds": 160864209, + "AcknowledgedRevision": 12 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3012396, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1843200, + "LatestSceneBytes": 127260, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 578968, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1928036, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 190720, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127200, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 9, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 92329657888208, + "Engine": { + "EnqueuedInputs": 144, + "DroppedInputs": 0, + "ConsumedInputs": 144, + "PublishedScenes": 42, + "AcquiredScenes": 42, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1385, + "LayoutPasses": 77, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 6262377, + "InputEventsDispatched": 237, + "InputCallbacksInvoked": 77, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 718084, + "LastScenePublicationNanoseconds": 1084, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 56, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 45, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 39, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 505000, + "LastSceneBuildNanoseconds": 322250, + "MaximumScenePublicationNanoseconds": 1830375 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 2844750, + "MaximumDispatchNanoseconds": 9741958, + "LastDispatchSequence": 639244583466074065, + "DispatchedInputs": 48, + "TotalDispatchNanoseconds": 154527040 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 39, + "TotalDispatchNanoseconds": 28625, + "LastDispatchNanoseconds": 750, + "MaximumDispatchNanoseconds": 1792, + "LastTimestampMicroseconds": 92329154453 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 82, + "BlockedPublications": 2, + "AcknowledgedScenes": 42, + "TotalAcknowledgementNanoseconds": 2214234543, + "LastAcknowledgementNanoseconds": 48713916, + "MaximumAcknowledgementNanoseconds": 160864209, + "AcknowledgedRevision": 42 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 29, + "AnimationFramesInvoked": 29, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 119, + "WorkerWaits": 192, + "WorkerSignalledWakes": 156, + "WorkerTimeoutWakes": 35, + "SceneBuilds": 30, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3012396, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1843200, + "LatestSceneBytes": 160176, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 578968, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1928036, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 190720, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127200, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5970848, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 1790, + "StringBytes": 111048, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 25, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 65, + "Renders": 16, + "AppliedDiffs": 30, + "InvalidationCalls": 16, + "DamageRectangles": 85, + "ChangedLayers": 28, + "EmptyDamageDiffs": 1, + "PartialDamageDiffs": 29, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 29, + "SkippedEmptyAnimationFrames": 36, + "RenderCallbacks": 16, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.8560415", + "EnqueuedInputs": 111, + "DroppedInputs": 0, + "ConsumedInputs": 111, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 65, + "AppliedAnimationFrames": 29, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 66, + "BlockedPublications": 0, + "PublishedScenes": 30, + "AcquiredScenes": 30, + "AcknowledgedScenes": 30, + "RenderedScenes": 16, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 29, + "AnimationFramesInvoked": 29, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 119, + "WorkerWaits": 192, + "WorkerSignalledWakes": 156, + "WorkerTimeoutWakes": 35, + "SceneBuilds": 30, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 65, + "CompositionRenders": 16, + "CompositionAppliedDiffs": 30, + "CompositionInvalidations": 16, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 29, + "CompositionSkippedEmptyAnimationFrames": 36, + "CompositionRenderCallbacks": 16, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "Kestrel pan composition timeline": { + "timestampFrequency": 1000000000, + "traceStarted": 92327803995333, + "publications": [ + { + "Timestamp": 92327809659708, + "Revision": 13, + "ConsumedInputSequence": 639244583466073984, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92327843448916, + "Revision": 14, + "ConsumedInputSequence": 639244583466073986, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92327867143625, + "Revision": 15, + "ConsumedInputSequence": 639244583466073988, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92327947126000, + "Revision": 16, + "ConsumedInputSequence": 639244583466073993, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92327962436250, + "Revision": 17, + "ConsumedInputSequence": 639244583466073994, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328036443791, + "Revision": 18, + "ConsumedInputSequence": 639244583466073998, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328058805333, + "Revision": 19, + "ConsumedInputSequence": 639244583466073999, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328124530583, + "Revision": 20, + "ConsumedInputSequence": 639244583466074003, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328150205083, + "Revision": 21, + "ConsumedInputSequence": 639244583466074005, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328230525208, + "Revision": 22, + "ConsumedInputSequence": 639244583466074009, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328246573875, + "Revision": 23, + "ConsumedInputSequence": 639244583466074010, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328324539875, + "Revision": 24, + "ConsumedInputSequence": 639244583466074015, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328347164000, + "Revision": 25, + "ConsumedInputSequence": 639244583466074016, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328427133583, + "Revision": 26, + "ConsumedInputSequence": 639244583466074021, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328446216250, + "Revision": 27, + "ConsumedInputSequence": 639244583466074022, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328515350000, + "Revision": 28, + "ConsumedInputSequence": 639244583466074026, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328529894000, + "Revision": 29, + "ConsumedInputSequence": 639244583466074027, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328583065083, + "Revision": 30, + "ConsumedInputSequence": 639244583466074030, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328596036833, + "Revision": 31, + "ConsumedInputSequence": 639244583466074031, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328665922333, + "Revision": 32, + "ConsumedInputSequence": 639244583466074035, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328680074250, + "Revision": 33, + "ConsumedInputSequence": 639244583466074036, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328760149250, + "Revision": 34, + "ConsumedInputSequence": 639244583466074041, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328779344625, + "Revision": 35, + "ConsumedInputSequence": 639244583466074042, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328849520708, + "Revision": 36, + "ConsumedInputSequence": 639244583466074046, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328863884250, + "Revision": 37, + "ConsumedInputSequence": 639244583466074047, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328946666250, + "Revision": 38, + "ConsumedInputSequence": 639244583466074052, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92328965041083, + "Revision": 39, + "ConsumedInputSequence": 639244583466074053, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92329073289583, + "Revision": 40, + "ConsumedInputSequence": 639244583466074059, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92329091177000, + "Revision": 41, + "ConsumedInputSequence": 639244583466074060, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 92329163225750, + "Revision": 42, + "ConsumedInputSequence": 639244583466074065, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 92327849457416, + "Revision": 13, + "ConsumedInputSequence": 639244583466073984, + "AcceptedTimestamp": 92327824545583 + }, + { + "Timestamp": 92327936014125, + "Revision": 15, + "ConsumedInputSequence": 639244583466073988, + "AcceptedTimestamp": 92327932927041 + }, + { + "Timestamp": 92328028248416, + "Revision": 17, + "ConsumedInputSequence": 639244583466073994, + "AcceptedTimestamp": 92328025749958 + }, + { + "Timestamp": 92328114467750, + "Revision": 19, + "ConsumedInputSequence": 639244583466073999, + "AcceptedTimestamp": 92328111769875 + }, + { + "Timestamp": 92328222109541, + "Revision": 21, + "ConsumedInputSequence": 639244583466074005, + "AcceptedTimestamp": 92328219266458 + }, + { + "Timestamp": 92328311137333, + "Revision": 23, + "ConsumedInputSequence": 639244583466074010, + "AcceptedTimestamp": 92328308649333 + }, + { + "Timestamp": 92328414892833, + "Revision": 25, + "ConsumedInputSequence": 639244583466074016, + "AcceptedTimestamp": 92328411638833 + }, + { + "Timestamp": 92328507798666, + "Revision": 27, + "ConsumedInputSequence": 639244583466074022, + "AcceptedTimestamp": 92328504533291 + }, + { + "Timestamp": 92328574903416, + "Revision": 29, + "ConsumedInputSequence": 639244583466074027, + "AcceptedTimestamp": 92328571306208 + }, + { + "Timestamp": 92328658040958, + "Revision": 31, + "ConsumedInputSequence": 639244583466074031, + "AcceptedTimestamp": 92328655253250 + }, + { + "Timestamp": 92328747463291, + "Revision": 33, + "ConsumedInputSequence": 639244583466074036, + "AcceptedTimestamp": 92328744854333 + }, + { + "Timestamp": 92328841593166, + "Revision": 35, + "ConsumedInputSequence": 639244583466074042, + "AcceptedTimestamp": 92328839045625 + }, + { + "Timestamp": 92328938770208, + "Revision": 37, + "ConsumedInputSequence": 639244583466074047, + "AcceptedTimestamp": 92328933807375 + }, + { + "Timestamp": 92329061749416, + "Revision": 39, + "ConsumedInputSequence": 639244583466074053, + "AcceptedTimestamp": 92329058047750 + }, + { + "Timestamp": 92329151731500, + "Revision": 41, + "ConsumedInputSequence": 639244583466074060, + "AcceptedTimestamp": 92329146222541 + }, + { + "Timestamp": 92329215026708, + "Revision": 42, + "ConsumedInputSequence": 639244583466074065, + "AcceptedTimestamp": 92329211940708 + } + ], + "drawCallbackCompletions": [ + 92327849061000, + 92327936011375, + 92328028247083, + 92328114462000, + 92328222108083, + 92328311136208, + 92328414885583, + 92328507793166, + 92328574896375, + 92328658031125, + 92328747459458, + 92328841591791, + 92328938766000, + 92329061746291, + 92329151728083, + 92329215021166 + ], + "physicalPresentationVerified": false + } + } +} diff --git a/docs/graphics/evidence/kestrel/upstream-hover-subset.json b/docs/graphics/evidence/kestrel/upstream-hover-subset.json new file mode 100644 index 000000000..bba2a87d6 --- /dev/null +++ b/docs/graphics/evidence/kestrel/upstream-hover-subset.json @@ -0,0 +1,48 @@ +{ + "schema": "webscene-wpt-subset-result-v3", + "profile": "webscene-component-1", + "profileSha256": "52e16847a409688d6229b2ffd5b99ed14b505b99fd8e56861ffae187aacff57c", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "engine": "native", + "nativeEngineIdentity": "abi=3;sha256=3d61bda1b7b46c42159bbeb392e3d9f465e2cf334c971ee63f19a8d418768eef", + "chromiumIdentity": null, + "startedAt": "2026-09-08T07:46:58.046407+00:00", + "duration": "00:00:00.2269505", + "selection": "required", + "summary": { + "tests": 1, + "passed": 1, + "failed": 0, + "timedOut": 0, + "harnessErrors": 0, + "subtests": 2, + "subtestsPassed": 2, + "subtestsFailed": 0 + }, + "results": [ + { + "path": "css/selectors/hover-002.html", + "type": "testharness", + "status": "PASS", + "duration": "00:00:00.2252450", + "message": null, + "subtests": [ + { + "name": "Hover #hovered element should make it go green", + "status": "PASS", + "message": null, + "stack": null + }, + { + "name": "Hover #hoveredContents child should make it go green", + "status": "PASS", + "message": null, + "stack": null + } + ], + "artifacts": null, + "chromiumOracle": null + } + ] +} \ No newline at end of file diff --git a/docs/graphics/evidence/kestrel/validated-pan-workload.json b/docs/graphics/evidence/kestrel/validated-pan-workload.json new file mode 100644 index 000000000..6aef225f3 --- /dev/null +++ b/docs/graphics/evidence/kestrel/validated-pan-workload.json @@ -0,0 +1,1352 @@ +{ + "workloadValidation": "passed; ordered subsequence of 80 injected moves with right-button boundaries", + "physicalPresentationVerified": false, + "limitations": [ + "Single investigative run, not sustained-60fps qualification.", + "Coordinate matching cannot prove input provenance for identical coordinates.", + "Counters include 500ms settling." + ], + "records": { + "Kestrel pan performance": { + "elapsedMilliseconds": 1861.0584, + "baseline": { + "ContextId": 1, + "Timestamp": 91483799466750, + "Engine": { + "EnqueuedInputs": 20, + "DroppedInputs": 0, + "ConsumedInputs": 20, + "PublishedScenes": 10, + "AcquiredScenes": 10, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 11072500, + "InputEventsDispatched": 81, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 761583, + "LastScenePublicationNanoseconds": 2011333, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 5, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 6, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 7, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 1591625, + "LastSceneBuildNanoseconds": 306833, + "MaximumScenePublicationNanoseconds": 2011333 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 125041, + "MaximumDispatchNanoseconds": 386709, + "LastDispatchSequence": 639244575026634813, + "DispatchedInputs": 7, + "TotalDispatchNanoseconds": 1732875 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 7, + "TotalDispatchNanoseconds": 5708, + "LastDispatchNanoseconds": 750, + "MaximumDispatchNanoseconds": 1625, + "LastTimestampMicroseconds": 91481016494 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 10, + "BlockedPublications": 0, + "AcknowledgedScenes": 10, + "TotalAcknowledgementNanoseconds": 641657792, + "LastAcknowledgementNanoseconds": 26196042, + "MaximumAcknowledgementNanoseconds": 200053083, + "AcknowledgedRevision": 10 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 2752988, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 102484, + "V8PeakMallocedBytes": 1798144, + "LatestSceneBytes": 127068, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 328512, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1919084, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 190720, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127200, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 10, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 1, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 1, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 91485662730125, + "Engine": { + "EnqueuedInputs": 143, + "DroppedInputs": 0, + "ConsumedInputs": 143, + "PublishedScenes": 53, + "AcquiredScenes": 53, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1406, + "LayoutPasses": 98, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 11072500, + "InputEventsDispatched": 205, + "InputCallbacksInvoked": 108, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 761583, + "LastScenePublicationNanoseconds": 460708, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 34, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 57, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 48, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 566166, + "LastSceneBuildNanoseconds": 340042, + "MaximumScenePublicationNanoseconds": 3829125 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 3816667, + "MaximumDispatchNanoseconds": 12960625, + "LastDispatchSequence": 639244575026634895, + "DispatchedInputs": 60, + "TotalDispatchNanoseconds": 216752166 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 48, + "TotalDispatchNanoseconds": 45580, + "LastDispatchNanoseconds": 1208, + "MaximumDispatchNanoseconds": 2250, + "LastTimestampMicroseconds": 91485169564 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 107, + "BlockedPublications": 1, + "AcknowledgedScenes": 53, + "TotalAcknowledgementNanoseconds": 2960984580, + "LastAcknowledgementNanoseconds": 40653291, + "MaximumAcknowledgementNanoseconds": 200053083, + "AcknowledgedRevision": 53 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 37, + "AnimationFramesInvoked": 37, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 161, + "WorkerWaits": 207, + "WorkerSignalledWakes": 172, + "WorkerTimeoutWakes": 34, + "SceneBuilds": 43, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 2752988, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 102484, + "V8PeakMallocedBytes": 1798144, + "LatestSceneBytes": 160228, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 328512, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1919084, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 190720, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127200, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5970848, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 2004, + "StringBytes": 118870, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 53, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 70, + "Renders": 43, + "AppliedDiffs": 43, + "InvalidationCalls": 43, + "DamageRectangles": 116, + "ChangedLayers": 37, + "EmptyDamageDiffs": 1, + "PartialDamageDiffs": 42, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 41, + "SkippedEmptyAnimationFrames": 29, + "RenderCallbacks": 43, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.8632633", + "EnqueuedInputs": 123, + "DroppedInputs": 0, + "ConsumedInputs": 123, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 90, + "AppliedAnimationFrames": 41, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 97, + "BlockedPublications": 1, + "PublishedScenes": 43, + "AcquiredScenes": 43, + "AcknowledgedScenes": 43, + "RenderedScenes": 43, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 37, + "AnimationFramesInvoked": 37, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 161, + "WorkerWaits": 207, + "WorkerSignalledWakes": 172, + "WorkerTimeoutWakes": 34, + "SceneBuilds": 43, + "NoDamageSceneBuilds": 1, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 69, + "CompositionRenders": 43, + "CompositionAppliedDiffs": 43, + "CompositionInvalidations": 43, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 41, + "CompositionSkippedEmptyAnimationFrames": 28, + "CompositionRenderCallbacks": 43, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "Kestrel pan diagnostics": { + "events": [ + { + "type": "pointerdown", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 91483814.370041, + "panning": false + }, + { + "type": "pointermove", + "x": 629, + "y": 437.5, + "button": 2, + "buttons": 2, + "time": 91483818.6405, + "panning": true + }, + { + "type": "pointermove", + "x": 633, + "y": 438.5, + "button": 2, + "buttons": 2, + "time": 91483832.4725, + "panning": true + }, + { + "type": "pointermove", + "x": 637, + "y": 439.5, + "button": 2, + "buttons": 2, + "time": 91483856.719041, + "panning": true + }, + { + "type": "pointermove", + "x": 641, + "y": 440.5, + "button": 2, + "buttons": 2, + "time": 91483861.420041, + "panning": true + }, + { + "type": "pointermove", + "x": 649, + "y": 442.5, + "button": 2, + "buttons": 2, + "time": 91483896.079916, + "panning": true + }, + { + "type": "pointermove", + "x": 657, + "y": 444.5, + "button": 2, + "buttons": 2, + "time": 91483931.817875, + "panning": true + }, + { + "type": "pointermove", + "x": 665, + "y": 446.5, + "button": 2, + "buttons": 2, + "time": 91483968.912041, + "panning": true + }, + { + "type": "pointermove", + "x": 673, + "y": 448.5, + "button": 2, + "buttons": 2, + "time": 91484008.815083, + "panning": true + }, + { + "type": "pointermove", + "x": 677, + "y": 449.5, + "button": 2, + "buttons": 2, + "time": 91484012.736208, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 91484051.443291, + "panning": true + }, + { + "type": "pointermove", + "x": 693, + "y": 453.5, + "button": 2, + "buttons": 2, + "time": 91484093.290875, + "panning": true + }, + { + "type": "pointermove", + "x": 697, + "y": 454.5, + "button": 2, + "buttons": 2, + "time": 91484096.036208, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 91484111.971833, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 91484147.094125, + "panning": true + }, + { + "type": "pointermove", + "x": 717, + "y": 459.5, + "button": 2, + "buttons": 2, + "time": 91484180.855166, + "panning": true + }, + { + "type": "pointermove", + "x": 725, + "y": 461.5, + "button": 2, + "buttons": 2, + "time": 91484216.177208, + "panning": true + }, + { + "type": "pointermove", + "x": 733, + "y": 463.5, + "button": 2, + "buttons": 2, + "time": 91484249.165541, + "panning": true + }, + { + "type": "pointermove", + "x": 737, + "y": 464.5, + "button": 2, + "buttons": 2, + "time": 91484269.306375, + "panning": true + }, + { + "type": "pointermove", + "x": 745, + "y": 466.5, + "button": 2, + "buttons": 2, + "time": 91484303.155916, + "panning": true + }, + { + "type": "pointermove", + "x": 753, + "y": 468.5, + "button": 2, + "buttons": 2, + "time": 91484344.531, + "panning": true + }, + { + "type": "pointermove", + "x": 761, + "y": 470.5, + "button": 2, + "buttons": 2, + "time": 91484377.3055, + "panning": true + }, + { + "type": "pointermove", + "x": 769, + "y": 472.5, + "button": 2, + "buttons": 2, + "time": 91484412.172375, + "panning": true + }, + { + "type": "pointermove", + "x": 777, + "y": 474.5, + "button": 2, + "buttons": 2, + "time": 91484445.553958, + "panning": true + }, + { + "type": "pointermove", + "x": 785, + "y": 476.5, + "button": 2, + "buttons": 2, + "time": 91484477.959916, + "panning": true + }, + { + "type": "pointermove", + "x": 777, + "y": 474.5, + "button": 2, + "buttons": 2, + "time": 91484513.134041, + "panning": true + }, + { + "type": "pointermove", + "x": 769, + "y": 472.5, + "button": 2, + "buttons": 2, + "time": 91484545.341958, + "panning": true + }, + { + "type": "pointermove", + "x": 761, + "y": 470.5, + "button": 2, + "buttons": 2, + "time": 91484583.1055, + "panning": true + }, + { + "type": "pointermove", + "x": 757, + "y": 469.5, + "button": 2, + "buttons": 2, + "time": 91484586.537166, + "panning": true + }, + { + "type": "pointermove", + "x": 753, + "y": 468.5, + "button": 2, + "buttons": 2, + "time": 91484616.518083, + "panning": true + }, + { + "type": "pointermove", + "x": 749, + "y": 467.5, + "button": 2, + "buttons": 2, + "time": 91484619.578875, + "panning": true + }, + { + "type": "pointermove", + "x": 745, + "y": 466.5, + "button": 2, + "buttons": 2, + "time": 91484636.91225, + "panning": true + }, + { + "type": "pointermove", + "x": 741, + "y": 465.5, + "button": 2, + "buttons": 2, + "time": 91484670.445208, + "panning": true + }, + { + "type": "pointermove", + "x": 737, + "y": 464.5, + "button": 2, + "buttons": 2, + "time": 91484674.147666, + "panning": true + }, + { + "type": "pointermove", + "x": 733, + "y": 463.5, + "button": 2, + "buttons": 2, + "time": 91484702.148541, + "panning": true + }, + { + "type": "pointermove", + "x": 729, + "y": 462.5, + "button": 2, + "buttons": 2, + "time": 91484704.900375, + "panning": true + }, + { + "type": "pointermove", + "x": 725, + "y": 461.5, + "button": 2, + "buttons": 2, + "time": 91484733.807791, + "panning": true + }, + { + "type": "pointermove", + "x": 717, + "y": 459.5, + "button": 2, + "buttons": 2, + "time": 91484767.8875, + "panning": true + }, + { + "type": "pointermove", + "x": 713, + "y": 458.5, + "button": 2, + "buttons": 2, + "time": 91484785.493041, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 91484788.999916, + "panning": true + }, + { + "type": "pointermove", + "x": 705, + "y": 456.5, + "button": 2, + "buttons": 2, + "time": 91484823.112958, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 91484828.256458, + "panning": true + }, + { + "type": "pointermove", + "x": 693, + "y": 453.5, + "button": 2, + "buttons": 2, + "time": 91484863.076375, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 91484896.917125, + "panning": true + }, + { + "type": "pointermove", + "x": 677, + "y": 449.5, + "button": 2, + "buttons": 2, + "time": 91484929.199041, + "panning": true + }, + { + "type": "pointermove", + "x": 669, + "y": 447.5, + "button": 2, + "buttons": 2, + "time": 91484961.677666, + "panning": true + }, + { + "type": "pointermove", + "x": 661, + "y": 445.5, + "button": 2, + "buttons": 2, + "time": 91484998.602958, + "panning": true + }, + { + "type": "pointermove", + "x": 653, + "y": 443.5, + "button": 2, + "buttons": 2, + "time": 91485030.641083, + "panning": true + }, + { + "type": "pointermove", + "x": 645, + "y": 441.5, + "button": 2, + "buttons": 2, + "time": 91485063.245416, + "panning": true + }, + { + "type": "pointermove", + "x": 637, + "y": 439.5, + "button": 2, + "buttons": 2, + "time": 91485103.237708, + "panning": true + }, + { + "type": "pointermove", + "x": 629, + "y": 437.5, + "button": 2, + "buttons": 2, + "time": 91485138.661958, + "panning": true + }, + { + "type": "pointermove", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 91485164.571083, + "panning": true + }, + { + "type": "pointerup", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 0, + "time": 91485165.419291, + "panning": false + } + ], + "captures": [], + "frames": [ + { + "timestamp": 91483817.547458, + "start": 91483834.531333, + "duration": 2.2416249960660934 + }, + { + "timestamp": 91483850.861083, + "start": 91483862.814, + "duration": 1.3879159986972809 + }, + { + "timestamp": 91483893.39087501, + "start": 91483897.461541, + "duration": 0.7339999973773956 + }, + { + "timestamp": 91483929.01062499, + "start": 91483932.795916, + "duration": 1.111625000834465 + }, + { + "timestamp": 91483965.99804099, + "start": 91483969.984625, + "duration": 0.6739159971475601 + }, + { + "timestamp": 91484004.245416, + "start": 91484013.566958, + "duration": 0.7179170101881027 + }, + { + "timestamp": 91484090.37862499, + "start": 91484096.596625, + "duration": 0.742125004529953 + }, + { + "timestamp": 91484109.183, + "start": 91484112.630708, + "duration": 0.5802080035209656 + }, + { + "timestamp": 91484144.677916, + "start": 91484147.838291, + "duration": 0.8688749969005585 + }, + { + "timestamp": 91484178.401208, + "start": 91484181.769791, + "duration": 1.228542000055313 + }, + { + "timestamp": 91484246.179833, + "start": 91484249.951625, + "duration": 0.8082079887390137 + }, + { + "timestamp": 91484266.437208, + "start": 91484270.180166, + "duration": 0.7054589986801147 + }, + { + "timestamp": 91484299.944166, + "start": 91484304.018333, + "duration": 0.8223329931497574 + }, + { + "timestamp": 91484341.576166, + "start": 91484345.2545, + "duration": 0.6233749985694885 + }, + { + "timestamp": 91484374.60912499, + "start": 91484378.067541, + "duration": 0.6381250023841858 + }, + { + "timestamp": 91484409.595333, + "start": 91484412.823791, + "duration": 0.69862499833107 + }, + { + "timestamp": 91484443.005458, + "start": 91484446.3665, + "duration": 0.5869999974966049 + }, + { + "timestamp": 91484475.40487501, + "start": 91484478.653416, + "duration": 0.5621670037508011 + }, + { + "timestamp": 91484510.60004099, + "start": 91484514.38925, + "duration": 0.9866660088300705 + }, + { + "timestamp": 91484542.718166, + "start": 91484545.955875, + "duration": 0.7272500097751617 + }, + { + "timestamp": 91484613.71775, + "start": 91484620.362875, + "duration": 0.943791002035141 + }, + { + "timestamp": 91484633.49425, + "start": 91484637.729416, + "duration": 0.6689590066671371 + }, + { + "timestamp": 91484666.581416, + "start": 91484676.418333, + "duration": 0.7059170007705688 + }, + { + "timestamp": 91484699.407041, + "start": 91484705.592333, + "duration": 0.690750002861023 + }, + { + "timestamp": 91484765.151833, + "start": 91484768.623125, + "duration": 0.6026249974966049 + }, + { + "timestamp": 91484782.82525, + "start": 91484789.725291, + "duration": 0.6280000060796738 + }, + { + "timestamp": 91484817.65908301, + "start": 91484828.942125, + "duration": 1.5149160027503967 + }, + { + "timestamp": 91484860.89925, + "start": 91484863.742625, + "duration": 0.6821659952402115 + }, + { + "timestamp": 91484893.819791, + "start": 91484898.680166, + "duration": 0.6458749920129776 + }, + { + "timestamp": 91484926.52904099, + "start": 91484929.860083, + "duration": 0.5275830030441284 + }, + { + "timestamp": 91484959.21762499, + "start": 91484962.437208, + "duration": 0.5581670105457306 + }, + { + "timestamp": 91484996.1115, + "start": 91484999.378166, + "duration": 0.7034169882535934 + }, + { + "timestamp": 91485028.379791, + "start": 91485031.45775, + "duration": 0.7978750020265579 + }, + { + "timestamp": 91485061.06333299, + "start": 91485064.086458, + "duration": 0.9796250015497208 + }, + { + "timestamp": 91485100.569958, + "start": 91485104.160625, + "duration": 0.7216250002384186 + }, + { + "timestamp": 91485135.97354099, + "start": 91485139.780583, + "duration": 0.6080830097198486 + }, + { + "timestamp": 91485169.5645, + "start": 91485169.991166, + "duration": 0.6408340036869049 + } + ], + "panning": false, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } + } +} diff --git a/docs/graphics/evidence/kestrel/validated-sidebar-publication-baseline.json b/docs/graphics/evidence/kestrel/validated-sidebar-publication-baseline.json new file mode 100644 index 000000000..da0e5a287 --- /dev/null +++ b/docs/graphics/evidence/kestrel/validated-sidebar-publication-baseline.json @@ -0,0 +1,1715 @@ +{ + "workloadValidated": true, + "limitations": [ + "No physical presentation measurement", + "Includes 500ms settling", + "Identical-coordinate external input provenance cannot be distinguished" + ], + "timeline": { + "traceStarted": 94700607834041, + "timestampFrequency": 1000000000, + "originalWidth": 222, + "width": 342, + "baseline": { + "ContextId": 1, + "Timestamp": 94700601984500, + "Engine": { + "EnqueuedInputs": 4, + "DroppedInputs": 0, + "ConsumedInputs": 4, + "PublishedScenes": 5, + "AcquiredScenes": 5, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 4384793, + "InputEventsDispatched": 17, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 433250, + "LastScenePublicationNanoseconds": 6135667, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 1, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 2, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 4842916, + "LastSceneBuildNanoseconds": 1007333, + "MaximumScenePublicationNanoseconds": 6135667 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 401458, + "MaximumDispatchNanoseconds": 401458, + "LastDispatchSequence": 639244607198084972, + "DispatchedInputs": 1, + "TotalDispatchNanoseconds": 401458 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 2, + "TotalDispatchNanoseconds": 1209, + "LastDispatchNanoseconds": 542, + "MaximumDispatchNanoseconds": 667, + "LastTimestampMicroseconds": 94697827423 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 6, + "BlockedPublications": 0, + "AcknowledgedScenes": 5, + "TotalAcknowledgementNanoseconds": 358627168, + "LastAcknowledgementNanoseconds": 52924292, + "MaximumAcknowledgementNanoseconds": 155294500, + "AcknowledgedRevision": 5 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5963776, + "V8UsedHeapBytes": 3159872, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5963776, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1855488, + "LatestSceneBytes": 127068, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 724976, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1928508, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 191232, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127684, + "V8TrustedSpacePhysicalBytes": 1048576, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 4, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 94702181604041, + "Engine": { + "EnqueuedInputs": 128, + "DroppedInputs": 0, + "ConsumedInputs": 128, + "PublishedScenes": 19, + "AcquiredScenes": 19, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1365, + "LayoutPasses": 164, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 15, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 4511043, + "InputEventsDispatched": 161, + "InputCallbacksInvoked": 170, + "BusiestCanvasWidthMilli": 686000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 433250, + "LastScenePublicationNanoseconds": 1375, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 2, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 59, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 64, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 837125, + "LastSceneBuildNanoseconds": 304709, + "MaximumScenePublicationNanoseconds": 6135667 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 156458, + "MaximumDispatchNanoseconds": 10870708, + "LastDispatchSequence": 639244607198085034, + "DispatchedInputs": 61, + "TotalDispatchNanoseconds": 296995497 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 64, + "TotalDispatchNanoseconds": 43582, + "LastDispatchNanoseconds": 875, + "MaximumDispatchNanoseconds": 2666, + "LastTimestampMicroseconds": 94701682435 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 197, + "BlockedPublications": 0, + "AcknowledgedScenes": 19, + "TotalAcknowledgementNanoseconds": 729566085, + "LastAcknowledgementNanoseconds": 24209875, + "MaximumAcknowledgementNanoseconds": 155294500, + "AcknowledgedRevision": 19 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 49, + "AnimationFramesInvoked": 49, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 243, + "WorkerWaits": 214, + "WorkerSignalledWakes": 189, + "WorkerTimeoutWakes": 24, + "SceneBuilds": 14, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 15, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5963776, + "V8UsedHeapBytes": 3159872, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5963776, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1855488, + "LatestSceneBytes": 156976, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 724976, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1928508, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 191232, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127684, + "V8TrustedSpacePhysicalBytes": 1048576, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 4, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5111520, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 981, + "StringBytes": 80034, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 18, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 94, + "Renders": 14, + "AppliedDiffs": 14, + "InvalidationCalls": 14, + "DamageRectangles": 40, + "ChangedLayers": 13, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 14, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 62, + "SkippedEmptyAnimationFrames": 32, + "RenderCallbacks": 14, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.5796195", + "EnqueuedInputs": 124, + "DroppedInputs": 0, + "ConsumedInputs": 124, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 156, + "AppliedAnimationFrames": 62, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 191, + "BlockedPublications": 0, + "PublishedScenes": 14, + "AcquiredScenes": 14, + "AcknowledgedScenes": 14, + "RenderedScenes": 14, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 1, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 49, + "AnimationFramesInvoked": 49, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 243, + "WorkerWaits": 214, + "WorkerSignalledWakes": 189, + "WorkerTimeoutWakes": 24, + "SceneBuilds": 14, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 94, + "CompositionRenders": 14, + "CompositionAppliedDiffs": 14, + "CompositionInvalidations": 14, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 62, + "CompositionSkippedEmptyAnimationFrames": 32, + "CompositionRenderCallbacks": 14, + "CompositionUnchangedRenderCallbacks": 0 + }, + "submittedMoves": [ + { + "sequence": 639244607198084974, + "submittedAt": 94700608005666, + "step": 1, + "x": 222.5, + "y": 436.5 + }, + { + "sequence": 639244607198084975, + "submittedAt": 94700626183875, + "step": 2, + "x": 224.5, + "y": 436.5 + }, + { + "sequence": 639244607198084976, + "submittedAt": 94700643236208, + "step": 3, + "x": 226.5, + "y": 436.5 + }, + { + "sequence": 639244607198084977, + "submittedAt": 94700661376708, + "step": 4, + "x": 228.5, + "y": 436.5 + }, + { + "sequence": 639244607198084978, + "submittedAt": 94700679473291, + "step": 5, + "x": 230.5, + "y": 436.5 + }, + { + "sequence": 639244607198084979, + "submittedAt": 94700697555750, + "step": 6, + "x": 232.5, + "y": 436.5 + }, + { + "sequence": 639244607198084980, + "submittedAt": 94700715715125, + "step": 7, + "x": 234.5, + "y": 436.5 + }, + { + "sequence": 639244607198084981, + "submittedAt": 94700733359375, + "step": 8, + "x": 236.5, + "y": 436.5 + }, + { + "sequence": 639244607198084982, + "submittedAt": 94700751433000, + "step": 9, + "x": 238.5, + "y": 436.5 + }, + { + "sequence": 639244607198084983, + "submittedAt": 94700769499875, + "step": 10, + "x": 240.5, + "y": 436.5 + }, + { + "sequence": 639244607198084984, + "submittedAt": 94700787590958, + "step": 11, + "x": 242.5, + "y": 436.5 + }, + { + "sequence": 639244607198084985, + "submittedAt": 94700805725625, + "step": 12, + "x": 244.5, + "y": 436.5 + }, + { + "sequence": 639244607198084986, + "submittedAt": 94700821911541, + "step": 13, + "x": 246.5, + "y": 436.5 + }, + { + "sequence": 639244607198084987, + "submittedAt": 94700839975416, + "step": 14, + "x": 248.5, + "y": 436.5 + }, + { + "sequence": 639244607198084988, + "submittedAt": 94700858090916, + "step": 15, + "x": 250.5, + "y": 436.5 + }, + { + "sequence": 639244607198084989, + "submittedAt": 94700876246875, + "step": 16, + "x": 252.5, + "y": 436.5 + }, + { + "sequence": 639244607198084990, + "submittedAt": 94700893165333, + "step": 17, + "x": 254.5, + "y": 436.5 + }, + { + "sequence": 639244607198084991, + "submittedAt": 94700911241583, + "step": 18, + "x": 256.5, + "y": 436.5 + }, + { + "sequence": 639244607198084992, + "submittedAt": 94700928738000, + "step": 19, + "x": 258.5, + "y": 436.5 + }, + { + "sequence": 639244607198084993, + "submittedAt": 94700945315958, + "step": 20, + "x": 260.5, + "y": 436.5 + }, + { + "sequence": 639244607198084994, + "submittedAt": 94700963366208, + "step": 21, + "x": 262.5, + "y": 436.5 + }, + { + "sequence": 639244607198084995, + "submittedAt": 94700981441083, + "step": 22, + "x": 264.5, + "y": 436.5 + }, + { + "sequence": 639244607198084996, + "submittedAt": 94700999515416, + "step": 23, + "x": 266.5, + "y": 436.5 + }, + { + "sequence": 639244607198084997, + "submittedAt": 94701017588416, + "step": 24, + "x": 268.5, + "y": 436.5 + }, + { + "sequence": 639244607198084998, + "submittedAt": 94701034459916, + "step": 25, + "x": 270.5, + "y": 436.5 + }, + { + "sequence": 639244607198084999, + "submittedAt": 94701052508833, + "step": 26, + "x": 272.5, + "y": 436.5 + }, + { + "sequence": 639244607198085000, + "submittedAt": 94701070689833, + "step": 27, + "x": 274.5, + "y": 436.5 + }, + { + "sequence": 639244607198085001, + "submittedAt": 94701088775541, + "step": 28, + "x": 276.5, + "y": 436.5 + }, + { + "sequence": 639244607198085002, + "submittedAt": 94701106824875, + "step": 29, + "x": 278.5, + "y": 436.5 + }, + { + "sequence": 639244607198085003, + "submittedAt": 94701124988250, + "step": 30, + "x": 280.5, + "y": 436.5 + }, + { + "sequence": 639244607198085004, + "submittedAt": 94701143071333, + "step": 31, + "x": 282.5, + "y": 436.5 + }, + { + "sequence": 639244607198085005, + "submittedAt": 94701159415208, + "step": 32, + "x": 284.5, + "y": 436.5 + }, + { + "sequence": 639244607198085006, + "submittedAt": 94701177507041, + "step": 33, + "x": 286.5, + "y": 436.5 + }, + { + "sequence": 639244607198085007, + "submittedAt": 94701195580750, + "step": 34, + "x": 288.5, + "y": 436.5 + }, + { + "sequence": 639244607198085008, + "submittedAt": 94701213651958, + "step": 35, + "x": 290.5, + "y": 436.5 + }, + { + "sequence": 639244607198085009, + "submittedAt": 94701231788083, + "step": 36, + "x": 292.5, + "y": 436.5 + }, + { + "sequence": 639244607198085010, + "submittedAt": 94701249862875, + "step": 37, + "x": 294.5, + "y": 436.5 + }, + { + "sequence": 639244607198085011, + "submittedAt": 94701267924166, + "step": 38, + "x": 296.5, + "y": 436.5 + }, + { + "sequence": 639244607198085012, + "submittedAt": 94701285973458, + "step": 39, + "x": 298.5, + "y": 436.5 + }, + { + "sequence": 639244607198085013, + "submittedAt": 94701304107250, + "step": 40, + "x": 300.5, + "y": 436.5 + }, + { + "sequence": 639244607198085014, + "submittedAt": 94701322199583, + "step": 41, + "x": 302.5, + "y": 436.5 + }, + { + "sequence": 639244607198085015, + "submittedAt": 94701340028250, + "step": 42, + "x": 304.5, + "y": 436.5 + }, + { + "sequence": 639244607198085016, + "submittedAt": 94701357626791, + "step": 43, + "x": 306.5, + "y": 436.5 + }, + { + "sequence": 639244607198085017, + "submittedAt": 94701375768750, + "step": 44, + "x": 308.5, + "y": 436.5 + }, + { + "sequence": 639244607198085018, + "submittedAt": 94701392211875, + "step": 45, + "x": 310.5, + "y": 436.5 + }, + { + "sequence": 639244607198085019, + "submittedAt": 94701410391250, + "step": 46, + "x": 312.5, + "y": 436.5 + }, + { + "sequence": 639244607198085020, + "submittedAt": 94701428526791, + "step": 47, + "x": 314.5, + "y": 436.5 + }, + { + "sequence": 639244607198085021, + "submittedAt": 94701446584208, + "step": 48, + "x": 316.5, + "y": 436.5 + }, + { + "sequence": 639244607198085022, + "submittedAt": 94701464633666, + "step": 49, + "x": 318.5, + "y": 436.5 + }, + { + "sequence": 639244607198085023, + "submittedAt": 94701482751000, + "step": 50, + "x": 320.5, + "y": 436.5 + }, + { + "sequence": 639244607198085024, + "submittedAt": 94701500809375, + "step": 51, + "x": 322.5, + "y": 436.5 + }, + { + "sequence": 639244607198085025, + "submittedAt": 94701518884041, + "step": 52, + "x": 324.5, + "y": 436.5 + }, + { + "sequence": 639244607198085026, + "submittedAt": 94701536703333, + "step": 53, + "x": 326.5, + "y": 436.5 + }, + { + "sequence": 639244607198085027, + "submittedAt": 94701554750208, + "step": 54, + "x": 328.5, + "y": 436.5 + }, + { + "sequence": 639244607198085028, + "submittedAt": 94701572809958, + "step": 55, + "x": 330.5, + "y": 436.5 + }, + { + "sequence": 639244607198085029, + "submittedAt": 94701590926333, + "step": 56, + "x": 332.5, + "y": 436.5 + }, + { + "sequence": 639244607198085030, + "submittedAt": 94701609043625, + "step": 57, + "x": 334.5, + "y": 436.5 + }, + { + "sequence": 639244607198085031, + "submittedAt": 94701626584166, + "step": 58, + "x": 336.5, + "y": 436.5 + }, + { + "sequence": 639244607198085032, + "submittedAt": 94701644671666, + "step": 59, + "x": 338.5, + "y": 436.5 + }, + { + "sequence": 639244607198085033, + "submittedAt": 94701662132416, + "step": 60, + "x": 340.5, + "y": 436.5 + } + ], + "publications": [ + { + "Timestamp": 94700610856125, + "Revision": 6, + "ConsumedInputSequence": 639244607198084973, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94700686543958, + "Revision": 7, + "ConsumedInputSequence": 639244607198084977, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94700737181208, + "Revision": 8, + "ConsumedInputSequence": 639244607198084980, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94700932755875, + "Revision": 9, + "ConsumedInputSequence": 639244607198084991, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94700984062750, + "Revision": 10, + "ConsumedInputSequence": 639244607198084994, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94701024221708, + "Revision": 11, + "ConsumedInputSequence": 639244607198084996, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94701068840333, + "Revision": 12, + "ConsumedInputSequence": 639244607198084999, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94701199312375, + "Revision": 13, + "ConsumedInputSequence": 639244607198085006, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94701249978500, + "Revision": 14, + "ConsumedInputSequence": 639244607198085009, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94701450643166, + "Revision": 15, + "ConsumedInputSequence": 639244607198085020, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94701485711166, + "Revision": 16, + "ConsumedInputSequence": 639244607198085022, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94701533130375, + "Revision": 17, + "ConsumedInputSequence": 639244607198085025, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94701650896583, + "Revision": 18, + "ConsumedInputSequence": 639244607198085031, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 94701687889708, + "Revision": 19, + "ConsumedInputSequence": 639244607198085034, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 94700647432625, + "Revision": 6, + "ConsumedInputSequence": 639244607198084973, + "AcceptedTimestamp": 94700638633000 + }, + { + "Timestamp": 94700720738541, + "Revision": 7, + "ConsumedInputSequence": 639244607198084977, + "AcceptedTimestamp": 94700714627833 + }, + { + "Timestamp": 94700769343541, + "Revision": 8, + "ConsumedInputSequence": 639244607198084980, + "AcceptedTimestamp": 94700763921791 + }, + { + "Timestamp": 94700967413750, + "Revision": 9, + "ConsumedInputSequence": 639244607198084991, + "AcceptedTimestamp": 94700962800583 + }, + { + "Timestamp": 94701014615125, + "Revision": 10, + "ConsumedInputSequence": 639244607198084994, + "AcceptedTimestamp": 94701011520666 + }, + { + "Timestamp": 94701048909875, + "Revision": 11, + "ConsumedInputSequence": 639244607198084996, + "AcceptedTimestamp": 94701044394541 + }, + { + "Timestamp": 94701100319791, + "Revision": 12, + "ConsumedInputSequence": 639244607198084999, + "AcceptedTimestamp": 94701095800208 + }, + { + "Timestamp": 94701230803916, + "Revision": 13, + "ConsumedInputSequence": 639244607198085006, + "AcceptedTimestamp": 94701227685500 + }, + { + "Timestamp": 94701281406458, + "Revision": 14, + "ConsumedInputSequence": 639244607198085009, + "AcceptedTimestamp": 94701277139666 + }, + { + "Timestamp": 94701480312916, + "Revision": 15, + "ConsumedInputSequence": 639244607198085020, + "AcceptedTimestamp": 94701475591291 + }, + { + "Timestamp": 94701513755500, + "Revision": 16, + "ConsumedInputSequence": 639244607198085022, + "AcceptedTimestamp": 94701510638833 + }, + { + "Timestamp": 94701563400208, + "Revision": 17, + "ConsumedInputSequence": 639244607198085025, + "AcceptedTimestamp": 94701560319041 + }, + { + "Timestamp": 94701680863125, + "Revision": 18, + "ConsumedInputSequence": 639244607198085031, + "AcceptedTimestamp": 94701677731416 + }, + { + "Timestamp": 94701715013791, + "Revision": 19, + "ConsumedInputSequence": 639244607198085034, + "AcceptedTimestamp": 94701712102541 + } + ], + "physicalPresentationVerified": false + }, + "diagnostics": { + "events": [ + { + "type": "pointerdown", + "x": 220.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 222.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 226.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 228.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 230.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 232.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 234.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 236.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 240.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 242.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 244.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 246.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 248.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 250.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 252.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 254.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 256.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 258.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 260.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 262.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 264.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 266.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 268.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 270.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 272.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 274.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 276.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 278.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 280.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 282.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 284.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 286.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 288.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 290.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 292.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 294.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 296.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 298.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 300.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 302.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 304.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 306.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 308.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 310.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 312.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 314.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 316.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 318.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 320.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 322.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 324.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 326.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 328.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 330.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 332.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 334.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 336.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 338.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointermove", + "x": 340.5, + "y": 436.5, + "button": 0, + "buttons": 1 + }, + { + "type": "pointerup", + "x": 340.5, + "y": 436.5, + "button": 0, + "buttons": 0 + } + ], + "panning": false, + "errors": 0 + } +} diff --git a/docs/graphics/evidence/kestrel/view-transitions.json b/docs/graphics/evidence/kestrel/view-transitions.json new file mode 100644 index 000000000..f91ba9ec6 --- /dev/null +++ b/docs/graphics/evidence/kestrel/view-transitions.json @@ -0,0 +1,44 @@ +{ + "date": "2026-09-08", + "originalDocumentSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "input": "DOM change events on original view-select and style-select controls", + "steps": [ + { + "view": "iso", + "style": "shaded-edges", + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0, + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands." + }, + { + "view": "front", + "style": "shaded", + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0, + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands." + }, + { + "view": "iso", + "style": "xray", + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0, + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands." + }, + { + "view": "top", + "style": "wireframe", + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0, + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands." + }, + { + "view": "iso", + "style": "shaded-edges", + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0, + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands." + } + ], + "visualCheck": "Final isometric viewport differs from top view and remains visibly rendered. Screenshot macos-isometric-webgpu.png.", + "qualification": "Scripted view smoke test only; flat floor plan does not qualify shaded mesh, pointer interaction, editing, export, or layout correctness." +} diff --git a/docs/graphics/evidence/kestrel/vsync-current-pan-investigation.json b/docs/graphics/evidence/kestrel/vsync-current-pan-investigation.json new file mode 100644 index 000000000..b45694424 --- /dev/null +++ b/docs/graphics/evidence/kestrel/vsync-current-pan-investigation.json @@ -0,0 +1,2570 @@ +{ + "purpose": "Current vsync scheduling investigation; not a qualified benchmark", + "displayReport": "Graphics/Displays:\n\n Apple M4:\n\n Chipset Model: Apple M4\n Type: GPU\n Bus: Built-In\n Total Number of Cores: 10\n Vendor: Apple (0x106b)\n Metal Support: Metal 4\n Displays:\n LG ULTRAFINE:\n Resolution: 3840 x 2160 (2160p/4K UHD 1 - Ultra High Definition)\n UI Looks like: 1920 x 1080 @ 60.00Hz\n Main Display: Yes\n Mirror: Off\n Online: Yes\n Rotation: Supported\n LG HDR 4K:\n Resolution: 3840 x 2160 (2160p/4K UHD 1 - Ultra High Definition)\n UI Looks like: 1920 x 1080 @ 60.00Hz\n Mirror: Off\n Online: Yes\n Rotation: Supported\n\n", + "originalDocumentSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "records": { + "Kestrel pan performance": { + "elapsedMilliseconds": 1857.0318, + "baseline": { + "ContextId": 1, + "Timestamp": 91366037240166, + "Engine": { + "EnqueuedInputs": 294, + "DroppedInputs": 0, + "ConsumedInputs": 294, + "PublishedScenes": 68, + "AcquiredScenes": 67, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1443, + "LayoutPasses": 135, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 4866501, + "InputEventsDispatched": 205, + "InputCallbacksInvoked": 88, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 414417, + "LastScenePublicationNanoseconds": 4125, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 149, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 79, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 65, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 898541, + "LastSceneBuildNanoseconds": 341125, + "MaximumScenePublicationNanoseconds": 2085291 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 3431583, + "MaximumDispatchNanoseconds": 13340875, + "LastDispatchSequence": 639244573849660859, + "DispatchedInputs": 79, + "TotalDispatchNanoseconds": 162027287 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 65, + "TotalDispatchNanoseconds": 59287, + "LastDispatchNanoseconds": 834, + "MaximumDispatchNanoseconds": 2333, + "LastTimestampMicroseconds": 91365994604 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 146, + "BlockedPublications": 6, + "AcknowledgedScenes": 67, + "TotalAcknowledgementNanoseconds": 3888895626, + "LastAcknowledgementNanoseconds": 53634625, + "MaximumAcknowledgementNanoseconds": 183506833, + "AcknowledgedRevision": 67 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5439488, + "V8UsedHeapBytes": 3153188, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5439488, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1798144, + "LatestSceneBytes": 162152, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 719816, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1927980, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 190720, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 127200, + "V8TrustedSpacePhysicalBytes": 524288, + "PendingSceneCount": 1, + "PendingSceneBytes": 162152 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 66, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 1, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 91367896299291, + "Engine": { + "EnqueuedInputs": 593, + "DroppedInputs": 0, + "ConsumedInputs": 592, + "PublishedScenes": 124, + "AcquiredScenes": 125, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1125, + "LayoutPasses": 331, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 4866501, + "InputEventsDispatched": 619, + "InputCallbacksInvoked": 427, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 414417, + "LastScenePublicationNanoseconds": 880542, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 217, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 253, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 119, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 664792, + "LastSceneBuildNanoseconds": 697208, + "MaximumScenePublicationNanoseconds": 2085291 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 764333, + "MaximumDispatchNanoseconds": 13340875, + "LastDispatchSequence": 639244573849661103, + "DispatchedInputs": 255, + "TotalDispatchNanoseconds": 857571033 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 119, + "TotalDispatchNanoseconds": 109539, + "LastDispatchNanoseconds": 1083, + "MaximumDispatchNanoseconds": 2792, + "LastTimestampMicroseconds": 91367832964 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 332, + "BlockedPublications": 95, + "AcknowledgedScenes": 124, + "TotalAcknowledgementNanoseconds": 6398524915, + "LastAcknowledgementNanoseconds": 14792500, + "MaximumAcknowledgementNanoseconds": 183506833, + "AcknowledgedRevision": 124 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 32, + "AnimationFramesInvoked": 33, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 447, + "WorkerWaits": 289, + "WorkerSignalledWakes": 275, + "WorkerTimeoutWakes": 13, + "SceneBuilds": 56, + "NoDamageSceneBuilds": 13, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 14876672, + "V8UsedHeapBytes": 4565688, + "V8ExecutableHeapBytes": 786432, + "V8PhysicalHeapBytes": 10797056, + "V8ExternalBytes": 451721, + "V8MallocedBytes": 49196, + "V8PeakMallocedBytes": 3457024, + "LatestSceneBytes": 117120, + "ProcessCompilationCacheBytes": 253776, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1125, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1125000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 54687, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 47567, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 668, + "NativeDomAttributeEntryCount": 1833, + "NativeDomAttributeStorageBytes": 181664, + "NativeWrapperHandleCount": 292, + "NativeWrapperStorageBytes": 12536, + "NativeTextMeasurementCacheEntryCount": 1553, + "NativeTextMeasurementCacheStorageBytes": 315966, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 745, + "NativeDomTextualStyleStorageBytes": 481666, + "NativeDomNodePoolReservedBytes": 1536000, + "NativeDomNodePoolPeakBytes": 1536000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 44, + "NativeDomFormControlStorageBytes": 3476, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 36, + "NativeEventListenerStorageBytes": 5852, + "V8YoungSpaceUsedBytes": 211308, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 3553984, + "V8OldSpacePhysicalBytes": 7700480, + "V8CodeSpaceUsedBytes": 405088, + "V8CodeSpacePhysicalBytes": 573440, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 207836, + "V8TrustedSpacePhysicalBytes": 229376, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 327, + "LogicalBitmapBytes": 5970848, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 5612, + "StringBytes": 264718, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 124, + "RoutedInputEvents": 163, + "AcceptedInputEvents": 163, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 71, + "Renders": 57, + "AppliedDiffs": 57, + "InvalidationCalls": 59, + "DamageRectangles": 104, + "ChangedLayers": 30, + "EmptyDamageDiffs": 13, + "PartialDamageDiffs": 44, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 54, + "SkippedEmptyAnimationFrames": 17, + "RenderCallbacks": 59, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.8590591", + "EnqueuedInputs": 299, + "DroppedInputs": 0, + "ConsumedInputs": 298, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 196, + "AppliedAnimationFrames": 54, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 186, + "BlockedPublications": 89, + "PublishedScenes": 56, + "AcquiredScenes": 58, + "AcknowledgedScenes": 57, + "RenderedScenes": 58, + "CompositionUiWakes": 0, + "RoutedInputEvents": 163, + "AcceptedInputEvents": 163, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 0, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 32, + "AnimationFramesInvoked": 33, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 447, + "WorkerWaits": 289, + "WorkerSignalledWakes": 275, + "WorkerTimeoutWakes": 13, + "SceneBuilds": 56, + "NoDamageSceneBuilds": 13, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 71, + "CompositionRenders": 57, + "CompositionAppliedDiffs": 57, + "CompositionInvalidations": 59, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 54, + "CompositionSkippedEmptyAnimationFrames": 17, + "CompositionRenderCallbacks": 59, + "CompositionUnchangedRenderCallbacks": 0 + } + }, + "Kestrel pan diagnostics": { + "events": [ + { + "type": "pointermove", + "x": 798.74609375, + "y": 446.875, + "button": 0, + "buttons": 0, + "time": 91365785.643541, + "panning": false + }, + { + "type": "pointermove", + "x": 825.76953125, + "y": 450.078125, + "button": 0, + "buttons": 0, + "time": 91365822.303875, + "panning": false + }, + { + "type": "pointermove", + "x": 825.76953125, + "y": 449.84765625, + "button": 0, + "buttons": 0, + "time": 91365823.830333, + "panning": false + }, + { + "type": "pointermove", + "x": 837.37109375, + "y": 452.22265625, + "button": 0, + "buttons": 0, + "time": 91365840.192041, + "panning": false + }, + { + "type": "pointermove", + "x": 845.61328125, + "y": 452.56640625, + "button": 0, + "buttons": 0, + "time": 91365874.466458, + "panning": false + }, + { + "type": "pointermove", + "x": 846.7109375, + "y": 452.56640625, + "button": 0, + "buttons": 0, + "time": 91365909.282, + "panning": false + }, + { + "type": "pointermove", + "x": 848.03515625, + "y": 452.79296875, + "button": 0, + "buttons": 0, + "time": 91365944.106625, + "panning": false + }, + { + "type": "pointermove", + "x": 848.26171875, + "y": 452.79296875, + "button": 0, + "buttons": 0, + "time": 91365976.82625, + "panning": false + }, + { + "type": "pointermove", + "x": 848.26171875, + "y": 453.01953125, + "button": 0, + "buttons": 0, + "time": 91365994.9265, + "panning": false + }, + { + "type": "pointermove", + "x": 848.71875, + "y": 453.24609375, + "button": 0, + "buttons": 0, + "time": 91366032.279791, + "panning": false + }, + { + "type": "pointerdown", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 91366042.871791, + "panning": false + }, + { + "type": "pointermove", + "x": 629, + "y": 437.5, + "button": 2, + "buttons": 2, + "time": 91366043.203458, + "panning": true + }, + { + "type": "pointermove", + "x": 849.17578125, + "y": 453.24609375, + "button": 0, + "buttons": 0, + "time": 91366050.575333, + "panning": true + }, + { + "type": "pointermove", + "x": 849.40234375, + "y": 453.47265625, + "button": 0, + "buttons": 0, + "time": 91366055.338583, + "panning": true + }, + { + "type": "pointermove", + "x": 849.859375, + "y": 453.47265625, + "button": 0, + "buttons": 0, + "time": 91366061.074541, + "panning": true + }, + { + "type": "pointermove", + "x": 633, + "y": 438.5, + "button": 2, + "buttons": 2, + "time": 91366065.598708, + "panning": true + }, + { + "type": "pointermove", + "x": 850.95703125, + "y": 453.47265625, + "button": 0, + "buttons": 0, + "time": 91366075.443291, + "panning": true + }, + { + "type": "pointermove", + "x": 637, + "y": 439.5, + "button": 2, + "buttons": 2, + "time": 91366080.140916, + "panning": true + }, + { + "type": "pointermove", + "x": 851.4140625, + "y": 453.69921875, + "button": 0, + "buttons": 0, + "time": 91366083.503291, + "panning": true + }, + { + "type": "pointermove", + "x": 851.87109375, + "y": 453.69921875, + "button": 0, + "buttons": 0, + "time": 91366091.692541, + "panning": true + }, + { + "type": "pointermove", + "x": 641, + "y": 440.5, + "button": 2, + "buttons": 2, + "time": 91366095.827583, + "panning": true + }, + { + "type": "pointermove", + "x": 852.78515625, + "y": 453.69921875, + "button": 0, + "buttons": 0, + "time": 91366108.756958, + "panning": true + }, + { + "type": "pointermove", + "x": 645, + "y": 441.5, + "button": 2, + "buttons": 2, + "time": 91366112.516625, + "panning": true + }, + { + "type": "pointermove", + "x": 853.01171875, + "y": 453.92578125, + "button": 0, + "buttons": 0, + "time": 91366117.859458, + "panning": true + }, + { + "type": "pointermove", + "x": 853.8828125, + "y": 454.21484375, + "button": 0, + "buttons": 0, + "time": 91366121.466125, + "panning": true + }, + { + "type": "pointermove", + "x": 653, + "y": 443.5, + "button": 2, + "buttons": 2, + "time": 91366144.003916, + "panning": true + }, + { + "type": "pointermove", + "x": 854.33984375, + "y": 454.21484375, + "button": 0, + "buttons": 0, + "time": 91366152.824333, + "panning": true + }, + { + "type": "pointermove", + "x": 854.33984375, + "y": 453.984375, + "button": 0, + "buttons": 0, + "time": 91366159.392875, + "panning": true + }, + { + "type": "pointermove", + "x": 657, + "y": 444.5, + "button": 2, + "buttons": 2, + "time": 91366166.142, + "panning": true + }, + { + "type": "pointermove", + "x": 854.56640625, + "y": 453.984375, + "button": 0, + "buttons": 0, + "time": 91366175.66575, + "panning": true + }, + { + "type": "pointermove", + "x": 661, + "y": 445.5, + "button": 2, + "buttons": 2, + "time": 91366179.284541, + "panning": true + }, + { + "type": "pointermove", + "x": 855.0234375, + "y": 454.2109375, + "button": 0, + "buttons": 0, + "time": 91366193.147916, + "panning": true + }, + { + "type": "pointermove", + "x": 665, + "y": 446.5, + "button": 2, + "buttons": 2, + "time": 91366200.277125, + "panning": true + }, + { + "type": "pointermove", + "x": 855.25, + "y": 454.2109375, + "button": 0, + "buttons": 0, + "time": 91366210.551166, + "panning": true + }, + { + "type": "pointermove", + "x": 669, + "y": 447.5, + "button": 2, + "buttons": 2, + "time": 91366213.877458, + "panning": true + }, + { + "type": "pointermove", + "x": 855.4765625, + "y": 453.75, + "button": 0, + "buttons": 0, + "time": 91366216.681416, + "panning": true + }, + { + "type": "pointermove", + "x": 856.34765625, + "y": 453.75, + "button": 0, + "buttons": 0, + "time": 91366227.094916, + "panning": true + }, + { + "type": "pointermove", + "x": 673, + "y": 448.5, + "button": 2, + "buttons": 2, + "time": 91366232.173041, + "panning": true + }, + { + "type": "pointermove", + "x": 857.67578125, + "y": 453.9765625, + "button": 0, + "buttons": 0, + "time": 91366244.278375, + "panning": true + }, + { + "type": "pointermove", + "x": 677, + "y": 449.5, + "button": 2, + "buttons": 2, + "time": 91366248.83525, + "panning": true + }, + { + "type": "pointermove", + "x": 858.1328125, + "y": 453.9765625, + "button": 0, + "buttons": 0, + "time": 91366255.588, + "panning": true + }, + { + "type": "pointermove", + "x": 859.00390625, + "y": 453.9765625, + "button": 0, + "buttons": 0, + "time": 91366267.357375, + "panning": true + }, + { + "type": "pointermove", + "x": 681, + "y": 450.5, + "button": 2, + "buttons": 2, + "time": 91366279.720333, + "panning": true + }, + { + "type": "pointermove", + "x": 862.25390625, + "y": 454.7890625, + "button": 0, + "buttons": 0, + "time": 91366285.663791, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 91366290.189916, + "panning": true + }, + { + "type": "pointermove", + "x": 864.10546875, + "y": 455.1953125, + "button": 0, + "buttons": 0, + "time": 91366293.770458, + "panning": true + }, + { + "type": "pointermove", + "x": 689, + "y": 452.5, + "button": 2, + "buttons": 2, + "time": 91366297.483541, + "panning": true + }, + { + "type": "pointermove", + "x": 866.484375, + "y": 455.1953125, + "button": 0, + "buttons": 0, + "time": 91366300.708208, + "panning": true + }, + { + "type": "pointermove", + "x": 868.109375, + "y": 455.1953125, + "button": 0, + "buttons": 0, + "time": 91366311.289833, + "panning": true + }, + { + "type": "pointermove", + "x": 693, + "y": 453.5, + "button": 2, + "buttons": 2, + "time": 91366320.167041, + "panning": true + }, + { + "type": "pointermove", + "x": 868.98046875, + "y": 455.77734375, + "button": 0, + "buttons": 0, + "time": 91366327.989125, + "panning": true + }, + { + "type": "pointermove", + "x": 697, + "y": 454.5, + "button": 2, + "buttons": 2, + "time": 91366330.995541, + "panning": true + }, + { + "type": "pointermove", + "x": 869.4375, + "y": 455.77734375, + "button": 0, + "buttons": 0, + "time": 91366337.073375, + "panning": true + }, + { + "type": "pointermove", + "x": 870.30859375, + "y": 455.484375, + "button": 0, + "buttons": 0, + "time": 91366340.024541, + "panning": true + }, + { + "type": "pointermove", + "x": 870.765625, + "y": 455.484375, + "button": 0, + "buttons": 0, + "time": 91366344.912583, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 91366352.017875, + "panning": true + }, + { + "type": "pointermove", + "x": 870.9921875, + "y": 455.484375, + "button": 0, + "buttons": 0, + "time": 91366356.153208, + "panning": true + }, + { + "type": "pointermove", + "x": 705, + "y": 456.5, + "button": 2, + "buttons": 2, + "time": 91366368.551041, + "panning": true + }, + { + "type": "pointermove", + "x": 871.44921875, + "y": 455.484375, + "button": 0, + "buttons": 0, + "time": 91366379.141583, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 91366384.192625, + "panning": true + }, + { + "type": "pointermove", + "x": 870.98828125, + "y": 455.7109375, + "button": 0, + "buttons": 0, + "time": 91366396.128708, + "panning": true + }, + { + "type": "pointermove", + "x": 713, + "y": 458.5, + "button": 2, + "buttons": 2, + "time": 91366403.605208, + "panning": true + }, + { + "type": "pointermove", + "x": 721, + "y": 460.5, + "button": 2, + "buttons": 2, + "time": 91366443.096125, + "panning": true + }, + { + "type": "pointermove", + "x": 725, + "y": 461.5, + "button": 2, + "buttons": 2, + "time": 91366459.273541, + "panning": true + }, + { + "type": "pointermove", + "x": 729, + "y": 462.5, + "button": 2, + "buttons": 2, + "time": 91366480.071458, + "panning": true + }, + { + "type": "pointermove", + "x": 870.7578125, + "y": 455.7109375, + "button": 0, + "buttons": 0, + "time": 91366483.19125, + "panning": true + }, + { + "type": "pointermove", + "x": 733, + "y": 463.5, + "button": 2, + "buttons": 2, + "time": 91366493.790666, + "panning": true + }, + { + "type": "pointermove", + "x": 745, + "y": 466.5, + "button": 2, + "buttons": 2, + "time": 91366537.593083, + "panning": true + }, + { + "type": "pointermove", + "x": 753, + "y": 468.5, + "button": 2, + "buttons": 2, + "time": 91366573.249541, + "panning": true + }, + { + "type": "pointermove", + "x": 761, + "y": 470.5, + "button": 2, + "buttons": 2, + "time": 91366609.303708, + "panning": true + }, + { + "type": "pointermove", + "x": 765, + "y": 471.5, + "button": 2, + "buttons": 2, + "time": 91366624.099083, + "panning": true + }, + { + "type": "pointermove", + "x": 870.296875, + "y": 455.9375, + "button": 0, + "buttons": 0, + "time": 91366635.09725, + "panning": true + }, + { + "type": "pointermove", + "x": 769, + "y": 472.5, + "button": 2, + "buttons": 2, + "time": 91366640.509583, + "panning": true + }, + { + "type": "pointermove", + "x": 870.06640625, + "y": 455.9375, + "button": 0, + "buttons": 0, + "time": 91366644.501041, + "panning": true + }, + { + "type": "pointermove", + "x": 869.8359375, + "y": 455.9375, + "button": 0, + "buttons": 0, + "time": 91366650.498458, + "panning": true + }, + { + "type": "pointermove", + "x": 773, + "y": 473.5, + "button": 2, + "buttons": 2, + "time": 91366656.263208, + "panning": true + }, + { + "type": "pointermove", + "x": 866.578125, + "y": 455.16796875, + "button": 0, + "buttons": 0, + "time": 91366667.82075, + "panning": true + }, + { + "type": "pointermove", + "x": 777, + "y": 474.5, + "button": 2, + "buttons": 2, + "time": 91366671.951041, + "panning": true + }, + { + "type": "pointermove", + "x": 864.1953125, + "y": 455.16796875, + "button": 0, + "buttons": 0, + "time": 91366679.800875, + "panning": true + }, + { + "type": "pointermove", + "x": 861.8125, + "y": 455.16796875, + "button": 0, + "buttons": 0, + "time": 91366684.155583, + "panning": true + }, + { + "type": "pointermove", + "x": 781, + "y": 475.5, + "button": 2, + "buttons": 2, + "time": 91366688.181083, + "panning": true + }, + { + "type": "pointermove", + "x": 855.546875, + "y": 455.16796875, + "button": 0, + "buttons": 0, + "time": 91366699.93775, + "panning": true + }, + { + "type": "pointermove", + "x": 785, + "y": 476.5, + "button": 2, + "buttons": 2, + "time": 91366703.971666, + "panning": true + }, + { + "type": "pointermove", + "x": 850.03125, + "y": 455.16796875, + "button": 0, + "buttons": 0, + "time": 91366719.05075, + "panning": true + }, + { + "type": "pointermove", + "x": 781, + "y": 475.5, + "button": 2, + "buttons": 2, + "time": 91366723.003166, + "panning": true + }, + { + "type": "pointermove", + "x": 843.765625, + "y": 455.16796875, + "button": 0, + "buttons": 0, + "time": 91366732.270625, + "panning": true + }, + { + "type": "pointermove", + "x": 777, + "y": 474.5, + "button": 2, + "buttons": 2, + "time": 91366735.649666, + "panning": true + }, + { + "type": "pointermove", + "x": 840.5078125, + "y": 454.34765625, + "button": 0, + "buttons": 0, + "time": 91366750.877375, + "panning": true + }, + { + "type": "pointermove", + "x": 773, + "y": 473.5, + "button": 2, + "buttons": 2, + "time": 91366754.406416, + "panning": true + }, + { + "type": "pointermove", + "x": 838.41796875, + "y": 454.39453125, + "button": 0, + "buttons": 0, + "time": 91366772.539916, + "panning": true + }, + { + "type": "pointermove", + "x": 769, + "y": 472.5, + "button": 2, + "buttons": 2, + "time": 91366778.474666, + "panning": true + }, + { + "type": "pointermove", + "x": 837.89453125, + "y": 455.265625, + "button": 0, + "buttons": 0, + "time": 91366789.967166, + "panning": true + }, + { + "type": "pointermove", + "x": 765, + "y": 471.5, + "button": 2, + "buttons": 2, + "time": 91366801.114208, + "panning": true + }, + { + "type": "pointermove", + "x": 837.01953125, + "y": 455.265625, + "button": 0, + "buttons": 0, + "time": 91366809.222833, + "panning": true + }, + { + "type": "pointermove", + "x": 761, + "y": 470.5, + "button": 2, + "buttons": 2, + "time": 91366813.207041, + "panning": true + }, + { + "type": "pointermove", + "x": 833.12109375, + "y": 455.4921875, + "button": 0, + "buttons": 0, + "time": 91366818.101791, + "panning": true + }, + { + "type": "pointermove", + "x": 757, + "y": 469.5, + "button": 2, + "buttons": 2, + "time": 91366824.310166, + "panning": true + }, + { + "type": "pointermove", + "x": 829.11328125, + "y": 455.71875, + "button": 0, + "buttons": 0, + "time": 91366838.632041, + "panning": true + }, + { + "type": "pointermove", + "x": 753, + "y": 468.5, + "button": 2, + "buttons": 2, + "time": 91366842.028083, + "panning": true + }, + { + "type": "pointermove", + "x": 822.84765625, + "y": 455.71875, + "button": 0, + "buttons": 0, + "time": 91366857.733833, + "panning": true + }, + { + "type": "pointermove", + "x": 749, + "y": 467.5, + "button": 2, + "buttons": 2, + "time": 91366862.001958, + "panning": true + }, + { + "type": "pointermove", + "x": 818.83984375, + "y": 455.71875, + "button": 0, + "buttons": 0, + "time": 91366867.831666, + "panning": true + }, + { + "type": "pointermove", + "x": 745, + "y": 466.5, + "button": 2, + "buttons": 2, + "time": 91366873.318083, + "panning": true + }, + { + "type": "pointermove", + "x": 810.171875, + "y": 455.1328125, + "button": 0, + "buttons": 0, + "time": 91366888.82225, + "panning": true + }, + { + "type": "pointermove", + "x": 741, + "y": 465.5, + "button": 2, + "buttons": 2, + "time": 91366897.282833, + "panning": true + }, + { + "type": "pointermove", + "x": 805.51171875, + "y": 455.71484375, + "button": 0, + "buttons": 0, + "time": 91366903.101083, + "panning": true + }, + { + "type": "pointermove", + "x": 800.8515625, + "y": 453.96484375, + "button": 0, + "buttons": 0, + "time": 91366906.59375, + "panning": true + }, + { + "type": "pointermove", + "x": 737, + "y": 464.5, + "button": 2, + "buttons": 2, + "time": 91366912.204875, + "panning": true + }, + { + "type": "pointermove", + "x": 788.48046875, + "y": 453.96484375, + "button": 0, + "buttons": 0, + "time": 91366922.049166, + "panning": true + }, + { + "type": "pointermove", + "x": 733, + "y": 463.5, + "button": 2, + "buttons": 2, + "time": 91366928.195833, + "panning": true + }, + { + "type": "pointermove", + "x": 775.34375, + "y": 453.96484375, + "button": 0, + "buttons": 0, + "time": 91366937.888333, + "panning": true + }, + { + "type": "pointermove", + "x": 729, + "y": 462.5, + "button": 2, + "buttons": 2, + "time": 91366942.857416, + "panning": true + }, + { + "type": "pointermove", + "x": 761.4453125, + "y": 453.359375, + "button": 0, + "buttons": 0, + "time": 91366956.738125, + "panning": true + }, + { + "type": "pointermove", + "x": 725, + "y": 461.5, + "button": 2, + "buttons": 2, + "time": 91366960.722291, + "panning": true + }, + { + "type": "pointermove", + "x": 746.78125, + "y": 453.359375, + "button": 0, + "buttons": 0, + "time": 91366973.117458, + "panning": true + }, + { + "type": "pointermove", + "x": 721, + "y": 460.5, + "button": 2, + "buttons": 2, + "time": 91366977.139916, + "panning": true + }, + { + "type": "pointermove", + "x": 739.06640625, + "y": 453.359375, + "button": 0, + "buttons": 0, + "time": 91366980.483, + "panning": true + }, + { + "type": "pointermove", + "x": 731.3515625, + "y": 453.359375, + "button": 0, + "buttons": 0, + "time": 91366989.68625, + "panning": true + }, + { + "type": "pointermove", + "x": 717, + "y": 459.5, + "button": 2, + "buttons": 2, + "time": 91366993.759333, + "panning": true + }, + { + "type": "pointermove", + "x": 719.7421875, + "y": 453.94140625, + "button": 0, + "buttons": 0, + "time": 91367008.646, + "panning": true + }, + { + "type": "pointermove", + "x": 713, + "y": 458.5, + "button": 2, + "buttons": 2, + "time": 91367014.565291, + "panning": true + }, + { + "type": "pointermove", + "x": 708.12890625, + "y": 453.94140625, + "button": 0, + "buttons": 0, + "time": 91367022.767458, + "panning": true + }, + { + "type": "pointermove", + "x": 701.94140625, + "y": 453.94140625, + "button": 0, + "buttons": 0, + "time": 91367028.955666, + "panning": true + }, + { + "type": "pointermove", + "x": 709, + "y": 457.5, + "button": 2, + "buttons": 2, + "time": 91367033.560208, + "panning": true + }, + { + "type": "pointermove", + "x": 688.04296875, + "y": 453.94140625, + "button": 0, + "buttons": 0, + "time": 91367042.859166, + "panning": true + }, + { + "type": "pointermove", + "x": 705, + "y": 456.5, + "button": 2, + "buttons": 2, + "time": 91367048.153416, + "panning": true + }, + { + "type": "pointermove", + "x": 683.26953125, + "y": 454.78515625, + "button": 0, + "buttons": 0, + "time": 91367059.087666, + "panning": true + }, + { + "type": "pointermove", + "x": 701, + "y": 455.5, + "button": 2, + "buttons": 2, + "time": 91367062.48225, + "panning": true + }, + { + "type": "pointermove", + "x": 670.8984375, + "y": 455.98828125, + "button": 0, + "buttons": 0, + "time": 91367076.320416, + "panning": true + }, + { + "type": "pointermove", + "x": 697, + "y": 454.5, + "button": 2, + "buttons": 2, + "time": 91367080.609083, + "panning": true + }, + { + "type": "pointermove", + "x": 663.8671875, + "y": 458.66015625, + "button": 0, + "buttons": 0, + "time": 91367095.083333, + "panning": true + }, + { + "type": "pointermove", + "x": 693, + "y": 453.5, + "button": 2, + "buttons": 2, + "time": 91367099.017583, + "panning": true + }, + { + "type": "pointermove", + "x": 659.96875, + "y": 459.76953125, + "button": 0, + "buttons": 0, + "time": 91367101.943875, + "panning": true + }, + { + "type": "pointermove", + "x": 654.546875, + "y": 461.57421875, + "button": 0, + "buttons": 0, + "time": 91367110.340625, + "panning": true + }, + { + "type": "pointermove", + "x": 689, + "y": 452.5, + "button": 2, + "buttons": 2, + "time": 91367113.849708, + "panning": true + }, + { + "type": "pointermove", + "x": 643.69921875, + "y": 462.19140625, + "button": 0, + "buttons": 0, + "time": 91367126.817458, + "panning": true + }, + { + "type": "pointermove", + "x": 685, + "y": 451.5, + "button": 2, + "buttons": 2, + "time": 91367129.556958, + "panning": true + }, + { + "type": "pointermove", + "x": 635.90234375, + "y": 461.6328125, + "button": 0, + "buttons": 0, + "time": 91367143.792583, + "panning": true + }, + { + "type": "pointermove", + "x": 681, + "y": 450.5, + "button": 2, + "buttons": 2, + "time": 91367146.457208, + "panning": true + }, + { + "type": "pointermove", + "x": 622.51171875, + "y": 468, + "button": 0, + "buttons": 0, + "time": 91367153.895958, + "panning": true + }, + { + "type": "pointermove", + "x": 677, + "y": 449.5, + "button": 2, + "buttons": 2, + "time": 91367161.864333, + "panning": true + }, + { + "type": "pointermove", + "x": 605.55859375, + "y": 473.2109375, + "button": 0, + "buttons": 0, + "time": 91367172.672916, + "panning": true + }, + { + "type": "pointermove", + "x": 673, + "y": 448.5, + "button": 2, + "buttons": 2, + "time": 91367177.87075, + "panning": true + }, + { + "type": "pointermove", + "x": 589.2578125, + "y": 479.7265625, + "button": 0, + "buttons": 0, + "time": 91367192.777041, + "panning": true + }, + { + "type": "pointermove", + "x": 669, + "y": 447.5, + "button": 2, + "buttons": 2, + "time": 91367196.468041, + "panning": true + }, + { + "type": "pointermove", + "x": 565.2421875, + "y": 489.45703125, + "button": 0, + "buttons": 0, + "time": 91367211.522833, + "panning": true + }, + { + "type": "pointermove", + "x": 665, + "y": 446.5, + "button": 2, + "buttons": 2, + "time": 91367215.931125, + "panning": true + }, + { + "type": "pointermove", + "x": 550.05859375, + "y": 500.671875, + "button": 0, + "buttons": 0, + "time": 91367228.189041, + "panning": true + }, + { + "type": "pointermove", + "x": 661, + "y": 445.5, + "button": 2, + "buttons": 2, + "time": 91367231.632833, + "panning": true + }, + { + "type": "pointermove", + "x": 539.19921875, + "y": 504.87890625, + "button": 0, + "buttons": 0, + "time": 91367245.996166, + "panning": true + }, + { + "type": "pointermove", + "x": 657, + "y": 444.5, + "button": 2, + "buttons": 2, + "time": 91367251.0455, + "panning": true + }, + { + "type": "pointermove", + "x": 523.93359375, + "y": 518.140625, + "button": 0, + "buttons": 0, + "time": 91367263.331458, + "panning": true + }, + { + "type": "pointermove", + "x": 653, + "y": 443.5, + "button": 2, + "buttons": 2, + "time": 91367270.440333, + "panning": true + }, + { + "type": "pointermove", + "x": 517.875, + "y": 527.55859375, + "button": 0, + "buttons": 0, + "time": 91367276.9255, + "panning": true + }, + { + "type": "pointermove", + "x": 508.25390625, + "y": 536.48828125, + "button": 0, + "buttons": 0, + "time": 91367280.609791, + "panning": true + }, + { + "type": "pointermove", + "x": 649, + "y": 442.5, + "button": 2, + "buttons": 2, + "time": 91367286.112125, + "panning": true + }, + { + "type": "pointermove", + "x": 495.96875, + "y": 556.8828125, + "button": 0, + "buttons": 0, + "time": 91367293.914375, + "panning": true + }, + { + "type": "pointermove", + "x": 490.6875, + "y": 564.80078125, + "button": 0, + "buttons": 0, + "time": 91367297.1165, + "panning": true + }, + { + "type": "pointermove", + "x": 645, + "y": 441.5, + "button": 2, + "buttons": 2, + "time": 91367304.560083, + "panning": true + }, + { + "type": "pointermove", + "x": 482.26171875, + "y": 579.03515625, + "button": 0, + "buttons": 0, + "time": 91367315.371916, + "panning": true + }, + { + "type": "pointermove", + "x": 641, + "y": 440.5, + "button": 2, + "buttons": 2, + "time": 91367321.392625, + "panning": true + }, + { + "type": "pointermove", + "x": 471.97265625, + "y": 591.2421875, + "button": 0, + "buttons": 0, + "time": 91367329.233791, + "panning": true + }, + { + "type": "pointermove", + "x": 637, + "y": 439.5, + "button": 2, + "buttons": 2, + "time": 91367334.265458, + "panning": true + }, + { + "type": "pointermove", + "x": 465.71875, + "y": 603.125, + "button": 0, + "buttons": 0, + "time": 91367347.562208, + "panning": true + }, + { + "type": "pointermove", + "x": 633, + "y": 438.5, + "button": 2, + "buttons": 2, + "time": 91367352.093375, + "panning": true + }, + { + "type": "pointermove", + "x": 462.63671875, + "y": 614.7265625, + "button": 0, + "buttons": 0, + "time": 91367364.091541, + "panning": true + }, + { + "type": "pointermove", + "x": 629, + "y": 437.5, + "button": 2, + "buttons": 2, + "time": 91367369.811583, + "panning": true + }, + { + "type": "pointermove", + "x": 462.63671875, + "y": 622.4375, + "button": 0, + "buttons": 0, + "time": 91367373.974083, + "panning": true + }, + { + "type": "pointermove", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 2, + "time": 91367389.131833, + "panning": true + }, + { + "type": "pointermove", + "x": 459.59765625, + "y": 639.4609375, + "button": 0, + "buttons": 0, + "time": 91367399.771916, + "panning": true + }, + { + "type": "pointerup", + "x": 625, + "y": 436.5, + "button": 2, + "buttons": 0, + "time": 91367401.388791, + "panning": false + }, + { + "type": "pointermove", + "x": 455.73046875, + "y": 670.30078125, + "button": 0, + "buttons": 0, + "time": 91367424.771041, + "panning": false + }, + { + "type": "pointermove", + "x": 453.80078125, + "y": 680.390625, + "button": 0, + "buttons": 0, + "time": 91367442.742791, + "panning": false + }, + { + "type": "pointermove", + "x": 451.0625, + "y": 728.859375, + "button": 0, + "buttons": 0, + "time": 91367478.128458, + "panning": false + }, + { + "type": "pointermove", + "x": 451.0625, + "y": 748.09765625, + "button": 0, + "buttons": 0, + "time": 91367495.736, + "panning": false + }, + { + "type": "pointermove", + "x": 449.7578125, + "y": 765.046875, + "button": 0, + "buttons": 0, + "time": 91367516.465041, + "panning": false + }, + { + "type": "pointermove", + "x": 449.7578125, + "y": 776.6484375, + "button": 0, + "buttons": 0, + "time": 91367533.581958, + "panning": false + }, + { + "type": "pointermove", + "x": 449.13671875, + "y": 785.2109375, + "button": 0, + "buttons": 0, + "time": 91367549.526958, + "panning": false + }, + { + "type": "pointermove", + "x": 449.54296875, + "y": 788.91796875, + "button": 0, + "buttons": 0, + "time": 91367566.421416, + "panning": false + }, + { + "type": "pointermove", + "x": 449.54296875, + "y": 789.375, + "button": 0, + "buttons": 0, + "time": 91367582.607625, + "panning": false + }, + { + "type": "pointermove", + "x": 449.54296875, + "y": 789.6015625, + "button": 0, + "buttons": 0, + "time": 91367616.415166, + "panning": false + }, + { + "type": "pointermove", + "x": 449.54296875, + "y": 789.828125, + "button": 0, + "buttons": 0, + "time": 91367649.757875, + "panning": false + }, + { + "type": "pointermove", + "x": 449.54296875, + "y": 790.0546875, + "button": 0, + "buttons": 0, + "time": 91367666.335041, + "panning": false + }, + { + "type": "pointermove", + "x": 449.54296875, + "y": 790.28125, + "button": 0, + "buttons": 0, + "time": 91367683.109291, + "panning": false + }, + { + "type": "pointermove", + "x": 449.54296875, + "y": 790.5078125, + "button": 0, + "buttons": 0, + "time": 91367733.127083, + "panning": false + }, + { + "type": "pointermove", + "x": 449.54296875, + "y": 790.734375, + "button": 0, + "buttons": 0, + "time": 91367833.222125, + "panning": false + }, + { + "type": "pointermove", + "x": 449.54296875, + "y": 791.19140625, + "button": 0, + "buttons": 0, + "time": 91367899.753625, + "panning": false + } + ], + "captures": [], + "frames": [ + { + "timestamp": 91365821.486708, + "start": 91365824.830958, + "duration": 0.5970830023288727 + }, + { + "timestamp": 91365839.72608301, + "start": 91365841.58925, + "duration": 0.5226249992847443 + }, + { + "timestamp": 91365873.902916, + "start": 91365875.777125, + "duration": 0.6460410058498383 + }, + { + "timestamp": 91365908.962916, + "start": 91365910.431625, + "duration": 0.4919160008430481 + }, + { + "timestamp": 91365976.572583, + "start": 91365977.987, + "duration": 0.5140829980373383 + }, + { + "timestamp": 91365994.604, + "start": 91365996.045666, + "duration": 0.5669170022010803 + }, + { + "timestamp": 91366041.782916, + "start": 91366056.585375, + "duration": 1.0792080014944077 + }, + { + "timestamp": 91366077.93775, + "start": 91366084.733833, + "duration": 1.2907499969005585 + }, + { + "timestamp": 91366114.393208, + "start": 91366122.674125, + "duration": 0.7472500056028366 + }, + { + "timestamp": 91366149.8925, + "start": 91366160.658041, + "duration": 1.0522920042276382 + }, + { + "timestamp": 91366172.102, + "start": 91366180.198375, + "duration": 0.7193329930305481 + }, + { + "timestamp": 91366209.32004099, + "start": 91366217.955666, + "duration": 0.6659590005874634 + }, + { + "timestamp": 91366243.12104098, + "start": 91366258.751583, + "duration": 1.086457997560501 + }, + { + "timestamp": 91366330.33787501, + "start": 91366341.007958, + "duration": 0.8586250096559525 + }, + { + "timestamp": 91366348.481041, + "start": 91366356.798166, + "duration": 0.8169589936733246 + }, + { + "timestamp": 91366401.103375, + "start": 91366404.300333, + "duration": 0.9234580099582672 + }, + { + "timestamp": 91366438.93525, + "start": 91366444.394375, + "duration": 0.7551660090684891 + }, + { + "timestamp": 91366456.72225, + "start": 91366459.956375, + "duration": 0.6547079980373383 + }, + { + "timestamp": 91366491.167166, + "start": 91366495.00025, + "duration": 0.6088330000638962 + }, + { + "timestamp": 91366534.69504099, + "start": 91366539.007916, + "duration": 0.6709589958190918 + }, + { + "timestamp": 91366569.946, + "start": 91366573.953958, + "duration": 0.7259999960660934 + }, + { + "timestamp": 91366604.70333299, + "start": 91366610.067708, + "duration": 0.646917000412941 + }, + { + "timestamp": 91366641.56579101, + "start": 91366645.682958, + "duration": 0.73854199051857 + }, + { + "timestamp": 91366695.45308301, + "start": 91366705.285041, + "duration": 0.9514589905738831 + }, + { + "timestamp": 91366747.42587501, + "start": 91366754.998916, + "duration": 0.6716669946908951 + }, + { + "timestamp": 91366805.516916, + "start": 91366818.896583, + "duration": 1.2580000013113022 + }, + { + "timestamp": 91366853.70675, + "start": 91366868.6085, + "duration": 0.8050410002470016 + }, + { + "timestamp": 91366935.086916, + "start": 91366944.026833, + "duration": 0.7535420060157776 + }, + { + "timestamp": 91366973.607, + "start": 91366981.69425, + "duration": 0.6457909941673279 + }, + { + "timestamp": 91367009.326916, + "start": 91367023.695375, + "duration": 1.3130000084638596 + }, + { + "timestamp": 91367056.51583299, + "start": 91367063.699791, + "duration": 0.6152089983224869 + }, + { + "timestamp": 91367092.433625, + "start": 91367103.228666, + "duration": 0.979750007390976 + }, + { + "timestamp": 91367150.92608301, + "start": 91367154.64725, + "duration": 1.098916009068489 + }, + { + "timestamp": 91367169.799541, + "start": 91367174.233958, + "duration": 0.791374996304512 + }, + { + "timestamp": 91367225.710875, + "start": 91367234.171625, + "duration": 0.878040999174118 + }, + { + "timestamp": 91367291.251125, + "start": 91367298.723375, + "duration": 0.7319580018520355 + }, + { + "timestamp": 91367327.045916, + "start": 91367335.539333, + "duration": 0.6548749953508377 + }, + { + "timestamp": 91367368.24004099, + "start": 91367375.325666, + "duration": 0.9786249995231628 + }, + { + "timestamp": 91367424.212, + "start": 91367424.825625, + "duration": 0.6872079968452454 + } + ], + "panning": false, + "backend": "WebGPU \u00b7 GPU pipeline", + "errors": 0 + } + }, + "limitations": [ + "Observed events exceed the controlled 80-move workload; additional input invalidates a controlled comparison.", + "RAF timestamps and rendered scene counters are not physical presentation timestamps.", + "Both connected displays report 60Hz; this does not prove application frame rate.", + "Existing interactive Kestrel window was left open." + ] +} diff --git a/docs/graphics/evidence/kestrel/within-frame-resize-hold.json b/docs/graphics/evidence/kestrel/within-frame-resize-hold.json new file mode 100644 index 000000000..10a98547f --- /dev/null +++ b/docs/graphics/evidence/kestrel/within-frame-resize-hold.json @@ -0,0 +1,853 @@ +{ + "runtimeRegressionPassed": true, + "sidebarGeometryPassed": true, + "physicalPresentationVerified": false, + "performanceQualified": false, + "fix": "Submission of a replacement output clears a bitmap-reset hold created after frame admission.", + "timeline": { + "traceStarted": 93911981644750, + "timestampFrequency": 1000000000, + "originalWidth": 222, + "width": 342, + "baseline": { + "ContextId": 1, + "Timestamp": 93911979510458, + "Engine": { + "EnqueuedInputs": 3, + "DroppedInputs": 0, + "ConsumedInputs": 3, + "PublishedScenes": 5, + "AcquiredScenes": 3, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1316, + "LayoutPasses": 8, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 13, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3485709, + "InputEventsDispatched": 17, + "InputCallbacksInvoked": 1, + "BusiestCanvasWidthMilli": 806000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 101833, + "LastScenePublicationNanoseconds": 3324542, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 0, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 1, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 1, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 2673292, + "LastSceneBuildNanoseconds": 479750, + "MaximumScenePublicationNanoseconds": 3324542 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 198625, + "MaximumDispatchNanoseconds": 198625, + "LastDispatchSequence": 639244599307960332, + "DispatchedInputs": 1, + "TotalDispatchNanoseconds": 198625 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 1, + "TotalDispatchNanoseconds": 542, + "LastDispatchNanoseconds": 542, + "MaximumDispatchNanoseconds": 542, + "LastTimestampMicroseconds": 93908971450 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 8, + "BlockedPublications": 0, + "AcknowledgedScenes": 3, + "TotalAcknowledgementNanoseconds": 380527708, + "LastAcknowledgementNanoseconds": 37855500, + "MaximumAcknowledgementNanoseconds": 174193833, + "AcknowledgedRevision": 5 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 0, + "AnimationFramesInvoked": 0, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 0, + "WorkerWaits": 0, + "WorkerSignalledWakes": 0, + "WorkerTimeoutWakes": 0, + "SceneBuilds": 0, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 0, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 0, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 13, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3141516, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1519616, + "LatestSceneBytes": 127068, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 706808, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920176, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196288, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 130772, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 2, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 0, + "RetainedCommandCount": 0, + "LogicalBitmapBytes": 0, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 0, + "StringCount": 0, + "StringBytes": 0, + "TypefaceCount": 0, + "SvgPictureCount": 0, + "ProcessSvgPictureCount": 0, + "ProcessSvgPictureReferenceCount": 0, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 2, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 0, + "Renders": 0, + "AppliedDiffs": 0, + "InvalidationCalls": 0, + "DamageRectangles": 0, + "ChangedLayers": 0, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 0, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 0, + "SkippedEmptyAnimationFrames": 0, + "RenderCallbacks": 0, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "after": { + "ContextId": 1, + "Timestamp": 93913550800250, + "Engine": { + "EnqueuedInputs": 128, + "DroppedInputs": 0, + "ConsumedInputs": 128, + "PublishedScenes": 18, + "AcquiredScenes": 16, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "DomNodes": 1367, + "LayoutPasses": 169, + "IframeNodes": 0, + "IframeHtmlBytes": 0, + "FrameScriptsExecuted": 10, + "FrameScriptErrors": 0, + "CanvasNodes": 2, + "ComponentReady": 0, + "CompilationRequests": 14, + "CompilationMemoryHits": 0, + "CompilationPersistentHits": 0, + "CompilationPersistentMisses": 0, + "CompilationCacheRejections": 0, + "CompilationCacheBytesRead": 0, + "CompilationCacheBytesWritten": 0, + "CompilationTimeNanoseconds": 3548084, + "InputEventsDispatched": 163, + "InputCallbacksInvoked": 113, + "BusiestCanvasWidthMilli": 686000, + "BusiestCanvasHeightMilli": 463000, + "CoalescedResizeInputs": 0, + "AppliedResizeInputs": 1, + "LastResizeDispatchNanoseconds": 101833, + "LastScenePublicationNanoseconds": 1750, + "LastResizeOuterListenersNanoseconds": 0, + "LastResizeFrameListenersNanoseconds": 0, + "LastResizeLayoutNanoseconds": 0, + "LastResizeObserversNanoseconds": 0, + "CoalescedPointerMoveInputs": 1, + "CoalescedWheelInputs": 0, + "AppliedPointerMoveInputs": 60, + "AppliedWheelInputs": 0, + "AppliedAnimationFrames": 64, + "CoalescedAnimationFrames": 0, + "LastAnimationAdvanceNanoseconds": 0, + "LastLayoutNanoseconds": 1002542, + "LastSceneBuildNanoseconds": 348834, + "MaximumScenePublicationNanoseconds": 3324542 + }, + "InputDispatch": { + "StructSize": 48, + "Reserved": 0, + "LastDispatchNanoseconds": 123916, + "MaximumDispatchNanoseconds": 8558250, + "LastDispatchSequence": 639244599307960394, + "DispatchedInputs": 62, + "TotalDispatchNanoseconds": 304606417 + }, + "AnimationFrames": { + "StructSize": 48, + "Reserved": 0, + "DispatchedFrames": 64, + "TotalDispatchNanoseconds": 51127, + "LastDispatchNanoseconds": 2667, + "MaximumDispatchNanoseconds": 5709, + "LastTimestampMicroseconds": 93913058481 + }, + "SceneFlow": { + "StructSize": 64, + "Reserved": 0, + "PublicationAttempts": 208, + "BlockedPublications": 0, + "AcknowledgedScenes": 16, + "TotalAcknowledgementNanoseconds": 719534751, + "LastAcknowledgementNanoseconds": 23833542, + "MaximumAcknowledgementNanoseconds": 174193833, + "AcknowledgedRevision": 18 + }, + "ResizeFrames": { + "StructSize": 136, + "Reserved": 0, + "SubmittedPairs": 0, + "AppliedPairs": 0, + "PublishedPairs": 0, + "TotalQueueNanoseconds": 0, + "LastQueueNanoseconds": 0, + "MaximumQueueNanoseconds": 0, + "TotalDispatchNanoseconds": 0, + "LastDispatchNanoseconds": 0, + "MaximumDispatchNanoseconds": 0, + "AnimationFrameCallbacks": 0, + "TotalAnimationFrameBatchNanoseconds": 0, + "LastAnimationFrameBatchNanoseconds": 0, + "MaximumAnimationFrameBatchNanoseconds": 0, + "TotalToPublicationNanoseconds": 0, + "LastToPublicationNanoseconds": 0, + "MaximumToPublicationNanoseconds": 0 + }, + "ResourceCache": { + "StructSize": 56, + "Reserved": 0, + "Requests": 1, + "Hits": 0, + "Misses": 1, + "Rejections": 0, + "BytesRead": 0, + "BytesWritten": 0 + }, + "RuntimeWork": { + "StructSize": 168, + "Reserved": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 51, + "AnimationFramesInvoked": 51, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 249, + "WorkerWaits": 226, + "WorkerSignalledWakes": 202, + "WorkerTimeoutWakes": 23, + "SceneBuilds": 13, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0 + }, + "ProcessCache": { + "StructSize": 112, + "Reserved": 0, + "CompilationMemoryHits": 0, + "CompilationLeaders": 14, + "CompilationWaiters": 0, + "CompilationSharedBytes": 0, + "ResourceMemoryHits": 0, + "ResourceLoadLeaders": 1, + "ResourceLoadWaiters": 0, + "ResourceSharedBytes": 0, + "ScriptSourceMemoryHits": 0, + "ScriptSourceSharedBytes": 0, + "SharedIsolateSlot": 18446744073709551615, + "SharedIsolateActiveContexts": 1, + "SharedIsolatePeakContexts": 1 + }, + "Memory": { + "StructSize": 608, + "Reserved": 0, + "V8TotalHeapBytes": 5701632, + "V8UsedHeapBytes": 3141516, + "V8ExecutableHeapBytes": 524288, + "V8PhysicalHeapBytes": 5701632, + "V8ExternalBytes": 442729, + "V8MallocedBytes": 73812, + "V8PeakMallocedBytes": 1519616, + "LatestSceneBytes": 157080, + "ProcessCompilationCacheBytes": 250464, + "ProcessResourceCacheBytes": 0, + "V8CodeAndMetadataBytes": 0, + "V8BytecodeAndMetadataBytes": 0, + "V8ExternalScriptSourceBytes": 785023, + "NativeDomNodeCount": 1051, + "NativeDomNodeSizeBytes": 1000, + "NativeDomInlineBytes": 1051000, + "NativeDomPseudoStorageBytes": 6800, + "NativeDomCanvasNodeCount": 2, + "NativeDomCanvasStorageBytes": 448, + "NativeDomAnimationCount": 3, + "NativeDomAnimationStorageBytes": 3801, + "NativeDomCustomPropertyNodeCount": 1, + "NativeDomCustomPropertyEntryCount": 22, + "NativeDomCustomPropertyStorageBytes": 2700, + "NativeDomBackgroundImageCount": 2, + "NativeDomBackgroundImageStorageBytes": 1006, + "NativeDomGridCount": 1, + "NativeDomGridStorageBytes": 569, + "NativeDomAuthoredStyleNodeCount": 12, + "NativeDomAuthoredStyleEntryCount": 14, + "NativeDomAuthoredStyleStorageBytes": 2692, + "NativeCssRuleCount": 407, + "NativeCssRuleStorageBytes": 16495, + "NativeCssIndexStorageBytes": 45535, + "ProcessSharedCssRuleCount": 407, + "ProcessSharedCssRuleStorageBytes": 369573, + "LowMemoryNotifications": 0, + "NativeDomAttributeNodeCount": 678, + "NativeDomAttributeEntryCount": 1848, + "NativeDomAttributeStorageBytes": 183026, + "NativeWrapperHandleCount": 269, + "NativeWrapperStorageBytes": 11000, + "NativeTextMeasurementCacheEntryCount": 425, + "NativeTextMeasurementCacheStorageBytes": 99812, + "ProcessCompilationMappedCacheBytes": 0, + "ProcessResourceMappedCacheBytes": 0, + "NativeDomTextualStyleCount": 751, + "NativeDomTextualStyleStorageBytes": 485542, + "NativeDomNodePoolReservedBytes": 1088000, + "NativeDomNodePoolPeakBytes": 1088000, + "NativeDomTableLayoutCount": 0, + "NativeDomTableLayoutStorageBytes": 0, + "NativeDomFormControlCount": 52, + "NativeDomFormControlStorageBytes": 4108, + "HiddenLowMemoryNotifications": 0, + "NativeEventListenerCount": 30, + "NativeEventListenerStorageBytes": 4685, + "V8YoungSpaceUsedBytes": 706808, + "V8YoungSpacePhysicalBytes": 2097152, + "V8OldSpaceUsedBytes": 1920176, + "V8OldSpacePhysicalBytes": 2097152, + "V8CodeSpaceUsedBytes": 196288, + "V8CodeSpacePhysicalBytes": 524288, + "V8MapSpaceUsedBytes": 0, + "V8MapSpacePhysicalBytes": 0, + "V8LargeObjectSpaceUsedBytes": 187472, + "V8LargeObjectSpacePhysicalBytes": 196608, + "V8ReadOnlySpaceUsedBytes": 1816740, + "V8ReadOnlySpacePhysicalBytes": 1835008, + "V8SharedSpaceUsedBytes": 0, + "V8SharedSpacePhysicalBytes": 0, + "V8TrustedSpaceUsedBytes": 130772, + "V8TrustedSpacePhysicalBytes": 786432, + "PendingSceneCount": 0, + "PendingSceneBytes": 0 + }, + "InteropPool": { + "StructSize": 200, + "Version": 3, + "OutstandingResults": 0, + "PooledBytes": 1022, + "PoolHits": 3, + "PoolMisses": 1, + "OversizeAllocations": 0, + "HighWaterOutstandingResults": 1, + "PooledRequestRecords": 0, + "RequestPoolHits": 0, + "RequestPoolMisses": 0, + "RequestOversizeAllocations": 0, + "ActiveOperationSlots": 0, + "AvailableOperationSlots": 1, + "OperationSlotHighWater": 1, + "PooledResultBytes4K": 1022, + "PooledResultBytes16K": 0, + "PooledResultBytes64K": 0, + "PooledResultBytes256K": 0, + "PooledResultBytes1M": 0, + "TakenResultLeases": 0, + "OperationResultLeases": 0, + "QueuedCallbacks": 0, + "TakenCallbackLeases": 0, + "PendingCallbackPromises": 0, + "CallbackQueueHighWater": 0 + }, + "RendererMemory": { + "RetainedLayerCount": 1, + "RetainedCommandCount": 315, + "LogicalBitmapBytes": 5141152, + "IsolationLayerCount": 0, + "IsolationLogicalBitmapBytes": 0, + "DomCommandCount": 484, + "StringCount": 835, + "StringBytes": 74448, + "TypefaceCount": 2, + "SvgPictureCount": 73, + "ProcessSvgPictureCount": 73, + "ProcessSvgPictureReferenceCount": 73, + "ProcessSvgPictureMemoryHits": 0 + }, + "Surface": { + "RenderedScenes": 15, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "CompositionUiWakes": 0, + "PendingCompositionPublications": 0, + "ResizePublicationNotifications": 0 + }, + "ProcessWebTypefaces": { + "Entries": 0, + "References": 0, + "Hits": 0, + "Misses": 0 + }, + "ProcessComposition": { + "AnimationFrames": 94, + "Renders": 13, + "AppliedDiffs": 13, + "InvalidationCalls": 13, + "DamageRectangles": 37, + "ChangedLayers": 12, + "EmptyDamageDiffs": 0, + "PartialDamageDiffs": 13, + "FullInvalidations": 0, + "SuppressedLiveResizeAnimationFrames": 0, + "SubmittedAnimationFrames": 63, + "SkippedEmptyAnimationFrames": 31, + "RenderCallbacks": 13, + "UnchangedRenderCallbacks": 0, + "LastAnimationFrameDemand": 0 + } + }, + "delta": { + "Elapsed": "00:00:01.5712897", + "EnqueuedInputs": 125, + "DroppedInputs": 0, + "ConsumedInputs": 125, + "ExecutedScripts": 0, + "ScriptErrors": 0, + "LayoutPasses": 161, + "AppliedAnimationFrames": 63, + "CoalescedAnimationFrames": 0, + "PublicationAttempts": 200, + "BlockedPublications": 0, + "PublishedScenes": 13, + "AcquiredScenes": 13, + "AcknowledgedScenes": 13, + "RenderedScenes": 13, + "CompositionUiWakes": 0, + "RoutedInputEvents": 0, + "AcceptedInputEvents": 0, + "ResourceRequests": 0, + "ResourceHits": 0, + "ResourceMisses": 0, + "InteropPoolHits": 1, + "InteropPoolMisses": 0, + "InteropRequestPoolHits": 0, + "InteropRequestPoolMisses": 0, + "TimersScheduled": 0, + "TimersFired": 0, + "TimersCancelled": 0, + "LateTimers": 0, + "TotalTimerLatenessNanoseconds": 0, + "AnimationFramesRequested": 51, + "AnimationFramesInvoked": 51, + "AnimationFramesCancelled": 0, + "MicrotaskCheckpoints": 249, + "WorkerWaits": 226, + "WorkerSignalledWakes": 202, + "WorkerTimeoutWakes": 23, + "SceneBuilds": 13, + "NoDamageSceneBuilds": 0, + "FullCheckpointSceneBuilds": 0, + "ArbitraryEvaluationCalls": 1, + "GeneratedInvokeCalls": 0, + "GeneratedCallbackCalls": 0, + "ArbitraryEvaluationSourceBytes": 47, + "GeneratedRequestBytes": 0, + "WebTypefaceCacheHits": 0, + "WebTypefaceCacheMisses": 0, + "CompositionAnimationFrames": 94, + "CompositionRenders": 13, + "CompositionAppliedDiffs": 13, + "CompositionInvalidations": 13, + "CompositionFullInvalidations": 0, + "CompositionSubmittedAnimationFrames": 63, + "CompositionSkippedEmptyAnimationFrames": 31, + "CompositionRenderCallbacks": 13, + "CompositionUnchangedRenderCallbacks": 0 + }, + "publications": [ + { + "Timestamp": 93911982644541, + "Revision": 6, + "ConsumedInputSequence": 639244599307960333, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93912128357958, + "Revision": 7, + "ConsumedInputSequence": 639244599307960341, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93912410191333, + "Revision": 8, + "ConsumedInputSequence": 639244599307960357, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93912458833500, + "Revision": 9, + "ConsumedInputSequence": 639244599307960360, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93912508910833, + "Revision": 10, + "ConsumedInputSequence": 639244599307960363, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93912660637333, + "Revision": 11, + "ConsumedInputSequence": 639244599307960371, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93912709080041, + "Revision": 12, + "ConsumedInputSequence": 639244599307960374, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93912767106833, + "Revision": 13, + "ConsumedInputSequence": 639244599307960377, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93912893386958, + "Revision": 14, + "ConsumedInputSequence": 639244599307960384, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93912941283875, + "Revision": 15, + "ConsumedInputSequence": 639244599307960387, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93912980204666, + "Revision": 16, + "ConsumedInputSequence": 639244599307960389, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93913030301041, + "Revision": 17, + "ConsumedInputSequence": 639244599307960392, + "ViewportWidth": 1280, + "ViewportHeight": 800 + }, + { + "Timestamp": 93913062908875, + "Revision": 18, + "ConsumedInputSequence": 639244599307960394, + "ViewportWidth": 1280, + "ViewportHeight": 800 + } + ], + "renderedScenes": [ + { + "Timestamp": 93912010588916, + "Revision": 6, + "ConsumedInputSequence": 639244599307960333, + "AcceptedTimestamp": 93912003692041 + }, + { + "Timestamp": 93912161653291, + "Revision": 7, + "ConsumedInputSequence": 639244599307960341, + "AcceptedTimestamp": 93912156771541 + }, + { + "Timestamp": 93912442930333, + "Revision": 8, + "ConsumedInputSequence": 639244599307960357, + "AcceptedTimestamp": 93912438061083 + }, + { + "Timestamp": 93912493069291, + "Revision": 9, + "ConsumedInputSequence": 639244599307960360, + "AcceptedTimestamp": 93912487946666 + }, + { + "Timestamp": 93912539874000, + "Revision": 10, + "ConsumedInputSequence": 639244599307960363, + "AcceptedTimestamp": 93912536772708 + }, + { + "Timestamp": 93912691829458, + "Revision": 11, + "ConsumedInputSequence": 639244599307960371, + "AcceptedTimestamp": 93912687094875 + }, + { + "Timestamp": 93912744700166, + "Revision": 12, + "ConsumedInputSequence": 639244599307960374, + "AcceptedTimestamp": 93912738687833 + }, + { + "Timestamp": 93912791507416, + "Revision": 13, + "ConsumedInputSequence": 639244599307960377, + "AcceptedTimestamp": 93912786955208 + }, + { + "Timestamp": 93912925232041, + "Revision": 14, + "ConsumedInputSequence": 639244599307960384, + "AcceptedTimestamp": 93912920663958 + }, + { + "Timestamp": 93912974507333, + "Revision": 15, + "ConsumedInputSequence": 639244599307960387, + "AcceptedTimestamp": 93912970084666 + }, + { + "Timestamp": 93913008366375, + "Revision": 16, + "ConsumedInputSequence": 639244599307960389, + "AcceptedTimestamp": 93913005385166 + }, + { + "Timestamp": 93913057055333, + "Revision": 17, + "ConsumedInputSequence": 639244599307960392, + "AcceptedTimestamp": 93913053993833 + }, + { + "Timestamp": 93913091022083, + "Revision": 18, + "ConsumedInputSequence": 639244599307960394, + "AcceptedTimestamp": 93913086743583 + } + ], + "physicalPresentationVerified": false + } +} diff --git a/docs/graphics/evidence/kestrel/write-buffer-startup.json b/docs/graphics/evidence/kestrel/write-buffer-startup.json new file mode 100644 index 000000000..39c42c9d2 --- /dev/null +++ b/docs/graphics/evidence/kestrel/write-buffer-startup.json @@ -0,0 +1,13 @@ +{ + "date": "2026-09-08", + "originalDocumentSha256": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "status": { + "ready": "true", + "backend": "WebGPU \u00b7 GPU pipeline", + "history": "Kestrel CADPrecision starts here. Open a drawing or choose a tool.RendererWebGPU active \u00b7 4\u00d7 MSAA \u00b7 instanced lines \u00b7 depth-tested meshesReadyL line \u00b7 C circle \u00b7 REC rectangle \u00b7 M move \u00b7 Ctrl+K commands.Renderpass.setBindGroup is not a function", + "errors": 1, + "gpu": true + }, + "exitCode": 1, + "acceptance": "failed: initial render requires setBindGroup" +} diff --git a/docs/graphics/evidence/v8-write-buffer-detachment.json b/docs/graphics/evidence/v8-write-buffer-detachment.json new file mode 100644 index 000000000..35182b117 --- /dev/null +++ b/docs/graphics/evidence/v8-write-buffer-detachment.json @@ -0,0 +1,22 @@ +{ + "scope": "V8 GPUQueue.writeBuffer call-time bytes verified by Dawn buffer mapping on macOS", + "sourceBaseCommit": "75bf60d693b0dbed3ab5541492881d11da9dcc2f", + "testCommand": "ctest --test-dir artifacts/graphics-build/native-v8-enabled -R '^webscene_graphics_v8_runtime_tests$' --output-on-failure", + "buildCommand": "cmake --build artifacts/graphics-build/native-v8-enabled --target webscene_graphics_v8_runtime_tests -j4", + "result": { + "passed": 1, + "failed": 0, + "seconds": 0.75 + }, + "checks": [ + "Typed-array buffer transferred immediately after writeBuffer; transferred bytes overwritten; GPU word remains 0x12345678", + "Direct ArrayBuffer transferred immediately after writeBuffer; transferred bytes overwritten; GPU word remains 0x23456789", + "SharedArrayBuffer overwritten after writes; GPU words retain original values", + "Source byteLength confirms detachment before awaiting GPU mapping" + ], + "limits": [ + "Diagnostic buffer mapping is test readback, not presentation transport", + "SharedArrayBuffer test uses same-thread mutation; no cross-worker concurrency claim", + "No full CTS or cross-platform qualification" + ] +} diff --git a/docs/graphics/evidence/webgpu-document/macos-clear.png b/docs/graphics/evidence/webgpu-document/macos-clear.png new file mode 100644 index 000000000..bee5763e5 Binary files /dev/null and b/docs/graphics/evidence/webgpu-document/macos-clear.png differ diff --git a/docs/graphics/evidence/webgpu-document/macos-intrinsic-canvas.png b/docs/graphics/evidence/webgpu-document/macos-intrinsic-canvas.png new file mode 100644 index 000000000..bee5763e5 Binary files /dev/null and b/docs/graphics/evidence/webgpu-document/macos-intrinsic-canvas.png differ diff --git a/docs/graphics/evidence/webgpu-document/macos-resize-final.png b/docs/graphics/evidence/webgpu-document/macos-resize-final.png new file mode 100644 index 000000000..6e9d9ca7e Binary files /dev/null and b/docs/graphics/evidence/webgpu-document/macos-resize-final.png differ diff --git a/docs/graphics/evidence/webgpu-document/macos-triangle.png b/docs/graphics/evidence/webgpu-document/macos-triangle.png new file mode 100644 index 000000000..7de8d1eb8 Binary files /dev/null and b/docs/graphics/evidence/webgpu-document/macos-triangle.png differ diff --git a/docs/graphics/gpu-canvas-lifetime.md b/docs/graphics/gpu-canvas-lifetime.md new file mode 100644 index 000000000..f5c8e0040 --- /dev/null +++ b/docs/graphics/gpu-canvas-lifetime.md @@ -0,0 +1,648 @@ +# GPU canvas lifetime implementation (G03 / issue #25) + +Status: in progress. A native image lease ABI is implemented, but GPU canvas +publication and production GPU presentation are not available yet. G01/G02 qualification and integration gaps remain open. + +## Backing state + +Every allocated canvas node now owns a stable backing identity. Context modes +are exclusive: none may become 2D, WebGL 1, WebGL 2 or WebGPU; requesting the same +mode is allowed, while switching modes is rejected. Existing 2D context creation +claims this mode. Unsupported browser context factories still return null without +claiming a mode. GPU factories are future binding work. + +Backing state distinguishes bitmap dimensions, allocation generation and content +serial. Publishing content advances the content serial alone. A same-size bitmap +reset advances content but permits storage reuse; changed dimensions advance the +allocation generation as well. Mode and identity survive either reset. The +metadata owns no image allocation, pixels, native texture pointer or Skia object. + +The native state test checks exclusive modes, distinct identities, 1,000 content +updates without an allocation-generation change, same-size reset, changed-size +reset and zero-sized bitmaps. This demonstrates metadata behavior, not actual +GPU allocation/copy counters. Canvas command appends and backing-store resets now publish content changes. +Initial 2D context creation synchronizes dimensions, and width/height property +resets update bitmap dimensions. Existing canvas command generations remain +unchanged while the lease API is implemented. General attribute mutation and +standards-level dimension normalization still need coverage. + +## Remaining G03 integration + +- Complete general attribute mutation and dimension-normalization coverage; + connect backing versions to the new scene publication path. +- Extend the new v3 acquisition/capability envelope with GPU image lease + records while preserving existing scene-view consumers. +- Carry opaque allocation identity, generation/content serial, format, alpha, + color space, orientation and producer readiness in portable scene metadata. +- Implement explicit retained resource leases and consumer GPU completion. + Scene acknowledgement must not release or recycle leased image storage. +- Bound active canvas presentation storage to three reusable color images, with + backpressure for busy slots and retained old-generation leases on resize. +- Integrate GPU images into retained paint order, transforms, clips, opacity, + isolation and damage; cover old consumers and stale-scene recovery. + +Advancing a frame serial must never itself allocate an image or copy pixels. +GPU completion, scene retention and image reuse are separate lifetime conditions. + +## Runtime backing-reset verification + +The real V8 fixture draws into a 2D canvas and verifies content advances without +an allocation-generation change. CSS width changes preserve both bitmap content +and allocation generation. Resetting the width property to the same bitmap width +advances content while retaining identity/generation. Changing bitmap width to +640 advances allocation generation and updates dimensions while preserving 2D +context ownership. Dimension conversion for backing metadata checks finiteness +and bounds before integer conversion; it does not claim a redesign of existing +HTML attribute parsing/getter semantics. + +The full 14-test local suite passed after the runtime wiring; the focused V8 +fixture is rerun with the separate CSS-size assertion. These remain metadata and +2D recording checks, not proof of GPU pool allocation or zero-copy presentation. + +## Bounded image lease state machine + +`image_lease_pool` now implements the lifetime metadata for exactly three image +slots and a fixed-capacity ticket table (128 tickets by default). Each retained +reference and consumer GPU use receives a separate generation-bearing ticket; +duplicate release/completion cannot decrement another consumer's ownership. + +```mermaid +stateDiagram-v2 + [*] --> Idle + Idle --> Writing: acquire writer + Writing --> Submitted: begin producer before backend use + Submitted --> Published: publish retained reference + Submitted --> Idle: cancel after producer completion + Writing --> Idle: cancel before backend use + Published --> Published: retain / begin consumer / release / completion + Published --> Idle: producer done AND no retained references AND no consumers +``` + +Producer completion can arrive before CPU publication. Consumer registration can +precede producer completion, but the backend must enqueue the appropriate GPU +wait before sampling. No CPU wait is imposed by the metadata pool. Releasing a +scene/reference does not finish consumer work. Close stops new writers while +allowing existing retained images to be redrawn and their tickets completed. +Publish/retain/consumer admission returns backpressure when ticket storage is +full; image storage cannot grow beyond three slots. + +The native test checks three-slot saturation, a retained reference independent +of the scene reference, two separate consumer tickets, cross-thread completion, +stale writer generations, duplicate/wrong-kind completions, close with retained +redraw, producer completion arriving last or before publication, and ticket-table +saturation/retry. The focused CTest and Clang ThreadSanitizer run both pass. + +This class owns no GPU allocation and performs no pixel copy. Backend image +objects, versioned scene acquisition, opaque lease ABI, resize metadata and +presenter fence integration are still to be connected. Integration must keep the +pool and its native image owner alive until every outstanding ticket has retired; +the metadata class alone does not implement engine-detachment lifetime transfer. + +## Immutable image metadata bound to leases + +Each writer now supplies portable image metadata before publication: canvas and +allocation identities, allocation generation, content serial, dimensions, format, +alpha mode, color space, orientation and producer timeline/value. Invalid or +missing metadata is rejected. Publication freezes it for that image use; retained +and consumer lease tickets resolve the same metadata until released. A consumer +can still resolve its image after all scene references have been released. + +The pool test keeps old-size and resized frames live concurrently, verifies each +lease retains its own dimensions/generation, and rejects mutation after publication, +foreign/stale lease lookup, zero dimensions and unknown formats. It passes under +ThreadSanitizer. These descriptors contain values only; native texture/Skia +pointers must remain in the backend provider's allocation registry. No native +image allocation or pixel copy is performed by descriptor publication/lookup. + +This is the internal descriptor/lease association. C ABI versioning, native image +provider lookup, scene acquisition and actual resize allocation/fence integration +remain outstanding; no physical resize or zero-copy presentation pass is claimed. + +## Active allocation aliasing and abandoned producers + +Metadata binding now rejects an allocation identity already assigned to another +busy frame slot, even if its allocation-generation value differs. Re-presenting +unchanged pixels must retain the existing lease; it must not acquire a second +writer for the same physical image. The backend provider must also enforce its +allocation ownership across different pools. + +A writer must explicitly begin producer work before backend submission and +publication. This freezes its metadata. Cancellation while producer work remains +pending is rejected; an abandoned frame can be cancelled once producer completion +arrives. Closing the pool does not silently recycle such a frame. Unsubmitted +writers remain cancellable without a GPU fence. Duplicate producer starts and +completion before a start are rejected. + +The native fixture covers a busy-allocation alias across resized generations, +failed metadata mutation preserving the original descriptor, cancellation of an +in-flight producer, completion after close and final slot reclamation. Focused +CTest and ThreadSanitizer runs pass. These are lifetime protocol checks; physical +backend submission, memory accounting and scene ABI integration remain pending. + +## Separately versioned scene acquisition + +The C ABI now provides acquire_latest_scene_v3/acquire_next_scene_v3, explicit +acquisition statuses, version/size validation and consumer capability negotiation. +The v3 view currently wraps a borrowed CPU view with the unchanged v2 layout. +Callers release/acknowledge through the v3 functions; the borrowed CPU view must +not be released separately. Required scene capabilities are checked against the +consumer mask. Legacy acquisition refuses scenes requiring capabilities it cannot +represent. Current producers require zero capabilities; GPU image export is not +yet advertised or implemented by this envelope. + +The native runtime fixture checks unsupported versions, short options, null +engines, ordered acquisition, legacy acquisition, v3 acknowledgement and retaining +the CPU view after engine destruction. All 15 local tests passed in 13.37 seconds. +The built macOS library exports all four new functions. A separate plain-C fixture +checks options/status sizes and view-field offsets without linking the engine. +GPU capability rejection with real GPU scenes, GPU lease operations, provider +lookup, managed consumers and Windows/Linux ABI/package verification remain open. + +The plain-C fixture exposed a missing typedef for the existing interop callback +view; adding the forward typedef restores C compilation without changing layout. +The independent C layout test now passes. + +## Scene view validation + +Versioned acknowledgement and release reject short or unknown-version views +before reading their lease tokens. The runtime fixture supplies incompatible +copies of a live view, verifies rejection leaves the original lease usable, and +checks successful latest-versioned acquisition as well as ordered acquisition. +The focused runtime and plain-C ABI layout tests both pass (0.65 seconds): + +```sh +ctest --test-dir artifacts/graphics-build/native-v8-enabled -R 'webscene_graphics_(v8_runtime|scene_abi_layout)_tests' --output-on-failure +``` + +These checks validate the ABI envelope; callers must still provide a valid, live +view returned by the library. They do not make arbitrary or already-freed pointers +safe to pass. GPU lease export and backend image ownership remain outstanding. + +## Owner lifetime across engine disposal + +`owned_image_pool` adds move-only producer, retained-image and consumer handles +that share ownership of the pool and a native provider lifetime anchor. Destroying +the canvas/engine-facing owner closes writer admission; existing frames can still +be retained and redrawn. Retained handles release their CPU tickets automatically. +Producer and consumer handles keep the provider alive until explicit completion; +destroying an unfinished submitted handle terminates rather than claiming that GPU +work completed. Unsubmitted producer destruction cancels the writer reservation. +Completion callbacks must own these handles until the backend signals completion. + +The provider's final destructor may run on a completion thread, so a provider with +thread-affine objects must dispatch their destruction to its native owner thread. +This is an explicit integration contract, not an implemented Dawn/ANGLE provider. +The portable descriptors still contain no native pointers. + +The native fixture disposes the owner with a producer, retained redraw and GPU +consumer outstanding, redraws after disposal, releases the final consumer on +another thread, and verifies the provider is destroyed exactly once, only after +completion. It also checks unsubmitted cancellation and ticket backpressure. +Focused CTest and Clang ThreadSanitizer runs pass. Concrete GPU allocations, +provider resolution, scene attachment and the exported lease ABI remain pending. + +## Exported native image lease operations + +The v3 C API now exposes scene image count/indexed retain, independent image +retain/release, fixed-layout metadata lookup, and begin/complete consumer. +Retaining an image allocates a CPU handle and bounded ticket; it neither allocates +nor copies pixel storage. Ticket exhaustion returns explicit backpressure. The +consumer wrapper is allocated before registering use so allocation failure cannot +abandon a live GPU ticket. Completion consumes the consumer handle. Calls on the +same handle must be serialized, and freed pointers cannot be reused. + +Scene storage now retains shared image references; capability computation includes +the GPU bit whenever that collection is nonempty. The old view layout remains +unchanged, and v3 callers access images separately by index. CPU scene memory +accounting includes the image-reference vector, not provider GPU allocations. + +The runtime fixture exercises the exported retain/describe/consumer operations +with the native lifetime fixture, including backpressure, version rejection, owner +disposal and final completion on another thread. All three focused runtime, pool +and C ABI layout tests pass (0.86 seconds). The C fixture checks the 80-byte image +metadata layout, and `nm -gU` confirms all seven new macOS exports. + +No canvas producer populates the scene image collection yet. End-to-end GPU scene +capability rejection, paint placement, provider lookup, real allocation/fence +integration, managed consumers and Windows/Linux ABI verification remain open. +The fixture provider owns no texture; these results prove lease ownership through +the exported operations, not native GPU presentation or its copy budget. + +## Canvas-to-scene image capture + +Canvas node state can now accept a native image reference only when its canvas +identity, allocation generation, content serial and bitmap dimensions match the +current backing. Scene construction captures connected, visible GPU canvases, +retains their references and incorporates their image versions in change detection. +GPU changes currently use conservative full-viewport damage. References are shared +without copying pixels or allocating another image ticket for each scene. + +Bitmap property reset clears the canvas's current image reference. Previously +captured frames remain immutable and retained. The runtime fixture verifies +capture, remove/reinsert, resize with an older capture alive, stale-generation +rejection, and final pool reclamation. The focused V8 test passes in 0.62 seconds. +Native image producers must update the backing version and request scene +publication on the engine thread; browser GPU bindings are still outstanding. + +This connects native canvas state to the scene image collection. It does not yet +provide paint operations identifying where each GPU image is sampled, clipping, +transforms or isolation. Actual native GPU texture production and end-to-end +ordered-scene GPU fixtures remain incomplete, so G03 remains open. + +## GPU paint operation in the native traversal + +Scene command kind 256 identifies GPU image sampling at the canvas layout bounds. +The node ID remains the canvas node ID; the otherwise-unused color field carries +the scene-local GPU image index. Publication resolves that index before hashing +and exporting commands. The operation is emitted at canvas content traversal, +inside the existing transform/clip/opacity command scopes, and invalidated image +versions produce no sampling operation. Legacy consumers are rejected through +the GPU scene capability guard. + +The V8 fixture checks placement and command ordering between sibling backgrounds, +including the existing foreground-background command variant, and confirms bitmap +reset removes the stale operation. It passes in 0.55 seconds. Producer completion +is recorded before these CPU paint assertions so test failure cannot abandon an +in-flight producer. + +These are command-stream checks. The existing managed renderer separates some +foreground/background passes; a GPU-aware presenter must implement the unified +ordered sampling path. No rendered clip/transform/opacity or Skia sampling result +is claimed, and actual native textures remain to be connected. + +## Dawn texture storage + +`dawn_canvas_images` now allocates actual Dawn textures behind the three-slot +lease pool. Only an acquired idle slot may replace its texture. Matching size and +format reuse that slot's allocation identity; content serial changes alone do +not create textures. Replacement drops the slot's previous reference before +creating the next texture. Supported color formats match the portable metadata. +Device dimension limits and a per-pool color-byte budget are checked before +allocation. The budget counts width × height × bytes per pixel; driver padding, +backend heaps and other GPU resources are not included in this logical counter. + +The hardware Dawn fixture clears three textures through a real render pass and +queue submission, records producer completion from OnSubmittedWorkDone, and +verifies retained frames still block reuse afterward. One hundred subsequent +metadata-only acquisitions/cancellations keep the creation count at three. Resize +creates one replacement; an over-budget request creates none. The full focused +Dawn event test passes on the local Metal hardware adapter in 0.50 seconds. + +This allocator is native engine-thread code, not yet called by browser canvas +bindings. Its frame's native texture is for trusted producer use; native callers +must not retain unaccounted texture references beyond that use. Provider lifetime +retains Dawn object references but does not prevent an external Device.Destroy. +Device-loss handling, provider resolution for presenters, Skia sharing, physical +memory telemetry and rendered pixel/copy verification remain outstanding. + +## Native presenter resolution + +A live consumer now exposes its native provider lifetime anchor internally. +`dawn_canvas_images::resolve` validates the provider type, exact Dawn device, +allocation identity, generation and content serial before returning the retained +texture. A storage mutex protects resolution against allocation changes in other +slots. No native pointer is added to portable scene records or the JS surface. +Callers must keep the consumer until its GPU fence and may only read the resolved +texture; resolution itself does not wait for the producer timeline. + +The Metal fixture resolves the identical native texture and creates a view on a +presenter thread after disposing the canvas owner and all CPU scene references. +A different live Dawn device is rejected. Completion invalidates resolution and +releases the final provider anchor. The focused hardware test passes in 0.45 +seconds. This proves native object lookup/lifetime, not a consumer draw, shared +Skia image import, or cross-device synchronization. + +After native presenter resolution, a complete graphics-enabled rebuild and CTest +run passed all 16 tests in 13.86 seconds. The earlier intermittent DOM activation +failure remains an unresolved historical observation; this pass does not diagnose it. + +## Producer-to-consumer pixel evidence on Dawn + +The hardware fixture now clears a 64×64 RGBA8 texture, publishes its retained +image, resolves it through a consumer lease, and submits a diagnostic texture-to- +buffer copy to the same Dawn queue before processing completion events. Canvas +owner and scene references are released while the submissions are outstanding. +Queue completion retires both producer and consumer; MapAsync then allows exact +verification of all 4,096 pixels as RGBA (64, 128, 191, 255). The focused Dawn +hardware test passes in 0.48 seconds on Metal. + +There is no CPU completion wait between producer and consumer submission. This +uses the ordering of the same native queue, not cross-device or cross-backend +synchronization. The readback is deliberately diagnostic and is not a production +presentation path or evidence of zero-copy Skia composition. Browser bindings, +Skia import/sampling and synchronization across backend APIs remain unfinished. + +## Scene capability and ordered acknowledgement fixture + +Acquisition now shares a small core that checks scene capabilities and creates +the versioned lease. A white-box fixture, compiled only into the graphics runtime +test executable, supplies retained GPU scenes to this production core and uses +the exported scene/image operations. No fixture entry point is added to the +production library or JS surface. + +The fixture rejects a consumer with no GPU capability without changing pending +scenes, accepts a capable consumer, rejects an invalid image index, and retains +an image plus a consumer independently. A later image-removal scene cannot be +acknowledged before its base. After both acknowledgements and scene/owner disposal, +the independent reference remains readable; releasing it still keeps the provider +alive until consumer completion. Focused runtime CTest passes in 0.61 seconds. + +This tests production scene acquisition/acknowledgement code with native fixture +images. Browser-driven worker publication and rendered Skia integration remain +outstanding; it is not an end-to-end WebGPU browser test. + +## Attribute-driven backing resets + +Canvas width/height changes through setAttribute/removeAttribute, null-namespace +setAttributeNS/removeAttributeNS, attached Attr.value, setAttributeNode, +removeAttributeNode and toggleAttribute now invoke the same backing reset as +property assignment. Same-value sets reset content; removing an absent attribute +or forcing toggleAttribute to its existing state does not. Detached Attr mutation +does not touch its former canvas. Nonempty namespaces do not invoke this bitmap +reset hook; the existing attribute namespace representation is not redesigned. + +The runtime fixture publishes a retained image before each effective mutation, +checks bitmap dimensions and generation/content increments, and verifies that the +canvas drops its current image while an independent reference keeps the old +metadata. Dimension parsing and complete 2D drawing-state reset conformance still +need separate verification; these changes address backing lifetime invalidation. + +The complete graphics-enabled rebuild and CTest run after attribute reset wiring +passed all 16 tests in 13.07 seconds. SDK CI was checked once between work and +remained pending; no hosted or cross-platform qualification pass is inferred. + +## Capacity wakeups + +Image pools accept the shared engine wake sink. Releasing a retained/consumer +ticket, cancelling a writer, or completing the last producer that makes an image +idle signals after unlocking the pool. A producer completion that leaves all +capacity occupied does not signal. The owned pool and Dawn allocator carry this +sink through their constructors so future binding admission can retry on engine +wake instead of polling. The sink contains no engine pointer and remains safe +when retained by a detached image owner. + +The fixture exhausts ticket capacity, releases on another thread, and verifies +that a late-starting wait observes the latched wake. Its sink reenters the pool's +read-only occupancy method, verifying notification is outside the mutex. Producer- +last completion and abandoned writer cancellation are covered. Pool and Dawn +hardware CTests pass (0.72 seconds), and the pool fixture passes ThreadSanitizer. +Browser binding admission/retry still needs to supply this sink and preserve its +pending operation; this change does not create that browser binding. + +## Native producer scene notification + +Native producers now publish through `native_document::publish_gpu_canvas_image`. +It validates that the target is a canvas owned by this document, validates the +image against its backing version, and advances scene generation when replacing +the current reference. It does not invalidate style/layout. Publishing the same +reference again performs validation without scheduling redundant scene work. +Like other native_document mutations, this entry point is engine-thread-only. + +Runtime fixtures now use this path and verify scene generation advances while +layout stays clean, duplicate publication is quiet, and a foreign document rejects +the canvas. Focused runtime CTest passes in 0.63 seconds. Browser GPU factories +and their actual queue-to-publication calls still need implementation. + +## Shared managed ABI declarations + +Avalonia now declares the v3 acquisition, image metadata and lease functions in +`NativeGpuSceneInterop.cs`; Uno links the same source under its native namespace. +Managed layout uses fixed-width fields and pointer-sized borrowed views. The +existing renderer is not switched to GPU capabilities. Consumer completion is +explicitly separate from CPU disposal/finalization in this low-level API; higher- +level presenter ownership wrappers remain to be implemented. + +Two focused tests pass without skips on both .NET 8 and .NET 10 using the rebuilt +local native library. They check sizes/offsets and call native acquisition to +verify null-engine rejection, unsupported version, ordered acquisition, image +index rejection on a CPU scene, acknowledgement and scene retention after engine +disposal. These are CPU scene ABI interoperability checks, not managed GPU +presentation or non-null GPU image marshaling tests. + +Uno compiled successfully with zero warnings/errors after restoring its missing +NuGet assets. This verifies the shared declarations compile in both backends. + +## Managed scene ownership + +`NativeSceneLeaseV3` now owns the native scene through SafeHandle. Managed +ownership is allocated before native acquisition; unsuccessful acquisition +disposes that candidate. Acknowledge and image-count P/Invokes hold the SafeHandle +for the native call. WithView holds a reference across its callback, keeping the +borrowed CPU view alive even if another thread disposes the scene concurrently. +Disposal/finalization releases CPU scene retention only, never GPU completion. + +The concurrent-disposal native test destroys the engine, disposes the managed +scene on another thread while WithView is active, and then reads the borrowed +CPU ABI version. Duplicate disposal is harmless and later WithView throws. All +three focused managed tests pass without skips on .NET 8 and .NET 10; Uno builds +with zero warnings/errors. Actual renderer adoption and managed GPU image/consumer +ownership are still outstanding. + +## Resize budget recovery + +Dawn allocation now evicts idle cached textures when they would otherwise block +a resize within the configured byte budget. Each candidate is reserved through +the lease pool before eviction; retained, submitted or consumed slots cannot be +selected. Temporary reservations roll back quietly so an unsuccessful allocation +does not wake itself into a retry loop. Real external capacity release retains +its normal wake behavior. + +The hardware fixture fills all three slots, retains one image and requests a +larger frame. The request remains blocked while the retained image makes it +exceed budget, without generating a wake. After releasing that image, the larger +frame succeeds by reclaiming idle cache storage, with one new texture and resident +logical color bytes equal to the requested frame. Pool and Dawn CTests pass in +0.71 seconds. Driver heap residency remains outside these logical byte counters. + +## Dawn submission ownership + +`dawn_canvas_images::submit` accepts a frame from that allocator and a recorded +command buffer. It allocates callback ownership, starts the producer and reserves +the published lease before submitting. Ticket backpressure cancels the unsubmitted +frame quietly. Dawn's spontaneous queue-completion callback owns the frame until +completion, retires the producer, records success/failure atomically and optionally +signals the engine wake sink. Command validation still belongs to device error +scopes; queue completion is not a substitute for those errors. + +The exact-pixel producer/consumer test now uses this helper. Additional hardware +checks reject a foreign allocator without consuming its frame, exhaust publication +tickets without leaving a producer busy or generating a retry wake, and reclaim +the successful frame after completion. Focused Dawn CTest passes in 0.47 seconds. +Browser promise/error delivery, lost-device publication policy and cross-backend +presenter synchronization remain to be integrated. + +## GPU paint scopes and default canvas dimensions + +The runtime fixture now applies rounded overflow clipping, scale, rotation and +group opacity to a retained GPU canvas. It checks each scope is active at the GPU +sampling operation, checks the clip radius/transform/opacity values, verifies +balanced scope exits and confirms CSS effects preserve bitmap content serial. + +This exposed a real intrinsic-layout gap: a canvas with no height attribute had +a zero-height layout despite its default 150-pixel bitmap height. Canvas intrinsic +size now supplies the 300/150 defaults when the respective attribute is absent. +The scope fixture passes after that fix. This is not a full rewrite of replaced- +element aspect-ratio sizing or invalid dimension parsing. Rendered Skia clipping, +blending and transform comparisons remain outstanding. + +The complete rebuilt native CTest suite passes all 16 tests in 13.21 seconds +after the intrinsic-size fix and GPU paint-scope assertions. + +## GPU checkpoint recovery + +Checkpoint reset of acknowledgement state is now shared by the engine request +path and its white-box fixture. The fixture acquires a GPU scene with a stale +base, verifies rejection leaves the current base intact, resets pending state, +and acknowledges a new checkpoint retaining the image. The old acquired scene +remains readable across reset, but its late acknowledgement cannot replace the +recovered base. Independent image/consumer leases still determine final provider +release. The focused runtime test passes in 0.60 seconds. + +This exercises native scene/lease recovery; it does not establish rendered +framework recovery or browser-driven GPU checkpoint publication. + +The native engine regression suite also passes against the rebuilt library +(11.86 seconds) after sharing the checkpoint-reset implementation. + +## Retained old/new resize pixel verification + +The Dawn pixel fixture now publishes a 32×32 red frame with allocation generation +2 while a consumer lease still retains the previous 64×64 blue-toned frame. It +checks distinct allocation identities and the old immutable dimensions, then +copies both images diagnostically into separate buffer regions before disposing +the owner. Both consumers retire after the queue fence. Exact verification covers +all 4,096 old pixels and 1,024 new pixels, including the new image's padded row +layout. The focused Metal hardware test passes in 0.48 seconds. + +This proves old/new image content remains distinct while the old consumer lease +is outstanding. It does not guarantee physical overlap of GPU execution, and the +diagnostic copies remain test-only rather than a presentation implementation. + +## Managed retained-image ownership + +`NativeGpuImageLeaseV3` owns CPU image references with SafeHandle. Scene acquisition +and image retention allocate their managed owner before entering native code, +clean up on failure, and use SafeHandle P/Invoke parameters to protect source +handles during native access. Describe uses the versioned metadata structure. +Finalization releases retention only; GPU completion remains explicit. + +The three managed interop tests pass on .NET 8 and .NET 10, including new invalid +image-index and disposed-scene checks. Uno builds without warnings/errors. +Positive managed GPU-image acquisition/retention is not exercised yet because +the managed fixture currently produces CPU scenes; native GPU image lifetime +fixtures do not substitute for that missing integration coverage. + +### macOS native consumer lookup + +The additive webscene_gpu_image_get_iosurface_v3 export accepts an active +consumer and a size/versioned webscene_gpu_iosurface_view_v3. It returns a +borrowed IOSurface pointer and its padded allocation size only for the native +IOSurface lease provider. Portable image metadata remains pointer-free. +The caller must retain the consumer through native GPU completion; lookup +does not wait for the producer or begin/end a graphics access interval. + +Graphics-disabled and non-macOS implementations return unavailable (zero). +Valid output views are cleared before failure. Invalid size/version descriptors +are rejected without writing beyond their declared layout. The opaque consumer +must itself be a live API handle, as with the existing consumer operations. + +The macOS graphics runtime test verifies lookup after canvas and retained-image +release, invalid views and foreign provider rejection. Provider identification +uses a native virtual kind tag because the runtime disables RTTI. Both enabled +and disabled native libraries built; the new symbol was inspected in the enabled +dylib, and a ctypes load of the disabled dylib verified rejection/cleared output. +The common image-lease CTest and graphics V8 runtime CTest passed. + +This is the native presenter lookup boundary. Producer completion resolution, +framework import and actual V8 GPU canvas publication remain unfinished. + +### Managed IOSurface consumer binding + +The shared Avalonia/Uno interop source now declares the additive IOSurface view +and lookup export. NativeGpuImageConsumerV3 provides explicit acquisition and +GPU completion ownership. It intentionally has no Dispose/finalizer: collection +of a managed wrapper cannot prove GPU completion. Presenters must retain it +until their completion path calls Complete. + +Synchronous WithIOSurface imports protect the native consumer from concurrent +completion. Completion requests prevent new borrows and defer native deletion +until existing callbacks finish, without holding a monitor during the callback. +Missing lookup exports on older v3 runtimes report unavailable. Importers still +must retain their own native objects as required and preserve the consumer +through actual GPU completion; a successful lookup does not synchronize work. + +All four NativeGpuSceneInteropTests passed against the enabled native library +on net8.0 and net10.0, with no skips. This covers layouts, null lookup clearing and +existing scene ABI ownership tests. The managed GPU consumer's positive import +and concurrent completion paths still need an actual GPU scene fixture; they +are not claimed hardware-qualified by these tests. + +### Positive managed IOSurface lifetime fixture + +A separate EXCLUDE_FROM_ALL native test library now creates an actual IOSurface +lease and reports whether its native provider remains alive. It has no install +rule and adds no test export to the production runtime. Build it explicitly: + +```sh +cmake --build artifacts/graphics-build/native-v8-enabled --target webscene_graphics_iosurface_fixture +``` + +Set WEBSCENE_TEST_NATIVE_LIBRARY to the enabled native runtime and +WEBSCENE_TEST_GPU_FIXTURE_LIBRARY to libwebscene_graphics_iosurface_fixture.dylib, +then run NativeGpuSceneInteropTests with dotnet test for net8.0 and net10.0. + +Both targets passed all five tests without skips. The positive fixture verifies +managed SafeHandle acquisition, native IOSurface lookup after retained-image +release, completion on a different thread during the import callback, allocation +survival until callback return, eventual release, duplicate completion rejection, +and importer-exception unwinding followed by a successful retry. + +The fixture submits no GPU commands. It proves the managed/native ownership +protocol and race behavior, not Metal/CGL fence completion or rendered pixels. + +### macOS asynchronous Dawn producer handoff + +`dawn_iosurface_submission` joins the versioned IOSurface pool to Dawn shared +texture access. It records work synchronously, submits without waiting, ends +shared access, and keeps the producer, imported texture, device and optional +callback-state anchor alive through `OnSubmittedWorkDone`. An optional wake only +signals the engine task queue. A private retained image cannot be transferred +through `take_ready()` until successful queue completion, validation and EndAccess; transfer +is permitted once. Queue failure discards publication. Empty/throwing recorders +end access and release the unsubmitted slot. + +The macOS fixture now uses this component for its Dawn clear instead of publishing +an unrelated logical producer after a standalone GPU wait. Its explicit timed +wait is diagnostic only. The .NET 8/10 interop tests exercise actual producer +submission, one-time image transfer, failed recording cleanup, native leases, +CGL import, GPU copy and consumer fence retirement. A scoped validation result +and queue completion jointly gate publication. The completion wake is signaled +once, after both callbacks have arrived, outside the submission mutex. Invalid +recording (a buffer with no usage flags) is explicitly tested: no scene image is +published, the pool slot is released, and the failure wake occurs once. The valid +pixel path also verifies one wake. Empty/throwing recorders balance their scope. +Queue success alone cannot prove valid rendering. The device owner still handles +out-of-memory/internal errors and device loss; this scope covers validation. + +This component is not yet connected to the JavaScript canvas or retained renderer. +It currently imports on each submission; import caching by allocation/device and +long-run resource/performance qualification remain required. Device-loss recovery, +ANGLE production, delayed-completion stress, and presenter scheduling remain open +under #27 and the presenter issues. No platform issue is closed by this fixture. + +### Ordered paint capability propagation + +The native builder now derives required capabilities from the full DOM paint +stream during its existing hash pass: command 256 requires GPU_IMAGES; command +257 requires ORDERED_CANVAS. It does so before removing unchanged DOM commands +from an incremental payload. Therefore a layer-only diff still carries its +retained paint stream's consumer requirements. Acquisition checks the stored +mask without rescanning commands. + +The native graphics V8 runtime fixture verifies ordered-only scenes reject +CPU-only and GPU-only consumers, and mixed masks require both bits. Empty +incremental command payloads retain their required mask. Validation command: +`ctest --test-dir artifacts/graphics-build/native-v8-enabled -R '^webscene_graphics_v8_runtime_tests$' --output-on-failure` +passes on the macOS arm64 build. The native builder's current DOM producer still +does not emit GPU/ordered-canvas commands; this change secures the publication +boundary for that upcoming integration, without advertising a working browser API. diff --git a/docs/graphics/pr43-milestone-scope.md b/docs/graphics/pr43-milestone-scope.md new file mode 100644 index 000000000..7941b9cb0 --- /dev/null +++ b/docs/graphics/pr43-milestone-scope.md @@ -0,0 +1,42 @@ +# PR #43: macOS and Windows WebGPU milestone + +The agreed scope is the existing Avalonia native WebGPU rendering path on macOS (Metal) and Windows (Dawn D3D12 with ANGLE/D3D11 interop), running the unchanged Kestrel CAD fixture. This is a bounded milestone within epic #22. Subsequent application work adds AOT-safe resource/DOM compatibility and the macOS-first HTML media/Web Audio milestones (#54/#55). Windows/Linux video hardware parity remains #61/#62; the complete Frameforge editor remains #53. + +## Merge acceptance + +- Required ordinary CI, existing runtime packaging and package-consumer checks pass on the final revision. Linux baseline checks remain enabled to prevent regressions in existing support; they do not claim Linux GPU parity. +- macOS and Windows native builds, applicable native/managed regressions and unchanged Kestrel startup, editing/undo/redo, panning, sidebar/window resizing and BOX command have evidence for the integrated production code. +- Normal GPU presentation retains bounded image ownership and GPU synchronization, without a per-frame CPU pixel transfer. Retained-image resizing is compared against Chrome; a browser difference must be understood before claiming parity. +- NativeAOT is a product requirement. The Kestrel native executable must render successfully; generated checkpoint/archive serialization is covered by native executable CI probes. Broader unqualified interop APIs remain an explicit audit item, not a blanket AOT support claim. +- Unexpected runtime errors observed during validation are diagnosed and addressed or explicitly scoped with supporting evidence. +- Skipped platform tests and callback timing are not presented as complete conformance or physical scanout measurements. + +## Deferred epic work + +Browser WebGL 1/2 APIs and WebGPU-to-WebGL fallback, Linux GPU parity (#46), Uno GPU qualification, broader WebGPU APIs, workers/OffscreenCanvas, full CTS/WPT conformance, exhaustive loss/recovery and long-run qualification remain later epic work. They are not merge requirements for this milestone. Collapsed select popup support remains #44. These deferrals do not close epic #22 or mark its incomplete children complete. + +## Binary packaging + +This milestone ships multiple native libraries. V8, miniaudio and the existing static native dependencies are linked into WebScene's native engine. Dawn's shared monolith remains separate. macOS uses Dawn/Metal without ANGLE; Windows also ships ANGLE's EGL/GLES libraries. ANGLE is needed for the current Windows composition path even though browser WebGL fallback is deferred. Host Skia/native assets and runtime data also follow their existing packaging contracts. + +A single distributable installer or archive is different from a single native binary. Static integration of all GPU libraries is outside this PR: it needs dependency symbol isolation (including the V8/Dawn Abseil collision), platform linking and package-consumer verification. OS graphics frameworks and drivers remain external. + +## Final application smoke checks + +Use the exact native runtime revision intended for packaging, including matching +Dawn, ICU and snapshot sidecars. Keep the original application assets unchanged. + +- Kestrel: Native AOT with reflection JSON disabled; startup, edit/undo/redo, + pan, sidebar and continuous/stepped window resize. Probe traces must use generated + serialization so diagnostics cannot fail only in published builds. +- Frameforge: package with `experiments/WebScene.Frameforge/package-macos.py`, then + run `verify-media.py` with the executable, native library and assets inside that + bundle. `--media-demo` is the interactive player; it is separate from the editor. +- TradingView: live chart readiness through `--startup-profile`; interactive + panning/performance remains distinct from this startup check. +- Final-revision CI must include all native packages and package consumers, not + just managed builds or SDK cache validation. + +The media contracts qualify the documented subset, not complete browser APIs. +Physical frame cadence, identical Chrome resize transients and long-duration A/V +latency must not be inferred from automated callback timestamps. diff --git a/docs/graphics/webgpu-v8-bindings.md b/docs/graphics/webgpu-v8-bindings.md new file mode 100644 index 000000000..af1d00e74 --- /dev/null +++ b/docs/graphics/webgpu-v8-bindings.md @@ -0,0 +1,2377 @@ +# Native V8 WebGPU binding implementation + +The binding contract uses the repository's existing @webref/idl 3.82.1 dependency, +installed with its package lock. `webgpu-v8-contract.json` records the WebGPU IDL +hash. This is a separate pin from the Dawn implementation. Private Dawn adapter +selection extensions must not become JavaScript descriptor members. + +`v8_webgpu_adapter_options.h` implements GPURequestAdapterOptions dictionary +conversion for native V8. It supports null/undefined defaults, inherited members, +lexicographic getter access, JavaScript boolean conversion, valid power-preference +enums, and DOMString UTF-16 preservation. Getter/coercion exceptions propagate; +failed conversions leave the native descriptor unchanged. Unknown feature-level +strings are preserved for discovery to handle according to its algorithm; +featureLevel is a DOMString, not an enum in this IDL. + +The native V8 runtime fixture verifies those cases, including proxies, Symbol +conversion failure, invalid power preferences, and unpaired UTF-16 surrogates. +Build and verification: + +```sh +cmake --build artifacts/graphics-build/native-v8-enabled --target webscene_graphics_v8_runtime_tests -j8 +ctest --test-dir artifacts/graphics-build/native-v8-enabled -R '^webscene_graphics_v8_runtime_tests$' --output-on-failure +``` + +Both pass against the current macOS arm64 V8 15.3.10/Dawn SDK build. This converter +is ready for the discovery binding but is not yet called by navigator.gpu. +Secure-origin exposure, asynchronous adapter/device promises, wrapper identity, +resources, pipelines and command encoding remain incomplete. No browser WebGPU +capability is advertised by this change, and no JavaScript triangle has run yet. + +## Dawn adapter request mapping + +`webgpu_adapter_options.h` separates the converted browser dictionary from V8 and +maps it to Dawn request options. Core/compatibility levels, power preference and +forceFallbackAdapter are preserved. Unknown feature-level strings produce no +request, matching the null-adapter outcome in the +[WebGPU requestAdapter algorithm](https://gpuweb.github.io/gpuweb/#dom-gpu-requestadapter). +XR-compatible requests currently produce no request because WebScene has no WebXR +device integration. Backend selection is a separate host argument; it is never +read from a JavaScript dictionary or exposed as a browser extension. + +The Dawn event test now uses mapped browser defaults for real asynchronous adapter +discovery, followed by its device/resource/completion tests. Both +`webscene_graphics_dawn_event_tests` and `webscene_graphics_v8_runtime_tests` pass +on the macOS arm64 hardware build. Mapping tests also cover fallback/preferences, +compatibility, unknown levels and unsupported XR. This verifies native selection +plumbing, not the still-unimplemented navigator.gpu promise/wrapper exposure. + +## Asynchronous adapter promise delivery + +`v8_webgpu_adapter_request` bridges native Dawn discovery to a V8 promise. It +reserves the bounded graphics completion mailbox, retains native callback results +separately from V8 handles, and resolves only during engine-thread delivery for +the matching operation, owner, isolate and realm. Driver callbacks capture no V8 +handles. Unsupported requests or admission backpressure resolve null; native +failure/cancellation also resolves null. Destruction abandons native storage so a +late callback cannot publish an adapter into a discarded binding object. + +The runtime fixture now starts real asynchronous Dawn discovery from an active +V8 graphics callback and observes its JavaScript promise continuation while RAF +is paused. A separate request is cancelled through the mailbox; its promise +resolves null without invoking the wrapper factory. The test waits for physical +native callback retirement as well as logical cancellation. The enabled macOS +`webscene_graphics_v8_runtime_tests` passes. + +This is the promise-delivery component, not complete navigator.gpu exposure. The +fixture uses a diagnostic JavaScript wrapper while retaining the real native +adapter. The standards GPUAdapter object/prototype/feature/limit registry and +secure-origin navigator integration still need implementation. Navigation-wide +binding teardown must route through the existing cancellation lifecycle before +releasing the realm; that full integration remains unqualified. + +### Service-owned adapters + +Discovery completion now adopts the native adapter into the graphics service's +bounded, typed generational table. The V8 fixture retains a service handle, not +an untracked native adapter. Borrowed adapter access is confined to an engine +execution scope; an asynchronous device request must take its own native +reference within that scope. Releasing a wrapper handle therefore does not +invalidate a native reference already retained by an in-flight request. + +Adapter destruction and service closure are rejected during borrowed access. +Finalizers can enqueue value-only adapter release commands, with stale duplicate +releases harmless. Service closure releases adapters before closing Dawn's event +service. Hardware tests exercise foreign/stale handles, slot reuse, scope guards, +null rejection, capacity exhaustion, deferred release and reference survival. +This remains internal ownership plumbing; public GPUAdapter bindings are pending. + +### Wrapper creation failures + +Adapter completion consumes its resolver before invoking the wrapper factory. +The factory may return a `MaybeLocal`: a caught JavaScript exception rejects +the adapter promise with that same value, while an empty result without an +exception or a native `std::exception` rejects with a generic Error. Native error +text is not exposed. Terminated execution is not converted to an ordinary error. +Factories remain responsible for rolling back any partially registered resources. + +The macOS V8 hardware fixture verifies a throwing native factory and a JavaScript +exception sentinel, observes both rejections, and rejects duplicate completion. +Handlers are installed before returning to the graphics pump because spontaneous +Dawn discovery can complete within that same dispatch batch. The targeted runtime +CTest passes; this does not qualify the still-pending public GPUAdapter bindings. + +### Standards feature mapping + +`generate-webgpu-features.mjs` reads the pinned WebGPU IDL and checks its SHA256 +before generating `webgpu_feature_names.h`. All 23 GPUFeatureName values have +explicit Dawn spellings. Unknown names and native-only shared-texture/fence +features have no mapping. `npm run check --prefix tools/webidl-v8-bindings` now +checks this generated catalog as well as the existing DOM bindings. + +The native helper can query either an adapter or a device, using HasFeature on +that exact object. The macOS hardware test checks the full standards mapping +against actual adapter support, private-feature exclusion, null rejection, and +the default device's enabled subset. It passes. This is the feature translation +layer for discovery and device descriptors; GPUSupportedFeatures setlike objects, +SameObject identity and public capability exposure still require integration. + +### GPUBufferDescriptor conversion + +`v8_webgpu_buffer_descriptor.h` converts the pinned buffer dictionary atomically. +It reads inherited label before mappedAtCreation, size and usage, preserves +property/coercion exceptions, replaces lone label surrogates for USVString, and +preserves embedded NUL bytes. Required size/usage members are checked before +native allocation. EnforceRange uses truncation followed by bounds checking: +GPUSize64 accepts at most 2^53−1 and usage at most 2^32−1. BigInt, Symbol, +non-finite values and out-of-range integers produce TypeError. These rules follow +[WebIDL integer conversion](https://webidl.spec.whatwg.org/#abstract-opdef-converttoint). + +The V8 runtime test covers defaults, inherited properties, coercion/getter order, +USVString encoding, both integer boundaries, invalid inputs, atomic failure and +exception identity. It passes on the macOS enabled build. Usage combinations, +mapped alignment and device limits are not dictionary conversion errors: those +remain native WebGPU validation. The converter is not yet connected to a public +GPUDevice.createBuffer binding; buffer allocation and JS resource wrappers still +require integration. + +### Native buffer descriptor translation + +The converted buffer data now lives in a V8-independent descriptor. Its native +translator explicitly maps the ten pinned GPUBufferUsage flags instead of casting +JavaScript flags into Dawn's larger enum. Unknown bits (including Dawn's private +TexelBuffer bit at 0x400) return an invalid translation. The forthcoming public +createBuffer binding must route this through WebGPU validation/error-buffer +semantics; it must not reinterpret it as a WebIDL exception or successful buffer. + +Native descriptors borrow label bytes with an explicit length, preserving embedded +NUL. Translation from temporaries is deleted to prevent an immediately dangling +label. The hardware test creates a mapped Dawn buffer from a translated descriptor, +checks size/usage/map state and mapped range, then unmaps and destroys it. It also +checks all 32 individual usage bits and unknown-bit combinations. Both the Dawn +hardware and V8 runtime CTests pass on macOS. This does not yet connect JavaScript +createBuffer to native allocation or qualify the public validation-error path. + +### Device-owned buffer handles + +Each native Dawn device now owns a bounded generational buffer table (default +capacity 1024). Internal native-descriptor creation returns a typed handle; scoped +access rejects a foreign device's table and stale generations. This is an internal +entry point, not the public createBuffer binding or its error-object policy. + +`destroy_buffer` invokes Dawn Destroy while preserving the wrapper handle and +metadata; repeated destruction remains valid. `release_buffer` removes only the +wrapper's native reference. The service supplies a value-only deferred release +command for the existing finalizer release channel. Queued/native users retain +independent Dawn references; wrapper release does not destroy their buffer and +does not imply GPU completion. Device teardown clears its remaining buffer table. + +The macOS hardware test verifies cross-device rejection, stale/reused handles, +borrowed-access destruction/release/close guards, asynchronous finalizer delivery, +mapped-buffer survival after wrapper release, and metadata after repeated Destroy. +The Dawn hardware and V8 runtime CTests pass. Public buffer wrappers, mapping +ArrayBuffer detachment, device-loss browser semantics and error scopes still need +integration and qualification. + +### Buffer admission and regression checks + +Buffer creation now checks table capacity before invoking Dawn, avoiding native +allocation churn when wrapper capacity is exhausted. The owner-thread admission +check treats deferred GPU resources as occupied until completion and excludes +slots whose generations cannot be reused. It is not a cross-thread reservation; +no reentrant table mutation is allowed between the check and insertion. + +The hardware test configures a one-buffer device table, verifies saturation and +successful reuse after release. Resource-table tests cover zero capacity and a +slot remaining unavailable until its completion serial retires. After this change, +resource, Dawn hardware, graphics service and V8 runtime tests pass: + +```sh +ctest --test-dir artifacts/graphics-build/native-v8-enabled -R '^webscene_graphics_(resource|dawn_event|service|v8_runtime)_tests$' --output-on-failure +``` + +Before this admission change, rebuilding the enabled graphics targets and running +`ctest --test-dir artifacts/graphics-build/native-v8-enabled -L graphics --output-on-failure` +also passed all five tests, including ANGLE ES2 and ES3 on macOS. These are native +regression results, not evidence of running an unchanged WebGPU application. + +### First native-backed V8 buffer objects + +`v8_webgpu_buffers.h` provides an internal realm-owned wrapper factory with native +size/usage getters and destroy callbacks. Its bounded registry reserves finalizer +release capacity before exposing a wrapper. Receiver branding, device/buffer +handles and realm identity are checked; duplicate wrapping of a handle is rejected. +Registry teardown invalidates live objects before dropping their native entry, +so retained JavaScript references cannot dereference freed binding state. GC +callbacks only publish value-only release tickets. + +The V8 runtime fixture now requests a real Dawn device asynchronously, creates a +buffer on the engine, wraps it, and runs JavaScript metadata/brand/repeated-destroy +assertions. It also checks duplicate ownership, wrong-realm wrapping and retained +object calls after registry teardown, followed by device retirement before the +queued wrapper release. The rebuilt macOS runtime CTest passes. + +This internal factory is deliberately not installed as a public GPUBuffer +constructor. Label, mapState, mapAsync/getMappedRange/unmap, mapping detachment, +public createBuffer/error-object integration and complete WebIDL prototypes remain +unfinished. Existing fixtures exercise explicit registry teardown; GC reclamation +and full navigation lifecycle for this specific registry still need qualification. + +### Live buffer mapState + +The internal buffer prototype now has a branded read-only mapState getter backed +by Dawn GetMapState. It translates Unmapped, Pending and Mapped to the pinned +browser strings and rejects unknown native states. The V8 hardware fixture creates +a mapped-at-creation buffer, observes `mapped` in JavaScript, destroys it and +observes `unmapped` while size remains available. Wrong-receiver checks include +this getter. The rebuilt macOS runtime CTest passes. Pending-state behavior still +needs end-to-end qualification when mapAsync is connected; mapped-range exposure +and ArrayBuffer detachment remain unfinished. + +### Buffer labels + +The internal buffer prototype now implements a label getter/setter. Wrapper creation +accepts the already-converted descriptor label; subsequent writes perform USVString +conversion, pass explicit-length bytes to Dawn SetLabel and retain the converted +value for reads. Branding is checked before conversion and receiver state is +reacquired afterwards, because user conversion code may invalidate binding state. + +The rebuilt macOS V8 test passes default-label, embedded NUL/lone-surrogate, +Symbol rejection, thrown conversion identity, unchanged value after failure, +wrong-receiver-before-coercion, and reentrant Destroy during ToString assertions. +Labels remain readable after buffer destruction. Full registry teardown during +coercion is guarded by reacquisition but is not yet independently exercised; public +GPUDevice/createBuffer wiring and mapping APIs remain outstanding. + +### Buffer wrapper garbage collection qualification + +The macOS V8 fixture now retains a buffer through a global JavaScript reference, +drops that reference, and explicitly triggers collection. Native live-buffer +counts remain unchanged during GC and reach zero only after the engine pumps its +release channel. The one-slot wrapper registry rejects overflow without taking +ownership, then successfully reuses the collected slot. Registry teardown with a +new live wrapper followed by device retirement also drains its delayed release +without stale-handle failure. The rebuilt V8 runtime CTest passes. + +This closes the previously untested GC-release path for the internal buffer +registry. It does not prove mapped ArrayBuffer lifetime/detachment, in-flight GPU +submission behavior for JavaScript buffers, or full navigation integration. + +### Mapped ArrayBuffer lifetime primitive + +`v8_webgpu_mapped_ranges.h` creates ArrayBuffers directly over an already mapped +native memory region. Each view privately retains its buffer wrapper; the tracker +holds weak view handles so it does not make dead mappings permanently reachable. +Native storage remains owned by the buffer, never by a V8 backing-store deleter. +A private detach key prevents outside detachment. Engine-side detachment clears +all reachable views and their private owner references before native unmapping. + +The tracker enforces offset/size alignment, mapping bounds and non-overlap; empty +ranges occupy no bytes. Range reservations last until unmap even if views become +unreachable. Its owner must detach it before native destruction or unmapping. +It is not yet connected to the public getMappedRange/unmap entry points. + +The rebuilt macOS V8 test checks that ArrayBuffer pointers equal Dawn's mapped +addresses, JavaScript writes reach native mapped bytes, invalid ranges fail, +foreign detachment is rejected, and authorized detachment empties typed-array +views. This proves the tested mapped-memory path without a staging copy; it does +not qualify mapAsync, device-loss detachment or full public error semantics. + +### JavaScript mapped-at-creation buffer operations + +The internal buffer prototype now implements getMappedRange and unmap. Wrapping +a mapped-at-creation buffer attaches a full-range tracker to the wrapper entry. +getMappedRange applies GPUSize64 conversion and defaults, rechecks mapping state +after user coercion, and returns a direct mapped ArrayBuffer. Alignment, bounds, +overlap and absent-mapping errors use the caller-supplied trusted DOMException +constructor with OperationError. Registry setup must receive that constructor +from trusted runtime initialization, not discover it during an API call. + +unmap and destroy detach all tracked views before invoking Dawn. Registry teardown +also detaches before invalidating wrapper entries or queuing release. The rebuilt +macOS V8 fixture exercises JavaScript writes to native memory, omitted sizes, +WebIDL errors, OperationError cases, repeated unmap, and retained ArrayBuffer and +typed-array detachment on both unmap and destroy. User numeric coercion that calls +unmap is revalidated before creating a view. + +This is working internal mapped-at-creation behavior, not complete mapping support. +mapAsync and selected subrange attachment, device-loss/device-destroy detachment, +full navigation integration and public WebGPU discovery/resource exposure remain +unfinished. The runtime CTest passes; those missing paths remain unqualified. + +### Device-wide mapped-view detachment hook + +The buffer registry now supplies detach_device for the binding's device lifecycle. +It matches the service and complete device handle, detaches matching mappings, +and leaves wrapper/native device destruction to the caller. Calls are idempotent +and stale device generations cannot affect a live mapping. It requires the owning +isolate scope and must run before native device destruction or before JavaScript +resumes after device-loss delivery. + +The rebuilt macOS V8 test creates a mapped buffer and JavaScript range, confirms +that a stale generation does not detach it, detaches with the live device handle, +then destroys the native device and disposes the wrapper registry. It passes. +This establishes the lifecycle hook and explicit ordering, not automatic device-loss +integration: the public device binding and runtime loss delivery still must call it. + +### Reachable mapped views retain native buffers + +The macOS V8 lifetime fixture now drops the ordinary buffer reference while +keeping only its mapped ArrayBuffer reachable. After forced collection and an +engine pump, the native buffer and wrapper release registration remain live; the +ArrayBuffer retains its byte length and accepts a JavaScript write. Dropping that +view and collecting again leaves native storage intact during GC, then the next +engine pump releases the buffer and registration. The collected registry slot is +subsequently reused by the existing teardown test. The rebuilt runtime CTest passes. + +This verifies the mapped view's private owner edge as well as deferred native +release. Asynchronous mapping, device loss and application-level WebGPU exposure +remain outside this test's coverage. + +### Asynchronous map promise bridge + +`v8_webgpu_map_request.h` reserves a completion slot before issuing Dawn MapAsync. +Driver callbacks retain only native buffer/mailbox state. The engine owns V8 +resolver and wrapper references; successful delivery attaches the selected mapping +before resolving undefined. Cancellation unmaps and rejects AbortError immediately, +then consumes the eventual native completion without attaching memory or resolving +again. Validation failure status maps to OperationError. Requests are one-shot and +completion identity/realm are checked; disposal of a still-pending request aborts +native mapping, while hosts must explicitly cancel before disposal if its promise +remains observable in a live realm. + +The rebuilt macOS V8 fixture maps a real 16-byte subrange at offset 8 and observes +its promise continuation while RAF is paused. A separate request is cancelled; +its promise rejects with AbortError and late callback retirement is consumed. +Duplicate completions are rejected. The runtime CTest passes. + +This bridge is not yet installed as GPUBuffer.mapAsync. That binding still needs +argument conversion, early rejection/validation semantics, pending mapState, +selected-range attachment, and unmap/destroy/device-loss cancellation wiring. +Admission saturation and mapping attachment failure policies also remain internal +behavior requiring qualification against the complete browser binding. + +### Internal GPUBuffer.mapAsync integration + +The internal buffer prototype now exposes mapAsync and routes native completion +records through its registry. WebIDL conversion failures return rejected promises; +pending state is tracked on the content side until the engine handles completion. +Successful requests attach their selected range before resolving. unmap, destroy +and the device-detachment hook cancel pending promises, while a separate request +list keeps canceled native operations identifiable until their callbacks retire. +An immediate remap therefore cannot be completed by the old canceled operation. + +WRITE views reference Dawn mapped memory directly. READ mappings copy the selected +bytes into mutable CPU storage so JavaScript changes are discarded on unmap; a +second read verifies the native buffer remained unchanged. This explicit buffer +read operation does not add pixel readback to ordinary canvas composition. +Mapping allocation failures reject RangeError and unmap native storage. + +The rebuilt macOS runtime test now invokes mapAsync from JavaScript and passes +pending/mapped/unmapped state, selected WRITE range, cancel-and-immediate-remap, +READ data and discarded writes, promise-based BigInt conversion rejection, method +arity, and injected mapping-allocation failure checks. Native callbacks still do +not enter V8 directly. + +This remains an internal factory. Public navigator/device creation, complete +error-scope/uncaptured-error integration, loss delivery, all invalid-descriptor +and saturation cases, trusted DOMException-construction reentrancy, and complete +WebIDL interface exposure remain unfinished or unqualified. No app-level WebGPU +readiness or conformance claim follows from this test. + +### Mapping rejection remains settled if exception construction fails + +The map request now clears pending resolver ownership before invoking its exception +factory, while a local handle retains the wrapper through construction. A thrown +JavaScript exception becomes the rejection reason instead of leaving a pending +request. Repeated cancel remains inert, and completions for requests that were +never started are ignored. + +The rebuilt macOS runtime test deliberately supplies a throwing exception factory +for a canceled native map. It verifies thrown-value identity in the promise +rejection, cleared pending state, repeat cancellation and late callback retirement. +The ordinary prototype cancellation/remap tests continue to verify AbortError. +Broader public error-scope and device-loss integration remains outstanding. + +### JavaScript descriptor to owned buffer creation + +The buffer registry's create entry point now combines GPUBufferDescriptor +conversion, native allocation and wrapper registration. It rejects a misaligned +mapped-at-creation size with RangeError before allocating. Unknown browser usage +bits enter Dawn's validation/error-buffer path via invalid usage None, never a +private native usage extension. Wrapper metadata preserves the original browser +usage, size and label even for error buffers. Metadata getters now read retained +wrapper values rather than dispatching native calls. + +Creation releases its native handle if wrapping fails and propagates an exception +instead of silently returning no object. The macOS V8 fixture now creates its WRITE +mapping buffers from JavaScript descriptors via this entry point. It also verifies +misaligned mapped size without allocation, an invalid-usage error buffer with +preserved metadata and a usable mapped-at-creation region, actual Dawn validation +scope delivery, and native handle rollback when release registration is saturated. +The rebuilt runtime CTest passes and explicitly requires callback retirement. + +This is the native entry point for the forthcoming GPUDevice.createBuffer method; +it does not install a public device object. Full device creation, error-scope/event +exposure, native allocation failure/admission policy and loss integration still +require implementation or qualification. + +### Internal V8 device objects + +`v8_webgpu_devices.h` introduces bounded, branded device wrappers with createBuffer +and destroy methods. createBuffer calls the descriptor/allocation/wrapper path and +privately links each returned buffer to its parent device wrapper. destroy first +cancels/detaches that device's buffer mappings, then invokes native Dawn destruction; +repeated destruction is inert. Explicit native device table release remains a +separate teardown step. Registry disposal invalidates device and buffer receivers +before cancellation can construct JavaScript exceptions, and queues native release. + +The rebuilt macOS runtime test now calls device.createBuffer from JavaScript, +checks descriptors/metadata and receiver/arity errors, writes a mapped view, +destroys the device twice, and verifies view detachment, retained buffer metadata +and rejected mapped access. Calls through a retained device object after registry +disposal also fail safely. The runtime CTest passes. + +No global GPUDevice or navigator.gpu is installed by this factory. Adapter requestDevice, +queue/resources beyond buffers, capabilities, labels, device.lost/error events, +full WebIDL prototypes and automatic loss/navigation integration remain unfinished. +The parent-device GC edge and asynchronous completion routing through this new +device registry still need dedicated qualification. + +### Pending map cancellation through the device registry + +The macOS runtime fixture now creates a buffer through the internal device object, +starts mapAsync from JavaScript, and destroys the device while the map is pending. +It verifies immediate unmapped state and AbortError rejection. Completion routing +passes through the device registry after the native device handle has been released; +the late callback retires safely before registry disposal. A retained device object +is then checked for rejected access after disposal. The rebuilt runtime CTest passes +and explicitly waits for native retirement rather than treating rejection as GPU +completion. + +This qualifies the tested device-destroy path. Physical device loss, parent-device +GC retention, public adapter discovery and complete device/queue capabilities remain +unfinished or require dedicated verification. + +### Device labels + +Internal device wrappers now retain an initial converted descriptor label and +implement the label getter/setter. Writes use USVString conversion, preserve +embedded NUL via explicit byte lengths, recheck the receiver after user coercion, +and forward the label to Dawn. Failed conversion leaves the prior label intact. +The rebuilt macOS V8 test passes default, surrogate/NUL, Symbol, throwing-conversion +and post-destroy label assertions. Public discovery and remaining device/queue +capabilities are still unfinished. + +### Device enabled-feature snapshot + +The internal device factory now exposes a SameObject `features` snapshot backed +by a traced, inaccessible V8 Set. Only the generated standard GPUFeatureName +mapping is admitted, and contents come from the device's enabled features rather +than the adapter's available capabilities. The read-only setlike surface includes +size, has, keys/values/iteration, entries and forEach. Iterators use built-ins +captured when the factory is initialized in the trusted realm; this factory must +be constructed before running untrusted scripts. Snapshot objects retain their +backing data independently of native device ownership and factory lifetime. + +The macOS V8 runtime test compares every standard feature against the actual Dawn +device, checks count, identity, iteration, callback arguments, DOMString coercion, +receiver checks, mutation rejection and exception propagation, and uses a retained +snapshot after device registry disposal. This remains an internal binding; public +navigator.gpu, requestDevice and rendering commands are still required before a +JavaScript WebGPU app can render. + +### Native requestDevice promise delivery + +`v8_webgpu_device_request` now connects an already-validated native device +request to a V8 promise. The Dawn callback captures only synchronized native +result storage and a completion ticket. The engine thread consumes that result, +invokes the device wrapper factory, and settles the realm-owned promise. Native +request failure rejects with an OperationError constructed through the captured +trusted DOMException constructor; native diagnostics are not leaked to scripts. +A released bridge abandons its result, and a late callback retains no V8 handles. +Completions are consumed before invoking wrapper/exception factories. Duplicate +and wrong-owner completions cannot create another wrapper, and wrong-realm +completion does not consume the pending request. + +The macOS V8 runtime fixture now uses this bridge to create the actual Dawn +device used by its buffer tests. It verifies promise fulfillment, duplicate and +wrong-realm handling, and an actual Dawn rejection for an impossible requested +maxBufferSize, including the OperationError rejection object. This fixture still +uses a diagnostic result object at the promise factory boundary. Browser device +descriptor conversion, adapter validity/consumption, public GPUAdapter wiring, +device-loss promise integration and full teardown/admission qualification remain +unfinished; this is not public requestDevice exposure. + +### Device descriptor WebIDL conversion + +The pinned GPUDeviceDescriptor converter now reads inherited label, defaultQueue, +requiredFeatures and requiredLimits in WebIDL dictionary order. Device and queue +labels use USVString conversion. Feature sequences accept iterable objects, +cache the iterator's next method, convert every item through the standard feature +enum, and retain duplicate entries for subsequent validation. Private native +feature names are rejected. Required-limit records snapshot all own keys and +then inspect each property's descriptor before reading its value; getters can +change later properties. DOMString keys preserve unpaired UTF-16 surrogates, +undefined values remain distinct from zero, and GPUSize64 uses EnforceRange. +Conversion commits the output only after all members succeed. + +These behaviors follow the WebIDL [sequence conversion](https://webidl.spec.whatwg.org/#es-sequence) +and [record conversion](https://webidl.spec.whatwg.org/#es-record) algorithms. +The macOS V8 runtime tests verify defaults, getter/proxy order, mutation during +record conversion, inherited/non-enumerable exclusions, feature iteration and +coercion, USVString versus DOMString, integer boundaries, exception identity, and +no partially committed output after failure. The converter does not yet map +required limits to Dawn or enforce adapter capabilities. Those validation steps +and the public requestDevice entry point remain required. + +### Native required-limit mapping + +A generated catalog maps every GPUSupportedLimits attribute in the pinned IDL to +its typed Dawn member. The four per-vertex/per-fragment storage limits map to +Dawn's CompatibilityModeLimits chain; the remaining limits map to base Limits. +The generator verifies the IDL hash, participates in npm generate/check, and +requires an explicit review for new attribute types. Unknown/native-only names +are not admitted by the catalog. + +`prepare_webgpu_required_limits` validates defined values against the supplied +adapter limits, including the reversed comparison and power-of-two constraint +for alignment limits. Unknown names with undefined values are ignored. Missing +native capability values, narrowing overflow and Dawn's undefined sentinels are +rejected; none silently becomes an omitted request. Output is committed only +on success. The caller owns and chains any compatibility structures used for a +native request. Failure is intended to become requestDevice OperationError; +public promise wiring remains unfinished. + +The macOS runtime tests exercise every catalog member and integer width, missing +names, limits above adapter capacity, invalid alignments, undefined entries and +atomic failure. Its real Dawn device request now uses a maxBufferSize requirement +validated against that actual adapter. The test and npm generation checks pass. +This does not establish complete adapter capability reporting or compatibility +mode qualification, nor does it expose public requestDevice. + +### Prepared native device requests + +The device descriptor data model is now independent of V8. +`webgpu_prepared_device_descriptor` validates required features against the actual +adapter before consumed-adapter and required-limit validation. Its result +classifies unsupported features separately (for TypeError) from operation +failures (for OperationError). Private native features are rejected even when +passed directly to the internal helper. Required features are deduplicated into +a set while preserving first-occurrence order. Adapter compatibility limits are +queried and chained only when the request includes those defined limits. + +The prepared object owns device/queue labels, features, limits and optional +compatibility-chain storage. It cannot move or copy; its native descriptor is a +borrowed view used while the owner remains alive. The macOS runtime fixture now +converts an actual JavaScript device descriptor, prepares it against the real +adapter, and uses that descriptor for Dawn RequestDevice. Tests verify every +standard feature against the actual adapter, duplicate removal, consumed-state +and feature-error precedence, unknown limits, and source-label mutation not +changing prepared storage. The runtime test passed. + +This helper accepts consumed state from its caller; it does not implement the +public adapter state machine. Public requestDevice still needs that state, +error-to-promise integration, trusted realm initialization and wrapper lifetime +ownership. Compatibility mode and full device-loss behavior remain unqualified. + +### Checked request promise entry + +The internal request bridge's `start_checked` entry combines JavaScript descriptor +conversion, adapter-state reacquisition, native preparation and asynchronous Dawn +request submission. Its ownership callback runs after all descriptor getters and +coercions, allowing the eventual adapter registry to reject invalidated or +consumed receivers instead of carrying a stale native-entry pointer across user +code. Conversion exceptions reject the returned promise with the original thrown +value. Unsupported features reject with TypeError, and limit/consumed failures +with OperationError. Rejected preparation does not reserve native completion +storage or call Dawn RequestDevice. + +The macOS runtime device fixture now uses this checked entry for successful native +creation. Tests verify rejected promise types for unknown features and limits, +getter exception identity, unchanged completion occupancy after rejection, and +adapter consumed state changed by a descriptor getter. Runtime CTest passes. +This remains an internal entry: GPUAdapter prototype installation, receiver brand +handling, real consumed/expired state and public exposure are still pending. + +### Adapter wrapper identity and lifetime + +The realm-owned adapter registry now wraps generation-checked graphics-service +adapter handles. It rejects duplicate wrappers within the registry and foreign +realms, reserves deferred-release storage before exposure, and exposes a stable +read-only feature snapshot from the native adapter. Weak callbacks publish only +release commands; registry disposal invalidates native receiver access and queues +release instead of calling GPU APIs in GC. Retained feature snapshots remain +usable after wrapper registry disposal. + +The macOS runtime fixture verifies feature membership against every standard +Dawn feature, repeated snapshot identity, duplicate/foreign-realm rejection, +receiver invalidation after disposal, snapshot survival and actual deferred +native-handle reclamation. It uses an independent graphics-service fixture +because the existing buffer test deliberately occupies every release slot. +Runtime CTest passes. This registry does not yet dispatch requestDevice or install +navigator.gpu; those remain necessary for public discovery. + +### Adapter requestDevice dispatch + +Internal adapter wrappers now expose requestDevice with optional-descriptor +arity. The callback delegates conversion and preparation to the checked request +bridge, reacquires adapter ownership after user-controlled conversion, and retains +the adapter wrapper while native completion is pending. Successful completion +adopts the native device into the graphics service and creates a device wrapper +through a caller-owned device registry. That registry must outlive the adapter +registry. Device wrapper registration failure rolls back the adopted handle. +The original device label survives asynchronous completion. Wrong receivers +reject with TypeError; a repeated request after admission rejects OperationError. + +The macOS runtime fixture discovers a fresh adapter, calls requestDevice from +JavaScript, waits for its actual native completion, and uses the returned device +to create, map, write, unmap and destroy a buffer. It checks the label, method +arity, repeated-request/wrong-receiver rejection types and deferred reclamation +of both adapter and device handles. Runtime CTest passes. A previously consumed +native adapter is not reused to simulate fresh discovery. + +This is still an internal registry. navigator.gpu exposure, unified prototypes, +limits/adapterInfo, expired/lost-device semantics and pending-request teardown +qualification remain unfinished. The registry currently consumes an adapter at +native request admission; resource-failure/lost-device behavior needs the full +adapter state machine before public exposure. No WebGPU rendering sample is yet +claimed to work. + +### Pending device-request cancellation + +The request bridge now supports explicit owner-realm cancellation. It clears its +resolver before constructing the rejection, abandons any native result, and +leaves the native callback responsible for retiring its completion ticket. Late +completion cannot invoke a device wrapper factory. Adapter registry teardown +invalidates every receiver before cancelling outstanding requests, then releases +its request keep-alives and queues native adapter release. + +The macOS runtime test cancels an admitted native request with an impossible +limit, verifies wrong-realm rejection and idempotence, observes a rejected promise, +and waits for the real native callback to retire without wrapping. Runtime CTest +passes. This tests cancellation of the native failure path; successful native +creation racing registry teardown still needs targeted qualification. The host +teardown policy currently rejects OperationError, while full browser expired/ +lost-device behavior remains separate and unfinished. + +### Successful creation racing adapter-registry teardown + +The macOS runtime test now discovers a second fresh adapter, starts requestDevice, +and disposes its registry while the promise is pending, before delivering the +native completion. It requires an actual successful Dawn device completion rather +than accepting a native error as evidence. The original cancellation rejection +remains unchanged, no orphan device is adopted into the graphics service, and +the completion ticket and adapter handle are reclaimed. Runtime CTest passed. +This closes the specific successful-completion teardown gap above; it does not +qualify all browser device-loss or navigation behavior. + +### Discovery object connected to adapter/device registries + +An internal realm-owned discovery object now exposes requestAdapter and routes +real Dawn discovery completion into the adapter registry. The host retains the +controller; service and adapter/device registries outlive it. It rechecks its +receiver after option conversion, rejects conversion/receiver errors through +promises, resolves unavailable adapter requests to null, and rolls back native +adapter adoption if wrapper construction fails. Controller disposal invalidates +its receiver and cancels pending discovery promises before native callbacks +retire independently. No navigator property or secure-context claim is installed. + +The macOS runtime test exercises discovery through JavaScript, obtains the real +adapter wrapper, invokes requestDevice, and performs createBuffer/getMappedRange/ +unmap/destroy on the returned native device. It verifies unsupported feature-level +null results, invalid-option rejection and final native handle reclamation. +Runtime CTest passes. Public exposure policy, complete GPU prototypes and +capabilities, canvas configuration and rendering commands remain unfinished. + +### Preferred canvas format + +The internal GPU discovery object now implements getPreferredCanvasFormat as a +synchronous, receiver-checked method. The host selects BGRA8Unorm or RGBA8Unorm; +other formats are rejected during controller construction. The default is +bgra8unorm, matching the current macOS IOSurface submission path. No adapter +request, allocation or readback is required to report this preference. +The macOS runtime test checks default/explicit format selection, method arity, +wrong-receiver TypeError and invalid host configuration. Runtime CTest passes. +GPUCanvasContext configuration/current-texture and ordinary presentation wiring +remain unfinished; this method alone does not enable canvas rendering. + +### WGSL language capability snapshot + +The internal discovery object now exposes SameObject wgslLanguageFeatures. An +explicit allowlist of 13 language extensions from the [W3C WGSL specification](https://www.w3.org/TR/WGSL/#language-extensions-sec), +reviewed 2026-09-07, is filtered against the actual Dawn instance's +HasWGSLLanguageFeature results. Chromium testing/printing extensions are excluded; +additional draft/native extension names require a standards review before adding +them. Enable extensions such as f16 are not language-feature entries. + +The read-only set implementation now has distinct GPU and WGSL brands and +prototype tags while sharing its iteration/coercion implementation. Discovery +construction belongs in trusted realm initialization, before scripts can modify +Set built-ins. The macOS runtime test compares every allowlisted feature with +Dawn, checks count, iteration, identity, prototype tag and private-name exclusion, +and rejects borrowing GPU feature-set methods onto a WGSL feature object. +Runtime CTest passed. Shader compilation and public navigator exposure remain +unfinished; reporting capabilities does not establish shader execution coverage. + +### Adapter and device limits snapshots + +Internal adapter/device wrappers now expose SameObject limits. The GPUSupportedLimits +factory builds read-only prototype getters for every pinned catalog entry and +stores values in traced JavaScript storage, independent of native lifetimes. +Adapter values come from that adapter; device values come from that device's +GetLimits result, including compatibility-chain values. An unavailable native +sentinel aborts snapshot construction instead of being advertised as a capacity. + +The macOS runtime test compares every exposed adapter/device value with the +corresponding native source, verifies stable identity, strict-mode mutation +rejection, receiver branding and the prototype tag, and reads retained limits +after device-registry disposal. Runtime CTest passes. This provides capability +inspection in the internal bindings; public exposure, rendering commands and +full browser conformance remain unfinished. + +### Native adapter-information extraction + +A native adapter-information value now owns vendor, architecture, device, +description and subgroup metadata independently of Dawn's temporary AdapterInfo +allocation. Identifier fields must satisfy the WebGPU normalized-identifier +pattern; malformed/unknown values become empty strings rather than exposing +nonconforming driver names. Subgroup limits use the actual adapter when the +subgroups feature is supported, or the specified 4/128 defaults otherwise. + +Fallback classification is an explicit discovery-policy input. Inspection of the +pinned Dawn source found that Vulkan's forceFallbackAdapter filter identifies +SwiftShader via vendor/device IDs, while Metal rejects forced fallback. CPU +adapter type alone is therefore not used as a fallback test. The macOS runtime +test verifies copied description, subgroup values and identifier handling against +a real adapter; CTest passes. JavaScript info/adapterInfo wrappers and propagation +of authoritative discovery fallback state remain unfinished. + +### JavaScript adapter information + +Internal adapters now expose SameObject info and devices expose SameObject +adapterInfo. Both use the originating adapter's metadata, including subgroup +support, so disabling optional device features does not change adapter information. +Read-only prototype getters access traced snapshot data that survives registry +teardown. Private backend type and numeric vendor/device IDs are not exposed. + +Fallback classification now mirrors the pinned Dawn source: Vulkan recognizes +Google SwiftShader (vendor 0x1ae0, device 0xc0de), matching BackendVk.cpp and +src/dawn/gpu_info.json; Metal, D3D, GL and Null backends reject forced fallback. +Unknown backend classifications fail explicitly. This supersedes the earlier +caller-supplied fallback flag. Updating Dawn requires rechecking this mapping. + +The macOS runtime test verifies native strings, adapter/device agreement across +all seven fields, stable identity, read-only/brand behavior, private-field +exclusion, fallback classification cases and retained information after teardown. +The expanded allocations also exposed an existing GC test's unrooted probe being +collected before its explicit GC step; that probe is now rooted until the test +intentionally drops it. Runtime CTest passed. Hardware Windows/Linux fallback +qualification, public exposure and rendering remain unfinished. + +### Owned native shader modules + +Dawn devices now own a bounded shader-module resource table with independent +capacity, generation-checked handles, scoped borrowing and explicit reference +release. Capacity is checked before native creation. Device close retires shader +references alongside buffers; close/release during an active shader borrow is +rejected. Invalid WGSL still produces Dawn's error shader-module object, with +validation delivered through the native error scope. + +The macOS Dawn event tests verify capacity, stale/reused generations, borrow +lifetime guards and actual error-scope outcomes for valid and invalid WGSL through +the owned-module path. Dawn event and V8 runtime CTests both pass. Native shader +ownership is now ready for the V8 module registry; JavaScript shader descriptors, +compilation information and rendering pipeline/command bindings remain pending. + +### Deferred shader-module release + +Graphics-service release commands now carry generation-checked device and shader +handles without native pointers. Finalizers can publish these commands through +the existing bounded release channel. The engine resolves and retires the native +shader reference; stale device/module handles are ignored. +The macOS Dawn event test publishes releases from another thread, verifies no +inline native retirement, and verifies a stale release cannot remove a replacement +module in a reused table slot. Release storage returns to zero after draining. +Dawn event and V8 runtime CTests pass. V8 shader-module wrappers remain pending. + +### Internal V8 shader-module wrappers + +A bounded realm-owned shader registry now wraps native device/module handles, +rejects duplicate ownership, and keeps a private JavaScript edge to the parent +device wrapper. Label getters retain content-side metadata; setters perform +USVString conversion, recheck the receiver after coercion and call Dawn SetLabel. +The prototype has the GPUShaderModule tag. Weak callbacks and registry disposal +publish deferred module-release commands; disposal invalidates native receivers +first. Neither path calls GPU APIs from GC. + +The macOS runtime test wraps an actual compiled shader, verifies initial label, +NUL/lone-surrogate conversion, Symbol rejection, throwing coercion, receiver tag/ +brand, duplicate and foreign-realm checks, and deferred rather than inline native +release on disposal. Runtime CTest passes. Device createShaderModule descriptor +conversion/dispatch and getCompilationInfo are still pending; the internal +wrapper is not a complete exposed GPUShaderModule implementation. + + +### Internal device shader creation + +The internal GPUDevice prototype now dispatches createShaderModule to Dawn's +owned shader table and returns a GPUShaderModule wrapper retaining its parent +device. The descriptor converter reads label, required WGSL code and iterable +compilation hints in WebIDL order, preserves getter/coercion exceptions, converts +USVStrings, and commits converted state only after success. Dispatch rechecks the +device receiver after conversion, passes explicit string lengths to Dawn, and +releases the native module if wrapper adoption fails. + +Compilation hints are converted but not forwarded as optimization hints. The +converter has a pipeline-layout resolver hook; genuine GPUPipelineLayout wrapper +recognition remains pending with that interface. The current internal surface +accepts omitted/auto layouts and exposes no pipeline-layout objects. This is not +complete public WebGPU exposure or shader compilation diagnostics support. + +The macOS V8 runtime test creates a module from JavaScript using real WGSL, +checks module branding and label updates, required arguments, wrong receivers, +getter exception identity and native module ownership count. Converter coverage +includes iterable hints, dictionary order, invalid enums and atomic failure. +Render pipelines, command submission and normal canvas presentation remain the +next rendering integration work; this test does not draw an app or triangle. + + +### Owned native render pipelines + +Dawn devices now own render pipelines in a separately bounded, generational +resource table. Creation checks capacity before calling Dawn, borrowed pipeline +scopes prevent release/device close, and close retires the table. A value-only +deferred release command carries both device and pipeline generations for future +V8 wrapper collection. + +The macOS Dawn event test compiles vertex/fragment WGSL, creates an owned render +pipeline, encodes a triangle into an offscreen 4x4 RGBA8 texture, releases the +table's pipeline reference, and submits the retained command buffer. A completed +native validation error scope reports no error. It also verifies capacity, +borrowed-scope guards and stale-handle rejection. Dawn event and V8 runtime tests +pass. This is native command/lifetime validation, not a pixel assertion, physical +presentation check or JavaScript render-pipeline implementation. Those remain +required before claiming app rendering. + + +### Internal render-pipeline wrappers and native interface conversion + +Shader modules and render pipelines now share a typed labeled-resource registry. +Each specialization has a distinct V8 brand and prototype, bounded wrapper +storage, private parent-device reachability, and deferred generational release. +The render-pipeline specialization wraps owned Dawn pipeline handles. Checked +native-reference conversion invokes no JavaScript and retains the native object +through later descriptor conversion; wrong-interface and forged objects are +rejected before native access. Native cross-device compatibility remains Dawn's +validation responsibility. + +The macOS V8 runtime test constructs a real render pipeline using a shader +reference obtained from its wrapper, verifies exact native identity, rejects +shader/pipeline cross-brand conversion and a plain forged object, checks labels +and prototype tags, and verifies disposal invalidates wrapper access without +inline GPU release. Existing JavaScript shader-creation tests still pass through +the shared implementation. Public createRenderPipeline descriptor dispatch, +getBindGroupLayout, async pipeline creation and rendering from JavaScript are +still pending; this internal wrapper is not a complete GPURenderPipeline API. + + +### Programmable pipeline-stage conversion + +The shared GPUProgrammableStage converter now reads constants, entryPoint and +required module in WebIDL order. It retains the native shader reference, keeps +omitted entry points distinct from empty strings, and converts constants as a +record of USVString keys to finite doubles. Record conversion snapshots own keys, +checks current enumerability before each value, propagates exceptions, and +replaces values when distinct UTF-16 keys normalize to the same USVString. The +native constant-entry view borrows stable converted key storage and explicitly +disallows access through a temporary descriptor. + +MacOS V8 runtime coverage verifies defaults, shader identity, numeric coercion, +USVString replacement/NUL handling, normalized-key collisions, proxy property +order, property deletion during enumeration, invalid values and atomic failure. +The test passes against real shader wrappers. Derived vertex-buffer and fragment +color-target conversion plus complete render-pipeline dispatch remain pending. + + +### Render-state dictionaries and pinned enum mappings + +Primitive, multisample, blend-component/blend-state and stencil-face converters +now apply WebIDL member order, defaults, boolean coercion and EnforceRange +unsigned integers before committing native state. Invalid enums and non-finite/ +out-of-range integers throw TypeError; application getter exceptions propagate. +Native semantic constraints, such as supported sample counts, remain Dawn's +validation responsibility. + +Eleven render-related enum catalogs are generated from the pinned IDL and an +explicit native spelling map. Generation rejects changed IDL membership/order; +compilation verifies the mappings against the pinned Dawn headers. The npm +check/generate scripts include this catalog. Browser-unknown native enum values +are excluded. macOS V8 runtime tests cover defaults, conversion order, blend and +stencil values, numeric truncation/bounds, exception identity and atomic failure. +Both the runtime test and generator check pass. Vertex layouts, depth/color +state and complete pipeline dispatch remain pending. + + +### Depth/stencil and color-target conversion + +Depth/stencil conversion now preserves omitted depthWriteEnabled/depthCompare, +converts signed depth bias with EnforceRange, rejects non-finite float values, +and applies stencil defaults/masks in WebIDL order. Color-target conversion +requires a standard texture format, owns optional blend state and preserves +invalid write-mask bits for native validation. Its native view borrows blend +storage and cannot be obtained from a temporary descriptor. + +The macOS V8 runtime test verifies omitted versus explicit depth settings, +fractional bias truncation, signed bounds, float overflow/infinity rejection, +stencil updates, target defaults, blend storage identity and atomic failures. +The runtime test passes. Vertex layouts and final pipeline descriptor assembly/ +dispatch still remain before JavaScript can create a render pipeline. + + +### Vertex-stage and buffer-layout conversion + +Vertex-state conversion now extends the shared programmable stage with iterable, +nullable buffer slots. Buffer layouts own their attribute vectors, preserve +stride/step mode and expose borrowed native views. Attribute conversion requires +format, offset and shaderLocation in WebIDL order. GPUSize64 and required +GPUIndex32 values use EnforceRange before native validation. The sequence helper +captures iterator/next once, checks iterator results and reads done before value. + +The macOS V8 runtime test covers default empty buffers, Set iteration, null and +undefined slots, instance layouts, native attribute views, required fields, +integer bounds, exact property access order and atomic conversion failure. It +passes. Complete render-pipeline descriptor assembly and device dispatch remain +pending; no JavaScript draw or presentation is claimed by these tests. + + +### Internal createRenderPipeline dispatch + +The internal GPUDevice prototype now accepts render-pipeline descriptors and +calls Dawn's owned render-pipeline creation. Conversion follows inherited label/ +layout then depthStencil, fragment, multisample, primitive and vertex order. +Fragment targets preserve null slots. Native assembly keeps nested arrays, +constant keys, entry-point strings and blend pointers alive for the call. It +rechecks the device after JavaScript conversion and rolls back native ownership +if wrapper adoption fails. Returned pipeline wrappers retain their parent device. + +The exposed internal layout branch is currently auto. Descriptor conversion has +an explicit-layout resolver hook, but GPUPipelineLayout objects and their creation +remain unimplemented. Async pipeline creation and getBindGroupLayout also remain +pending. This is not complete WebGPU or navigator.gpu exposure. + +The macOS runtime test now creates a shader module and render pipeline from +JavaScript, checks labels/branding, rejects invalid calls, and verifies native +module/pipeline counts. Descriptor tests check property order and native nested +storage after moving converted data, including null buffer/color slots. The +bounded eight-ticket fixture drains disposed resources before its next unrelated +registration; collection still only publishes deferred releases. Runtime CTest +passes. Drawing commands, textures and normal canvas presentation remain needed +for a visible JavaScript triangle; this test does not verify rendered pixels. + + +### Owned native textures and views + +Dawn devices now own separately bounded texture and texture-view tables with +full generational handles. Creation checks capacity before native allocation; +borrowed scopes guard release, destruction and device close. Texture destroy +invalidates storage while preserving API handles and retained views. Reference +release does not call Destroy. Deferred commands carry device/resource identity +for future wrapper collection, and device close retires both tables. + +The macOS native draw test now obtains its target from these owned tables. It +releases table references before drawing through a retained view and verifies +native validation succeeds. Capacity, stale handles, borrowed-scope guards and +repeated destroy preserving handles are covered. Dawn event and V8 runtime tests +pass. JavaScript texture/view descriptor conversion and wrappers remain pending; +these tests do not establish JavaScript rendering or pixel correctness. + + +### Texture descriptor conversion + +GPUTextureDescriptor conversion now handles required extent/format/usage, +dimension, mip/sample counts, optional binding view dimension and iterable view +formats in WebIDL order. Extent union conversion retrieves the iterator method +once; dictionary extents preserve width requirements and height/layer defaults. +Sequence shape validity is recorded after consuming the sequence, allowing the +remaining dictionary conversion to finish before native use rejects invalid +shapes. Native views retain label, format arrays and the binding-dimension chain +through a scoped call. Unknown browser usage bits become invalid native usage, +preventing accidental exposure of host-only flags. + +MacOS runtime tests cover extent defaults, single iterator acquisition, exact +property order, binding/view-format storage, late shape validation, range errors +and atomic failure. Runtime and generator checks pass. Texture/view wrappers and +createTexture dispatch remain pending. + + +### Texture-view conversion and wrappers + +A typed texture-view registry now uses the shared labeled-resource lifetime +machinery, with its own interface brand and retained native-reference conversion. +The descriptor converter preserves optional mip/layer counts, format/dimension, +aspect, base offsets, usage and DOMString swizzle. Nonidentity swizzles use Dawn's +component-swizzle chain. Explicit UINT_MAX counts cannot silently become native +unspecified sentinels; these and unknown usage bits produce an invalid native +dimension for subsequent Dawn validation. Invalid swizzle characters likewise +remain native validation errors, not WebIDL enum exceptions. + +MacOS runtime coverage verifies descriptor defaults, explicit sentinel +preservation, invalid inputs and DOMString surrogate preservation. A real native +view is wrapped, retained and disposed; native table removal happens only after +engine release draining, and cross-interface conversion is rejected. Runtime and +generator checks pass. Public createTexture/createView dispatch remains pending; +full native error-scope qualification of translated invalid view descriptors is +also still required before conformance claims. + + +### Internal JavaScript texture creation and views + +GPUDevice.createTexture now converts descriptors, validates extent shape after +conversion, creates an owned Dawn texture and wraps it with a retained parent +device. GPUTexture exposes readonly descriptor metadata, mutable label, +createView and destroy. Metadata remains available after destruction. View +creation rechecks the receiver after descriptor conversion, creates an owned +native view, and retains the texture wrapper through the view's private parent +edge. Wrapper adoption failures release the newly created native reference. + +Texture wrappers extend the shared typed resource registry; their views reuse +its existing deferred release implementation. The registry now records its +owning factory for specialized callbacks. Native resource destruction remains +on the engine thread, and collection still publishes only release commands. + +The macOS V8 runtime test obtains a device through adapter.requestDevice, +creates a texture/view in JavaScript, verifies dimensions/format/usage/defaults, +labels and readonly metadata, rejects wrong receivers and invalid extent shapes, +and checks metadata after repeated destroy. Runtime CTest passes. Command +encoding, queue submission and ordinary GPU canvas presentation remain pending; +this test does not draw from JavaScript or prove pixel output. + + +### Owned native command resources + +Dawn devices now own bounded generational tables for command encoders, render +passes and command buffers. Capacity is checked before create/begin/finish; +borrowed command scopes prevent release or device close. Deferred release +commands carry complete device/resource identity. Native WebGPU recording-state +validation remains Dawn's responsibility rather than being replaced with silent +host no-ops. + +The macOS triangle-command test now uses these tables to begin a render pass, +set its pipeline, draw, end, finish and submit. It releases the pass and encoder +before submission, then releases the submitted command-buffer reference. Tests +verify capacity rejection, borrowed scopes, stale-handle rejection and empty +tables, alongside the completed native validation scope. Dawn event and V8 +runtime tests pass. JavaScript command methods, queue submission and canvas +presentation remain pending; this is not evidence of JavaScript pixel output. + + +### Internal JavaScript command encoders and finish + +GPUDevice.createCommandEncoder and GPUCommandEncoder.finish now convert their +optional label dictionaries, recheck receivers after user coercion, invoke the +owned Dawn command-resource APIs and adopt typed wrappers. Command buffers retain +an encoder/device reachability chain. Failed wrapper adoption releases the newly +created native reference. The shared label converter preserves USVString and +exception semantics; native recording-state validation remains in Dawn. + +The macOS V8 runtime test creates and finishes encoders from JavaScript, checks +brands, labels and defaults, rejects invalid dictionaries/receivers, and verifies +a throwing finish label getter leaves the encoder usable for a valid finish. +Runtime CTest passes. These are empty command buffers: JavaScript render-pass +recording, queue submission and canvas presentation remain pending. + + +### Internal JavaScript render-pass recording + +Command encoders now expose beginRenderPass; render-pass wrappers expose +setPipeline, draw and end. Descriptors convert nullable color attachments, +texture-or-view references, clear colors, optional depth/stencil state and draw +limits. Texture shorthand resolves to an implicit native view after conversion. +GPUColor shape validation happens after dictionary conversion, before native pass +creation. Draw arguments use GPUSize32 conversion and recheck ownership after +coercion. Native recording-state validation remains Dawn's responsibility. + +Query-set wrappers remain unexposed, so supplied query objects are rejected +rather than ignored. Occlusion/timestamp support, indexed/indirect draws, +vertex/index buffers, bind groups and the rest of the render-pass API remain +required. Explicit depth-slice sentinel values stay invalid when translated to +Dawn; full native error-scope qualification remains part of conformance work. + +The macOS runtime test now creates a texture, shader and pipeline in JavaScript, +records a triangle pass, ends it and finishes a command buffer. It checks invalid +clear shapes leave the encoder usable, invalid draw/interface calls reject, and +labels/brands persist. Descriptor tests cover color conversion, dictionary order, +nullable attachments, native storage and atomic failure. Runtime and generator +checks pass. The recorded command buffer is not submitted in this test: queue +submission, pixel verification and ordinary canvas presentation remain pending. + + +### First verified JavaScript WebGPU pixels (macOS) + +The internal device now exposes a SameObject GPUQueue with label and submit. +Queue submission converts an iterable of genuine command-buffer wrappers before +calling Dawn, retaining native references through conversion and rechecking the +queue afterward. The traced queue/device relationship preserves lifetime without +an independent GPU reference in GC callbacks. Default-queue labels survive +asynchronous requestDevice completion. Internal device-reference conversion is +branded and scoped through the owning graphics service. + +The macOS V8 runtime test creates a 4x2 RGBA8 texture, WGSL shader module, +auto-layout render pipeline, command encoder and render pass entirely through +JavaScript wrappers. It records a full-target red triangle, ends and finishes, +then submits through device.queue.submit(new Set([commands])). Native diagnostic +code subsequently copies the target to a mapped readback buffer and checks all +eight pixels equal RGBA [255,0,0,255]. The mapping must complete successfully +within five seconds; missing completion or any pixel mismatch fails. This proves +actual offscreen JavaScript-driven GPU rendering, not only wrapper construction. + +The runtime test passes on this macOS host. It also checks queue identity, +default/mutable labels, invalid receivers/buffers and iterator exception identity. +Diagnostic texture readback is used only to assert pixels; it is not introduced +into the composition path. navigator.gpu installation, GPUCanvasContext, +ordinary scene image consumption and window presentation are still pending. +Queue completion promises, writes and other APIs also remain incomplete. This +result does not establish Kestrel readiness or cross-platform qualification. + + +### Canvas configuration conversion + +GPUCanvasConfiguration conversion now preserves device identity, required format, +usage, view formats, alpha mode, color space and tone mapping in WebIDL order. +PredefinedColorSpace comes from the pinned @webref/idl html.idl, including its +linear sRGB/P3 values. Requested modes are preserved for subsequent capability +negotiation; conversion does not claim the current presenter supports every mode. +The device resolver retains a genuine native GPUDevice and rejects forged +interfaces. Conversion commits only on success and propagates getter exceptions. + +The macOS runtime test covers defaults, full member order, iterable view formats, +requested HDR/color modes, invalid values and atomic failure. It passes together +with the existing JavaScript triangle pixel assertion. GPUCanvasContext remains +unconnected. The existing IOSurface submission helper owns recording/submission +inside one callback; integration must instead span current-texture acquisition, +application queue submissions, shared-image end access and versioned publication +at the frame boundary, without introducing CPU texture readback. + + +### Shared-frame texture expiration + +Shared Dawn images can now expire their texture object after EndAccess. Expiry +is idempotent, refuses active/failed access and prevents later BeginAccess. +IOSurface submission expires the per-frame texture before marking the image +ready; queued native work retains the resources it needs. This closes the route +by which an old canvas texture alias could otherwise submit writes after the +underlying image allocation becomes eligible for reuse. + +The macOS fixture retains an old texture alias, attempts to clear through it +after frame completion, and requires a native validation error. Creating a view +alone does not establish invalid submission, so the test records and submits the +attempted write. The real Ganesh window probe then verifies the original pixels: +32 rendered frames, one import, completed GPU retirement, zero explicit transport +copies and four diagnostic readbacks. Physical scanout is not verified. Evidence: +`evidence/ganesh-host/frame-texture-expiry.json`. This validates native frame +expiration/presentation; JavaScript GPUCanvasContext remains unconnected. + + +### Handoff after application-owned submission + +The IOSurface producer can now publish work already submitted on the device +queue. This path never resubmits application commands. It verifies device/native +allocation identity, ends shared access, expires the frame texture, requires +initialized contents, and waits for queue completion plus its own handoff +validation scope before exposing a versioned image. Its error scope covers only +host handoff operations, not application recording or the application's scope +stack. Publication backpressure still retains the producer through completion. + +The macOS fixture now records and submits independently, then invokes this +handoff. It rejects a foreign allocation before consuming its frame, rejects +reopening an expired texture, and verifies an expired alias cannot submit writes. +The real Ganesh window pixel probe passes: 32 frames, one import, completed GPU +retirement, zero explicit transport copies and four diagnostic readbacks. +Evidence is in `evidence/ganesh-host/submitted-frame-handoff.json`; physical +scanout is not verified. This supplies a native canvas frame-boundary primitive. +JavaScript GPUCanvasContext, host device-feature provisioning and ordinary scene +consumption remain integration work, not completed by this native fixture. + + +### Imported canvas texture adoption + +The native device texture table now accepts an already-created/imported texture +through a host-only adoption method. It verifies the supplied source device, +rejects null textures and checks capacity before retaining the object. The host +importer is responsible for supplying the texture's true source device; this is +not a JavaScript API. Adoption preserves exact native identity and performs no +texture allocation or pixel transfer. + +The macOS Dawn test verifies missing-source rejection, identity preservation, +capacity handling, view creation and repeated destruction of an adopted texture. +Dawn event and V8 runtime tests pass, including the JavaScript offscreen pixel +test. GPUCanvasContext must still connect this boundary to its current texture +and shared-frame publication lifecycle. + + +### V8 adoption of imported canvas textures + +The internal device bridge can now adopt an imported native texture directly +into its V8 texture registry. It validates the device brand, checks descriptor +metadata against native dimensions/format/usage/mips/samples, adopts the native +reference and rolls back table ownership if wrapping fails. The wrapper retains +its device and uses the ordinary texture/view implementation. No second texture +allocation or pixel transfer occurs during adoption. + +The macOS runtime test rejects mismatched metadata, verifies exact native +identity, and accesses metadata/createView from JavaScript on an adopted texture. +Runtime CTest passes, including the offscreen triangle pixel assertion. The host +canvas controller still needs to provide/cache this wrapper from getCurrentTexture +and connect frame expiration/publication to ordinary scene consumption. + + +### Canvas texture descriptor construction + +Canvas-specific content validation now restricts the base format to bgra8unorm, +rgba8unorm or rgba16float and rejects TRANSIENT_ATTACHMENT usage. The canvas +texture descriptor builder copies bitmap width/height, format, usage and view +formats, preserving zero dimensions and explicit usage rather than adding render +or host-only flags. Other texture members retain standard defaults. + +MacOS runtime tests cover all three base formats, rejected sRGB base formats, +transient usage, zero dimensions and unchanged explicit usage/defaults. Runtime +CTest passes. Required-format feature checks must precede this validation; +native texture validation and presenter capability negotiation still follow. +These helpers do not expose GPUCanvasContext or establish presenter support for +all required canvas color/HDR modes. + +### Internal GPUCanvasContext and rendered current texture + +The host-owned V8 canvas context now implements configure, unconfigure, +getConfiguration, getCurrentTexture and the canvas getter. It retains the +configured device, returns fresh configuration snapshots and caches one texture +wrapper until the host expires the frame, resizes the bitmap or unconfigures. +Host acquisition feeds the existing native-texture adoption bridge, with no pixel +transfer in the context. The controller cannot be copied or moved because V8 +receivers carry its address; released receivers are invalidated. + +The macOS runtime fixture supplies a diagnostic native-texture allocator. Its +JavaScript triangle now targets getCurrentTexture, submits through GPUQueue and +verifies all eight RGBA pixels using explicit diagnostic readback. Coverage also +checks unconfigured InvalidStateError, snapshot isolation, current-texture +identity, one acquisition per frame, retirement, resized dimensions and released +receiver rejection. + +This is an internal context milestone, not public canvas integration. Ordinary +HTMLCanvasElement.getContext, shared IOSurface acquisition/publication and normal +scene consumption remain unconnected. Presenter feature/color validation, +invalid-texture and allocation-failure semantics, and host retirement failure +handling still need qualification before exposing this as a complete API. + +### Submitted-frame discard retirement + +The IOSurface submission handoff accepts an explicit non-presenting retirement +for canvas resize/unconfigure. This path ends shared access and expires the +WebGPU texture, keeps the producer allocation alive through queue completion, +and then cancels the pool reservation without creating a scene lease. Its +terminal status is `discarded`; initialization is required for presentation but +not for a discarded image. Validation and completion failures remain failures. + +The native Metal fixture exercises both an uninitialized texture and a texture +with an application-submitted clear. Both reach discarded status, produce no +scene image, wake once and leave no occupied pool slot after completion. The +Ganesh window pixel probe still reports 32 frames, one import, completed GPU +retirement, zero explicit transport copies and four diagnostic readbacks. +Evidence: `evidence/ganesh-host/submitted-frame-discard.json`. This establishes +native discard behavior; the JavaScript canvas host is not connected to it yet. + +### Reusable IOSurface canvas texture import + +`import_dawn_iosurface_canvas_texture` provides the native acquisition operation +for the upcoming canvas host. It imports the reserved pool frame using the +caller's texture descriptor, rejects mismatched bitmap dimensions/base format, +and retains the IOSurface independently through the shared-image owner. It does +not allocate another pixel store, copy pixels or submit commands. BeginAccess +and completion-based retirement remain explicit caller responsibilities. + +Both the native recording helper and application-submission fixture now use this +operation. The macOS fixture verifies descriptor mismatch rejection, requested +usage and allocation identity, then exercises presentation and discard. The +Ganesh window pixel test passes with 32 frames, one import, zero explicit +transport copies and four diagnostic readbacks. This importer currently targets +the negotiated BGRA8 pool; it does not establish RGBA16/HDR support or connect +ordinary JavaScript canvas acquisition by itself. + +### Native canvas frame provider + +`dawn_iosurface_canvas_host` now combines the IOSurface pool, descriptor-preserving +import and submitted-frame retirement. It permits one active current texture, +reserves one of three pending retirement slots before acquisition, rejects foreign +texture retirement and exposes completed retained images through `take_ready`. +Failed/discarded/consumed retirements are pruned on the engine thread. The caller +must retire the active texture before destroying the provider; pending GPU work +retains its allocation independently. Scene publication must still filter canvas +generations and content serials when consuming completed images. + +The Metal fixture verifies duplicate acquisition and foreign retirement +rejection, discarded-frame cleanup, then renders and returns the provider's own +image to the Ganesh window probe. The probe passes with 32 frames, one import, +completed GPU retirement, zero explicit transport copies and four diagnostic +readbacks, including presentation after provider destruction. Ordinary V8 canvas +host callbacks and scene scheduling still need to be connected to this provider. + +### V8 canvas to IOSurface provider bridge + +`make_iosurface_webgpu_canvas_host` connects the V8 canvas controller's validate, +acquire and retire callbacks to the native IOSurface provider. The document +supplies canvas/generation/content and producer timeline identities; the bridge +adds bitmap dimensions, alpha mode, color space and orientation. Current +negotiation is BGRA8, sRGB and standard tone mapping, with native IOSurface and +MTLSharedEvent capabilities required on the device. Acquisition uses the original +canvas texture descriptor and retirement hands already-submitted work to the +provider without resubmission or pixel transport. + +A new macOS V8 runtime test requests a fresh Metal adapter/device with those +private native features, wraps the device, configures the internal canvas and +clears its current texture from JavaScript through GPUQueue.submit. The resulting +retained IOSurface image preserves canvas 123, generation 7, content serial 1 and +4x2 bitmap dimensions. After producer completion, explicit diagnostic IOSurface +inspection verifies all eight opaque red pixels; this CPU inspection is test-only. +The private sharing feature names remain absent from the JavaScript feature set. +Unconfigure/consumer completion leave the provider idle with no occupied image. +Runtime CTest passes after rebuilding the target. + +The test deliberately provisions private device features internally. Ordinary +requestDevice still needs host capability provisioning, and HTMLCanvasElement +getContext, automatic frame expiration and normal scene publication remain +unconnected. This evidence is a JavaScript-to-shared-image milestone, not a +normal WebScene application or Kestrel pass. + +### Host sharing capabilities on JavaScript device requests + +The adapter registry accepts an internal canvas-interop policy, defaulting to +none. With IOSurface selected, native device preparation appends the two required +sharing capabilities only after browser feature/limit/consumed validation. Missing +host capabilities reject preparation; JavaScript cannot select this policy or +request the native features through requiredFeatures. The existing browser +feature snapshots continue filtering native-only capabilities. + +The shared-canvas runtime test now obtains its device through JavaScript +adapter.requestDevice() with this host policy, instead of making a raw native +device request. It verifies both native features are enabled, their names remain +hidden in JavaScript, and an explicit private-feature request rejects TypeError +without consuming the adapter. The subsequent normal request renders the shared +canvas and all eight diagnostic pixels pass. Runtime CTest passes after rebuild. +The ordinary runtime still needs to install the registry with the negotiated host +policy together with navigator.gpu, getContext and scene/frame scheduling. + +### Realm ownership and completion routing + +`v8_webgpu_realm` owns discovery, adapter and device registries in dependency +order, routes completion records to the owning registry, and destroys them in +reverse dependency order. It carries host interop/backend/preferred-format +selection into the registries. It does not install globals or decide secure +context exposure; canvas controllers must retire before this owner is destroyed, +and the graphics service must outlive it. + +The IOSurface runtime fixture now begins with JavaScript GPU.requestAdapter(), +then adapter.requestDevice(), and uses getPreferredCanvasFormat() to configure +its internal canvas. No raw native adapter/device request remains in this test. +It verifies the shared-image pixels and checks that a retained discovery wrapper +rejects calls after realm destruction. Rebuilt runtime CTest passes. This provides +the ownership and dispatch unit for runtime installation; navigator exposure and +normal DOM canvas/frame scheduling are still outstanding. + +### Runtime Navigator installation entry point + +`v8_dom_runtime::install_webgpu` is a host-only opt-in before application scripts. +It requires the host's secure-context decision and negotiated interop policy, +installs a stable navigator.gpu getter, and routes realm completions through the +runtime's existing graphics task pump. A denied decision returns false without +initializing graphics. Runtime ownership retires the realm before its graphics +service and enters the owning isolate during direct destruction. + +Navigation explicitly removes the installed GPU property because this runtime +retains its Navigator object across loads. The next document requires another +host installation decision. Explicit shutdown invalidates retained GPU receivers. +Tests cover denied exposure, stable identity, actual JavaScript adapter/device +creation through the ordinary runtime task pump, navigation/removal/reinstall, +shutdown and direct destruction with a live realm. Rebuilt runtime CTest passes; +the V8-disabled native library also builds successfully. + +This is an integration entry point, not automatic enablement in desktop hosts. +The host's origin/secure-context computation and full Navigator/WebIDL semantics +still need qualification. HTMLCanvasElement.getContext, automatic GPU frame +expiration and normal scene presentation are still pending; installing discovery +alone does not make Kestrel runnable. + +### DOM WebGPU canvas binding on the opted-in macOS path + +HTMLCanvasElement.getContext("webgpu") now creates the shared IOSurface context +when the main runtime realm has WebGPU installed with IOSurface interop. It +returns the same context for repeated calls, preserves the canvas object, and +locks canvas mode against Canvas 2D. Unconfigure does not release that mode. +Bitmap dimension setters/attribute resets resize the context and retire its +current texture, including already-submitted work. The bridge uses the document +canvas identity/generation/content serial and a runtime producer timeline. +The trusted DOMException constructor is retained at installation rather than +looked up again during application calls. + +The runtime test obtains a normal DOM canvas, configures it with the device from +navigator.gpu, submits a clear, then resizes and verifies a replacement texture's +dimensions. It checks both directions of 2D/WebGPU exclusion and mode retention +after unconfigure. Rebuilt runtime CTest passes. Canvas controllers retire before +the realm during navigation and destruction. + +This initial integration retains canvas controllers until document retirement +and uses a 64 MiB per-canvas pool cap. Detached-canvas collection, aggregate +resource budgeting, subframe exposure and failure semantics remain qualification +work. Automatic frame expiration/scene publication and normal presenter +consumption are not connected yet, so this is not a visible application pass. + +### Rendering opportunities and completed scene publication + +An acquired current texture now contributes to host frame demand even without +requestAnimationFrame. At a signaled rendering opportunity, the runtime expires +current textures after the last due RAF callback and hands already-submitted work +to the provider. Completed provider retirements participate in task readiness; +the normal task pump publishes matching canvas/generation/content versions into +the document. Stale versions are dropped before publication. No GPU completion +wait or pixel readback is introduced in this production path. + +Configure/unconfigure invalidate the document's displayed image and advance its +content version so an older pending completion cannot restore it. Published +unchanged content requests no further frame. Navigation clears pending rendering +opportunity state along with canvas controllers. + +The macOS runtime test draws through a real DOM canvas without RAF, signals a +host rendering opportunity and obtains the resulting document scene image. A +post-completion diagnostic IOSurface read verifies all 16 opaque red pixels in +the 8x2 image. It verifies automatic current-texture expiration, zero idle frame +demand, image removal on unconfigure and current-texture identity across two RAF +callbacks in one rendering opportunity. Runtime CTest passes after rebuild; the +V8-disabled native library also builds. Normal desktop host enablement and +managed v3 scene consumption remain outstanding, so this is document scene +publication evidence rather than a visible WebGPU application qualification. + +### Managed scene image ownership for Ganesh + +`NativeMacOSGpuSceneImages` captures indexed GPU image leases from a v3 scene +(or retains a supplied list), rolling back earlier captures if a later one fails. +It validates the negotiated image metadata, prepares every import before replay, +and retains completed imports across admission retries and unchanged renders. +Drawing resolves scene image indices; retirement releases unused CPU leases and +keeps imported resources until their host GPU fences complete. Callers must keep +the owner through successful TryComplete; it deliberately does not equate +managed disposal/finalization with GPU completion. + +The Ganesh window probe now uses this owner and passes its four destination-pixel +checks across 32 frames with one import, completed retirement and zero explicit +transport copies. A native managed integration test verifies rollback when a +later source has been disposed: one passed, zero skipped on macOS/net10.0. +The probe builds with zero warnings. Normal scene acquisition, old/new scene +replacement and shutdown scheduling still need to adopt this owner in the +production composition handler; this does not yet enable ordinary GPU scenes. + +### Bounded managed image-group replacement + +`NativeMacOSGpuScenePresenter` holds one current image group and at most two +retiring groups. Replacement transfers ownership only when a retirement slot is +available; otherwise the caller retains the candidate. Preparation drains old +groups under the host graphics lease, prepares the current group and preserves +its imports across unchanged draws. Shutdown stops admission and requires host +graphics callbacks until every group has completed retirement. Unimported rejected +candidates can now use DiscardUnprepared to release CPU leases without a graphics +context; imported groups must use fence-based retirement. + +The Ganesh window probe replaces its image group after 16 frames and checks four +destination pixels both before and after replacement. It retains the same source +image into the replacement group; this tests ownership replacement, not distinct +new image contents or cross-scene import deduplication. The run completes 32 +frames, two imports, eight diagnostic readbacks, zero explicit transport copies +and successful retirement of both groups. Evidence is recorded in +`evidence/ganesh-host/scene-image-replacement.json`. Two native managed tests pass +with zero skips for capture rollback and unimported-candidate release. Probe +build has zero warnings. Production composition-handler acquisition/replacement +and shutdown scheduling still need to call this owner. + +### Transactional v3 scene application + +The managed GPU presenter can now apply a v3 scene while holding its SafeHandle +view: it validates version/capabilities and CPU scene shape, retains all indexed +images, verifies GPU command indices, applies the renderer diff, commits image +bindings and only then acknowledges the native scene. Admission backpressure +leaves the renderer and caller's scene lease unchanged. Rejected candidates +release their unimported leases. A presenter with no imported groups can stop +synchronously via TryDiscardUnprepared; imported groups still require host +context retirement. + +A managed/native test applies and acknowledges an actual engine bootstrap v3 +scene, then verifies synchronous unimported cleanup and stopped admission. It +passes on macOS/net10.0 with one pass and zero skips. This bootstrap scene has no +GPU images; actual image import/draw/retirement remains covered separately by the +window probe and native image tests. The normal composition handler is not opted +in yet: its current Stop path removes the visual without guaranteeing later GPU +retirement callbacks. That lifecycle must be connected before enabling GPU scene +admission there. + +### Retirement after visual detach + +The pinned Avalonia 11.3.4 implementation establishes the locking contract needed +for detached cleanup: [native CGL MakeCurrent](https://github.com/AvaloniaUI/Avalonia/blob/11.3.4/native/Avalonia.Native/src/OSX/cgl.mm) +locks/restores the native context, and [Skia DrawingContextImpl](https://github.com/AvaloniaUI/Avalonia/blob/11.3.4/src/Skia/Avalonia.Skia/DrawingContextImpl.cs) +holds the GRContext monitor while drawing. Its platform API lease flushes Skia +before raw GL access and resets the state cache afterward. The public host +IGlContext.EnsureCurrent operation reaches that native context lock. + +Retained images and image groups now support retirement without a visual drawing +lease. After exclusive ownership transfers away from the rendering path, cleanup +acquires EnsureCurrent, then the GRContext monitor in Avalonia's order, releases +SKImage references, flushes pending reads, inserts/polls the GL fence with zero +GPU-wait timeout, and resets Skia's state cache. Context mismatch/loss fails while +retaining consumer ownership; it never fabricates GPU completion. The host must +keep its graphics context alive until cleanup completes. Concurrent framework +context destruction and device-loss recovery remain qualification work. + +The window probe's --detach-before-retirement mode removes its control from the +window after 32 frames, suppresses subsequent draw callbacks, and retires on a +worker that is asserted different from the rendering thread. It passes with two +imports, eight diagnostic destination-pixel checks, zero explicit transport +copies and completed GPU retirement. Evidence is +`evidence/ganesh-host/detached-retirement.json`; probe build has zero warnings. +Production stop must still transfer ownership to this path before normal GPU +scene admission is enabled. + +### Opt-in composition-handler GPU path + +NativeSceneCompositionHandler now has explicit macOS GPU admission (default +false). The admitted branch acquires ordered v3 scenes with image/ordered-canvas +capabilities, applies transactional image bindings, prepares imports under the +Skia lease and supplies indexed GPU drawing during retained replay. Admission +backpressure keeps the publication edge available while rendering drains old +retirements. GPU-only work can request full invalidation when CPU damage is empty. +The legacy CPU acquisition path remains the default. + +Stop removes the handler's presenter reference and transfers exclusive ownership +to NativeMacOSGpuRetirement. That service roots the owner independently of the +removed visual, polls detached retirement on a worker, and removes the root only +on completion. Failure/15-second timeout is traced and leaves resources retained +for diagnosis rather than fabricating completion. Framework context lifetime, +failed-owner recovery and final application shutdown coordination still require +qualification; the service is not a claim of complete device-loss handling. + +The detach window probe now uses this service and verifies its retained count +returns to zero after completion: 32 frames, two imports, eight diagnostic pixel +checks and zero explicit transport copies. Three native ownership/application +tests and 21 damage/mailbox policy tests pass with zero skips on macOS/net10.0; +probe build has zero warnings. These tests do not yet drive the admitted handler +with a normal GPU-producing engine. Desktop negotiation/engine enablement, +ordinary full-path rendering, captures/frozen scenes and Uno remain outstanding. + +### Per-document native host admission (2026-09-08) + +The optional tail of `webscene_engine_options` now accepts a WebGPU policy +callback. On graphics-enabled macOS the runtime evaluates it for the initial +about:blank document and each successfully resolved main-document navigation, +before application scripts. IOSurface admission is an explicit host assertion +that the document is trusted/secure and the scene consumer supports GPU images. +Unknown values deny admission. Older options sizes retain their previous +stylesheet callback boundary and do not read the new fields. + +The runtime regression verifies denial on the initial document, admission +visible to inline scripts, preservation after a failed resource load, and +removal before scripts in a subsequently denied document. The graphics-enabled +runtime test passed; the graphics-disabled native engine also built. This does +not yet validate the C callback through a GPU-producing engine, managed callback +lifetime, ordinary desktop host negotiation, redirects, or full origin isolation. +The existing navigation implementation reuses the global realm; this hook is +not a substitute for browser security-context conformance. + +The managed `NativeWebSceneApi.EngineCreate` now accepts an optional document +admission delegate and marshals the native policy tail. Its existing resource +bridge roots the delegate until native engine destruction; the static reverse +P/Invoke delegate is also rooted. Exceptions deny admission, and non-macOS +bridges cannot approve this IOSurface route. A native integration test passed +with no skips, verifying the initial URL, runtime-worker invocation, successful +admission, and safe teardown when the policy throws. The GPU host probe builds. +The normal surface still needs producer/consumer negotiation before enabling +this option; the callback test does not establish end-to-end window rendering. + +### Ordinary WebScene document window (2026-09-08) + +The opt-in NativeWebSceneView constructor now connects its host admission +delegate and GPU composition consumer. The --webgpu-document host probe loads +HTML through that view, requests an adapter/device, clears a 256x128 canvas, and +submits it. The macOS window visibly displays the green canvas; screenshot: +evidence/webgpu-document/macos-clear.png. Runtime diagnostics confirmed GPU +exposure, submission, and RAF execution. Temporary tracing confirmed one +scene image imported and drawn at 256x128; tracing was removed. + +This exposed and fixed ordinary task pumping failing to end GPU rendering +opportunities (only the resize-specialized pump previously did so). The native +runtime pixel-publication regression now uses the ordinary task pump and passes. +Explicit CSS display:block and dimensions are currently needed by this demo: +default inline canvas layout emitted a zero-sized GPU paint rectangle and is +still an open defect. This screenshot proves a clear pass in the ordinary view, +not Kestrel, full WebGPU support, calibrated color correctness, or lifecycle +qualification. The demo's admission callback approves only its generated local +document; it is an explicit trusted test host. + +Follow-up: the zero-sized inline canvas defect is fixed. The flattened text-run +layout path now rejects replaced elements, preserving their intrinsic boxes +through general inline layout. A regression verifies a canvas nested in a span, +256x128 attribute dimensions, and 300x150 defaults after removing attributes. +The native runtime suite passes. The demo no longer supplies CSS dimensions +or display:block; the window again visibly renders the green canvas, captured +in evidence/webgpu-document/macos-intrinsic-canvas.png. The managed probe builds +with zero warnings/errors. Broader replaced-element layout conformance remains +separate from these focused canvas checks. + +### Shader triangle in ordinary view (2026-09-08) + +The --webgpu-document demo now draws a 400x240 interpolated-color triangle. +Its JavaScript creates a WGSL shader module and an automatic-layout render +pipeline, sets that pipeline on a render pass, and draws three vertices before +queue submission. The normal macOS NativeWebSceneView visibly renders the +triangle (evidence/webgpu-document/macos-triangle.png). Runtime evaluation +reports GPU exposure, submission, and RAF completion without a demo error. +The managed probe builds with no warnings/errors. This is visual end-to-end +evidence for the basic shader/draw route; it does not qualify exact color +management, continuous frame replacement, or full WebGPU conformance. + +### Resize and repeated-frame probes (2026-09-08) + +The document demo now updates its canvas backing size from innerWidth/innerHeight +on window resize and schedules a redraw. --resize-webgpu drives 640x360, +280x180, 520x320, then 400x240 at 500ms intervals. The observed run submitted +five frames, ended with a 400x240 backing store, reported no demo error, and +visibly displayed the final triangle (macos-resize-final.png). This verifies +discrete resizing and final output, not smooth live dragging, Retina backing +resolution, or resize during sustained rendering. + +--stress-webgpu requests 120 RAF-driven frames and currently FAILS: one run +stopped at three frames with canvas texture acquisition unavailable; another +stopped at fifty with texture-view wrapper capacity exhausted. Temporary scene +tracing also found many zero-image scenes between completed GPU images. +Current generation/content-serial filtering excludes the previous completed +image as soon as the next texture is acquired; retaining the last completed +image until replacement needs an explicit invalidation/serial contract. +These failures remain open; the stress mode is separate from the default demo. +No continuous-rendering or smooth-resize acceptance is claimed. + +### Completed image retention (2026-09-08) + +Canvas backing now records the earliest content serial allowed after a bitmap +reset. Acquiring a newer frame advances the current serial without invalidating +an older completed image. Scene capture and painting accept completed images +within this interval; publication rejects regressions. Configure/unconfigure +advance the reset floor, including same-size resets, so delayed completions +cannot resurrect invalidated content. The runtime regression verifies next-frame +acquisition retains eligibility and unconfigure rejects the old image; the +runtime suite passes. Image-pool and wrapper-capacity stress failures remain +unresolved and must still be retested/fixed. + +Unchanged Kestrel-CAD from the supplied archive remains the application acceptance +test. The clear/triangle/resize probes are diagnostic fixtures and must not be +counted as Kestrel compatibility or epic completion. + +### Wrapper release pressure recovery (2026-09-08) + +Labeled GPU-resource wrapping now makes one V8 reclamation attempt when the +bounded release-ticket pool is full, then drains only releases whose accepted +command-prefix barrier is already satisfied and retries reservation. This +does not dispatch queued commands or JS completions. Weak callbacks still only +publish tickets; native GPU releases occur afterward on the runtime owner. +Reachable wrappers remain rooted and capacity remains bounded. + +The runtime regression creates 300 unreachable texture views across evaluation +scopes while retaining another view, then verifies the retained view's label +still works. The complete runtime suite passes. This is a pressure fallback, +not a performance-qualified GC scheduling policy; latency and native-memory +accounting still need qualification. The normal-window stress rerun still +failed after three frames with canvas texture acquisition unavailable, so +continuous rendering remains unqualified independently of this ticket fix. + +### RAF canvas-capacity admission (2026-09-08) + +The runtime now defers releasing a new RAF batch when a configured GPU canvas +has no current texture and its bounded provider cannot acquire an image. +Already-acquired textures still receive their end-of-frame opportunity. Pending +callbacks keep their waiting deadline and are reconsidered at a later host +frame; this adds no GPU wait, readback, or storage. Provider readiness accounts +for both the three image slots and submission retirement slots. + +Native runtime tests pass. After rebuilding, --webgpu-document --stress-webgpu +--resize-webgpu completed 124 submissions without a demo error and ended at +400x240 after the four-size sequence. Extra submissions come from resize redraws. +The probe now waits up to ten seconds for stress completion/error before reporting. +This verifies producer progress through combined rendering and discrete resizing, +not display of every submitted frame or live-drag timing. Non-RAF acquisition +under pressure, multi-canvas fairness, device loss, Retina resolution, and +smooth-presentation qualification remain open. + +### Original Kestrel startup acceptance baseline (2026-09-08) + +Run the host probe with --webgpu-document --kestrel /path/to/Kestrel-CAD.zip +--verify-kestrel, with WEBSCENE_TEST_NATIVE_LIBRARY pointing to the graphics +runtime. The harness extracts the standalone HTML unchanged, reports its SHA256, +loads it through the normal GPU-enabled NativeWebSceneView, inspects the app's +own readiness/backend label and history, disposes the view, and returns failure +unless Kestrel reports WebGPU startup. Without --verify-kestrel the window stays +open for investigation. This startup check does not replace interaction, +geometry, export, resize, or performance acceptance. + +The supplied original document starts but selects Canvas 2D compatibility: +"d.pushErrorScope is not a function". The real startup check returned exit 1. +The failure baseline and original hash are in evidence/kestrel/startup.json. +GPUDevice error scopes are the first observed WebGPU initialization blocker. + +### GPUDevice.pushErrorScope (2026-09-08) + +The device binding now implements pushErrorScope with required-argument checks, +WebIDL string conversion, exact validation/out-of-memory/internal filters, +receiver revalidation after conversion, and Dawn PushErrorScope forwarding. +Tests cover method arity, invalid filters and receivers, one conversion call, +exception propagation, and a JavaScript-pushed scope capturing a Dawn-injected +validation error retrieved through native PopErrorScope. The runtime suite passes. +The asynchronous JavaScript popErrorScope API and WebGPU error object types +remain unimplemented; this is not complete error-scope support. + +Unchanged Kestrel startup was rerun and still returned exit 1, now reporting +"shader.getCompilationInfo is not a function" as its Canvas 2D fallback reason. +The original document hash is unchanged. This is the next observed startup gap. + +### Owned shader diagnostic snapshots (2026-09-08) + +webgpu_compilation_info copies Dawn callback messages and source positions into +owned storage with message-count and aggregate-text budgets. It rejects excess +data instead of silently reporting an incomplete successful result. Tests verify +copy independence, explicit-length and NUL-terminated text, both budgets, and +a real invalid WGSL module delivering an error through Dawn GetCompilationInfo. +The native runtime suite passes. + +This is the native data-lifetime component for getCompilationInfo, not the +JavaScript API. Promise/mailbox routing, cancellation/realm teardown, +GPUCompilationInfo/GPUCompilationMessage objects, and UTF-16 position mapping +remain required. Kestrel's missing getCompilationInfo failure remains open. + +Diagnostic coordinate follow-up: the pinned Dawn CompilationMessages.cpp already +attaches DawnCompilationMessageUtf16 to every diagnostic. Owned snapshots now +preserve those UTF-16 coordinates separately from base byte offsets, with bounded +extension traversal and duplicate rejection. A real invalid WGSL shader containing +a supplementary Unicode character before the error verifies that the captured +byte and UTF-16 offsets differ correctly. The runtime suite passes. This avoids +reimplementing source mapping in WebScene; V8 delivery must use the UTF-16 fields +and must not silently substitute byte offsets if the extension is unavailable. + +### Asynchronous getCompilationInfo delivery (2026-09-08) + +GPUShaderModule.getCompilationInfo now returns a fresh promise, retains its +shader during the request, copies Dawn diagnostics in a native-only callback, +and settles through the existing completion mailbox on the runtime thread. +Requests are bounded; teardown cancels their mailbox owner and rejects pending +promises. Results expose diagnostic text/type and UTF-16 source positions with +a frozen messages array. Tests cover valid and invalid WGSL, concurrent requests, +fresh promises, receiver rejection, and delivery through ordinary task pumping; +the runtime suite passes. + +Full WebIDL object branding/prototype placement, specified failure exception +types, exhaustive allocation failure and teardown-race tests remain required. +These result objects currently use read-only own properties, not the final +GPUCompilationInfo/GPUCompilationMessage interface prototypes. This is functional +delivery, not a full conformance claim. + +The unchanged Kestrel check still exits 1, now with "GPUBufferUsage is not defined". +Its shader diagnostic step completes and initialization advances to buffer setup. + +### WebGPU flag namespaces (2026-09-08) + +Host-approved installation now exposes GPUBufferUsage, GPUTextureUsage, +GPUMapMode, GPUShaderStage, and GPUColorWrite. Namespace constants are enumerable, +non-writable and non-configurable; namespace globals are non-enumerable and +carry their toStringTag. Navigation retirement removes their global bindings. +Values follow the WebGPU draft: https://www.w3.org/TR/2026/CRD-webgpu-20260820/ +Tests verify every defined value/property descriptor, tags, and navigation +removal; the native runtime suite passes. Worker exposure and full namespace +WebIDL harness qualification remain part of the broader conformance work. + +Unchanged Kestrel was rerun and exits 1 with "d.createBindGroupLayout is not a +function". Buffer setup now advances to explicit binding-layout creation. + +### Explicit layout native ownership (2026-09-08) + +Dawn devices now own bounded bind-group-layout and pipeline-layout tables with +guarded access, close protection, native-reference conversion, distinct internal +V8 brands, and deferred release commands. Tests create a uniform-buffer binding +layout, use it to create a pipeline layout, release the original binding handle, +and retain the native pipeline layout. They also verify scope guards, wrong-brand +rejection, and deferred wrapper release. The runtime suite passes. Resource-count +assertions now compare against their actual pre-script baseline rather than +assuming earlier queued releases have not run. + +JavaScript createBindGroupLayout/createPipelineLayout descriptor conversion and +exposure remain unimplemented; Kestrel's observed binding-layout failure remains +open. The two tables currently use the existing pipeline-capacity setting. + +### createBindGroupLayout (2026-09-08) + +GPUDevice now exposes createBindGroupLayout with owned descriptor conversion, +iterable entries, required binding/visibility fields, the five binding variants, +native default values, and call-scoped external-texture chains. Binding enum +tables are generated and checked against the pinned WebIDL. Semantic validation +of incompatible/multiple entries remains with Dawn after conversion. + +Tests cover native wrapper identity/labels, missing and invalid fields, Set +iteration, every variant/default, external chains, and nested getter order. +The native runtime suite and generated-binding check pass. This does not claim +external-texture resource support or exhaustive bind-group-layout conformance. + +Unchanged Kestrel startup still exits 1, now reporting "d.createBindGroup is not +a function". Its layout creation step advances successfully to resource binding. + +### Bind-group ownership foundation (2026-09-08) + +Added bounded native bind-group handles, access guards, deferred release and a +distinct GPUBindGroup wrapper brand. The runtime suite passes with a uniform +buffer binding, release of the original buffer/layout handles, guarded device +closure, brand rejection and deferred wrapper retirement. This establishes +resource ownership; JavaScript createBindGroup conversion/exposure is still +pending, so Kestrel remains a failing acceptance test at that API. + +The unchanged original Kestrel archive remains mandatory acceptance evidence. +Triangle and synthetic resize probes do not replace successful Kestrel WebGPU +startup, app interaction and resize verification. + +### createBindGroup (2026-09-08) + +GPUDevice exposes createBindGroup with iterable entries, ordered dictionary +conversion, retained native resources and a device-retaining GPUBindGroup +wrapper. Binding resources currently support GPUBuffer, GPUBufferBinding, +GPUTextureView and GPUTexture (default view). Brand inspection does not invoke +JavaScript or use exceptions for normal union dispatch. Sampler and external +texture resource APIs remain outstanding; this is not complete union support. + +Tests cover labels, arity, receiver rejection, getter order and exceptions, +required fields, wrong layout/resource brands, invalid offsets, direct buffers, +texture views and textures. Native ownership/deferred retirement coverage is +recorded above. Semantic resource/layout validation remains in Dawn. + +The unchanged Kestrel document (SHA256 +0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563) +now gets past createBindGroup and reports “d.createPipelineLayout is not a +function”. Its startup probe still exits 1 with Canvas 2D compatibility fallback. + +### Explicit pipeline layouts (2026-09-08) + +GPUDevice exposes createPipelineLayout with an owned label, iterable nullable +bind-group layouts and immediateSize conversion. Native layout references are +retained across later getters. Render-pipeline descriptors and shader compilation +hints now recognize the distinct GPUPipelineLayout wrapper. Tests cover getter +order, labels, arity, missing/wrong layout values, invalid sizes, throwing getters, +nullable entries and explicit render-pipeline creation. The native runtime suite +passes. These checks do not establish complete validation or conformance. + +Unchanged Kestrel now advances to “d.popErrorScope is not a function”. Its +startup verification still exits 1 with Canvas 2D fallback; asynchronous error +scope delivery is next. Validation errors collected in its pushed scope have +not yet been exposed by the JavaScript API, so advancing through the creation +calls alone does not prove its pipelines are valid. + +### Error scope completion (2026-09-08) + +GPUDevice.popErrorScope now returns a fresh promise and uses the native +completion mailbox to settle it on the engine thread. Callback-owned messages +are copied with a 1 MiB limit; pending requests are bounded and retain their +device wrapper. Clean scopes resolve null, native validation/out-of-memory/ +internal errors become their GPUError subclasses, and failed pops reject with +OperationError. Realm teardown cancels pending delivery. GPUError has a branded +message getter and nonconstructible base; subclasses provide DOMString +constructors and inheritance. Their globals are removed on policy retirement. + +Tests exercise empty-stack rejection, wrong receivers, concurrent nested scopes, +a real invalid Dawn buffer descriptor, clean completion, constructors, message +branding and inheritance. The runtime suite passes. The larger fixture also +needed unique completion IDs: fixed numeric IDs collided with generated request +IDs after this addition and dispatched to a retired fixture request. It now +allocates its IDs through new_owner_token. Temporary crash diagnostics were +removed after verifying that root cause. + +Unchanged Kestrel advances through popErrorScope to “d.addEventListener is not a +function”. Startup still exits 1 with Canvas 2D fallback. GPUDevice EventTarget, +uncaptured error events and device-loss delivery remain outstanding. Exhaustive +teardown races, allocation-failure behavior and WebIDL/CTS conformance remain +qualification work; passing these tests does not close those requirements. + +### GPUDevice EventTarget integration (2026-09-08) + +The normal generated-DOM runtime passes its native EventTarget template into +the GPUDevice factory. Device wrappers inherit that template and prototype, +while a private target identity connects them to the existing listener registry. +This avoids duplicating event dispatch and keeps the GPU ownership fields +separate from DOM node identity. Standalone dispatch skips DOM-node unwrapping; +the generated identity helper also checks internal-field value types before +reading them. The generator source and checked-in output were updated together. + +GPU runtime tests cover EventTarget inheritance, target/currentTarget/this, +duplicate registration, removal, once and isolation from window listeners. +The runtime suite and generated-binding checks pass. Native uncaptured-error +events, onuncapturederror and comprehensive listener lifetime/DOM conformance +qualification are still outstanding. Internal factories without a host +EventTarget template retain their previous restricted surface. + +The unchanged Kestrel startup test now gets through addEventListener and fails +on d.lost.then: the device-loss promise is not exposed yet. The probe reports +Canvas 2D compatibility and exits 1. This remains a failed real-app acceptance +test, not a completed WebGPU application run. + +The broader native-engine suite initially failed its detached-DOM GC deadline +(before=622, after=657). A rebuilt prior-commit baseline passed; rebuilding the +current changes and rerunning both native-engine and GPU runtime suites also +passed (12.44 seconds combined). No GC assertion was weakened. The initial +failure remains unresolved intermittent evidence, not a proven GC fix. + +### Device-loss promise and dormant completions (2026-09-08) + +Browser device requests now configure Dawn's loss callback and pass its owned +signal into the adopted device. The signal captures reason and bounded message +bytes on the native callback thread, handles loss before subscription, and +publishes at most once. GPUDevice.lost returns a stable promise; engine-thread +delivery resolves it with a branded GPUDeviceLostInfo (destroyed or unknown +reason and message). Explicit destruction is covered by a real Dawn test. + +Lifetime callbacks reserve dormant completion slots: they remain bounded and +wake the engine on publication but do not cause periodic event polling. Normal +operations retain polling behavior. Tests cover dormant publication/cancellation, +early and late loss subscriptions, owned diagnostic data, duplicate suppression, +promise/result identity, destruction, and interface branding. Completion, graphics +service and GPU runtime suites pass. Existing teardown fixtures now identify +their own completion and preserve the baseline slots of live device subscriptions. + +Unchanged Kestrel selects WebGPU and reaches rendering, which fails with +“this.device.queue.writeBuffer is not a function”. The previous startup verifier +only checked the backend label and incorrectly accepted that run. It now also +rejects errors logged by the original application's command history. The rebuilt +probe reports one error and exits 1; this is still a failed acceptance test. + +Remaining qualification includes native unexpected-loss rendering recovery, +loss under memory pressure, listener/GC lifetime, allocation-failure settlement, +and complete standards coverage. Internal devices adopted without a configured +loss signal are not qualified for device.lost delivery. Native uncaptured-error +events are also still outstanding. + +### GPUQueue.writeBuffer (2026-09-08) + +writeBuffer now converts destination/source offsets in argument order, supports +ArrayBuffer, DataView, typed arrays and shared buffer sources, and preserves +typed-array element units versus byte units. Source bounds/alignment failures +throw OperationError; numeric/interface conversion failures throw TypeError. +Dawn handles destination alignment, usage, mapping and bounds validation. +Conversion and range handling follow the [WebGPU queue API](https://gpuweb.github.io/gpuweb/#dom-gpuqueue-writebuffer) +and were compared with the pinned Dawn node binding. + +Normal fixed ArrayBuffer sources are passed directly to Dawn's synchronous +WriteBuffer snapshot; no additional browser-side staging copy is introduced. +Shared sources use an atomic-byte snapshot before that call. Data is rechecked +after coercion, rejecting detached and resizable backing stores. This is buffer +upload functionality and does not add pixel readback to canvas composition. + +GPU runtime tests verify actual bytes through MAP_READ after typed subarray, +DataView, raw/shared buffer writes and immediate source mutation. They also +cover invalid ranges, conversion exceptions, native validation, and detachment +during offset coercion. Full concurrent shared-memory stress, allocation-budget +qualification and standards conformance remain outstanding. + +Unchanged Kestrel now advances to “pass.setBindGroup is not a function”. The +probe still reports one application render error and exits 1. WebGPU backend +selection alone remains insufficient for Kestrel acceptance. + +### Render-pass bindings and first original Kestrel render (2026-09-08) + +GPURenderPassEncoder now exposes setBindGroup (iterable offsets and the shared +Uint32Array range overload) and setVertexBuffer. Conversion retains native +resources across getters, enforces numeric ranges, snapshots dynamic offsets, +and rechecks backing stores after coercion. Typed ranges reject out-of-bounds +access with RangeError; nullable bindings and default vertex ranges are passed +to Dawn. Native layout, usage and command-state validation remains in Dawn. + +The existing diagnostic pixel test now gets vertex positions from a buffer at +a nonzero byte offset and red fragment color from a uniform binding at dynamic +offset 256. It exercises Set iteration, typed subranges and shared offsets, and +checks the rendered red pixels. Invalid interfaces, numeric/range conversion and +iterator exceptions are also covered. The GPU runtime suite passes. + +The unchanged Kestrel document now passes its startup/initial-render check with +WebGPU selected and zero logged application errors. The actual floor plan was +visually inspected in NativeWebSceneView; screenshots are in +[evidence/kestrel/macos-first-webgpu.png](evidence/kestrel/macos-first-webgpu.png) +and [evidence/kestrel/macos-resized-webgpu.png](evidence/kestrel/macos-resized-webgpu.png). +The host now opens Kestrel at 1280x800. The --resize-kestrel probe option samples +980x680, 1440x900, 1100x740 and 1280x800 with 750 ms settling intervals. All four +reported WebGPU and zero application errors; canvas backing dimensions matched +2x the reported CSS dimensions. The final floor plan remained visible. + +This is initial real-app rendering evidence, not full Kestrel acceptance. The +screenshots show UI layout/clipping defects and missing/incorrect toolbar and +layer presentation. Canvas height also remained at 614 CSS pixels during the +later shrink steps; complete resize/layout behavior needs investigation. Live +drag smoothness, interaction, editing, export, all render paths, native uncaptured +errors, WebGL fallback and cross-platform qualification remain open. Measurements +and original-document hash are recorded in first-render-and-resize.json. + +### Original Kestrel view transitions (2026-09-08) + +The --exercise-kestrel probe option dispatches change events through the original +view and visual-style controls. It samples iso/shaded-edges, front/shaded, +iso/xray, top/wireframe and iso/shaded-edges, with 750 ms between changes. +All five stages reported WebGPU and zero application errors. A separate run +with --verify-kestrel passed the startup/error-history gate. The final isometric +viewport was visually inspected and is visibly different from the top view. +Evidence is in view-transitions.json and macos-isometric-webgpu.png. The original +archive document is still extracted without modifications. + +This extends real-app evidence beyond startup but is a scripted DOM-event smoke +test. It does not prove native pointer delivery, editing, export, shaded mesh +rendering (the sample drawing is flat), or correct UI layout. The existing +clipping/toolbar/layer presentation defects remain visible. + +### Original Kestrel native-input editing (2026-09-08) + +The --edit-kestrel probe option focuses the original command field, sends text +through NativeSceneSurface.SubmitText, and queues native Enter down/up events. +It creates a line, undoes it, redoes it, and undoes it again. Each step asserts +its expected object count relative to the initial document. Observed counts +were 265 → 266 → 265 → 266 → 265; every stage reported WebGPU and zero logged +errors, and --verify-kestrel exited 0. The probe build passed. Measurements and +command history are in evidence/kestrel/native-command-edits.json. + +An earlier attempt using new KeyboardEvent('keydown', {key:'Enter'}) correctly +failed the count assertion: KeyboardEvent is currently aliased to Event and +does not preserve key initialization. That standards gap remains open; using +the native host input route is not a fix for synthetic KeyboardEvent. The +passing test does not qualify OS hardware event delivery, rendered-pixel +differences after each edit, export, or the remaining editing tools. + +### Original Kestrel solid-creation blockers (2026-09-08) + +The --mesh-kestrel probe submits BOX through native text/key input, fills the +original primitive form with a 2000x1500x2500 box, submits it, and asserts that +the document gained one object. It currently fails: the original application +logs that showModal is not a function, and form submission reports that +constructing FormData from a form is unsupported. The object count remains +265 rather than 266 and the probe exits 1. Its managed build passes. + +Both blockers were traced to current runtime code: no native dialog showModal +is exposed, and both main/frame FormData constructors explicitly reject form +arguments. These must be addressed as real browser-stack dependencies for full +Kestrel acceptance; the probe does not patch the app or inject geometry through +its internals. GPU solid rendering is not yet assessed by this failed workflow. +Evidence is in evidence/kestrel/mesh-creation-blockers.json. + +### Form-backed FormData dependency (2026-09-08) + +The main and frame FormData constructors now collect basic successful form +controls, preserving document order and duplicate names. Collection uses form +ownership (including external associated controls), disabled-state matching +(including fieldset/legend handling), checkbox/radio checked state, selected +non-disabled options, textarea values and optional submitters. Constructed +entries snapshot values. Invalid form/submitter interfaces are rejected. + +Tests cover duplicate names, disabled fieldsets with first-legend exemption, +checked and unchecked boxes, multiple-select option filtering, associated +controls outside the form, textareas, submitter inclusion and snapshot behavior. +Both native-engine and GPU runtime suites passed (12.85 seconds combined), and +the managed probe build passed. File controls remain explicitly unsupported; +FormDataEvent delivery, custom form-associated elements, dirname and full +standards conformance remain outstanding. This is partial FormData support. + +The mesh probe now requires the original dialog's open state before filling or +submitting it. Its latest run exits 1 at the missing showModal method, preserving +the real UI prerequisite rather than submitting a hidden form. Kestrel solid +creation/rendering is still unqualified. Dialog/top-layer support is the next +identified browser-stack dependency. + +### Closed dialog layout dependency (2026-09-08) + +Closed dialog elements now default to display:none, with author display styles +still able to override the default. Adding or removing the open attribute +triggers style recascade so opening creates a layout box and closing removes +it. A runtime regression checks computed display and geometry before opening, +after opening and after closing, plus an explicit author display override. +The native-engine and GPU runtime suites passed (12.55 seconds combined). +This is a layout prerequisite only: showModal/close, modal top-layer painting, +focus and inert input behavior remain outstanding, and the original Kestrel +solid-creation workflow remains unqualified. + +### Native dialog interface dependency (2026-09-08) + +HTML dialog nodes now use a generated HTMLDialogElement interface inheriting +HTMLElement. The open property reflects its boolean attribute, including +recascade on assignment. Native, lazily allocated dialog state stores +returnValue independently of attributes and JS wrappers; cloning starts with +the default empty return value. DOMString conversion preserves NUL and lone +surrogates and propagates conversion failures without changing the old value. +The generated DOM interfaces now expose the standard configurable, non-writable, +non-enumerable Symbol.toStringTag on their prototypes. + +Regression coverage checks interface inheritance/tag descriptors, receiver +brands, open reflection and geometry, returnValue independence, conversion +failure, string roundtrip and clone behavior. Native-engine and GPU runtime +suites passed (12.82 seconds combined); generated binding consistency passed. +The unchanged Kestrel SHA256 remains +0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563. +The real mesh probe starts with WebGPU active and zero startup errors, then +exits 1 at the missing showModal method with modal.open false. No geometry was +injected and no hidden form submitted. Modal top-layer rendering, focus/inert +input, show/close/cancel events and complete dialog semantics remain pending. +The API/state distinction follows the +[HTML dialog specification](https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element). + +### Dialog closing lifecycle dependency (2026-09-08) + +The generated dialog interface now exposes close and requestClose. Closing an +open dialog removes its open attribute, detaches any existing Attr wrapper, +updates returnValue only when supplied and queues a non-bubbling, +non-cancelable close event. Closing an already closed dialog does nothing. +requestClose dispatches a non-bubbling cancelable cancel event and leaves the +dialog open if the event is prevented. Both methods validate their receiver +and optional DOMString argument, including conversion failure on closed dialogs. + +Queued close events retain their target wrapper until dispatch and are cleared +on runtime/frame teardown. They participate in the ordinary runtime task pump; +handler failures report a dialog-specific error. Regression tests exercise +cancellation, asynchronous dispatch, event flags, duplicate-close suppression, +return values, omitted arguments, receiver/conversion validation and detached +Attr state. Native-engine and GPU runtime suites passed (12.72 seconds combined) +and generated binding consistency passed. + +This remains partial dialog support: show/showModal, modal top-layer layout and +painting, focus restoration, inert background input, Escape/close-watcher +integration, beforetoggle/toggle events and complete reentrancy/conformance +coverage are outstanding. Kestrel's original BOX workflow remains blocked at +showModal; these closing primitives do not establish a working modal workflow. + +### Inert input foundation for modal dialogs (2026-09-08) + +HTMLElement.inert now reflects the boolean attribute. Native document hit +testing rejects inert elements and their composed-tree descendants, including +fixed-position descendants tested separately from normal flow. Runtime focus +selection excludes these nodes for programmatic focus and Tab navigation. +Native text dispatch stops editing a control that has become inert, including +the retained target of a preceding keydown. The shared native is_inert query +walks composed ancestors, so shadow content inherits the host's inert state. + +Tests cover reflection without falsely reflecting inherited state, ordinary +and shadow focus rejection, fixed-position input/button hit rejection, +restoration after attribute removal and native text/Tab input. Both native +suites passed (12.78 seconds); the additional Tab regression passed in the +GPU runtime suite (0.68 seconds). Generated bindings are consistent. +The unchanged original Kestrel edit probe also exited 0: LINE, UNDO, REDO, +UNDO produced object counts 266,265,266,265 with WebGPU active and zero logged +errors at each step. This is command-state regression evidence, not new +presented-pixel or modal qualification. + +The implementation is an input prerequisite, not complete inert conformance. +Modal escape from ancestor inertness will be connected to top-layer state; +accessibility, find-in-page, selection, immediate focus fixup on mutation and +pointer-capture transitions remain unqualified. showModal and the original +BOX workflow remain outstanding. See the +[HTML inert subtree requirements](https://html.spec.whatwg.org/multipage/interaction.html#inert-subtrees). + +### Native modal ordering and lifetime foundation (2026-09-08) + +The native document now owns ordered modal registrations identified by native +scope/dialog IDs. Registration validates ownership, dialog identity and scope +connectivity; duplicate registration preserves ordering. Removing the top +registration restores the preceding modal in the same scope. The inert query +uses each applicable scope's active modal to block background nodes while +allowing the modal subtree to escape ancestor inert attributes. An inert +attribute on the modal itself still applies, and a nested scope cannot escape +an outer modal that blocks its embedding subtree. Documents without modal +registrations retain the attribute-only inert query path. + +Parser removal, removal of all children and runtime detach handling remove +registrations for the affected subtree. Native deletion removes invalid IDs, +and document clear removes all registrations. Regression coverage verifies +ordering, duplicate registration, restoration, explicit modal inertness, +nested scopes, ownership/type rejection, detach/reattach, deletion and clear. +Both native-engine and GPU runtime suites passed (12.97 seconds combined). + +This is internal state infrastructure only. No showModal method has been +exposed and the original Kestrel BOX workflow remains unqualified. Native +modal paint ordering, out-of-flow layout, hit-test routing into the active +modal, backdrop, focus entry/restoration and event integration must use this +state before modal opening is exposed. Cross-frame visual/input behavior and +full conformance/performance remain unqualified. + +### Native modal paint and hit ordering (2026-09-08) + +Hit testing now enters an active modal before rejecting its blocked scope as +inert. This routes controls inside the modal above ordinary/fixed background +controls, while hits outside its bounds currently return no target. Nested +scope traversal uses the same routing. Explicit inertness of the modal still +rejects input, and removing the top registration restores the previous modal's +hit targets. + +Scene building omits registered modal roots from normal DOM and fixed-layer +passes, then emits them once in registration order. Modal content participates +in the foreground paint layer used with retained canvases. Ancestor visibility +inheritance is retained; ordinary ancestor stacking/clip commands do not wrap +the modal's separate paint pass. Empty modal stacks have an early return on +the hot lookup path. + +Native regression fixtures lay out overlapping controls and verify routing +above a background with z-index 1000000, blocked background hits, explicit +inertness and restoration. Serialized scene checks assert exactly one background +command per modal and background < first modal < second modal in both ordered +canvas and legacy scene modes. Both native suites passed (12.82 seconds). +These are native geometry/command-order assertions, not a presented-pixel proof. +Modal out-of-flow layout/default styles, backdrop, focus entry/restoration, +showModal and complete events remain pending; Kestrel BOX remains unqualified. +Cross-frame clipping and modal pointer-capture behavior remain unqualified. + +### Positioned auto margins for centered dialogs (2026-09-08) + +Both ordinary and grid out-of-flow layout paths now distribute available space +to auto margins when both opposing insets are specified, after size constraints +are applied. Equal auto margins center the box; a single auto start margin +absorbs remaining space after the authored end margin. Oversized boxes preserve +negative vertical margins; horizontal overflow follows the modeled LTR rule. +This supports the positioning behavior needed by dialog defaults without +encoding centering as a Kestrel-specific offset. + +Native layout tests cover fixed boxes in block and grid parents at three +viewport sizes, a single auto margin and oversized geometry. Both native +suites passed (12.81 seconds). The unchanged Kestrel resize probe exited 0 with +WebGPU active and zero logged errors at all four sizes. Its canvas metrics +remain 1110x1006, 1432x1228, 862x1228 and 1112x1228 at DPR 2. In particular, +canvas CSS height still remains 614 after shrinking from the large window: +this regression run does not resolve or qualify the outstanding app resize +layout issue. Modal defaults, focus, showModal and BOX remain pending. RTL, +vertical writing modes and full positioned-layout conformance remain unqualified. +The sizing rules are based on +[CSS positioned layout](https://www.w3.org/TR/css-position-3/#abspos-margins) +and the existing horizontal LTR model. + + +### White UI investigation: theme and hover reduction (2026-09-08) + +Added the local candidate contract `css-root-theme-interaction-recascade.html` +to exercise Kestrel's dark defaults, conditional light root variables, descendant +class and focus changes, and testdriver pointer hover while switching themes. +Both subtests pass against the current macOS native V8 library. The unchanged +pinned upstream `css/selectors/hover-002.html` also passes both subtests. +Machine-readable results are in `evidence/kestrel/theme-recascade-subset.json` +and `evidence/kestrel/upstream-hover-subset.json`. + +This rules out the reduced computed-style theme scenario as a reproduction; it +is not evidence that the reported white painted regions or canvas flicker are +fixed. Full Kestrel painting, GPU image presentation, and resize remain under +investigation. The new contract is local coverage, not an upstream WPT import, +and remains candidate pending broader qualification. + + +### Native wheel zoom diagnostic (2026-09-08) + +The original-document probe now accepts `--zoom-kestrel`. It sends forty wheel +inputs through `NativeSceneSurface.SubmitWheel` at the viewport center, twenty +in each direction, and observes resize notifications and bitmap attribute +mutations without changing the source document or replacing application code. +Observers are disconnected after the run. The macOS run handled all forty events +(default prevented by the application's viewport handler), reported zero app +errors, one initial 556x614 resize notification, and no canvas width/height +mutations. This does not reproduce the hypothesized repeated bitmap reset during +zoom. Evidence: `evidence/kestrel/native-wheel-zoom-diagnostics.json`. + +Build and run with the existing ordinary-view probe commands, adding +`--zoom-kestrel --verify-kestrel`. The startup verifier remains a startup gate; +these diagnostics do not certify camera transformations, frame timing, or +flicker-free presentation. GPU presentation and overlay painting still require +investigation and captured-frame evidence. + + +### Initial full-document computed-style inspection (2026-09-08) + +`--inspect-kestrel-styles` records the real document's root theme, computed +background/text colors, and geometry for the explorer, viewport, canvas, and +explorer tabs. The macOS original-document run reports dark theme, transparent +explorer/canvas backgrounds, dark viewport RGB(18,28,41), and light text. Thus the +initial computed values inspected do not show a light-theme substitution. +`evidence/kestrel/native-initial-style-diagnostics.json` records the result. +This is before interaction, does not inspect every element, and does not prove +painted pixels agree with computed styles. No visual defect is declared fixed. + + +### Fix print-preview styles leaking from script text (2026-09-08) + +The navigation loader scanned raw HTML for ` +

Video cadence in WebScene

+

Compare 60fps motion, a 24fps film trailer and 4K HEVC, all with audio.

+
+
+
+

Blender Foundation · Big Buck Bunny / Sintel / Tears of Steel. HEVC sample provided by Microsoft.

+

Loading…

+ diff --git a/experiments/WebScene.Frameforge/media-demo.html b/experiments/WebScene.Frameforge/media-demo.html new file mode 100644 index 000000000..cc869e94b --- /dev/null +++ b/experiments/WebScene.Frameforge/media-demo.html @@ -0,0 +1,29 @@ + + +Video in WebScene + +

Video in WebScene

+

Native MP4 playback. Resize the window, pause or seek to check composition.

+
+
+
+

Loading…

+ diff --git a/experiments/WebScene.Frameforge/media-verify.html b/experiments/WebScene.Frameforge/media-verify.html new file mode 100644 index 000000000..ef80e3035 --- /dev/null +++ b/experiments/WebScene.Frameforge/media-verify.html @@ -0,0 +1,33 @@ +Frameforge native media verification + +

Frameforge native media verification

Loading original media engine…
+ diff --git a/experiments/WebScene.Frameforge/package-macos.py b/experiments/WebScene.Frameforge/package-macos.py new file mode 100644 index 000000000..67742fac8 --- /dev/null +++ b/experiments/WebScene.Frameforge/package-macos.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +"""Package the Frameforge AOT host and its pinned, unmodified browser assets.""" +import argparse +import json +import pathlib +import plistlib +import shutil +import subprocess + +p = argparse.ArgumentParser() +p.add_argument("--publish", required=True, type=pathlib.Path) +p.add_argument("--source", required=True, type=pathlib.Path) +p.add_argument("--runtime", required=True, type=pathlib.Path) +p.add_argument("--output", required=True, type=pathlib.Path) +a = p.parse_args() +if a.output.exists(): + raise SystemExit("Output already exists; choose a fresh path.") +mac = a.output / "Contents/MacOS" +mac.mkdir(parents=True) +for name in ["media-demo.html", "media-verify.html", "Frameforge", "libAvaloniaNative.dylib", "libSkiaSharp.dylib", "libHarfBuzzSharp.dylib"]: + shutil.copy2(a.publish / name, mac / name) +for name in ["libwebscene_native_engine.dylib", "libwebgpu_dawn.dylib", "icudtl.dat", + "webscene_bootstrap_snapshot.bin", "webscene_bootstrap_snapshot.meta", + "webscene-graphics-runtime.json", "webscene-miniaudio-LICENSE"]: + shutil.copy2(a.runtime / name, mac / name) +assets = mac / "Assets" +assets.mkdir() +for name in ["index.html", "styles.css", "src", "assets", "LICENSE", "THIRD_PARTY_NOTICES.md"]: + source = a.source / name + if source.is_dir(): + shutil.copytree(source, assets / name) + else: + shutil.copy2(source, assets / name) +commit = subprocess.check_output(["git", "-C", str(a.source), "rev-parse", "HEAD"], text=True).strip() +(mac / "BUILD-STATUS.json").write_text(json.dumps({ + "application": "Frameforge", "sourceCommit": commit, + "status": "Native AOT compatibility build; video editing is not qualified.", + "limitations": ["GPU external image/video textures and recording remain unqualified; HTML media and Web Audio have a separate --media-verify contract.", "IndexedDB persistence is unavailable."], + "runtime": "WebScene Native AOT, Avalonia 12.1.1, macOS arm64" +}, indent=2) + "\n") +(a.output / "Contents/Info.plist").write_bytes(plistlib.dumps({ + "CFBundleExecutable": "Frameforge", + "CFBundleIdentifier": "org.webscene.frameforge.aot", + "CFBundleName": "Frameforge", + "CFBundleDisplayName": "Frameforge", + "CFBundlePackageType": "APPL", + "CFBundleVersion": "1", + "NSHighResolutionCapable": True, +})) +# Remove development SDK search paths. Every shipped native dependency must +# resolve from this bundle; keep loader-relative rpaths. +for binary in [mac / "Frameforge", *mac.glob("*.dylib")]: + lines = subprocess.check_output(["otool", "-l", str(binary)], text=True).splitlines() + rpaths = [] + for index, line in enumerate(lines): + if line.strip() == "cmd LC_RPATH": + rpaths.append(lines[index + 2].strip().split(" (offset", 1)[0].removeprefix("path ")) + for rpath in rpaths: + if rpath.startswith("/"): + subprocess.run(["install_name_tool", "-delete_rpath", rpath, str(binary)], check=True) + if "@loader_path" not in rpaths: + subprocess.run(["install_name_tool", "-add_rpath", "@loader_path", str(binary)], check=True) +subprocess.run(["codesign", "--force", "--deep", "--sign", "-", str(a.output)], check=True) +subprocess.run(["ditto", "-c", "-k", "--sequesterRsrc", "--keepParent", + str(a.output), str(a.output.with_suffix(".zip"))], check=True) +print(a.output.with_suffix(".zip")) diff --git a/experiments/WebScene.Frameforge/prepare-video-cadence-samples.py b/experiments/WebScene.Frameforge/prepare-video-cadence-samples.py new file mode 100644 index 000000000..d877771f4 --- /dev/null +++ b/experiments/WebScene.Frameforge/prepare-video-cadence-samples.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +"""Prepare reproducible manual video-cadence fixtures (requires ffmpeg/ffprobe). + +Run with --output PATH, then copy the resulting MP4s to the demo's Assets/assets. +Use media-cadence-demo.html as media-demo.html in the existing AOT media host. +Original movie frames are preserved; only BBB audio is converted from MP3 to AAC. +""" +import argparse +import hashlib +import json +from pathlib import Path +import subprocess +import urllib.request +import zipfile + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument('--output', type=Path, required=True) +args = parser.parse_args() +args.output.mkdir(parents=True, exist_ok=True) +urls = { + 'sintel-trailer-24fps.mp4': 'https://download.blender.org/durian/trailer/sintel_trailer-1080p.mp4', + 'bbb-60fps-original.zip': 'https://download.blender.org/demo/movies/BBB/bbb_sunflower_1080p_60fps_normal.mp4.zip', + 'tears-of-steel-original.mp4': 'https://test.playready.microsoft.com/media/profficialsite/tearsofsteel_4k_60s_24fps.12000kbps.3840x2160.h265-8b.2ch.128kbps.aac.mp4', +} +for name, url in urls.items(): + target = args.output / name + if not target.exists(): + partial = target.with_suffix(target.suffix + '.partial') + urllib.request.urlretrieve(url, partial) + partial.replace(target) +original = args.output / 'bbb_sunflower_1080p_60fps_normal.mp4' +if not original.exists(): + with zipfile.ZipFile(args.output / 'bbb-60fps-original.zip') as archive: + # Extract one known member, not arbitrary archive paths. + with archive.open(original.name) as source, original.open('wb') as target: + import shutil + shutil.copyfileobj(source, target) +for output, source, options in [ + ('big-buck-bunny-60fps.mp4', original, ['-t', '60', '-c:v', 'copy', '-c:a', 'aac', '-b:a', '192k']), + ('tears-of-steel.mp4', args.output / 'tears-of-steel-original.mp4', ['-c', 'copy', '-tag:v', 'hvc1']), +]: + target = args.output / output + if not target.exists(): + subprocess.run(['ffmpeg', '-v', 'error', '-i', str(source), '-map', '0:v:0', '-map', '0:a:0', + *options, '-movflags', '+faststart', str(target)], check=True) +manifest = {'sources': urls, 'attribution': 'Blender Foundation: Big Buck Bunny, Sintel, Tears of Steel', 'files': {}} +for name in ['big-buck-bunny-60fps.mp4', 'sintel-trailer-24fps.mp4', 'tears-of-steel.mp4']: + path = args.output / name + info = json.loads(subprocess.check_output(['ffprobe', '-v', 'error', '-show_streams', '-show_format', '-of', 'json', str(path)])) + digest = hashlib.sha256() + with path.open('rb') as data: + for chunk in iter(lambda: data.read(1024 * 1024), b''): + digest.update(chunk) + manifest['files'][name] = {'sha256': digest.hexdigest(), 'probe': info} +(args.output / 'cadence-samples.json').write_text(json.dumps(manifest, indent=2) + '\n') +print(args.output.resolve()) diff --git a/experiments/WebScene.Frameforge/verify-media.py b/experiments/WebScene.Frameforge/verify-media.py new file mode 100644 index 000000000..28fb7e1c9 --- /dev/null +++ b/experiments/WebScene.Frameforge/verify-media.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Run the AOT media contract and verify actual asset-server byte ranges.""" +import argparse +import json +import os +from pathlib import Path +import subprocess +import urllib.request +import urllib.error + +parser = argparse.ArgumentParser() +parser.add_argument("--executable", type=Path, required=True) +parser.add_argument("--native-library", type=Path, required=True) +parser.add_argument("--assets", type=Path, required=True) +args = parser.parse_args() +env = dict(os.environ, FRAMEFORGE_ASSETS=str(args.assets.resolve()), + WEBSCENE_TEST_NATIVE_LIBRARY=str(args.native_library.resolve())) +process = subprocess.Popen([str(args.executable.resolve()), "--media-verify"], env=env, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) +try: + first = process.stdout.readline().strip() + assert first.startswith("Asset origin: "), first + origin = first.removeprefix("Asset origin: ") + expected = (args.assets / "assets/weightless.wav").read_bytes() + url = origin + "assets/weightless.wav" + with urllib.request.urlopen(urllib.request.Request(url, method="HEAD"), timeout=5) as response: + assert response.status == 200 and response.read() == b"" + assert int(response.headers["Content-Length"]) == len(expected) + assert response.headers["Accept-Ranges"] == "bytes" + assert response.headers["Content-Type"].startswith("audio/") + for value, start, end in [("bytes=0-31", 0, 31), ("bytes=-17", len(expected)-17, len(expected)-1), + (f"bytes={len(expected)-19}-", len(expected)-19, len(expected)-1)]: + with urllib.request.urlopen(urllib.request.Request(url, headers={"Range": value}), timeout=5) as response: + assert response.status == 206 + assert response.headers["Content-Range"] == f"bytes {start}-{end}/{len(expected)}" + assert response.read() == expected[start:end+1] + try: + urllib.request.urlopen(urllib.request.Request(url, headers={"Range": f"bytes={len(expected)}-"}), timeout=5) + raise AssertionError("Out-of-range request succeeded") + except urllib.error.HTTPError as error: + assert error.code == 416 and error.headers["Content-Range"] == f"bytes */{len(expected)}" + output, _ = process.communicate(timeout=90) + print("Asset server: MIME, HEAD, closed/open/suffix ranges, exact bytes and 416 passed") + print(output) + assert "Runtime: " not in output and "Resource: " not in output, output + assert process.returncode == 0, f"AOT media test exit code {process.returncode}" + line = next(line for line in output.splitlines() if line.startswith("Media verification: ")) + result = json.loads(line.removeprefix("Media verification: ")) + if isinstance(result, str): + result = json.loads(result) + assert result["complete"] and result["passed"], result +finally: + if process.poll() is None: + process.kill() + process.wait() diff --git a/experiments/WebScene.GpuHost.Probe/AotSerializationProbe.cs b/experiments/WebScene.GpuHost.Probe/AotSerializationProbe.cs new file mode 100644 index 000000000..3170eba4f --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/AotSerializationProbe.cs @@ -0,0 +1,49 @@ +using System.Text.Json; +using SkiaSharp; +using WebScene.Backends.Avalonia; +using WebScene.Backends.Avalonia.Native; +using WebScene.Core; + +// Runs in the published NativeAOT executable; no window, browser or GPU required. +internal static class AotSerializationProbe +{ + internal static int Run() + { + if (System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported + || JsonSerializer.IsReflectionEnabledByDefault) + throw new InvalidOperationException("This check requires NativeAOT with reflection JSON disabled."); + using var surface = SKSurface.Create(new SKImageInfo(2, 2)); + surface.Canvas.Clear(SKColors.CornflowerBlue); + using var image = surface.Snapshot(); + using var png = image.Encode(SKEncodedImageFormat.Png, 100); + var state = NativeCanvasSceneRenderer.CanvasState.Default; + state.GlobalAlpha = .375; + state.LineDash = [2, 5]; + var checkpoint = new NativeCanvasSceneRenderer.RasterCheckpoint + { + Png = Convert.ToBase64String(png.ToArray()), State = state, + Matrix = [1, 0, .25f, 0, 1, .5f, 0, 0, 1], Path = [[0, 1, 2]] + }; + var bytes = JsonSerializer.SerializeToUtf8Bytes(checkpoint, CanvasCheckpointJsonContext.Default.RasterCheckpoint); + var restored = JsonSerializer.Deserialize(bytes, CanvasCheckpointJsonContext.Default.RasterCheckpoint)!; + using var decoded = SKBitmap.Decode(Convert.FromBase64String(restored.Png)); + if (restored.State.GlobalAlpha != .375 || !restored.State.LineDash.SequenceEqual(new double[] { 2, 5 }) + || restored.State.FillStyle != state.FillStyle || restored.Matrix[2] != .25f + || restored.Path[0][2] != 2 || decoded.GetPixel(0, 0) != SKColors.CornflowerBlue) + throw new InvalidOperationException("AOT canvas checkpoint lost pixels or replay state."); + var directory = Path.Combine(Path.GetTempPath(), "webscene-aot-archive-" + Guid.NewGuid().ToString("N")); + try + { + var address = new Uri("https://example.test/aot.html"); + var original = new WebSceneTextResource("aot", "

AOT ✓

", "aot.html", null) { EntityTag = "v1" }; + var archive = AvaloniaResourceArchive.CreateCapture(directory); + archive.CaptureText(address, WebSceneResourceKind.Markup, default, original); + archive.Flush(); + var replay = AvaloniaResourceArchive.OpenReplay(directory).ReplayText(address, WebSceneResourceKind.Markup, default); + if (replay != original) throw new InvalidOperationException("AOT resource archive round trip failed."); + } + finally { if (Directory.Exists(directory)) Directory.Delete(directory, recursive: true); } + Console.WriteLine("NativeAOT checkpoint pixels/state and resource archive round trips passed."); + return 0; + } +} diff --git a/experiments/WebScene.GpuHost.Probe/CanvasBackingProbe.cs b/experiments/WebScene.GpuHost.Probe/CanvasBackingProbe.cs new file mode 100644 index 000000000..8b6d55da5 --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/CanvasBackingProbe.cs @@ -0,0 +1,245 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Media; +using Avalonia.Rendering.SceneGraph; +using Avalonia.Skia; +using Avalonia.Threading; +using SkiaSharp; +using WebScene.Backends.Avalonia.Native; + +internal sealed class CanvasBackingProbeApp : Application +{ + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var control = new CanvasBackingProbeControl(); + desktop.MainWindow = new Window { Width = 240, Height = 160, + Title = "Canvas2D GPU backing verification", Content = control }; + desktop.MainWindow.Opened += async (_, _) => + { + try { await control.Completed.Task.WaitAsync(TimeSpan.FromSeconds(20)); desktop.Shutdown(0); } + catch (Exception error) { Console.Error.WriteLine(error); desktop.Shutdown(1); } + }; + } + base.OnFrameworkInitializationCompleted(); + } +} + +internal sealed class CanvasBackingProbeControl : Control, ICustomDrawOperation +{ + private NativeCanvasCheckpointFence? _fence; + private SKImage? _fenceImage; + private NativeCanvasCheckpointTransfer? _transfer; + private Task? _workerImage; + private SKImage? _transferReference; + private System.Diagnostics.Stopwatch? _fenceDeadline; + internal readonly TaskCompletionSource Completed = new(TaskCreationOptions.RunContinuationsAsynchronously); + public override void Render(DrawingContext context) => context.Custom(this); + Rect ICustomDrawOperation.Bounds => new(0, 0, Bounds.Width, Bounds.Height); + public bool HitTest(Point point) => false; + public bool Equals(ICustomDrawOperation? other) => false; + public void Dispose() { } + public void Render(ImmediateDrawingContext context) + { + if (Completed.Task.IsCompleted) return; + try + { + var feature = context.TryGetFeature(typeof(ISkiaSharpApiLeaseFeature)) as ISkiaSharpApiLeaseFeature + ?? throw new NotSupportedException("No Skia graphics lease"); + using var lease = feature.Lease(); + if (_workerImage is not null) + { + if (!_workerImage.IsCompleted) + { + Dispatcher.UIThread.Post(InvalidateVisual); + return; + } + using var pixels = SKBitmap.FromImage(_workerImage.GetAwaiter().GetResult()); + using var expected = SKBitmap.FromImage(_transferReference!); + if (pixels.Bytes.Zip(expected.Bytes).Any(pair => Math.Abs(pair.First - pair.Second) > 1)) + throw new InvalidOperationException("Worker checkpoint transfer changed pixels or orientation."); + _workerImage.Result.Dispose(); _transferReference!.Dispose(); + Console.WriteLine("Worker checkpoint transfer preserved RGBA pixels and row orientation."); + Completed.TrySetResult(); + return; + } + if (_transfer is not null) + { + if (!_transfer.IsReady()) + { + if (_fenceDeadline!.Elapsed > TimeSpan.FromSeconds(5)) + throw new TimeoutException("Checkpoint transfer did not complete"); + Dispatcher.UIThread.Post(InvalidateVisual); + return; + } + var transfer = _transfer; _transfer = null; + _workerImage = Task.Run(() => { using (transfer) return transfer.ReadOnWorker(); }); + Dispatcher.UIThread.Post(InvalidateVisual); + return; + } + if (_fence is not null) + { + if (!_fence.IsReady()) + { + if (_fenceDeadline!.Elapsed > TimeSpan.FromSeconds(5)) + throw new TimeoutException("Checkpoint GPU fence did not complete"); + Dispatcher.UIThread.Post(InvalidateVisual); + return; + } + _fence.Dispose(); _fence = null; + using (var raster = _fenceImage!.ToRasterImage(true)) + using (var pixels = SKBitmap.FromImage(raster)) + if (pixels.GetPixel(0, 0) != SKColors.Red) + throw new InvalidOperationException("Deferred checkpoint snapshot changed after later drawing."); + _fenceImage.Dispose(); _fenceImage = null; + Console.WriteLine("Canvas checkpoint GPU fence completed; deferred snapshot preserved pixels."); + using var surface = SKSurface.Create(lease.GrContext, false, + new SKImageInfo(17, 13, SKColorType.Rgba8888, SKAlphaType.Premul))!; + surface.Canvas.Clear(SKColors.Transparent); + using (var paint = new SKPaint { Color = new SKColor(230, 50, 10, 123) }) + surface.Canvas.DrawRect(0, 0, 17, 5, paint); + using (var paint = new SKPaint { Color = SKColors.Blue }) + surface.Canvas.DrawRect(0, 9, 17, 4, paint); + using var snapshot = surface.Snapshot(); + _transferReference = snapshot.ToRasterImage(true); + _transfer = NativeCanvasCheckpointTransfer.Create(snapshot, lease) + ?? throw new NotSupportedException("Checkpoint pixel-buffer transfer unavailable"); + surface.Canvas.Clear(SKColors.Green); + _fenceDeadline!.Restart(); + Dispatcher.UIThread.Post(InvalidateVisual); + return; + } + Verify(lease.GrContext ?? throw new NotSupportedException("No GPU context")); + Verify(lease.GrContext!, checkpoints: true); + if (OperatingSystem.IsWindows()) + { + using var surface = SKSurface.Create(lease.GrContext, false, + new SKImageInfo(16, 16, SKColorType.Rgba8888, SKAlphaType.Premul))!; + surface.Canvas.Clear(SKColors.Red); + _fenceImage = surface.Snapshot(); + _fence = NativeCanvasCheckpointFence.Create(lease) + ?? throw new NotSupportedException("No checkpoint GPU fence"); + surface.Canvas.Clear(SKColors.Blue); + _fenceDeadline = System.Diagnostics.Stopwatch.StartNew(); + Dispatcher.UIThread.Post(InvalidateVisual); + return; + } + Completed.TrySetResult(); + } + catch (Exception error) { Completed.TrySetException(error); } + } + + private static unsafe void Verify(GRContext context, bool checkpoints = false) + { + var incremental = new NativeCanvasSceneRenderer { UseIncrementalCanvasBacking = true }; + var reference = new NativeCanvasSceneRenderer { UseIncrementalCanvasBacking = checkpoints }; + var info = new SKImageInfo(35, 18, SKColorType.Rgba8888, SKAlphaType.Premul); + using var actual = SKSurface.Create(context, false, info)!; + using var expected = SKSurface.Create(context, false, info)!; + using var actualPixels = new SKBitmap(info); + using var expectedPixels = new SKBitmap(info); + var commands = new List(); + var compared = 0; + byte[] checkpointBytes = []; + var checkpointAt = 0; + ulong generation = 1; + try + { + for (var frame = 0; frame < 48; ++frame) + { + // The backing width is 35; this clear stops at 34.5 pixels. + // A mark in that edge must survive with partial coverage. + commands.Add(new() { Kind = 4, V0 = 1.75, V3 = 1.75 }); + commands.Add(new() { Kind = 24, V2 = 34.5 / 1.75, V3 = 18 / 1.75 }); + commands.Add(new() { Kind = 1 }); + commands.Add(new() { Kind = 6, V0 = frame % 4 * 0.25, V1 = 0.5 }); + commands.Add(new() { Kind = 22, V0 = 2, V1 = 2, V2 = 7, V3 = 4 }); + commands.Add(new() { Kind = 2 }); + commands.Add(new() { Kind = 9 }); + commands.Add(new() { Kind = 11, V0 = 1, V1 = 1 }); + commands.Add(new() { Kind = 12, V0 = 18, V1 = 8 }); + commands.Add(new() { Kind = 20 }); + if (frame == 0) commands.Add(new() { Kind = 22, V0 = 19, V2 = 1, V3 = 10 }); + var storage = commands.ToArray(); + fixed (NativeCanvasCommand* data = storage) + { + var layer = new NativeCanvasLayer { NodeId = 7, Flags = 1, Generation = 1, + CommandCount = (uint)storage.Length, Width = 20, Height = 18 / 1.75f, + BitmapWidth = 35, BitmapHeight = 18 }; + var scene = new NativeSceneView { StructSize = (uint)sizeof(NativeSceneView), AbiVersion = 2, + CanvasLayers = &layer, CanvasCommands = data, CanvasCommandCount = (uint)storage.Length, + Header = new SceneHeader { Revision = (ulong)frame + 1, BaseRevision = (ulong)frame, + Flags = frame == 0 ? 1u : 0u, CanvasLayerCount = 1, + ViewportWidth = 20, ViewportHeight = 18 / 1.75f } }; + if (!reference.ApplyDiff(&scene)) throw new Exception("Reference diff rejected"); + if (checkpointBytes.Length == 0) + { + if (!incremental.ApplyDiff(&scene)) throw new Exception("Diff rejected"); + } + else + { + NativeCanvasCommand[] compacted = [new() { Kind=58 },..storage[checkpointAt..]]; + fixed (NativeCanvasCommand* compactData=compacted) + fixed (byte* resourceBytes=checkpointBytes) + { + var resource=new NativeSceneString { ByteLength=(uint)checkpointBytes.Length }; + layer.CommandCount=(uint)compacted.Length;layer.Generation=generation;layer.StringCount=1; + scene.CanvasCommands=compactData;scene.CanvasCommandCount=layer.CommandCount; + scene.Strings=&resource;scene.StringCount=1;scene.StringBytes=resourceBytes;scene.StringByteCount=resource.ByteLength; + if(!incremental.ApplyDiff(&scene))throw new Exception("Checkpoint diff rejected"); + } + } + foreach (var pair in new[] { (incremental, actual), (reference, expected) }) + { + var canvas = pair.Item2.Canvas; + canvas.ResetMatrix(); canvas.Clear(SKColors.Transparent); canvas.Scale(1.75f); + pair.Item1.RenderRetained(canvas, 20, 18 / 1.75f, null, canvasGpuContext: context); + } + } + if(checkpoints && frame==47) + { + // Drop all retained GPU pictures and rebuild from an independent + // raster checkpoint, as required after renderer/context replacement. + var recovery=incremental.EncodeRetainedCanvasCheckpoint(7); + incremental.Reset(); + fixed(byte* bytes=recovery) + { + var resource=new NativeSceneString { ByteLength=(uint)recovery.Length }; + var command=new NativeCanvasCommand { Kind=58 }; + var layer=new NativeCanvasLayer { NodeId=7,Flags=1,Generation=99,CommandCount=1,StringCount=1, + Width=20,Height=18/1.75f,BitmapWidth=35,BitmapHeight=18 }; + var scene=new NativeSceneView { StructSize=(uint)sizeof(NativeSceneView),AbiVersion=2, + CanvasLayers=&layer,CanvasCommands=&command,CanvasCommandCount=1,Strings=&resource, + StringCount=1,StringBytes=bytes,StringByteCount=resource.ByteLength, + Header=new SceneHeader { Revision=99,Flags=1,CanvasLayerCount=1,ViewportWidth=20,ViewportHeight=18/1.75f } }; + if(!incremental.ApplyDiff(&scene))throw new Exception("Checkpoint recovery rejected"); + actual.Canvas.ResetMatrix();actual.Canvas.Clear(SKColors.Transparent);actual.Canvas.Scale(1.75f); + incremental.RenderRetained(actual.Canvas,20,18/1.75f,null,canvasGpuContext:context); + } + } + // Compare actual GPU pixels; normal presentation remains GPU-only. + if (!actual.ReadPixels(info, actualPixels.GetPixels(), actualPixels.RowBytes, 0, 0) + || !expected.ReadPixels(info, expectedPixels.GetPixels(), expectedPixels.RowBytes, 0, 0)) + throw new Exception("Diagnostic readback failed"); + var a = actualPixels.Pixels; var b = expectedPixels.Pixels; + for (var pixel = 0; pixel < a.Length; ++pixel) + if (Math.Abs(a[pixel].Alpha - b[pixel].Alpha) > 1 + || Math.Abs(a[pixel].Red - b[pixel].Red) > 1 + || Math.Abs(a[pixel].Green - b[pixel].Green) > 1 + || Math.Abs(a[pixel].Blue - b[pixel].Blue) > 1) + throw new Exception($"GPU backing differs at frame {frame}, pixel {pixel}: {a[pixel]} / {b[pixel]}"); + compared++; + if(checkpoints && frame%12==11 && frame<47) + { + checkpointBytes=incremental.EncodeRetainedCanvasCheckpoint(7); + checkpointAt=commands.Count;++generation; + } + } + if (incremental.ResumedCanvasCompilations != (checkpoints ? 44 : 47)) throw new Exception("Append-only compilation was not reused"); + Console.WriteLine($"Canvas GPU backing verified: {compared} frames, fractional clears and transforms; checkpoints={checkpoints}."); + } + finally { incremental.Reset(); reference.Reset(); } + } +} diff --git a/experiments/WebScene.GpuHost.Probe/GaneshWindowProbe.cs b/experiments/WebScene.GpuHost.Probe/GaneshWindowProbe.cs new file mode 100644 index 000000000..5dd882295 --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/GaneshWindowProbe.cs @@ -0,0 +1,181 @@ +using System.Runtime.InteropServices; +using System.Text.Json; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Media; +using Avalonia.OpenGL; +using Avalonia.Rendering.SceneGraph; +using Avalonia.Skia; +using Avalonia.Threading; +using SkiaSharp; +using WebScene.Backends.Avalonia.Native; + +// Diagnostic window using the production-source import and retirement components. +// The fixture's producer wait runs before opening the window, never in Render. +internal sealed class GaneshWindowProbeApp : Application +{ + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var control = new GaneshImageControl(); + desktop.MainWindow = new Window { Width = 400, Height = 220, + Title = "WebScene direct Dawn / Ganesh", Content = control }; + desktop.MainWindow.Opened += async (_, _) => + { + var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(16) }; + timer.Tick += (_, _) => control.InvalidateVisual(); + timer.Start(); + var exit = 0; + try { await control.Completed.Task.WaitAsync(TimeSpan.FromSeconds(15)); } + catch (Exception error) { Console.Error.WriteLine(error); exit = 1; } + timer.Stop(); + Console.WriteLine(JsonSerializer.Serialize(new { route = Environment.GetCommandLineArgs().Contains("--ganesh-metal") ? "Dawn-IOSurface-Metal-Ganesh" : "Dawn-IOSurface-CGL-Ganesh", + renderedFrames = control.Frames, imports = control.Imports, + gpuRetirementCompleted = control.Completed.Task.IsCompletedSuccessfully, + explicitTransportCopies = 0, diagnosticReadbacks = control.VerifiedPixels, + physicalPresentationVerified = false, detachedBeforeRetirement = control.DetachedBeforeRetirement })); + desktop.Shutdown(exit); + }; + } + base.OnFrameworkInitializationCompleted(); + } +} +internal sealed class GaneshImageControl : Control, ICustomDrawOperation +{ + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate byte CreateImage(out NativeGpuImageLeaseV3 image); + internal readonly TaskCompletionSource Completed = new(TaskCreationOptions.RunContinuationsAsynchronously); + private readonly NativeCanvasSceneRenderer _renderer = new(); + private NativeGpuImageLeaseV3? _source; + private NativeGpuScenePresenter? _retained; + private NativeGpuSceneImages? _replacement; + internal int Frames, Imports, VerifiedPixels; + internal bool DetachedBeforeRetirement; + private int _retirementStarted; + private readonly bool _detachBeforeRetirement = Environment.GetCommandLineArgs().Contains("--detach-before-retirement"); + private readonly bool _verifyPixels = Environment.GetCommandLineArgs().Contains("--verify-window-pixels"); + public unsafe GaneshImageControl() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY") + ?? throw new InvalidOperationException("WEBSCENE_TEST_NATIVE_LIBRARY is required")); + var fixture = NativeLibrary.Load(Environment.GetEnvironmentVariable("WEBSCENE_TEST_GPU_FIXTURE_LIBRARY") + ?? throw new InvalidOperationException("WEBSCENE_TEST_GPU_FIXTURE_LIBRARY is required")); + var create = Marshal.GetDelegateForFunctionPointer( + NativeLibrary.GetExport(fixture, "webscene_test_create_dawn_iosurface")); + if (create(out var image) == 0) throw new InvalidOperationException("Dawn fixture creation failed"); + _source = image; + var commands = stackalloc SceneCommand[] { + new() { Kind = 1, Width = 400, Height = 220, Rgba = 0x191970ff }, + new() { Kind = 12, X = 30, Y = 25, Width = 320, Height = 140 }, + new() { Kind = 30, Rgba = 192 }, + new() { Kind = 256, X = 10, Y = 10, Width = 360, Height = 175, Rgba = 0 }, + new() { Kind = 31 }, new() { Kind = 13 }, + new() { Kind = 257, NodeId = 7 }, + new() { Kind = 9, X = 310, Y = 140, Width = 30, Height = 10, Rgba = 0xffff00ff } + }; + var canvasCommand = new NativeCanvasCommand { Kind = 22, V2 = 20, V3 = 20 }; + var layer = new NativeCanvasLayer { NodeId = 7, Flags = 1, CommandCount = 1, + X = 250, Y = 100, Width = 20, Height = 20, BitmapWidth = 20, BitmapHeight = 20, Generation = 1 }; + var scene = new NativeSceneView { Commands = commands, CanvasLayers = &layer, + CanvasCommands = &canvasCommand, CanvasCommandCount = 1, + Header = new SceneHeader { Revision = 1, Flags = 3, CommandCount = 8, + CanvasLayerCount = 1, ViewportWidth = 400, ViewportHeight = 220 } }; + if (!_renderer.ApplyDiff(&scene, orderedGpuImages: true)) throw new InvalidOperationException("Ordered scene rejected"); + } + public override void Render(DrawingContext context) => context.Custom(this); + Rect ICustomDrawOperation.Bounds => new(0, 0, Bounds.Width, Bounds.Height); + public bool HitTest(Point point) => false; + public bool Equals(ICustomDrawOperation? other) => false; + public void Dispose() { } // Retained owner spans draw-operation replacement; GPU fence retires it. + public void Render(ImmediateDrawingContext context) + { + if (Completed.Task.IsCompleted || Volatile.Read(ref _retirementStarted) != 0) return; + try + { + var feature = context.TryGetFeature(typeof(ISkiaSharpApiLeaseFeature)) as ISkiaSharpApiLeaseFeature + ?? throw new NotSupportedException("Host has no Skia API lease"); + using var lease = feature.Lease(); + if (_retained is null) + { + var status = NativeGpuSceneImages.Retain(new[] { _source! }, out var first); + if (status == NativeSceneAcquireStatus.Backpressure) return; + if (status != NativeSceneAcquireStatus.Success || first is null) throw new InvalidOperationException($"Scene image capture failed: {status}"); + _retained = new NativeGpuScenePresenter(); + if (!_retained.TryReplace(first)) throw new InvalidOperationException("Initial scene rejected"); + status = NativeGpuSceneImages.Retain(new[] { _source! }, out _replacement); + if (status != NativeSceneAcquireStatus.Success || _replacement is null) throw new InvalidOperationException("Replacement capture failed"); + _source!.Dispose(); _source = null; + } + if (_retained.IsStopping) + { + if (_retained.TryComplete(lease)) { _renderer.Reset(); Completed.TrySetResult(); } + return; + } + if (Frames == 16 && _replacement is not null) + { + if (!_retained.TryReplace(_replacement)) return; + _replacement = null; + } + if (!_retained.TryPrepare(lease)) return; + Imports = _retained.ImportedCount; + var canvas = lease.SkCanvas; + _renderer.RenderRetained(canvas, 400, 220, null, + (index, destination) => + { + if (index != 0) throw new InvalidOperationException("Unknown scene GPU image slot"); + _retained.Draw(lease, index, destination); + }); + if (_verifyPixels && (Frames == 0 || Frames == 16)) + { + var surface = lease.SkSurface ?? throw new NotSupportedException("Host has no diagnostic surface"); + // Read four destination pixels, solely when explicitly requested. + using var pixel = new SKBitmap(new SKImageInfo(1, 1, SKColorType.Rgba8888, SKAlphaType.Premul)); + foreach (var sample in new[] { (X: 60f, Y: 50f, R: 45, G: 83, B: 143), + (X: 15f, Y: 15f, R: 25, G: 25, B: 112), + (X: 320f, Y: 145f, R: 255, G: 255, B: 0), + (X: 255f, Y: 105f, R: 0, G: 0, B: 0) }) + { + var point = canvas.TotalMatrix.MapPoint(sample.X, sample.Y); + if (!surface.ReadPixels(pixel.Info, pixel.GetPixels(), pixel.RowBytes, (int)point.X, (int)point.Y)) + throw new InvalidOperationException("Host diagnostic pixel read failed"); + var actual = pixel.GetPixel(0, 0); + if (Math.Abs(actual.Red - sample.R) > 1 || Math.Abs(actual.Green - sample.G) > 1 || + Math.Abs(actual.Blue - sample.B) > 1 || actual.Alpha != 255) + throw new InvalidOperationException($"Host pixel mismatch at {point}: {actual}"); + ++VerifiedPixels; + } + } + if (++Frames == 32) + { + _retained.BeginShutdown(); + if (_detachBeforeRetirement) + { + Interlocked.Exchange(ref _retirementStarted, 1); + var retiring = _retained; + var renderingThread = Environment.CurrentManagedThreadId; + Dispatcher.UIThread.Post(() => + { + if (TopLevel.GetTopLevel(this) is not Window window) + { Completed.TrySetException(new InvalidOperationException("Probe window unavailable for detach")); return; } + window.Content = null; + DetachedBeforeRetirement = true; + _ = Task.Run(async () => + { + try + { + if (Environment.CurrentManagedThreadId == renderingThread) throw new InvalidOperationException("Detached probe must retire on a different thread"); + await NativeGpuRetirement.Start(retiring).WaitAsync(TimeSpan.FromSeconds(5)); + if (NativeGpuRetirement.RetainedCount != 0) throw new InvalidOperationException("Completed retirement retained its owner"); + _renderer.Reset();Completed.TrySetResult(); + } + catch (Exception error) { Completed.TrySetException(error); } + }); + }); + } + } + } + catch (Exception error) { Completed.TrySetException(error); } + } +} diff --git a/experiments/WebScene.GpuHost.Probe/KestrelDragWorkloadValidator.cs b/experiments/WebScene.GpuHost.Probe/KestrelDragWorkloadValidator.cs new file mode 100644 index 000000000..3b05c11e4 --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/KestrelDragWorkloadValidator.cs @@ -0,0 +1,51 @@ +internal static class KestrelDragWorkloadValidator +{ + internal static (double X, double Y) PanOffset(int step, bool circular) + { + var phase = (step - 1) % 80 + 1; + if (circular) return (40 * (1 - Math.Cos(phase * Math.PI / 40)), 40 * Math.Sin(phase * Math.PI / 40)); + var distance = phase <= 40 ? phase * 4 : (80 - phase) * 4; + return (distance, distance / 4.0); + } + + internal static void Validate(string diagnostics, double x, double y, bool sidebar = false, int panCycles = 1, bool circular = false) + { + if (panCycles is < 1 or > 120) throw new ArgumentOutOfRangeException(nameof(panCycles)); + using var parsed = System.Text.Json.JsonDocument.Parse(diagnostics); + var root = parsed.RootElement; + var events = root.GetProperty("events").EnumerateArray().ToArray(); + if (root.GetProperty("errors").GetInt32() != 0 || root.GetProperty("panning").GetBoolean() + || events.Length < 3 || events[0].GetProperty("type").GetString() != "pointerdown" + || events[^1].GetProperty("type").GetString() != "pointerup") + throw new InvalidOperationException("Invalid Kestrel drag workload: missing gesture boundary or application error."); + static bool At(System.Text.Json.JsonElement e, double px, double py) => + Math.Abs(e.GetProperty("x").GetDouble() - px) < 0.1 + && Math.Abs(e.GetProperty("y").GetDouble() - py) < 0.1; + if (!At(events[0], x, y) || !At(events[^1], sidebar ? x + 120 : x, y) + || events[0].GetProperty("button").GetInt32() != (sidebar ? 0 : 2) + || events[^1].GetProperty("button").GetInt32() != (sidebar ? 0 : 2)) + throw new InvalidOperationException("Invalid Kestrel drag workload: unexpected gesture boundary."); + // Coalescing may omit moves, but delivered moves must remain an ordered + // subsequence of the injected path. This detects extra routed input; + // it does not prove the provenance of identical-coordinate input. + var nextStep = 1; + foreach (var e in events.Skip(1).Take(events.Length - 2)) + { + if (e.GetProperty("type").GetString() != "pointermove" + || e.GetProperty("buttons").GetInt32() != (sidebar ? 1 : 2)) + throw new InvalidOperationException("Invalid Kestrel drag workload: unexpected pointer event."); + var matched = false; + while (nextStep <= (sidebar ? 60 : 80 * panCycles)) + { + var step = nextStep++; + var offset = sidebar ? (X: step * 2.0, Y: 0.0) : PanOffset(step, circular); + if (!At(e, x + offset.X, y + offset.Y)) continue; + matched = true; + break; + } + if (!matched) + throw new InvalidOperationException("Invalid Kestrel drag workload: moves differ from injected path; discard performance comparison."); + } + } + +} diff --git a/experiments/WebScene.GpuHost.Probe/MetalHostProbe.cs b/experiments/WebScene.GpuHost.Probe/MetalHostProbe.cs new file mode 100644 index 000000000..7c3653008 --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/MetalHostProbe.cs @@ -0,0 +1,172 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Media; +using Avalonia.Rendering.SceneGraph; +using Avalonia.Skia; +using System.Text.Json; +using System.Runtime.InteropServices; +using WebScene.Backends.Avalonia.Native; + +internal sealed class MetalHostProbeApp : Application +{ + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var probe = new MetalHostProbeControl(); + desktop.MainWindow = new Window { Width=320, Height=180, + Title="WebScene Metal host qualification", Content=probe }; + desktop.MainWindow.Opened += async (_, _) => { + var exit=0; + try { Console.WriteLine(await probe.Completed.Task.WaitAsync(TimeSpan.FromSeconds(20))); } + catch(Exception error) { Console.Error.WriteLine(error); exit=1; } + desktop.Shutdown(exit); + }; + } + base.OnFrameworkInitializationCompleted(); + } +} +internal sealed class MetalHostProbeControl : Control, ICustomDrawOperation +{ + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint="objc_getClass")] + private static extern IntPtr GetClass(string name); + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint="sel_registerName")] + private static extern IntPtr Selector(string name); + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint="objc_msgSend")] + private static extern IntPtr TextureDescriptor(IntPtr receiver,IntPtr selector,ulong format,ulong width,ulong height,[MarshalAs(UnmanagedType.I1)] bool mipmapped); + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint="objc_msgSend")] + private static extern IntPtr SendObject(IntPtr receiver,IntPtr selector,IntPtr argument); + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint="objc_msgSend")] + private static extern void SendVoid(IntPtr receiver,IntPtr selector); + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint="objc_msgSend")] + private static extern IntPtr SendNoArg(IntPtr receiver,IntPtr selector); + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint="objc_msgSend")] + private static extern void WaitEvent(IntPtr receiver,IntPtr selector,IntPtr sharedEvent,ulong value); + [DllImport("/usr/lib/libobjc.A.dylib", EntryPoint="objc_msgSend")] + private static extern void SignalEvent(IntPtr receiver,IntPtr selector,ulong value); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate byte CreateFixture(out NativeGpuImageLeaseV3 image); + private NativeGpuImageLeaseV3? _fixture; + public MetalHostProbeControl() + { + if (!Environment.GetCommandLineArgs().Contains("--metal-fixture")) return; + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY") + ?? throw new InvalidOperationException("Native library is required")); + var library=NativeLibrary.Load(Environment.GetEnvironmentVariable("WEBSCENE_TEST_GPU_FIXTURE_LIBRARY") + ?? throw new InvalidOperationException("Fixture library is required")); + var create=Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library,"webscene_test_create_dawn_iosurface")); + if(create(out var fixture)==0) throw new InvalidOperationException("Dawn fixture failed"); + _fixture=fixture; + } + public TaskCompletionSource Completed { get; } = new(TaskCreationOptions.RunContinuationsAsynchronously); + public override void Render(DrawingContext context) => context.Custom(this); + Rect ICustomDrawOperation.Bounds => new(0,0,Bounds.Width,Bounds.Height); + public bool HitTest(Point point) => false; + public bool Equals(ICustomDrawOperation? other) => ReferenceEquals(this,other); + public void Dispose() { } + public void Render(ImmediateDrawingContext context) + { + if(Completed.Task.IsCompleted) return; + try { + var feature=context.TryGetFeature(typeof(ISkiaSharpApiLeaseFeature)) as ISkiaSharpApiLeaseFeature + ?? throw new NotSupportedException("No Skia drawing lease"); + using var lease=feature.Lease(); + string hostName; + IntPtr metalDevice, metalQueue; + using (var platform=lease.TryLeasePlatformGraphicsApi() + ?? throw new NotSupportedException("No platform graphics lease")) + { + var host=platform.Context; + // Avalonia marks this interface PrivateApi and omits it from reference assemblies. + // Probe the pinned runtime interface while its owning lease is held. + var metal=host.GetType().GetInterface("Avalonia.Metal.IMetalDevice") + ?? throw new NotSupportedException("Host is not Metal: "+host.GetType().FullName); + var device=(IntPtr)metal.GetProperty("Device")!.GetValue(host)!; + var queue=(IntPtr)metal.GetProperty("CommandQueue")!.GetValue(host)!; + if(device==IntPtr.Zero || queue==IntPtr.Zero || lease.GrContext is null) + throw new InvalidOperationException("Incomplete Metal/Skia host"); + var descriptor=TextureDescriptor(GetClass("MTLTextureDescriptor"), + Selector("texture2DDescriptorWithPixelFormat:width:height:mipmapped:"),80,16,16,false); + var texture=SendObject(device,Selector("newTextureWithDescriptor:"),descriptor); + if(texture==IntPtr.Zero) throw new InvalidOperationException("Metal texture allocation failed"); + try { + using var wrapped=NativeMetalBackendTexture.Create(16,16,texture); + if(!wrapped.IsValid || wrapped.Width!=16 || wrapped.Height!=16) + throw new InvalidOperationException("Metal backend wrapper invalid"); + } finally { SendVoid(texture,Selector("release")); } + metalDevice=device; metalQueue=queue; + hostName=host.GetType().FullName!; + } + if (_fixture is not null) VerifyFixturePixels(lease,metalDevice,metalQueue); + lease.SkCanvas.Clear(SkiaSharp.SKColors.Teal); + Completed.TrySetResult(JsonSerializer.Serialize(new { + host=hostName, metalDeviceAvailable=true, + metalQueueAvailable=true, skiaGpuContextAvailable=true, metalTextureWrapperVerified=true, + dawnIOSurfaceImportVerified=Environment.GetCommandLineArgs().Contains("--metal-fixture"), + metalSampledPixelsVerified=Environment.GetCommandLineArgs().Contains("--metal-fixture"), + metalConsumerFenceVerified=Environment.GetCommandLineArgs().Contains("--metal-fixture"), + diagnosticReadbacks=Environment.GetCommandLineArgs().Contains("--metal-fixture")?1:0, + producerInteropVerified=false, physicalPresentationVerified=false })); + } catch(Exception error) { Completed.TrySetException(error); } + } + private void VerifyFixturePixels(ISkiaSharpApiLease lease,IntPtr device,IntPtr queue) + { + if(NativeGpuImageConsumerV3.Acquire(_fixture!,out var consumer)!=NativeSceneAcquireStatus.Success || consumer is null) + throw new InvalidOperationException("Fixture consumer acquisition failed"); + NativeMetalIOSurfaceTexture? imported=null; + try { + using(var platform=lease.TryLeasePlatformGraphicsApi() + ?? throw new NotSupportedException("No Metal platform lease")) + imported=NativeMetalIOSurfaceTexture.Import(device,consumer); + using var backend=NativeMetalBackendTexture.Create(imported.Width,imported.Height,imported.Handle); + using var image=SkiaSharp.SKImage.FromTexture(lease.GrContext,backend,SkiaSharp.GRSurfaceOrigin.TopLeft, + SkiaSharp.SKColorType.Bgra8888,SkiaSharp.SKAlphaType.Premul) + ?? throw new InvalidOperationException("Metal image wrapping failed"); + var info=new SkiaSharp.SKImageInfo(imported.Width,imported.Height,SkiaSharp.SKColorType.Bgra8888,SkiaSharp.SKAlphaType.Premul); + using var target=SkiaSharp.SKSurface.Create(lease.GrContext,false,info) + ?? throw new InvalidOperationException("Metal diagnostic surface creation failed"); + target.Canvas.Clear(SkiaSharp.SKColors.Magenta); + target.Canvas.DrawImage(image,0,0); + using var pixels=new SkiaSharp.SKBitmap(info); + if(!target.ReadPixels(info,pixels.GetPixels(),pixels.RowBytes,0,0)) + throw new InvalidOperationException("Metal diagnostic readback failed"); + for(var y=0;y1 || Math.Abs(color.Green-102)>1 || Math.Abs(color.Blue-153)>1 || color.Alpha!=255) + throw new InvalidOperationException($"Metal sampled pixel mismatch at {x},{y}: {color}"); + } + } finally { + lease.GrContext!.Flush(true,false); + NativeMetalConsumerFence fence; + using(var platform=lease.TryLeasePlatformGraphicsApi() + ?? throw new NotSupportedException("No Metal platform lease for retirement")) + { + var delayed=SendNoArg(device,Selector("newSharedEvent")); + if(delayed==IntPtr.Zero) throw new InvalidOperationException("Diagnostic event allocation failed"); + try { + var work=SendNoArg(queue,Selector("commandBuffer")); + WaitEvent(work,Selector("encodeWaitForEvent:value:"),delayed,1); + SendVoid(work,Selector("commit")); + fence=NativeMetalConsumerFence.Insert(queue); + System.Threading.Thread.Sleep(30); + if(fence.TryComplete()) throw new InvalidOperationException("Retirement overtook delayed GPU work"); + } finally { + SignalEvent(delayed,Selector("setSignaledValue:"),1); + SendVoid(delayed,Selector("release")); + } + } + // Bounded diagnostic polling only. Production must revisit on a later + // compositor opportunity while retaining the image and consumer. + var deadline=System.Diagnostics.Stopwatch.StartNew(); + while(!fence.TryComplete()) { + if(deadline.Elapsed>TimeSpan.FromSeconds(5)) + throw new TimeoutException("Metal consumer retirement remained pending; ownership retained"); + System.Threading.Thread.Sleep(1); + } + if(!fence.TryComplete()) throw new InvalidOperationException("Completed fence regressed"); + imported?.Dispose(); consumer.Complete(); _fixture!.Dispose(); _fixture=null; + } + } + +} diff --git a/experiments/WebScene.GpuHost.Probe/Program.cs b/experiments/WebScene.GpuHost.Probe/Program.cs new file mode 100644 index 000000000..236ec9cbe --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/Program.cs @@ -0,0 +1,249 @@ +using System.Text.Json; +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Controls; +using Avalonia.OpenGL; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Rendering.Composition; + +internal static class Program +{ + static Program() + { + if (Environment.GetEnvironmentVariable("WEBSCENE_TRACE_AOT_EXCEPTIONS") == "1") + AppDomain.CurrentDomain.FirstChanceException += (_, e) => Console.Error.WriteLine(e.Exception); + } + + [DllImport("webscene_graphite_host_probe", EntryPoint="webscene_graphite_host_probe")] + internal static extern int RenderGraphite(uint texture, uint serial); + [DllImport("webscene_graphite_host_probe", EntryPoint="webscene_graphite_host_poll")] + internal static extern int PollGraphite(int drain); + [DllImport("webscene_graphite_host_probe", EntryPoint="webscene_graphite_host_initializations")] + internal static extern uint GraphiteInitializations(); + [DllImport("webscene_graphite_host_probe", EntryPoint="webscene_graphite_host_context_initializations")] + internal static extern uint GraphiteContextInitializations(); + [DllImport("webscene_graphite_host_probe", EntryPoint="webscene_graphite_host_output_allocations")] + internal static extern uint GraphiteOutputAllocations(); + [DllImport("webscene_graphite_host_probe", EntryPoint="webscene_graphite_host_verify_marker")] + internal static extern int VerifyGraphiteMarker(uint texture, uint serial); + [DllImport("webscene_graphite_host_probe", EntryPoint="webscene_graphite_host_canvas_allocations")] + internal static extern uint GraphiteCanvasAllocations(); + [DllImport("webscene_graphite_host_probe", EntryPoint="webscene_graphite_host_canvas_busy")] + internal static extern uint GraphiteCanvasBusy(); + [DllImport("webscene_graphite_host_probe", EntryPoint="webscene_graphite_host_shutdown")] + internal static extern int ShutdownGraphite(); + [STAThread] + public static int Main(string[] args) => args.Contains("--aot-serialization-probe") + ? AotSerializationProbe.Run() + : args.Contains("--canvas-backing-probe") + ? AppBuilder.Configure().UsePlatformDetect().StartWithClassicDesktopLifetime(args) + : args.Contains("--metal-host") + ? AppBuilder.Configure().UsePlatformDetect() + .With(new AvaloniaNativePlatformOptions { RenderingMode = new[] { AvaloniaNativeRenderingMode.Metal } }) + .StartWithClassicDesktopLifetime(args) + : args.Contains("--ganesh-metal") + ? AppBuilder.Configure().UsePlatformDetect() + .With(new AvaloniaNativePlatformOptions { RenderingMode = new[] { AvaloniaNativeRenderingMode.Metal } }) + .StartWithClassicDesktopLifetime(args) + : args.Contains("--webgpu-metal") + ? AppBuilder.Configure().UsePlatformDetect() + .With(new AvaloniaNativePlatformOptions { RenderingMode = new[] { AvaloniaNativeRenderingMode.Metal } }) + .StartWithClassicDesktopLifetime(args) + : args.Contains("--webgpu-opengl") + ? AppBuilder.Configure().UsePlatformDetect() + .With(new AvaloniaNativePlatformOptions { RenderingMode = new[] { AvaloniaNativeRenderingMode.OpenGl } }) + .StartWithClassicDesktopLifetime(args) + : args.Contains("--webgpu-vsync") + ? WindowsVSyncProbe.Configure(AppBuilder.Configure().UsePlatformDetect()) + .StartWithClassicDesktopLifetime(args) + : args.Contains("--webgpu-document") + ? AppBuilder.Configure().UsePlatformDetect().StartWithClassicDesktopLifetime(args) + : args.Contains("--ganesh-window") + ? AppBuilder.Configure().UsePlatformDetect().StartWithClassicDesktopLifetime(args) + : AppBuilder.Configure().UsePlatformDetect().StartWithClassicDesktopLifetime(args); +} +internal sealed class ProbeApp : Application +{ + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var window = new Window { Width = 320, Height = 120, Title = "WebScene GPU host capability probe" }; + desktop.MainWindow = window; + window.Opened += async (_, _) => + { + var exit = 0; + try + { + var visual = ElementComposition.GetElementVisual(window) + ?? throw new InvalidOperationException("No composition visual"); + var interop = await visual.Compositor.TryGetCompositionGpuInterop().AsTask().WaitAsync(TimeSpan.FromSeconds(30)); + var sharing = await visual.Compositor.TryGetRenderInterfaceFeature(typeof(IOpenGlTextureSharingRenderInterfaceContextFeature)) + as IOpenGlTextureSharingRenderInterfaceContextFeature; + bool graphiteSource = Environment.GetCommandLineArgs().Contains("--graphite"); + bool sharedTextureUpdateCompleted = false; + bool visualCommitCompleted = false; + int graphiteSubmissionsCompleted = 0; + int hostUpdatesCompleted = 0; + int nativeShutdownsCompleted = 0; + uint canvasTextureAllocations=0, outputTextureAllocations=0, graphiteContextInitializations=0, dawnDeviceInitializations=0; + int diagnosticMarkersVerified = 0; + bool verifyMarkers = Environment.GetCommandLineArgs().Contains("--verify-markers"); + if (interop is not null && sharing?.CanCreateSharedContext == true) + { + using var glContext = sharing.CreateSharedContext() + ?? throw new InvalidOperationException("Shared context creation failed"); + using var texture = sharing.CreateSharedTextureForComposition(glContext, graphiteSource ? new PixelSize(17,4) : new PixelSize(32,32)); + using (glContext.EnsureCurrent()) + { + var gl = glContext.GlInterface; + var framebuffer = gl.GenFramebuffer(); + try + { + gl.BindFramebuffer(GlConsts.GL_FRAMEBUFFER, framebuffer); + gl.FramebufferTexture2D(GlConsts.GL_FRAMEBUFFER, GlConsts.GL_COLOR_ATTACHMENT0, + GlConsts.GL_TEXTURE_2D, texture.TextureId, 0); + if (gl.CheckFramebufferStatus(GlConsts.GL_FRAMEBUFFER) != GlConsts.GL_FRAMEBUFFER_COMPLETE) + throw new InvalidOperationException("Shared framebuffer incomplete"); + gl.Viewport(0,0,32,32); + gl.ClearColor(0.2f,0.4f,0.6f,1); + gl.Clear(GlConsts.GL_COLOR_BUFFER_BIT); gl.Flush(); + } + finally { gl.BindFramebuffer(GlConsts.GL_FRAMEBUFFER,0); gl.DeleteFramebuffer(framebuffer); } + } + using var surface = visual.Compositor.CreateDrawingSurface(); + await using var imported = interop.ImportImage(texture); + await imported.ImportCompleted.WaitAsync(TimeSpan.FromSeconds(30)); + var surfaceVisual = visual.Compositor.CreateSurfaceVisual(); + surfaceVisual.Size = new System.Numerics.Vector2(128,128); + surfaceVisual.Surface = surface; + ElementComposition.SetElementChildVisual(window, surfaceVisual); + try + { + if (graphiteSource) { + for (int submission=0; submission<64; submission++) { + using (glContext.EnsureCurrent()) { + if (Program.RenderGraphite((uint)texture.TextureId,(uint)submission+1) != 0) + throw new InvalidOperationException("Dawn/Graphite host texture verification failed"); + if (Program.RenderGraphite((uint)texture.TextureId,(uint)submission+1) == 0) + throw new InvalidOperationException("Overlapping host submission was accepted"); + if (Program.ShutdownGraphite() == 0) + throw new InvalidOperationException("Shutdown accepted outstanding native work"); + } + var deadline = DateTime.UtcNow.AddSeconds(30); + while (true) { + int completion; + using (glContext.EnsureCurrent()) completion = Program.PollGraphite(0); + if (completion == 1) { + using (glContext.EnsureCurrent()) { + if (Program.PollGraphite(0) != -1) + throw new InvalidOperationException("Duplicate completion was accepted"); + } + graphiteSubmissionsCompleted++; + break; + } + if (completion < 0 || DateTime.UtcNow >= deadline) { + using (glContext.EnsureCurrent()) Program.PollGraphite(1); + throw new InvalidOperationException("GL completion failed or timed out"); + } + await Task.Delay(1); + } + if (verifyMarkers) { + using (glContext.EnsureCurrent()) { + if (Program.VerifyGraphiteMarker((uint)texture.TextureId,(uint)submission+1) != 0) + throw new InvalidOperationException("Stale or incorrect host texture marker"); + if (Program.VerifyGraphiteMarker((uint)texture.TextureId,(uint)submission+2) == 0) + throw new InvalidOperationException("Incorrect expected marker was accepted"); + } + diagnosticMarkersVerified++; + } + // Await host consumption before overwriting the borrowed GL texture. + await surface.UpdateAsync(imported).WaitAsync(TimeSpan.FromSeconds(30)); + hostUpdatesCompleted++; + await visual.Compositor.RequestCommitAsync().WaitAsync(TimeSpan.FromSeconds(30)); + if (submission == 31) { + using (glContext.EnsureCurrent()) { + if (Program.ShutdownGraphite() != 0 || Program.GraphiteInitializations() != 0 || + Program.GraphiteCanvasAllocations() != 0 || Program.GraphiteOutputAllocations() != 0) + throw new InvalidOperationException("Quiescent native shutdown failed"); + } + nativeShutdownsCompleted++; + } + } + if (Program.GraphiteInitializations() != 1) + throw new InvalidOperationException("Dawn device was recreated between submissions"); + if (Program.GraphiteContextInitializations() != 1) + throw new InvalidOperationException("Graphite context was recreated between submissions"); + if (Program.GraphiteOutputAllocations() != 1) + throw new InvalidOperationException("Output texture was recreated between submissions"); + if (Program.GraphiteCanvasAllocations() != 2 || Program.GraphiteCanvasBusy() != 0) + throw new InvalidOperationException("Canvas pool did not reuse and retire its two source textures"); + } + if (!graphiteSource) { + await surface.UpdateAsync(imported).WaitAsync(TimeSpan.FromSeconds(30)); + hostUpdatesCompleted++; + } + sharedTextureUpdateCompleted = hostUpdatesCompleted == (graphiteSource ? 64 : 1); + await visual.Compositor.RequestCommitAsync().WaitAsync(TimeSpan.FromSeconds(30)); + visualCommitCompleted = true; + if (Environment.GetCommandLineArgs().Contains("--inspect")) + { + Console.WriteLine("Shared surface attached; inspection window is open for 30 seconds."); + await Task.Delay(TimeSpan.FromSeconds(30)); + } + } + finally + { + ElementComposition.SetElementChildVisual(window, null); + await visual.Compositor.RequestCommitAsync().WaitAsync(TimeSpan.FromSeconds(30)); + surfaceVisual.Surface = null; + if (graphiteSource) { + canvasTextureAllocations=Program.GraphiteCanvasAllocations(); + outputTextureAllocations=Program.GraphiteOutputAllocations(); + graphiteContextInitializations=Program.GraphiteContextInitializations(); + dawnDeviceInitializations=Program.GraphiteInitializations(); + using (glContext.EnsureCurrent()) { + if (Program.ShutdownGraphite() != 0) + throw new InvalidOperationException("Final native shutdown refused outstanding work"); + } + nativeShutdownsCompleted++; + } + } + } + Console.WriteLine(JsonSerializer.Serialize(new + { + schemaVersion = 1, + probe = "avalonia-gpu-host", + avalonia = typeof(Application).Assembly.GetName().Version?.ToString(), + status = interop is null ? "unavailable" : "available", + imageTypes = interop?.SupportedImageHandleTypes.Select(t => new + { type = t, synchronization = interop.GetSynchronizationCapabilities(t).ToString() }).ToArray(), + semaphoreTypes = interop?.SupportedSemaphoreTypes.ToArray(), + isLost = interop?.IsLost, + canCreateSharedOpenGlContext = sharing?.CanCreateSharedContext ?? false, + graphiteSource, + graphiteSubmissionsCompleted, + hostUpdatesCompleted, + diagnosticMarkersVerified, + nativeShutdownsCompleted, + nativeCounterScope = "last-runtime-cycle", + canvasTextureAllocations, + outputTextureAllocations, + graphiteContextInitializations, + dawnDeviceInitializations, + sharedTextureUpdateCompleted, + visualCommitCompleted, + presentationVerified = false + })); + if (interop is null) exit = 77; + } + catch (Exception error) + { + Console.Error.WriteLine(error); exit = 1; + } + Avalonia.Threading.Dispatcher.UIThread.Post(() => desktop.Shutdown(exit)); + }; + } + base.OnFrameworkInitializationCompleted(); + } +} diff --git a/experiments/WebScene.GpuHost.Probe/README.md b/experiments/WebScene.GpuHost.Probe/README.md new file mode 100644 index 000000000..ba4a78062 --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/README.md @@ -0,0 +1,73 @@ +# Avalonia GPU host capability probe + +Run `dotnet run --project experiments/WebScene.GpuHost.Probe` from the repository +root in a graphical desktop session. The probe briefly opens a window, queries the +actual compositor's GPU interop service, prints JSON and closes. Exit 0 means the +query completed with an interop object, not that any import or presentation works. +Exit 77 means no interop object; exit 1 means the query failed. + +Observed on Apple M4, macOS 26.6.2, default Avalonia 11.3.4 platform selection: +interop available, device not lost, imageTypes empty, semaphoreTypes empty. +Therefore no external-handle route can be selected from this capability result. +Shared-context APIs or another explicitly supported host backend require separate +investigation. This does not contradict the standalone Dawn/Graphite GPU test. + +The probe additionally queries Avalonia's public OpenGL texture-sharing feature. +On this host `canCreateSharedOpenGlContext` is true, despite the empty external +handle lists. This is the next candidate to exercise; no shared texture has yet +been drawn or presented by this capability probe. + +The probe now exercises the shared-context route when available: create a 32x32 +composition texture, attach it to an FBO, check completeness, clear via OpenGL, +flush, import and await CompositionDrawingSurface.UpdateAsync. On the M4 host it +reports `sharedTextureUpdateCompleted=true` and exits successfully. Imported image +disposal is awaited before texture/context teardown. The surface is not attached +to a visual and its snapshot pixels are not inspected, so `presentationVerified` +remains false. This is not yet a Dawn-to-host bridge test. + +The updated surface is now attached to the window as a 128x128 composition surface +visual. The probe awaits RequestCommitAsync before detaching, then awaits a second +commit before teardown. M4 reports `visualCommitCompleted=true`. This proves the +visual changes were applied on the render thread, not that pixels were displayed; +`presentationVerified` remains false until an independent pixel observation exists. + +Use `-- --inspect` to keep the attached visual alive for 30 seconds. A targeted +macOS window capture during this mode visibly confirms the blue shared-GL surface +on the left and untouched white background on the right. Evidence is stored at +`docs/graphics/evidence/avalonia-host/shared-gl-window.png`. This is visual evidence +for GL-to-Avalonia display, not a colorimetric pixel test or Dawn-to-host integration. +The runtime JSON keeps presentationVerified=false because the program itself does +not perform the independent window observation. + +## Kestrel on Avalonia 12 (sample only) + +WebScene's default build and published packages remain on Avalonia 11.3.4. +For the Kestrel host, opt into Avalonia 12.1.1 and its matching Skia dependencies +across the project-reference graph: + +```sh +dotnet publish experiments/WebScene.GpuHost.Probe -c Release -r osx-arm64 \ + -p:PublishAot=true -p:WebSceneAvalonia12Sample=true \ + -o artifacts/kestrel-aot-avalonia12/publish +artifacts/kestrel-aot-avalonia12/publish/WebScene.GpuHost.Probe --aot-serialization-probe +WEBSCENE_TEST_NATIVE_LIBRARY="$PWD/artifacts/checkpoint-native/libwebscene_native_engine.dylib" \ + artifacts/kestrel-aot-avalonia12/publish/WebScene.GpuHost.Probe \ + --webgpu-metal --kestrel tests/GraphicsCompatibility/fixtures/Kestrel-CAD.zip \ + --continuous-resize-kestrel --resize-kestrel --verify-kestrel +``` + +The native engine path must refer to a graphics-enabled macOS build with its +Dawn runtime dependencies available. Omit the workload/verification switches +to leave Kestrel open for manual use. + +Avalonia 12 includes the upstream Metal transactional presentation fix +([PR 21588](https://github.com/AvaloniaUI/Avalonia/pull/21588)). +This configuration does not patch Avalonia Native or change WebScene's +GPU mailbox/texture sharing. Packaging with this opt-in property is rejected. +The private Avalonia 11 Windows compositor-clock diagnostic is unavailable +in this configuration; the sample otherwise uses Avalonia's default render loop. + +The continuous resize trace uses generated JSON metadata so it works in Native +AOT. Its geometry/intermediate-frame assertions do not establish physical +presentation cadence or native mouse-drag smoothness. Also check live edge/corner +resizing, sidebar resizing, pan/zoom, and display-scale transitions manually. diff --git a/experiments/WebScene.GpuHost.Probe/ResizeTrace.cs b/experiments/WebScene.GpuHost.Probe/ResizeTrace.cs new file mode 100644 index 000000000..3d9dde3d4 --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/ResizeTrace.cs @@ -0,0 +1,35 @@ +using System.Text.Json.Serialization; +using WebScene.Backends.Avalonia.Native; + +// Typed diagnostics keep the resize workload usable in a reflection-free AOT host. +internal sealed record ResizeSizeSample(long timestamp, double width, double height, + long requestedAt = 0, string? reason = null); +internal sealed record ResizeTrace(long traceStarted, long inputEnded, long timestampFrequency, + List submittedSizes, List nativeWindowResizes, + List surfaceSizeChanges, NativeResizeSubmissionSample[] nativeSubmissions, + string diagnostics, NativeWebScenePerformanceSnapshot baseline, NativeWebScenePerformanceSnapshot after, + NativeScenePublicationSample[] publications, NativeSceneRenderSample[] renderedScenes, + NativeSceneSchedulingSample[] scheduling, + bool physicalPresentationVerified = false, bool nativeUserDragVerified = false); +internal sealed record PointerMoveTrace(ulong sequence, long submittedAt, int step, double x, double y); +internal sealed record PerformanceTrace(NativeWebScenePerformanceSnapshot baseline, + NativeWebScenePerformanceSnapshot after, NativeWebSceneWorkDelta delta, double elapsedMilliseconds = 0); +internal sealed record PanTrace(long timestampFrequency, int panInputHz, string panPath, + bool highResolutionInput, int panCycles, long traceStarted, List submittedMoves, + NativeScenePublicationSample[] publications, NativeSceneRenderSample[] renderedScenes, + NativeSceneSchedulingSample[] scheduling, long[] drawCallbackCompletions, + bool physicalPresentationVerified = false); +internal sealed record SidebarTrace(long traceStarted, long timestampFrequency, bool properties, + double originalWidth, double width, System.Text.Json.JsonElement initialGeometry, + NativeWebScenePerformanceSnapshot baseline, NativeWebScenePerformanceSnapshot after, + NativeWebSceneWorkDelta delta, List submittedMoves, + NativeScenePublicationSample[] publications, NativeSceneRenderSample[] renderedScenes, + NativeSceneSchedulingSample[] scheduling, bool physicalPresentationVerified = false); +internal sealed record DrawTrace(long frequency, long[] timestamps, bool physicalPresentationVerified = false); +[JsonSerializable(typeof(PerformanceTrace))] +[JsonSerializable(typeof(PanTrace))] +[JsonSerializable(typeof(SidebarTrace))] +[JsonSerializable(typeof(DrawTrace))] +[JsonSourceGenerationOptions(IncludeFields = true)] +[JsonSerializable(typeof(ResizeTrace))] +internal partial class ResizeTraceJsonContext : JsonSerializerContext; diff --git a/experiments/WebScene.GpuHost.Probe/WebGpuDocumentProbe.cs b/experiments/WebScene.GpuHost.Probe/WebGpuDocumentProbe.cs new file mode 100644 index 000000000..98c96c606 --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/WebGpuDocumentProbe.cs @@ -0,0 +1,576 @@ +using System.IO.Compression; +using System.Security.Cryptography; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using WebScene.Backends.Avalonia.Native; + +// Exercises the ordinary document/view/composition path, without a native fixture. +internal sealed class WebGpuDocumentProbeApp : Application +{ + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var path = Path.Combine(Path.GetTempPath(), $"webscene-webgpu-{Guid.NewGuid():N}.html"); + File.WriteAllText(path, """ + + + + """.Replace("__FRAME_LIMIT__", Environment.GetCommandLineArgs().Contains("--stress-webgpu") ? "120" : "1")); + var arguments = Environment.GetCommandLineArgs(); + var kestrelIndex = Array.IndexOf(arguments, "--kestrel"); + var kestrel = kestrelIndex >= 0; + if (kestrel) + { + if (kestrelIndex + 1 >= arguments.Length) throw new ArgumentException("--kestrel requires the original archive path"); + using var archive = ZipFile.OpenRead(arguments[kestrelIndex + 1]); + var entry = archive.GetEntry("Kestrel-CAD/Kestrel-CAD.html") + ?? throw new InvalidDataException("Original standalone Kestrel document is missing"); + using (var input = entry.Open()) + using (var output = File.Create(path)) input.CopyTo(output); + Console.WriteLine("Kestrel original document SHA256: " + Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(path))).ToLowerInvariant()); + } + var uri = new Uri(path).AbsoluteUri; + var view = new NativeWebSceneView(true, url => url == uri || url == path); + long documentExceptions = 0; + view.JavaScriptException += error => + { + Interlocked.Increment(ref documentExceptions); + Console.Error.WriteLine("WebGPU document JavaScript exception: " + $"{error.Message}\n{error.Stack}"); + }; + view.RuntimeFailed += error => Console.Error.WriteLine( + "WebGPU document runtime failure: " + $"{error.Message}\n{error.Stack}"); + if (arguments.Contains("--verify-webgpu")) view.EnablePerformanceMonitoring(); + desktop.MainWindow = new Window + { + Width = ReadDocumentDimension(arguments, "--document-width", kestrel ? 1280 : 400), + Height = ReadDocumentDimension(arguments, "--document-height", kestrel ? 800 : 240), + Title = kestrel ? arguments.Contains("--webgpu-vsync") + ? Environment.GetEnvironmentVariable("WEBSCENE_SINGLE_SCENE_PER_FRAME") == "1" + ? "Kestrel in WebScene — steady frame pacing" + : Environment.GetEnvironmentVariable("WEBSCENE_INCREMENTAL_CANVAS_GPU") == "1" + ? "Kestrel in WebScene — bounded canvas history" + : "Kestrel in WebScene — vsync + input pacing" + : "Kestrel in WebScene" : "WebScene WebGPU document", Content = view + }; + desktop.MainWindow.Opened += async (_, _) => + { + try + { + await view.LoadAsync(uri, Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY") + ?? throw new InvalidOperationException("Set WEBSCENE_TEST_NATIVE_LIBRARY")); + Console.WriteLine("WebGPU document loaded through NativeWebSceneView."); + if (kestrel) + { + await Task.Delay(3000); + Console.WriteLine(await view.EvaluateTextAsync("({ready:document.documentElement.dataset.ready,backend:document.getElementById('engine-label')?.textContent,history:document.getElementById('command-history')?.textContent,errors:document.querySelectorAll('#command-history .history-error').length,gpu:!!navigator.gpu})")); + if (arguments.Contains("--inspect-kestrel-styles")) + { + Console.WriteLine("Kestrel style diagnostics: " + await view.EvaluateTextAsync(""" + (()=>({theme:document.documentElement.getAttribute('data-theme'),nodes: + Array.from(document.querySelectorAll('#explorer-list,#explorer-list *,#viewport,#scene,#layers-tab,#objects-tab')).slice(0,40).map(n=>{ + const s=getComputedStyle(n),r=n.getBoundingClientRect(); + return {id:n.id,tag:n.tagName,classes:n.className,background:s.backgroundColor,color:s.color,display:s.display,rect:[r.x,r.y,r.width,r.height]}; + })}))() + """)); + } + if (arguments.Contains("--mesh-kestrel")) + { + var baseline = int.Parse(await view.EvaluateTextAsync("Number(document.getElementById('object-count').textContent)")); + await view.EvaluateTextAsync("(()=>{const c=document.getElementById('command-input');c.value='';c.focus();})()"); + var surface = (NativeSceneSurface)view.Content!; + if (surface.SubmitText("BOX") == 0 || surface.SubmitKey(7, 13) == 0 || surface.SubmitKey(8, 13) == 0) + throw new InvalidOperationException("Kestrel box command was not accepted."); + await Task.Delay(750); + if (await view.EvaluateTextAsync("document.getElementById('modal').open===true") != "true") + { + Console.WriteLine("Kestrel modal failure: " + await view.EvaluateTextAsync("({open:document.getElementById('modal').open,history:document.getElementById('command-history').textContent})")); + throw new InvalidOperationException("Kestrel's original primitive dialog did not open."); + } + await view.EvaluateTextAsync("(()=>{const f=document.getElementById('modal-form');for(const [name,value] of Object.entries({x:0,y:0,z:0,width:2000,depth:1500,height:2500}))f.querySelector('[name='+name+']').value=String(value);f.dispatchEvent(new Event('submit',{bubbles:true,cancelable:true}));})()"); + await Task.Delay(1000); + Console.WriteLine("Kestrel mesh: " + await view.EvaluateTextAsync("({objects:Number(document.getElementById('object-count').textContent),backend:document.getElementById('engine-label').textContent,errors:document.querySelectorAll('#command-history .history-error').length,modalError:document.getElementById('modal-error').textContent,history:document.getElementById('command-history').textContent})")); + var count = int.Parse(await view.EvaluateTextAsync("Number(document.getElementById('object-count').textContent)")); + if (count != baseline + 1) + throw new InvalidOperationException($"Kestrel box creation expected {baseline + 1} objects, got {count}."); + } + if (arguments.Contains("--edit-kestrel")) + { + var baseline = int.Parse(await view.EvaluateTextAsync("Number(document.getElementById('object-count').textContent)")); + foreach (var step in new[] { ("LINE 0,0 1000,1000 ENTER", 1), ("UNDO", 0), ("REDO", 1), ("UNDO", 0) }) + { + await view.EvaluateTextAsync("(()=>{const c=document.getElementById('command-input');c.value='';c.focus();})()"); + var surface = (NativeSceneSurface)view.Content!; + if (surface.SubmitText(step.Item1) == 0 || surface.SubmitKey(7, 13) == 0 || surface.SubmitKey(8, 13) == 0) + throw new InvalidOperationException("Kestrel native command input was not accepted."); + await Task.Delay(750); + Console.WriteLine("Kestrel edit: " + await view.EvaluateTextAsync("({objects:Number(document.getElementById('object-count').textContent),backend:document.getElementById('engine-label').textContent,errors:document.querySelectorAll('#command-history .history-error').length,history:document.getElementById('command-history').textContent})")); + var count = int.Parse(await view.EvaluateTextAsync("Number(document.getElementById('object-count').textContent)")); + if (count != baseline + step.Item2) + throw new InvalidOperationException($"Kestrel command {step.Item1} expected {baseline + step.Item2} objects, got {count}."); + } + } + if (arguments.Contains("--exercise-kestrel")) + { + foreach (var step in new[] { ("iso", "shaded-edges"), ("front", "shaded"), ("iso", "xray"), ("top", "wireframe"), ("iso", "shaded-edges") }) + { + await view.EvaluateTextAsync($"(()=>{{const v=document.getElementById('view-select'),s=document.getElementById('style-select');v.value='{step.Item1}';v.dispatchEvent(new Event('change',{{bubbles:true}}));s.value='{step.Item2}';s.dispatchEvent(new Event('change',{{bubbles:true}}));}})()"); + await Task.Delay(750); + Console.WriteLine("Kestrel view: " + await view.EvaluateTextAsync("({view:document.getElementById('view-select').value,style:document.getElementById('style-select').value,backend:document.getElementById('engine-label').textContent,errors:document.querySelectorAll('#command-history .history-error').length,history:document.getElementById('command-history').textContent})")); + } + } + if (arguments.Contains("--zoom-kestrel")) + { + // Observe the unchanged application's own resize behavior. Drive wheel + // through the host input queue, not synthetic JS events or camera calls. + await view.EvaluateTextAsync(""" + (()=>{ + const viewport=document.getElementById('viewport'),canvas=document.getElementById('scene'); + const probe=globalThis.kestrelZoomProbe={resizes:[],bitmapMutations:[],wheelEvents:0,handledWheelEvents:0}; + probe.wheel=e=>{++probe.wheelEvents;if(e.defaultPrevented)++probe.handledWheelEvents;}; + document.addEventListener('wheel',probe.wheel); + probe.resize=new ResizeObserver(entries=>{for(const e of entries)probe.resizes.push([e.contentRect.width,e.contentRect.height]);}); + probe.mutations=new MutationObserver(entries=>{for(const e of entries)probe.bitmapMutations.push([e.attributeName,canvas.width,canvas.height]);}); + probe.resize.observe(viewport); + probe.mutations.observe(canvas,{attributes:true,attributeFilter:['width','height']}); + })() + """); + try + { + await Task.Delay(250); // Let the required initial resize notification settle. + using var center = System.Text.Json.JsonDocument.Parse(await view.EvaluateTextAsync("(()=>{const r=document.getElementById('viewport').getBoundingClientRect();return [r.x+r.width/2,r.y+r.height/2]})()")); + var x = center.RootElement[0].GetDouble(); + var y = center.RootElement[1].GetDouble(); + var surface = (NativeSceneSurface)view.Content!; + var performanceBaseline = view.CapturePerformanceSnapshot(); + surface.SubmitPointerMove(x, y); + for (var step = 0; step < 40; ++step) + { + if (surface.SubmitWheel(x, y, step < 20 ? -25 : 25) == 0) + throw new InvalidOperationException("Kestrel zoom input was rejected."); + await Task.Delay(30); + } + await Task.Delay(500); + var performanceAfter = view.CapturePerformanceSnapshot(); + Console.WriteLine("Kestrel zoom performance: " + System.Text.Json.JsonSerializer.Serialize(new PerformanceTrace(performanceBaseline, performanceAfter, performanceAfter.Since(performanceBaseline)), ResizeTraceJsonContext.Default.PerformanceTrace)); + Console.WriteLine("Kestrel zoom diagnostics: " + await view.EvaluateTextAsync("(()=>{const p=globalThis.kestrelZoomProbe,c=document.getElementById('scene');return {wheelEvents:p.wheelEvents,handledWheelEvents:p.handledWheelEvents,resizes:p.resizes,bitmapMutations:p.bitmapMutations,canvas:[c.width,c.height],backend:document.getElementById('engine-label').textContent,errors:document.querySelectorAll('#command-history .history-error').length}})()")); + } + finally + { + await view.EvaluateTextAsync("(()=>{const p=globalThis.kestrelZoomProbe;p.resize.disconnect();p.mutations.disconnect();document.removeEventListener('wheel',p.wheel);delete globalThis.kestrelZoomProbe;})()"); + } + } + if (arguments.Contains("--pan-kestrel")) + { + if (arguments.Contains("--trace-kestrel-methods")) + await view.EvaluateTextAsync(""" + (()=>{ + const p=globalThis.kestrelMethodProbe={samples:{},restore:[]}; + const wrap=(owner,name)=>{const original=owner[name];if(typeof original!=='function')return; + p.restore.push(()=>owner[name]=original); + owner[name]=function(...args){const start=performance.now();try{return original.apply(this,args);} + finally{(p.samples[name]??=[]).push(performance.now()-start);}};}; + for(const name of ['pointerMove','eventPoint','snapPoint','ensureIndex','invalidate'])wrap(Kestrel.App.prototype,name); + wrap(Element.prototype,'closest');wrap(Element.prototype,'getBoundingClientRect'); + })() + """); + await view.EvaluateTextAsync(""" + (()=>{ + const p=globalThis.kestrelPanProbe={events:[],captures:[],frames:[],widths:[]}; + p.originalRaf=window.requestAnimationFrame; + p.raf=callback=>p.originalRaf.call(window,function(timestamp){ + const start=performance.now(); + try{return callback.call(this,timestamp);} + finally{p.frames.push({timestamp,start,duration:performance.now()-start});} + }); + window.requestAnimationFrame=p.raf; + p.resize=new ResizeObserver(es=>p.widths.push(es[0].contentRect.width));p.resize.observe(document.getElementById(globalThis.propertiesProbe?'properties':'explorer'));p.event=e=>p.events.push({type:e.type,x:e.clientX,y:e.clientY,button:e.button,buttons:e.buttons,time:performance.now(),panning:document.getElementById('viewport').classList.contains('panning')}); + p.capture=e=>p.captures.push(e.type); + for(const name of ['pointerdown','pointermove','pointerup'])document.addEventListener(name,p.event); + for(const name of ['gotpointercapture','lostpointercapture'])document.addEventListener(name,p.capture); + })() + """); + var surface = (NativeSceneSurface)view.Content!; + double x = 0, y = 0; + var pressed = false; + try + { + await Task.Delay(250); + using var center = System.Text.Json.JsonDocument.Parse(await view.EvaluateTextAsync("(()=>{const r=document.getElementById('viewport').getBoundingClientRect();return [r.x+r.width/2,r.y+r.height/2]})()")); + x = center.RootElement[0].GetDouble(); + y = center.RootElement[1].GetDouble(); + await view.EvaluateTextAsync("globalThis.kestrelPanProbe.events=[];globalThis.kestrelPanProbe.frames=[]"); + var baseline = arguments.Contains("--pan-no-telemetry") ? null : view.CapturePerformanceSnapshot(); + var traceStarted = System.Diagnostics.Stopwatch.GetTimestamp(); + var started = System.Diagnostics.Stopwatch.StartNew(); + var panCycles = ReadDocumentDimension(arguments, "--pan-cycles", arguments.Contains("--pan-long") ? 6 : 1); + var submittedMoves = new List(80 * panCycles); + var panInputHz = arguments.Contains("--pan-input-120hz") ? 120 : 60; + var circularPan = arguments.Contains("--pan-circular"); + using var inputPacer = arguments.Contains("--pan-high-resolution-input") + ? new WindowsInputPacer() : null; + if (surface.SubmitPointerButton(2, x, y, 2, true) == 0) + throw new InvalidOperationException("Kestrel pan press was rejected."); + pressed = true; + for (var step = 1; step <= 80 * panCycles; ++step) + { + var offset = KestrelDragWorkloadValidator.PanOffset(step, circularPan); + // Kind 1 routes a move with the right-button bit through the native queue. + var submittedAt = System.Diagnostics.Stopwatch.GetTimestamp(); + var sequence = surface.SubmitPointerButton(1, x + offset.X, y + offset.Y, 2, true); + if (sequence == 0) + throw new InvalidOperationException("Kestrel pan move was rejected."); + submittedMoves.Add(new PointerMoveTrace(sequence, submittedAt, step, x + offset.X, y + offset.Y)); + // Include submission work in the 60Hz input budget, + // as the continuous-resize workload already does. + var deadline = traceStarted + step * System.Diagnostics.Stopwatch.Frequency / panInputHz; + var remaining = deadline - System.Diagnostics.Stopwatch.GetTimestamp(); + if (inputPacer is not null) + await inputPacer.WaitUntilAsync(deadline); + else if (remaining > 0) + await Task.Delay(TimeSpan.FromSeconds((double)remaining / System.Diagnostics.Stopwatch.Frequency)); + else + await Task.Yield(); + } + if (surface.SubmitPointerButton(3, x, y, 2, false) == 0) + throw new InvalidOperationException("Kestrel pan release was rejected."); + pressed = false; + await Task.Delay(500); + if (baseline is not null) + { + var after = view.CapturePerformanceSnapshot(); + if (arguments.Contains("--verify-checkpoint-fence") + && after.RendererMemory.CanvasCheckpointDeferredReadbacks < 2) + throw new InvalidOperationException("Deferred checkpoint readback was not exercised."); + if (arguments.Contains("--verify-checkpoint-transfer") + && after.RendererMemory.CanvasCheckpointWorkerReadbacks < 2) + throw new InvalidOperationException("Worker checkpoint transfer was not exercised."); + if (arguments.Contains("--verify-canvas-history")) + { + var memory = after.RendererMemory; + if (memory.CanvasCheckpointSubmissions < 2 + || memory.MaximumRetainedCanvasCommands >= 2 * NativeCanvasSceneRenderer.CanvasCheckpointInterval) + throw new InvalidOperationException("Canvas checkpoint history did not stay bounded."); + Console.WriteLine($"Canvas history bounded: checkpoints={memory.CanvasCheckpointSubmissions}, peakCommands={memory.MaximumRetainedCanvasCommands}, retainedCommands={memory.RetainedCommandCount}."); + } + Console.WriteLine("Kestrel pan performance: " + System.Text.Json.JsonSerializer.Serialize(new PerformanceTrace(baseline, after, after.Since(baseline), started.Elapsed.TotalMilliseconds), ResizeTraceJsonContext.Default.PerformanceTrace)); + } + if (arguments.Contains("--trace-kestrel-methods")) + Console.WriteLine("Kestrel method samples: " + await view.EvaluateTextAsync("globalThis.kestrelMethodProbe.samples")); + var panDiagnostics = await view.EvaluateTextAsync("(()=>{const p=globalThis.kestrelPanProbe;return {events:p.events,captures:p.captures,frames:p.frames,panning:document.getElementById('viewport').classList.contains('panning'),backend:document.getElementById('engine-label').textContent,errors:document.querySelectorAll('#command-history .history-error').length}})()"); + Console.WriteLine("Kestrel pan diagnostics: " + panDiagnostics); + Console.WriteLine("Kestrel pan composition timeline: " + System.Text.Json.JsonSerializer.Serialize(new PanTrace( + System.Diagnostics.Stopwatch.Frequency, panInputHz, + circularPan ? "circle" : "out-and-back", inputPacer is not null, + panCycles, traceStarted, submittedMoves, + surface.PublishedScenes.Where(sample => sample.Timestamp >= traceStarted).ToArray(), + surface.RenderedScenes.Where(sample => sample.Timestamp >= traceStarted).ToArray(), + surface.SchedulingSamples.Where(sample => sample.Timestamp >= traceStarted).ToArray(), + surface.PresentationTimestamps.Where(timestamp => timestamp >= traceStarted).ToArray()), + ResizeTraceJsonContext.Default.PanTrace)); + KestrelDragWorkloadValidator.Validate(panDiagnostics, x, y, panCycles: panCycles, circular: circularPan); + Console.WriteLine("Kestrel pan workload validated (physical presentation remains unqualified)."); + } + finally + { + if (pressed) surface.SubmitPointerButton(3, x, y, 2, false); + await view.EvaluateTextAsync("(()=>{const p=globalThis.kestrelPanProbe;p.resize.disconnect();if(window.requestAnimationFrame===p.raf)window.requestAnimationFrame=p.originalRaf;for(const n of ['pointerdown','pointermove','pointerup'])document.removeEventListener(n,p.event);for(const n of ['gotpointercapture','lostpointercapture'])document.removeEventListener(n,p.capture);delete globalThis.kestrelPanProbe;})()"); + if (arguments.Contains("--trace-kestrel-methods")) + await view.EvaluateTextAsync("(()=>{for(const restore of kestrelMethodProbe.restore)restore();delete globalThis.kestrelMethodProbe;})()"); + } + } + if (arguments.Contains("--sidebar-kestrel") || arguments.Contains("--properties-kestrel")) + { + var surface = (NativeSceneSurface)view.Content!; + var properties = arguments.Contains("--properties-kestrel"); + var repeated = properties || arguments.Contains("--sidebar-cycles"); + await view.EvaluateTextAsync(properties ? "globalThis.propertiesProbe=true" : "globalThis.propertiesProbe=false"); + using var setup = System.Text.Json.JsonDocument.Parse(await view.EvaluateTextAsync("(()=>{const r=document.querySelector('.'+(globalThis.propertiesProbe?'right':'left')+'-resizer').getBoundingClientRect();return {x:r.x+r.width/2,y:r.y+r.height/2,width:document.getElementById(globalThis.propertiesProbe?'properties':'explorer').offsetWidth,viewport:[innerWidth,innerHeight],dpr:devicePixelRatio,canvas:[document.getElementById('scene').width,document.getElementById('scene').height]}})()")); + var x = setup.RootElement.GetProperty("x").GetDouble(); + var y = setup.RootElement.GetProperty("y").GetDouble(); + var originalWidth = setup.RootElement.GetProperty("width").GetDouble(); + if (originalWidth <= 0) throw new InvalidOperationException("The requested sidebar is hidden at the current viewport width; discard this resize workload."); + await view.EvaluateTextAsync("(()=>{const p=globalThis.kestrelSidebarProbe={events:[],widths:[]};p.resize=new ResizeObserver(es=>p.widths.push(es[0].contentRect.width));p.resize.observe(document.getElementById(globalThis.propertiesProbe?'properties':'explorer'));p.event=e=>p.events.push({type:e.type,x:e.clientX,y:e.clientY,button:e.button,buttons:e.buttons});for(const n of ['pointerdown','pointermove','pointerup'])document.addEventListener(n,p.event);})()"); + var submittedMoves = new List(60); + var baseline = view.CapturePerformanceSnapshot(); + var traceStarted = System.Diagnostics.Stopwatch.GetTimestamp(); + using var pacer = OperatingSystem.IsWindows() ? new WindowsInputPacer() : null; + var pressed = false; + try + { + if (surface.SubmitPointerButton(2, x, y, 0, true) == 0) + throw new InvalidOperationException("Sidebar press rejected."); + pressed = true; + for (var step = 1; step <= (repeated ? 600 : 60); ++step) + { + var offset = repeated ? (properties ? -120.0 : 120.0) * (1 - Math.Abs((step % 120) - 60) / 60.0) : step * 2.0; + var submittedAt = System.Diagnostics.Stopwatch.GetTimestamp(); + var sequence = surface.SubmitPointerButton(1, x + offset, y, 0, true); + if (sequence == 0) + throw new InvalidOperationException("Sidebar move rejected."); + submittedMoves.Add(new PointerMoveTrace(sequence, submittedAt, step, x + offset, y)); + var deadline = traceStarted + step * System.Diagnostics.Stopwatch.Frequency / 60; + if (pacer is not null) await pacer.WaitUntilAsync(deadline); + else { var remaining = deadline - System.Diagnostics.Stopwatch.GetTimestamp(); if (remaining > 0) await Task.Delay(TimeSpan.FromSeconds((double)remaining / System.Diagnostics.Stopwatch.Frequency)); } + } + if (surface.SubmitPointerButton(3, x + (repeated ? 0 : 120), y, 0, false) == 0) + throw new InvalidOperationException("Sidebar release rejected."); + pressed = false; + await Task.Delay(500); + var width = double.Parse(await view.EvaluateTextAsync("document.getElementById(globalThis.propertiesProbe?'properties':'explorer').offsetWidth"), System.Globalization.CultureInfo.InvariantCulture); + var after = view.CapturePerformanceSnapshot(); + var diagnostics = await view.EvaluateTextAsync("(()=>{return {events:globalThis.kestrelSidebarProbe.events,widths:globalThis.kestrelSidebarProbe.widths,panning:document.getElementById('viewport').classList.contains('panning'),errors:document.querySelectorAll('#command-history .history-error').length}})()"); + Console.WriteLine("Kestrel sidebar diagnostics: " + diagnostics); + Console.WriteLine("Kestrel sidebar timeline: " + System.Text.Json.JsonSerializer.Serialize(new SidebarTrace( + traceStarted, System.Diagnostics.Stopwatch.Frequency, properties, originalWidth, + width, setup.RootElement, baseline, after, after.Since(baseline), submittedMoves, + surface.PublishedScenes.Where(sample => sample.Timestamp >= traceStarted).ToArray(), + surface.RenderedScenes.Where(sample => sample.Timestamp >= traceStarted).ToArray(), + surface.SchedulingSamples.Where(sample => sample.Timestamp >= traceStarted).ToArray()), + ResizeTraceJsonContext.Default.SidebarTrace)); + // Preserve failure diagnostics before rejecting an interrupted gesture. + if (Math.Abs(width - (repeated ? originalWidth : Math.Clamp(originalWidth + 120, 170, 390))) > 1) + throw new InvalidOperationException($"Sidebar drag failed: width {originalWidth} became {width}."); + if (!repeated) KestrelDragWorkloadValidator.Validate(diagnostics, x, y, sidebar: true); + else { using var result = System.Text.Json.JsonDocument.Parse(diagnostics); if (result.RootElement.GetProperty("errors").GetInt32() != 0 || result.RootElement.GetProperty("panning").GetBoolean()) throw new InvalidOperationException("Properties workload reported an application error or panning."); if (result.RootElement.GetProperty("widths").EnumerateArray().Max(e => e.GetDouble()) - result.RootElement.GetProperty("widths").EnumerateArray().Min(e => e.GetDouble()) < 50) throw new InvalidOperationException("Properties divider did not resize."); } + Console.WriteLine("Kestrel sidebar workload validated (physical presentation remains unqualified)."); + } + finally + { + if (pressed) surface.SubmitPointerButton(3, x + (repeated ? 0 : 120), y, 0, false); + await view.EvaluateTextAsync("(()=>{const p=globalThis.kestrelSidebarProbe;p.resize.disconnect();for(const n of ['pointerdown','pointermove','pointerup'])document.removeEventListener(n,p.event);delete globalThis.kestrelSidebarProbe;})()"); + } + } + if (arguments.Contains("--continuous-resize-kestrel")) + { + var surface = (NativeSceneSurface)view.Content!; + var baseline = view.CapturePerformanceSnapshot(); + var initialWidth = desktop.MainWindow.Width; + var initialHeight = desktop.MainWindow.Height; + var traceStarted = System.Diagnostics.Stopwatch.GetTimestamp(); + var submittedSizes = new List(80); + var nativeWindowResizes = new List(); + var surfaceSizeChanges = new List(); + EventHandler onNativeResize = (_, e) => { + if (nativeWindowResizes.Count < 4096) nativeWindowResizes.Add(new ResizeSizeSample(System.Diagnostics.Stopwatch.GetTimestamp(), e.ClientSize.Width, e.ClientSize.Height, reason: e.Reason.ToString())); + }; + EventHandler onSurfaceResize = (_, e) => { + if (surfaceSizeChanges.Count < 4096) surfaceSizeChanges.Add(new ResizeSizeSample(System.Diagnostics.Stopwatch.GetTimestamp(), e.NewSize.Width, e.NewSize.Height)); + }; + desktop.MainWindow.Resized += onNativeResize; + surface.SizeChanged += onSurfaceResize; + long inputEnded; + try + { + for (var step = 1; step <= 80; ++step) + { + var offset = step <= 40 ? step : 80 - step; + var width = initialWidth + offset * 3; + var height = initialHeight + offset * 2; + var requestedAt = System.Diagnostics.Stopwatch.GetTimestamp(); + desktop.MainWindow.Width = width; + desktop.MainWindow.Height = height; + var timestamp = System.Diagnostics.Stopwatch.GetTimestamp(); + submittedSizes.Add(new ResizeSizeSample(timestamp, width, height, requestedAt)); + // Include synchronous resize work in the 60Hz budget. + // Adding a fresh 16ms sleep after it halves input cadence + // when the native setter already takes one display slot. + var deadline = traceStarted + step * System.Diagnostics.Stopwatch.Frequency / 60; + var remaining = deadline - System.Diagnostics.Stopwatch.GetTimestamp(); + if (remaining > 0) + await Task.Delay(TimeSpan.FromSeconds((double)remaining / System.Diagnostics.Stopwatch.Frequency)); + else + await Task.Yield(); // Keep late runs responsive; never busy-wait. + + } + inputEnded = System.Diagnostics.Stopwatch.GetTimestamp(); + await Task.Delay(750); + } + finally + { + desktop.MainWindow.Resized -= onNativeResize; + surface.SizeChanged -= onSurfaceResize; + } + var diagnostics = await view.EvaluateTextAsync("(()=>{const c=document.getElementById('scene'),r=c.getBoundingClientRect();const ancestors=[];for(let n=c.parentElement;n;n=n.parentElement){const b=n.getBoundingClientRect(),s=getComputedStyle(n);ancestors.push({id:n.id,tag:n.tagName,rect:[b.x,b.y,b.width,b.height],height:s.height,minHeight:s.minHeight,display:s.display,flex:s.flex,gridTemplateRows:s.gridTemplateRows});}return {window:[innerWidth,innerHeight],canvas:[c.width,c.height],css:[r.width,r.height],ancestors,dpr:devicePixelRatio,backend:document.getElementById('engine-label').textContent,errors:document.querySelectorAll('#command-history .history-error').length}})()"); + var published = surface.PublishedScenes.Where(sample => sample.Timestamp >= traceStarted).ToArray(); + var drawn = surface.RenderedScenes.Where(sample => sample.Timestamp >= traceStarted).ToArray(); + Console.WriteLine("Kestrel continuous window resize: " + System.Text.Json.JsonSerializer.Serialize( + new ResizeTrace(traceStarted, inputEnded, System.Diagnostics.Stopwatch.Frequency, + submittedSizes, nativeWindowResizes, surfaceSizeChanges, + surface.SubmittedResizes.Where(sample => sample.Timestamp >= traceStarted).ToArray(), + diagnostics, baseline, view.CapturePerformanceSnapshot(), published, drawn, + surface.SchedulingSamples.Where(sample => sample.Timestamp >= traceStarted).ToArray()), + ResizeTraceJsonContext.Default.ResizeTrace)); + ValidateResizeGeometry(diagnostics); + using var geometry = System.Text.Json.JsonDocument.Parse(diagnostics); + var finalWindow = geometry.RootElement.GetProperty("window"); + if (Math.Abs(finalWindow[0].GetDouble() - initialWidth) > 1 || + Math.Abs(finalWindow[1].GetDouble() - initialHeight) > 1) + throw new InvalidOperationException("Continuous resize did not restore the initial viewport."); + if (published.Where(sample => sample.Timestamp <= inputEnded) + .Select(sample => (sample.ViewportWidth, sample.ViewportHeight)).Distinct().Count() < 3) + throw new InvalidOperationException("Continuous resize did not publish intermediate viewport sizes."); + if (drawn.Count(sample => sample.Timestamp <= inputEnded) < 3) + throw new InvalidOperationException("Continuous resize did not draw intermediate scenes."); + Console.WriteLine("Kestrel continuous window resize workload validated (physical presentation and native user drag remain unqualified)."); + } + if (arguments.Contains("--resize-kestrel")) + { + if (arguments.Contains("--capture-resize-kestrel")) + await Task.Delay(5000); // Allow a window-scoped recorder to attach. + foreach (var size in new[] { (980, 680), (1440, 900), (1100, 740), (1280, 800) }) + { + desktop.MainWindow.Width = size.Item1; + desktop.MainWindow.Height = size.Item2; + await Task.Delay(750); + var resizeDiagnostics = await view.EvaluateTextAsync("(()=>{const c=document.getElementById('scene'),r=c.getBoundingClientRect();const ancestors=[];for(let n=c.parentElement;n;n=n.parentElement){const b=n.getBoundingClientRect(),s=getComputedStyle(n);ancestors.push({id:n.id,tag:n.tagName,rect:[b.x,b.y,b.width,b.height],height:s.height,minHeight:s.minHeight,display:s.display,flex:s.flex,gridTemplateRows:s.gridTemplateRows});}return {window:[innerWidth,innerHeight],canvas:[c.width,c.height],css:[r.width,r.height],ancestors,dpr:devicePixelRatio,backend:document.getElementById('engine-label').textContent,errors:document.querySelectorAll('#command-history .history-error').length}})()"); + Console.WriteLine("Kestrel resize: " + resizeDiagnostics); + ValidateResizeGeometry(resizeDiagnostics); + } + } + if (arguments.Contains("--verify-kestrel")) + { + var webGpuReady = await view.EvaluateTextAsync("document.documentElement.dataset.ready==='true'&&document.getElementById('engine-label').textContent.startsWith('WebGPU')&&document.querySelectorAll('#command-history .history-error').length===0"); + using var diagnosticTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)); + await view.FlushRuntimeDiagnosticsAsync(diagnosticTimeout.Token); + Console.WriteLine($"Kestrel uncaught JavaScript exceptions: {Interlocked.Read(ref documentExceptions)}"); + if (Interlocked.Read(ref documentExceptions) != 0) webGpuReady = "false"; + var renderedScenes = view.CapturePerformanceSnapshot().Surface.RenderedScenes; + Console.WriteLine($"Kestrel successfully rendered scenes: {renderedScenes}"); + if (renderedScenes == 0) webGpuReady = "false"; + Console.WriteLine(webGpuReady == "true" ? "Kestrel WebGPU startup check passed (interaction qualification remains)." : "FAIL: Kestrel WebGPU startup or initial rendering reported an error."); + await view.DisposeAsync(); + desktop.Shutdown(webGpuReady == "true" ? 0 : 1); + } + return; + } + if (Environment.GetCommandLineArgs().Contains("--resize-webgpu")) + { + foreach (var size in new[] { (640, 360), (280, 180), (520, 320), (400, 240) }) + { + desktop.MainWindow.Width = size.Item1; + desktop.MainWindow.Height = size.Item2; + await Task.Delay(500); + } + } + if (Environment.GetCommandLineArgs().Contains("--stress-webgpu")) + { + var deadline = DateTime.UtcNow.AddSeconds(10); + while (await view.EvaluateTextAsync("webGpuDemoFrames>=120||!!globalThis.webGpuDemoError") != "true" + && DateTime.UtcNow < deadline) + await Task.Delay(100); + } + else await Task.Delay(1000); + Console.WriteLine(await view.EvaluateTextAsync("({submitted:globalThis.webGpuDemoSubmitted,error:globalThis.webGpuDemoError,gpu:!!navigator.gpu,frames:globalThis.webGpuDemoFrames,width:document.getElementById('gpu').width,height:document.getElementById('gpu').height})")); + Console.WriteLine(view.SceneDiagnostics); + if (arguments.Contains("--verify-webgpu")) + { + if (await view.EvaluateTextAsync("webGpuDemoFrames>=120&&!globalThis.webGpuDemoError") != "true") + throw new InvalidOperationException("WebGPU stress workload did not complete 120 frames."); + Console.WriteLine("WebGPU frame times: " + await view.EvaluateTextAsync("webGpuDemoFrameTimes")); + var surface = (NativeSceneSurface)view.Content!; + Console.WriteLine("WebGPU draw times: " + System.Text.Json.JsonSerializer.Serialize(new DrawTrace( + System.Diagnostics.Stopwatch.Frequency, surface.PresentationTimestamps), + ResizeTraceJsonContext.Default.DrawTrace)); + await view.DisposeAsync(); + desktop.Shutdown(0); + } + } + catch (Exception error) { Console.Error.WriteLine(error); desktop.Shutdown(1); } + }; + desktop.Exit += (_, _) => File.Delete(path); + } + base.OnFrameworkInitializationCompleted(); + } + + private static void ValidateResizeGeometry(string diagnostics) + { + using var parsed = System.Text.Json.JsonDocument.Parse(diagnostics); + var root = parsed.RootElement; + var css = root.GetProperty("css"); + var bitmap = root.GetProperty("canvas"); + var dpr = root.GetProperty("dpr").GetDouble(); + if (root.GetProperty("errors").GetInt32() != 0 || !double.IsFinite(dpr) || dpr <= 0) + throw new InvalidOperationException("Kestrel resize reported an application error or invalid scale."); + for (var axis = 0; axis < 2; ++axis) + { + var size = css[axis].GetDouble(); + if (!double.IsFinite(size) || size <= 0 + || Math.Abs(bitmap[axis].GetDouble() - size * dpr) > 1) + throw new InvalidOperationException("Kestrel canvas bitmap does not match its resized CSS dimensions and DPR."); + } + var ancestors = root.GetProperty("ancestors").EnumerateArray().ToArray(); + var viewport = ancestors.Single(node => node.GetProperty("id").GetString() == "viewport").GetProperty("rect"); + var workbench = ancestors.Single(node => node.GetProperty("id").GetString() == "workbench").GetProperty("rect"); + if (Math.Abs(viewport[3].GetDouble() - workbench[3].GetDouble()) > 1) + throw new InvalidOperationException("Kestrel viewport no longer tracks the resized workbench height."); + } + + private static int ReadDocumentDimension(string[] arguments, string option, int fallback) + { + var index = Array.IndexOf(arguments, option); + if (index < 0) return fallback; + if (index + 1 >= arguments.Length || !int.TryParse(arguments[index + 1], out var value) || value <= 0) + throw new ArgumentException($"{option} requires a positive integer."); + return value; + } + +} diff --git a/experiments/WebScene.GpuHost.Probe/WebScene.GpuHost.Probe.csproj b/experiments/WebScene.GpuHost.Probe/WebScene.GpuHost.Probe.csproj new file mode 100644 index 000000000..e6ad5c9ee --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/WebScene.GpuHost.Probe.csproj @@ -0,0 +1,13 @@ + + + Exe + net10.0 + enable + enable + true + false + true + + + + diff --git a/experiments/WebScene.GpuHost.Probe/WindowsInputPacer.cs b/experiments/WebScene.GpuHost.Probe/WindowsInputPacer.cs new file mode 100644 index 000000000..940a3f6b7 --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/WindowsInputPacer.cs @@ -0,0 +1,43 @@ +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +// Synthetic input cadence only. Display/render scheduling uses the compositor +// clock; it must never depend on this workload timer. +internal sealed class WindowsInputPacer : IDisposable +{ + private sealed class TimerWaitHandle : WaitHandle + { + public TimerWaitHandle(IntPtr handle) => SafeWaitHandle = new SafeWaitHandle(handle, ownsHandle: true); + } + private readonly TimerWaitHandle _wait; + + public WindowsInputPacer() + { + if (!OperatingSystem.IsWindows()) throw new PlatformNotSupportedException("High-resolution input pacing requires Windows."); + var timer = CreateWaitableTimerEx(IntPtr.Zero, null, 2, 0x1f0003); + if (timer == IntPtr.Zero) throw new Win32Exception(Marshal.GetLastWin32Error()); + _wait = new TimerWaitHandle(timer); + } + + public Task WaitUntilAsync(long deadline) => Task.Run(() => + { + var remaining = deadline - Stopwatch.GetTimestamp(); + if (remaining <= 0) return; + var due = -(long)Math.Ceiling(remaining * 10_000_000.0 / Stopwatch.Frequency); + if (!SetWaitableTimer(_wait.SafeWaitHandle, in due, 0, IntPtr.Zero, IntPtr.Zero, false)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + _wait.WaitOne(); + }); + + public void Dispose() => _wait.Dispose(); + + [DllImport("kernel32.dll", EntryPoint = "CreateWaitableTimerExW", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern IntPtr CreateWaitableTimerEx(IntPtr attributes, string? name, uint flags, uint access); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetWaitableTimer(SafeWaitHandle timer, in long dueTime, int period, + IntPtr completion, IntPtr argument, [MarshalAs(UnmanagedType.Bool)] bool resume); +} diff --git a/experiments/WebScene.GpuHost.Probe/WindowsVSyncProbe.cs b/experiments/WebScene.GpuHost.Probe/WindowsVSyncProbe.cs new file mode 100644 index 000000000..f2696aa37 --- /dev/null +++ b/experiments/WebScene.GpuHost.Probe/WindowsVSyncProbe.cs @@ -0,0 +1,162 @@ +#if WEBSCENE_AVALONIA12 +// This private Avalonia 11 render-loop diagnostic is not part of the v12 sample. +internal static class WindowsVSyncProbe +{ + public static Avalonia.AppBuilder Configure(Avalonia.AppBuilder builder) + => throw new PlatformNotSupportedException( + "The compositor-clock diagnostic requires the default Avalonia 11 configuration."); +} +#else +using System.Diagnostics; +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Rendering; +using Avalonia.Rendering.Composition; +using WebScene.Backends.Avalonia.Native; + +// Diagnostic host: Avalonia 11.3.4 does not expose render-loop injection. +// Install the existing loop before platform initialization, without changing +// its rendering behavior or touching a running loop's subscriptions. +internal sealed class WindowsVSyncProbe : IRenderTimer +{ + private readonly Stopwatch _clock = Stopwatch.StartNew(); + private readonly object _gate = new(); + private Action? _tick; + private bool _started; + private long _vsyncTicks; + private long _fallbackTicks; + private uint _lastResult; + private readonly AutoResetEvent _ready = new(false); + private long _latestTick; + private long _latestPhase; + private readonly bool _statistics = Environment.GetCommandLineArgs().Contains("--compositor-clock-statistics"); + private long _statisticsTicks, _statisticsFailures; + private ulong _lastPeriod; + private readonly bool _trace = Environment.GetCommandLineArgs().Contains("--trace-vsync"); + private readonly List<(long Start, long End)> _renders = []; + public bool RunsInBackground => true; + public event Action? Tick + { + add + { + lock (_gate) + { + _tick += value; + if (!_started) + { + _started = true; + new Thread(Render) { IsBackground = true, Name = "WebScene vsync renderer" }.Start(); + new Thread(Run) { IsBackground = true, Name = "WebScene compositor clock" }.Start(); + } + Monitor.PulseAll(_gate); + } + } + remove { lock (_gate) _tick -= value; } + } + + public static AppBuilder Configure(AppBuilder builder) + { + if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000)) + throw new PlatformNotSupportedException("This diagnostic requires the Windows 11 compositor clock."); + var initialize = builder.RenderingSubsystemInitializer!; + return builder.UseRenderingSubsystem(() => + { + initialize(); + var assembly = typeof(Compositor).Assembly; + var loopType = assembly.GetType("Avalonia.Rendering.RenderLoop", true)!; + var loopInterface = assembly.GetType("Avalonia.Rendering.IRenderLoop", true)!; + var loop = Activator.CreateInstance(loopType, new WindowsVSyncProbe())!; + var registration = typeof(AvaloniaLocator).GetMethod("Bind")! + .MakeGenericMethod(loopInterface).Invoke(AvaloniaLocator.CurrentMutable, null)!; + registration.GetType().GetMethod("ToConstant")!.MakeGenericMethod(loopType) + .Invoke(registration, [loop]); + }, builder.RenderingSubsystemName ?? "Skia"); + } + + private void Run() + { + if (_statistics) + { + try { Console.WriteLine($"DXGI vblank virtualization disable: 0x{DXGIDisableVBlankVirtualization():x8}"); } + catch (EntryPointNotFoundException) { Console.WriteLine("DXGI vblank virtualization control unavailable."); } + } + AppDomain.CurrentDomain.ProcessExit += (_, _) => Console.WriteLine( + $"Windows compositor clock: vsyncTicks={_vsyncTicks}, fallbackTicks={_fallbackTicks}, lastStatus=0x{_lastResult:x8}, statisticsTicks={_statisticsTicks}, statisticsFailures={_statisticsFailures}, periodQpc={_lastPeriod}, phaseApplications={NativeCompositorFrameClock.AppliedTimestamps}"); + if (_trace) AppDomain.CurrentDomain.ProcessExit += (_, _) => + { + lock (_renders) Console.WriteLine("Windows render intervals: " + System.Text.Json.JsonSerializer.Serialize( + _renders.Select(sample => new { sample.Start, sample.End }))); + }; + while (true) + { + lock (_gate) while (_tick is null) Monitor.Wait(_gate); + var start = _clock.Elapsed; + var result = DCompositionWaitForCompositorClock(0, IntPtr.Zero, 100); + _lastResult = result; + // Chromium also guards early returns during desktop occlusion. + // Keep application startup/disposal live when the display is off, + // but explicitly count these synthetic ticks separately from vsync. + if (result != 0 || _clock.Elapsed - start < TimeSpan.FromMilliseconds(1)) + { + if (_fallbackTicks == 0) + Console.WriteLine($"Windows compositor clock unavailable: status=0x{result:x8}; fallback ticks are not vsync."); + Thread.Sleep(17); + _fallbackTicks++; + } + else _vsyncTicks++; + var phase = Stopwatch.GetTimestamp(); + if (_statistics && result == 0) + { + try + { + if (DCompositionGetFrameId(2, out var id) == 0 + && DCompositionGetStatistics(id, out var stats, 0, IntPtr.Zero, IntPtr.Zero) == 0 + && stats.Period > 0 && stats.Start > 0 && stats.Start <= (ulong)phase) + { + phase = checked((long)stats.Start); + _lastPeriod = stats.Period; + _statisticsTicks++; + } + else _statisticsFailures++; + } + catch (EntryPointNotFoundException) { _statisticsFailures++; } + } + Interlocked.Exchange(ref _latestPhase, Math.Max(Interlocked.Read(ref _latestPhase), phase)); + Interlocked.Exchange(ref _latestTick, _clock.Elapsed.Ticks); + _ready.Set(); + } + } + + private void Render() + { + // Sampling the display clock must not wait for scene preparation. + // One pending signal retains the latest tick and bounds backlog. + while (true) + { + _ready.WaitOne(); + Action? tick; + lock (_gate) tick = _tick; + var start = _trace ? Stopwatch.GetTimestamp() : 0; + var previousPhase = NativeCompositorFrameClock.CurrentTimestamp; + try + { + if (_statistics) NativeCompositorFrameClock.CurrentTimestamp = Interlocked.Read(ref _latestPhase); + tick?.Invoke(TimeSpan.FromTicks(Interlocked.Read(ref _latestTick))); + } + finally { NativeCompositorFrameClock.CurrentTimestamp = previousPhase; } + if (_trace) lock (_renders) + { + if (_renders.Count < 4096) _renders.Add((start, Stopwatch.GetTimestamp())); + } + } + } + + [DllImport("dcomp.dll", ExactSpelling = true)] + private static extern uint DCompositionWaitForCompositorClock(uint count, IntPtr handles, uint timeout); + [StructLayout(LayoutKind.Sequential)] private struct FrameStats { public ulong Start, Target, Period; } + [DllImport("dcomp.dll", ExactSpelling = true)] private static extern int DCompositionGetFrameId(uint kind, out ulong id); + [DllImport("dcomp.dll", ExactSpelling = true)] private static extern int DCompositionGetStatistics(ulong id, out FrameStats stats, uint count, IntPtr targets, IntPtr actualCount); + [DllImport("dxgi.dll", ExactSpelling = true)] private static extern int DXGIDisableVBlankVirtualization(); +} + +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/CMakeLists.txt b/experiments/WebScene.NativeEngine.Probe/CMakeLists.txt index 07bcb8d7a..9766101d4 100644 --- a/experiments/WebScene.NativeEngine.Probe/CMakeLists.txt +++ b/experiments/WebScene.NativeEngine.Probe/CMakeLists.txt @@ -4,6 +4,51 @@ project(WebSceneNativeEngineProbe LANGUAGES C CXX) include(CTest) include(FetchContent) +if(BUILD_TESTING) + add_executable(webscene_graphics_scene_abi_layout_tests tests/graphics_scene_abi_layout_tests.c) + target_compile_features(webscene_graphics_scene_abi_layout_tests PRIVATE c_std_11) + target_include_directories(webscene_graphics_scene_abi_layout_tests PRIVATE native) + add_test(NAME webscene_graphics_scene_abi_layout_tests COMMAND webscene_graphics_scene_abi_layout_tests) + add_executable(webscene_graphics_canvas_backing_tests tests/graphics_canvas_backing_tests.cpp) + target_compile_features(webscene_graphics_canvas_backing_tests PRIVATE cxx_std_20) + target_include_directories(webscene_graphics_canvas_backing_tests PRIVATE native) + add_test(NAME webscene_graphics_canvas_backing_tests COMMAND webscene_graphics_canvas_backing_tests) + find_package(Threads REQUIRED) + add_executable(webscene_graphics_image_lease_tests tests/graphics_image_lease_tests.cpp) + target_compile_features(webscene_graphics_image_lease_tests PRIVATE cxx_std_20) + target_include_directories(webscene_graphics_image_lease_tests PRIVATE native) + target_link_libraries(webscene_graphics_image_lease_tests PRIVATE Threads::Threads) + add_test(NAME webscene_graphics_image_lease_tests COMMAND webscene_graphics_image_lease_tests) + add_executable(webscene_nt_handle_tests tests/graphics_nt_handle_tests.cpp) + target_compile_features(webscene_nt_handle_tests PRIVATE cxx_std_20) + target_include_directories(webscene_nt_handle_tests PRIVATE native) + add_test(NAME webscene_nt_handle_tests COMMAND webscene_nt_handle_tests) + add_executable(webscene_graphics_dxgi_contract_tests tests/graphics_dxgi_contract_tests.cpp) + target_compile_features(webscene_graphics_dxgi_contract_tests PRIVATE cxx_std_20) + target_include_directories(webscene_graphics_dxgi_contract_tests PRIVATE native) + add_test(NAME webscene_graphics_dxgi_contract_tests COMMAND webscene_graphics_dxgi_contract_tests) + add_executable(webscene_graphics_resource_tests tests/graphics_resource_tests.cpp) + target_compile_features(webscene_graphics_resource_tests PRIVATE cxx_std_20) + target_include_directories(webscene_graphics_resource_tests PRIVATE native) + target_link_libraries(webscene_graphics_resource_tests PRIVATE Threads::Threads) + add_test(NAME webscene_graphics_resource_tests COMMAND webscene_graphics_resource_tests) + add_executable(webscene_graphics_queue_tests tests/graphics_queue_tests.cpp) + target_compile_features(webscene_graphics_queue_tests PRIVATE cxx_std_20) + target_include_directories(webscene_graphics_queue_tests PRIVATE native) + target_link_libraries(webscene_graphics_queue_tests PRIVATE Threads::Threads) + add_test(NAME webscene_graphics_queue_tests COMMAND webscene_graphics_queue_tests) + add_executable(webscene_graphics_completion_tests tests/graphics_completion_tests.cpp) + target_compile_features(webscene_graphics_completion_tests PRIVATE cxx_std_20) + target_include_directories(webscene_graphics_completion_tests PRIVATE native) + target_link_libraries(webscene_graphics_completion_tests PRIVATE Threads::Threads) + add_test(NAME webscene_graphics_completion_tests COMMAND webscene_graphics_completion_tests) + add_executable(webscene_graphics_wake_tests tests/graphics_wake_tests.cpp) + target_compile_features(webscene_graphics_wake_tests PRIVATE cxx_std_20) + target_include_directories(webscene_graphics_wake_tests PRIVATE native) + target_link_libraries(webscene_graphics_wake_tests PRIVATE Threads::Threads) + add_test(NAME webscene_graphics_wake_tests COMMAND webscene_graphics_wake_tests) +endif() + # CMake 3.24 made extracted archive timestamps deterministic through CMP0135. # Use the policy when available while remaining compatible with the Ubuntu # 22.04 runner's CMake 3.22, which does not recognize @@ -27,6 +72,7 @@ if(MSVC) endif() add_library(webscene_native_engine SHARED + native/graphics/windows_gpu_interop.cpp native/webscene_native_engine.cpp native/webscene_native_dom.cpp) @@ -34,6 +80,97 @@ target_compile_features(webscene_native_engine PRIVATE cxx_std_20) target_compile_definitions(webscene_native_engine PRIVATE WEBSCENE_NATIVE_ENGINE_BUILD) target_include_directories(webscene_native_engine PUBLIC native) +option(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA "Build native HTML media and Web Audio services" ON) +if(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) + add_subdirectory(native/media) + target_link_libraries(webscene_native_engine PRIVATE webscene_media) + target_compile_definitions(webscene_native_engine PRIVATE WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA=1) +endif() + + +option(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS "Link pinned native GPU dependencies for this platform" OFF) +if(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + set(WEBSCENE_GRAPHICS_COMPONENTS dawn) + set(webscene_graphics_libraries dawn::webgpu_dawn) + # The native macOS presenter shares Metal textures directly with Skia. + # Windows requires ANGLE for the D3D11/EGL composition bridge. + if(APPLE) + add_compile_definitions(WEBSCENE_GRAPHICS_ENABLE_ANGLE=0) + else() + list(APPEND WEBSCENE_GRAPHICS_COMPONENTS angle) + list(APPEND webscene_graphics_libraries webscene_angle_EGL webscene_angle_GLESv2) + endif() + include("${CMAKE_CURRENT_LIST_DIR}/../../eng/graphics/GraphicsDependencies.cmake") + target_link_libraries(webscene_native_engine PRIVATE + ${webscene_graphics_libraries}) + target_compile_definitions(webscene_native_engine PRIVATE WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS=1) + if(WIN32) + target_link_libraries(webscene_native_engine PRIVATE d3d12 d3d11 dxgi) + endif() + if(APPLE) + target_link_libraries(webscene_native_engine PRIVATE + "-framework CoreFoundation" "-framework IOSurface" "-framework CoreVideo") + endif() + if(BUILD_TESTING) + add_executable(webscene_graphics_dawn_event_tests tests/graphics_dawn_event_tests.cpp) + target_compile_features(webscene_graphics_dawn_event_tests PRIVATE cxx_std_20) + target_include_directories(webscene_graphics_dawn_event_tests PRIVATE native) + target_link_libraries(webscene_graphics_dawn_event_tests PRIVATE ${webscene_graphics_libraries} Threads::Threads) + add_test(NAME webscene_graphics_dawn_event_tests COMMAND webscene_graphics_dawn_event_tests) + set_tests_properties(webscene_graphics_dawn_event_tests PROPERTIES LABELS "graphics;hardware" TIMEOUT 40) + foreach(library IN LISTS webscene_graphics_libraries) + add_custom_command(TARGET webscene_graphics_dawn_event_tests POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "$" "$") + endforeach() + if(NOT APPLE) + add_executable(webscene_graphics_angle_context_tests tests/graphics_angle_context_tests.cpp) + target_compile_features(webscene_graphics_angle_context_tests PRIVATE cxx_std_20) + target_include_directories(webscene_graphics_angle_context_tests PRIVATE native) + target_link_libraries(webscene_graphics_angle_context_tests PRIVATE webscene_angle_EGL webscene_angle_GLESv2 Threads::Threads) + add_test(NAME webscene_graphics_angle_context_tests COMMAND webscene_graphics_angle_context_tests 2) + add_test(NAME webscene_graphics_angle_es3_context_tests COMMAND webscene_graphics_angle_context_tests 3) + set_tests_properties(webscene_graphics_angle_context_tests webscene_graphics_angle_es3_context_tests PROPERTIES LABELS "graphics;hardware" TIMEOUT 40) + add_executable(webscene_graphics_service_tests tests/graphics_service_tests.cpp) + target_compile_features(webscene_graphics_service_tests PRIVATE cxx_std_20) + target_include_directories(webscene_graphics_service_tests PRIVATE native) + target_link_libraries(webscene_graphics_service_tests PRIVATE ${webscene_graphics_libraries} Threads::Threads) + add_test(NAME webscene_graphics_service_tests COMMAND webscene_graphics_service_tests) + set_tests_properties(webscene_graphics_service_tests PROPERTIES LABELS "graphics;hardware" TIMEOUT 40) + foreach(library ${webscene_graphics_libraries}) + add_custom_command(TARGET webscene_graphics_service_tests POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "$" "$") + endforeach() + foreach(library EGL GLESv2) + add_custom_command(TARGET webscene_graphics_angle_context_tests POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "$" "$") + endforeach() + endif() + endif() + add_custom_command(TARGET webscene_native_engine POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "$" "$") + if(WIN32) + add_custom_command(TARGET webscene_native_engine POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "${WEBSCENE_GRAPHICS_SDK_ROOT}/dawn/bin/d3dcompiler_47.dll" "$") + endif() + if(APPLE) + set_target_properties(webscene_native_engine PROPERTIES BUILD_RPATH "@loader_path") + elseif(UNIX) + set_target_properties(webscene_native_engine PROPERTIES BUILD_RPATH "$ORIGIN") + endif() + if(NOT APPLE) + foreach(library EGL GLESv2) + add_custom_command(TARGET webscene_native_engine POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "$" "$") + endforeach() + endif() +endif() + option(WEBSCENE_NATIVE_ENGINE_ENABLE_V8 "Link the engine directly to the pinned V8 monolith" OFF) option(WEBSCENE_NATIVE_ENGINE_ENABLE_V8_INSPECTOR "Compile V8 Inspector state, hooks, queues, and worker dispatch into the runtime" @@ -373,6 +510,9 @@ if(WEBSCENE_NATIVE_ENGINE_ENABLE_V8) if(NOT EXISTS "${WEBSCENE_V8_ICU_DATA}") message(FATAL_ERROR "WebScene native engine: ICU data not found at ${WEBSCENE_V8_ICU_DATA}") endif() + if(WEBSCENE_NATIVE_ENGINE_ENABLE_V8_INSPECTOR) + include("${CMAKE_CURRENT_LIST_DIR}/cmake/VerifyV8Inspector.cmake") + endif() # WebSocket I/O is a native implementation detail. IXWebSocket supplies a # portable RFC 6455 client while the WebScene adapter below owns all V8 @@ -915,7 +1055,7 @@ if(MSVC) # conformance switch is enabled. V8 checks __cplusplus directly when # enforcing its C++20 embedder requirement. target_compile_options(webscene_native_engine PRIVATE - /W4 /permissive- /GR- /wd4996 /std:c++20 /Zc:__cplusplus) + /W4 /permissive- /GR- /wd4996 /std:c++20 /Zc:__cplusplus /bigobj) target_compile_definitions(webscene_native_engine PRIVATE NOMINMAX) if(TARGET webscene_native_engine_tests) set_property(TARGET webscene_native_engine_tests PROPERTY @@ -1009,3 +1149,48 @@ if(WEBSCENE_NATIVE_ENGINE_DENSE_LINK) endif() endif() endif() + +# White-box runtime coverage without adding test hooks to the production C ABI. +# Compile the same source/configuration graph into a separate executable. +if(BUILD_TESTING AND WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS AND WEBSCENE_NATIVE_ENGINE_ENABLE_V8) + get_target_property(graphics_runtime_sources webscene_native_engine SOURCES) + add_executable(webscene_graphics_v8_runtime_tests + tests/graphics_v8_runtime_tests.cpp ${graphics_runtime_sources}) + target_compile_features(webscene_graphics_v8_runtime_tests PRIVATE cxx_std_20) + target_include_directories(webscene_graphics_v8_runtime_tests PRIVATE + "$") + target_compile_definitions(webscene_graphics_v8_runtime_tests PRIVATE + WEBSCENE_GRAPHICS_SCENE_TESTS=1 + "$") + target_compile_options(webscene_graphics_v8_runtime_tests PRIVATE + "$") + target_link_libraries(webscene_graphics_v8_runtime_tests PRIVATE + "$" Threads::Threads) + # Preserve toolchain/LTO options, excluding the production ABI export lists. + target_link_options(webscene_graphics_v8_runtime_tests PRIVATE + "$,EXCLUDE,exported_symbols_list|version-script>") + get_target_property(graphics_runtime_ipo webscene_native_engine INTERPROCEDURAL_OPTIMIZATION) + if(graphics_runtime_ipo) + set_property(TARGET webscene_graphics_v8_runtime_tests PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) + endif() + add_dependencies(webscene_graphics_v8_runtime_tests webscene_native_engine) + add_test(NAME webscene_graphics_v8_runtime_tests COMMAND webscene_graphics_v8_runtime_tests) + set_tests_properties(webscene_graphics_v8_runtime_tests PROPERTIES TIMEOUT 30 LABELS "graphics;runtime;hardware") +endif() + +if(BUILD_TESTING AND APPLE AND WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + enable_language(OBJCXX) + add_executable(webscene_graphics_metal_event_handoff_tests tests/graphics_metal_event_handoff.mm) + target_compile_features(webscene_graphics_metal_event_handoff_tests PRIVATE cxx_std_20) + target_compile_options(webscene_graphics_metal_event_handoff_tests PRIVATE -fobjc-arc) + target_link_libraries(webscene_graphics_metal_event_handoff_tests PRIVATE dawn::webgpu_dawn "-framework Foundation" "-framework Metal") + add_test(NAME webscene_graphics_metal_event_handoff_tests COMMAND webscene_graphics_metal_event_handoff_tests) + set_tests_properties(webscene_graphics_metal_event_handoff_tests PROPERTIES TIMEOUT 15 LABELS "graphics;hardware;metal") + # Private managed-interop fixture, deliberately excluded from install/package targets. + add_library(webscene_graphics_iosurface_fixture SHARED EXCLUDE_FROM_ALL tests/graphics_iosurface_fixture.cpp) + target_compile_features(webscene_graphics_iosurface_fixture PRIVATE cxx_std_20) + target_compile_options(webscene_graphics_iosurface_fixture PRIVATE -fno-rtti) + target_include_directories(webscene_graphics_iosurface_fixture PRIVATE native) + target_link_libraries(webscene_graphics_iosurface_fixture PRIVATE dawn::webgpu_dawn + "-framework CoreFoundation" "-framework IOSurface" "-framework CoreVideo" "-framework OpenGL") +endif() diff --git a/experiments/WebScene.NativeEngine.Probe/cmake/VerifyV8Inspector.cmake b/experiments/WebScene.NativeEngine.Probe/cmake/VerifyV8Inspector.cmake new file mode 100644 index 000000000..f561ce42f --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/cmake/VerifyV8Inspector.cmake @@ -0,0 +1,51 @@ +# A patched header paired with an older archive changes V8Inspector's vtable +# layout. Header-only validation accepts that pair, but later virtual calls +# dispatch to unrelated functions. Require the patch's compiled implementation. +file(READ "${WEBSCENE_V8_ROOT}/include/v8-inspector.h" inspector_header) +if(NOT inspector_header MATCHES "virtual void consoleAPICalled") + message(FATAL_ERROR "V8 Inspector requires WebScene's console bridge patch in both headers and monolith. Rebuild the V8 SDK.") +endif() +set(inspector_llvm_nm "${WEBSCENE_V8_ROOT}/third_party/llvm-build/Release+Asserts/bin/llvm-nm") +if(WIN32) + string(APPEND inspector_llvm_nm ".exe") +endif() +if(EXISTS "${inspector_llvm_nm}") + # Match the archive's LLVM version, including ThinLTO bitcode members. + set(inspector_symbol_tool "${inspector_llvm_nm}") + set(inspector_symbol_args -g --defined-only) +elseif(MSVC) + get_filename_component(inspector_linker_directory "${CMAKE_LINKER}" DIRECTORY) + find_program(inspector_symbol_tool NAMES dumpbin HINTS "${inspector_linker_directory}" REQUIRED) + set(inspector_symbol_args /symbols) +elseif(APPLE) + set(inspector_symbol_tool "${CMAKE_NM}") + set(inspector_symbol_args -g -U) +else() + set(inspector_symbol_tool "${CMAKE_NM}") + set(inspector_symbol_args -g --defined-only) +endif() +execute_process( + COMMAND "${inspector_symbol_tool}" ${inspector_symbol_args} "${WEBSCENE_V8_MONOLITH}" + RESULT_VARIABLE inspector_symbols_status + OUTPUT_VARIABLE inspector_symbols + ERROR_VARIABLE inspector_symbols_error + TIMEOUT 120) +if(NOT inspector_symbols_status STREQUAL "0") + message(FATAL_ERROR "Cannot verify the V8 Inspector archive ABI: ${inspector_symbols_error}") +endif() +# Itanium and MSVC put the class/method names in opposite orders. On MSVC, +# exclude UNDEF records: a reference does not prove the implementation exists. +# Anchor each attempt at a line boundary. dumpbin emits a very large symbol +# table on Windows; an unanchored leading wildcard rescans each long line. +string(REGEX MATCHALL "(^|\n)[^\n]*V8InspectorImpl[^\n]*" inspector_symbol_lines "${inspector_symbols}") +set(inspector_bridge_found OFF) +foreach(symbol_line IN LISTS inspector_symbol_lines) + if(symbol_line MATCHES "consoleAPICalled" AND NOT symbol_line MATCHES "UNDEF") + set(inspector_bridge_found ON) + endif() +endforeach() +if(NOT inspector_bridge_found) + message(FATAL_ERROR "V8 Inspector header/archive ABI mismatch: the header declares the console bridge but the monolith has no V8InspectorImpl::consoleAPICalled implementation. Rebuild the monolith after applying V8InspectorConsolePatch.txt; do not reuse an older archive.") +endif() +unset(inspector_symbols) +unset(inspector_symbol_lines) diff --git a/experiments/WebScene.NativeEngine.Probe/native/generated/webscene_dom_bindings.inc b/experiments/WebScene.NativeEngine.Probe/native/generated/webscene_dom_bindings.inc index 203b3fb8c..81527bc51 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/generated/webscene_dom_bindings.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/generated/webscene_dom_bindings.inc @@ -1,5 +1,5 @@ // Generated by tools/webidl-v8-bindings/generate.mjs. Do not edit. -// Exposure manifest SHA-256: 9c41098f8ed9eb686b70b5a75b3368bd8eb2c897704e658c5fb2a976b10e511c +// Exposure manifest SHA-256: 65954cdff2f92cf111e965bd3fcf2617570cd74a3ec7a728e2dee2e4a20c27fb // Inputs: @webref/idl 3.82.1, webidl2 24.5.0. enum class generated_dom_interface : uint8_t { @@ -13,8 +13,10 @@ enum class generated_dom_interface : uint8_t { DocumentFragment, ShadowRoot, Element, + SVGElement, HTMLElement, HTMLTableCellElement, + HTMLDialogElement, HTMLFormElement, HTMLSelectElement, HTMLScriptElement, @@ -92,12 +94,18 @@ static bool generated_dom_receiver_is( case generated_dom_interface::Element: return !self->element_template.IsEmpty() && self->element_template.Get(isolate)->HasInstance(receiver); + case generated_dom_interface::SVGElement: + return !self->svg_element_template.IsEmpty() + && self->svg_element_template.Get(isolate)->HasInstance(receiver); case generated_dom_interface::HTMLElement: return !self->html_element_template.IsEmpty() && self->html_element_template.Get(isolate)->HasInstance(receiver); case generated_dom_interface::HTMLTableCellElement: return !self->html_table_cell_element_template.IsEmpty() && self->html_table_cell_element_template.Get(isolate)->HasInstance(receiver); + case generated_dom_interface::HTMLDialogElement: + return !self->html_dialog_element_template.IsEmpty() + && self->html_dialog_element_template.Get(isolate)->HasInstance(receiver); case generated_dom_interface::HTMLFormElement: return !self->html_form_element_template.IsEmpty() && self->html_form_element_template.Get(isolate)->HasInstance(receiver); @@ -121,9 +129,15 @@ static std::optional generated_standalone_event_target_id( v8::Isolate* isolate, v8::Local receiver) { + if (receiver.IsEmpty()) return std::nullopt; + v8::Local identity; + auto key = v8::Private::ForApi(isolate, js_string(isolate, "WebScene.EventTarget.identity")); + if (receiver->GetPrivate(isolate->GetCurrentContext(), key).ToLocal(&identity) && identity->IsUint32()) + return identity.As()->Value(); if (receiver.IsEmpty() || receiver->InternalFieldCount() < 2) { return std::nullopt; } + if (!receiver->GetInternalField(1)->IsValue()) return std::nullopt; auto value = receiver->GetInternalField(1).As(); if (!value->IsUint32()) return std::nullopt; return value->Uint32Value(isolate->GetCurrentContext()).FromMaybe(0U); @@ -863,6 +877,17 @@ static void generated_Element_shadowRoot_get( get_shadow_root(info.Data().As(), info); } +static void generated_SVGElement_style_get( + v8::Local, + const v8::PropertyCallbackInfo& info) +{ + if (!generated_dom_receiver_is(info.GetIsolate(), info.Holder(), generated_dom_interface::SVGElement)) { + throw_generated_illegal_invocation(info.GetIsolate()); + return; + } + get_style(info.Data().As(), info); +} + static void generated_HTMLElement_style_get( v8::Local, const v8::PropertyCallbackInfo& info) @@ -885,6 +910,29 @@ static void generated_HTMLElement_dataset_get( get_dataset(info.Data().As(), info); } +static void generated_HTMLElement_inert_get( + v8::Local, + const v8::PropertyCallbackInfo& info) +{ + if (!generated_dom_receiver_is(info.GetIsolate(), info.Holder(), generated_dom_interface::HTMLElement)) { + throw_generated_illegal_invocation(info.GetIsolate()); + return; + } + get_inert(info.Data().As(), info); +} + +static void generated_HTMLElement_inert_set( + v8::Local, + v8::Local value, + const v8::PropertyCallbackInfo& info) +{ + if (!generated_dom_receiver_is(info.GetIsolate(), info.Holder(), generated_dom_interface::HTMLElement)) { + throw_generated_illegal_invocation(info.GetIsolate()); + return; + } + set_inert(info.Data().As(), value, info); +} + static void generated_HTMLElement_innerText_get( v8::Local, const v8::PropertyCallbackInfo& info) @@ -1820,6 +1868,52 @@ static void generated_HTMLElement_contentDocument_get( get_content_document(info.Data().As(), info); } +static void generated_HTMLDialogElement_open_get( + v8::Local, + const v8::PropertyCallbackInfo& info) +{ + if (!generated_dom_receiver_is(info.GetIsolate(), info.Holder(), generated_dom_interface::HTMLDialogElement)) { + throw_generated_illegal_invocation(info.GetIsolate()); + return; + } + get_dialog_open(info.Data().As(), info); +} + +static void generated_HTMLDialogElement_open_set( + v8::Local, + v8::Local value, + const v8::PropertyCallbackInfo& info) +{ + if (!generated_dom_receiver_is(info.GetIsolate(), info.Holder(), generated_dom_interface::HTMLDialogElement)) { + throw_generated_illegal_invocation(info.GetIsolate()); + return; + } + set_dialog_open(info.Data().As(), value, info); +} + +static void generated_HTMLDialogElement_returnValue_get( + v8::Local, + const v8::PropertyCallbackInfo& info) +{ + if (!generated_dom_receiver_is(info.GetIsolate(), info.Holder(), generated_dom_interface::HTMLDialogElement)) { + throw_generated_illegal_invocation(info.GetIsolate()); + return; + } + get_dialog_return_value(info.Data().As(), info); +} + +static void generated_HTMLDialogElement_returnValue_set( + v8::Local, + v8::Local value, + const v8::PropertyCallbackInfo& info) +{ + if (!generated_dom_receiver_is(info.GetIsolate(), info.Holder(), generated_dom_interface::HTMLDialogElement)) { + throw_generated_illegal_invocation(info.GetIsolate()); + return; + } + set_dialog_return_value(info.Data().As(), value, info); +} + static void generated_HTMLFormElement_length_get( v8::Local, const v8::PropertyCallbackInfo& info) @@ -1904,6 +1998,9 @@ void install_generated_dom_templates(v8::Local) auto generated_EventTarget_template = v8::FunctionTemplate::New(isolate, event_target_constructor); generated_EventTarget_template->SetClassName(js_string(isolate, "EventTarget")); generated_EventTarget_template->SetInterfaceName(js_string(isolate, "EventTarget")); + generated_EventTarget_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "EventTarget"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_EventTarget_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_EventTarget_signature = v8::Signature::New(isolate, generated_EventTarget_template); @@ -1930,6 +2027,9 @@ void install_generated_dom_templates(v8::Local) auto generated_Node_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_Node_template->SetClassName(js_string(isolate, "Node")); generated_Node_template->SetInterfaceName(js_string(isolate, "Node")); + generated_Node_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "Node"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_Node_template->Inherit(generated_EventTarget_template); generated_Node_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_Node_signature = @@ -2362,6 +2462,9 @@ void install_generated_dom_templates(v8::Local) auto generated_Attr_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_Attr_template->SetClassName(js_string(isolate, "Attr")); generated_Attr_template->SetInterfaceName(js_string(isolate, "Attr")); + generated_Attr_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "Attr"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_Attr_template->Inherit(generated_Node_template); generated_Attr_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_Attr_signature = @@ -2536,6 +2639,9 @@ void install_generated_dom_templates(v8::Local) auto generated_CharacterData_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_CharacterData_template->SetClassName(js_string(isolate, "CharacterData")); generated_CharacterData_template->SetInterfaceName(js_string(isolate, "CharacterData")); + generated_CharacterData_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "CharacterData"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_CharacterData_template->Inherit(generated_Node_template); generated_CharacterData_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_CharacterData_signature = @@ -2560,6 +2666,9 @@ void install_generated_dom_templates(v8::Local) auto generated_Text_template = v8::FunctionTemplate::New(isolate, text_constructor); generated_Text_template->SetClassName(js_string(isolate, "Text")); generated_Text_template->SetInterfaceName(js_string(isolate, "Text")); + generated_Text_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "Text"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_Text_template->Inherit(generated_CharacterData_template); generated_Text_template->InstanceTemplate()->SetInternalFieldCount(2); text_template.Reset(isolate, generated_Text_template); @@ -2567,6 +2676,9 @@ void install_generated_dom_templates(v8::Local) auto generated_Comment_template = v8::FunctionTemplate::New(isolate, comment_constructor); generated_Comment_template->SetClassName(js_string(isolate, "Comment")); generated_Comment_template->SetInterfaceName(js_string(isolate, "Comment")); + generated_Comment_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "Comment"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_Comment_template->Inherit(generated_CharacterData_template); generated_Comment_template->InstanceTemplate()->SetInternalFieldCount(2); comment_template.Reset(isolate, generated_Comment_template); @@ -2574,6 +2686,9 @@ void install_generated_dom_templates(v8::Local) auto generated_ProcessingInstruction_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_ProcessingInstruction_template->SetClassName(js_string(isolate, "ProcessingInstruction")); generated_ProcessingInstruction_template->SetInterfaceName(js_string(isolate, "ProcessingInstruction")); + generated_ProcessingInstruction_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "ProcessingInstruction"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_ProcessingInstruction_template->Inherit(generated_CharacterData_template); generated_ProcessingInstruction_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_ProcessingInstruction_signature = @@ -2598,6 +2713,9 @@ void install_generated_dom_templates(v8::Local) auto generated_DocumentFragment_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_DocumentFragment_template->SetClassName(js_string(isolate, "DocumentFragment")); generated_DocumentFragment_template->SetInterfaceName(js_string(isolate, "DocumentFragment")); + generated_DocumentFragment_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "DocumentFragment"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_DocumentFragment_template->Inherit(generated_Node_template); generated_DocumentFragment_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_DocumentFragment_signature = @@ -2664,6 +2782,9 @@ void install_generated_dom_templates(v8::Local) auto generated_ShadowRoot_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_ShadowRoot_template->SetClassName(js_string(isolate, "ShadowRoot")); generated_ShadowRoot_template->SetInterfaceName(js_string(isolate, "ShadowRoot")); + generated_ShadowRoot_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "ShadowRoot"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_ShadowRoot_template->Inherit(generated_DocumentFragment_template); generated_ShadowRoot_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_ShadowRoot_signature = @@ -2763,6 +2884,9 @@ void install_generated_dom_templates(v8::Local) auto generated_Element_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_Element_template->SetClassName(js_string(isolate, "Element")); generated_Element_template->SetInterfaceName(js_string(isolate, "Element")); + generated_Element_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "Element"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_Element_template->Inherit(generated_Node_template); generated_Element_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_Element_signature = @@ -3123,9 +3247,39 @@ void install_generated_dom_templates(v8::Local) v8::ConstructorBehavior::kThrow)); element_template.Reset(isolate, generated_Element_template); + auto generated_SVGElement_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); + generated_SVGElement_template->SetClassName(js_string(isolate, "SVGElement")); + generated_SVGElement_template->SetInterfaceName(js_string(isolate, "SVGElement")); + generated_SVGElement_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "SVGElement"), + static_cast(v8::ReadOnly | v8::DontEnum)); + generated_SVGElement_template->Inherit(generated_Element_template); + generated_SVGElement_template->InstanceTemplate()->SetInternalFieldCount(2); + auto generated_SVGElement_signature = + v8::Signature::New(isolate, generated_SVGElement_template); + auto generated_SVGElement_style_symbol = v8::Symbol::New( + isolate, js_string(isolate, "WebScene.SVGElement.style")); + generated_SVGElement_template->InstanceTemplate()->SetNativeDataProperty( + generated_SVGElement_style_symbol, + generated_SVGElement_style_get, + nullptr, + js_string(isolate, "style"), + v8::PropertyAttribute::None); + generated_SVGElement_template->PrototypeTemplate()->SetAccessorProperty( + js_string(isolate, "style"), + v8::FunctionTemplate::New( + isolate, generated_dom_prototype_attribute_get, generated_SVGElement_style_symbol, + generated_SVGElement_signature, 0), + v8::Local(), + v8::PropertyAttribute::None); + svg_element_template.Reset(isolate, generated_SVGElement_template); + auto generated_HTMLElement_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_HTMLElement_template->SetClassName(js_string(isolate, "HTMLElement")); generated_HTMLElement_template->SetInterfaceName(js_string(isolate, "HTMLElement")); + generated_HTMLElement_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "HTMLElement"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_HTMLElement_template->Inherit(generated_Element_template); generated_HTMLElement_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_HTMLElement_signature = @@ -3160,6 +3314,21 @@ void install_generated_dom_templates(v8::Local) generated_HTMLElement_signature, 0), v8::Local(), v8::PropertyAttribute::None); + auto generated_HTMLElement_inert_symbol = v8::Symbol::New( + isolate, js_string(isolate, "WebScene.HTMLElement.inert")); + generated_HTMLElement_template->InstanceTemplate()->SetNativeDataProperty( + generated_HTMLElement_inert_symbol, + generated_HTMLElement_inert_get, + generated_HTMLElement_inert_set, + js_string(isolate, "inert"), + v8::PropertyAttribute::None); + generated_HTMLElement_template->PrototypeTemplate()->SetAccessorProperty( + js_string(isolate, "inert"), + v8::FunctionTemplate::New( + isolate, generated_dom_prototype_attribute_get, generated_HTMLElement_inert_symbol, + generated_HTMLElement_signature, 0), + v8::FunctionTemplate::New(isolate, generated_dom_prototype_attribute_set, generated_HTMLElement_inert_symbol, generated_HTMLElement_signature, 1), + v8::PropertyAttribute::None); auto generated_HTMLElement_innerText_symbol = v8::Symbol::New( isolate, js_string(isolate, "WebScene.HTMLElement.innerText")); generated_HTMLElement_template->InstanceTemplate()->SetNativeDataProperty( @@ -3996,13 +4165,85 @@ void install_generated_dom_templates(v8::Local) auto generated_HTMLTableCellElement_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_HTMLTableCellElement_template->SetClassName(js_string(isolate, "HTMLTableCellElement")); generated_HTMLTableCellElement_template->SetInterfaceName(js_string(isolate, "HTMLTableCellElement")); + generated_HTMLTableCellElement_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "HTMLTableCellElement"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_HTMLTableCellElement_template->Inherit(generated_HTMLElement_template); generated_HTMLTableCellElement_template->InstanceTemplate()->SetInternalFieldCount(2); html_table_cell_element_template.Reset(isolate, generated_HTMLTableCellElement_template); + auto generated_HTMLDialogElement_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); + generated_HTMLDialogElement_template->SetClassName(js_string(isolate, "HTMLDialogElement")); + generated_HTMLDialogElement_template->SetInterfaceName(js_string(isolate, "HTMLDialogElement")); + generated_HTMLDialogElement_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "HTMLDialogElement"), + static_cast(v8::ReadOnly | v8::DontEnum)); + generated_HTMLDialogElement_template->Inherit(generated_HTMLElement_template); + generated_HTMLDialogElement_template->InstanceTemplate()->SetInternalFieldCount(2); + auto generated_HTMLDialogElement_signature = + v8::Signature::New(isolate, generated_HTMLDialogElement_template); + auto generated_HTMLDialogElement_open_symbol = v8::Symbol::New( + isolate, js_string(isolate, "WebScene.HTMLDialogElement.open")); + generated_HTMLDialogElement_template->InstanceTemplate()->SetNativeDataProperty( + generated_HTMLDialogElement_open_symbol, + generated_HTMLDialogElement_open_get, + generated_HTMLDialogElement_open_set, + js_string(isolate, "open"), + v8::PropertyAttribute::None); + generated_HTMLDialogElement_template->PrototypeTemplate()->SetAccessorProperty( + js_string(isolate, "open"), + v8::FunctionTemplate::New( + isolate, generated_dom_prototype_attribute_get, generated_HTMLDialogElement_open_symbol, + generated_HTMLDialogElement_signature, 0), + v8::FunctionTemplate::New(isolate, generated_dom_prototype_attribute_set, generated_HTMLDialogElement_open_symbol, generated_HTMLDialogElement_signature, 1), + v8::PropertyAttribute::None); + auto generated_HTMLDialogElement_returnValue_symbol = v8::Symbol::New( + isolate, js_string(isolate, "WebScene.HTMLDialogElement.returnValue")); + generated_HTMLDialogElement_template->InstanceTemplate()->SetNativeDataProperty( + generated_HTMLDialogElement_returnValue_symbol, + generated_HTMLDialogElement_returnValue_get, + generated_HTMLDialogElement_returnValue_set, + js_string(isolate, "returnValue"), + v8::PropertyAttribute::None); + generated_HTMLDialogElement_template->PrototypeTemplate()->SetAccessorProperty( + js_string(isolate, "returnValue"), + v8::FunctionTemplate::New( + isolate, generated_dom_prototype_attribute_get, generated_HTMLDialogElement_returnValue_symbol, + generated_HTMLDialogElement_signature, 0), + v8::FunctionTemplate::New(isolate, generated_dom_prototype_attribute_set, generated_HTMLDialogElement_returnValue_symbol, generated_HTMLDialogElement_signature, 1), + v8::PropertyAttribute::None); + generated_HTMLDialogElement_template->PrototypeTemplate()->Set( + js_string(isolate, "show"), + v8::FunctionTemplate::New( + isolate, dialog_show, v8::Local(), + generated_HTMLDialogElement_signature, 0, + v8::ConstructorBehavior::kThrow)); + generated_HTMLDialogElement_template->PrototypeTemplate()->Set( + js_string(isolate, "showModal"), + v8::FunctionTemplate::New( + isolate, dialog_show_modal, v8::Local(), + generated_HTMLDialogElement_signature, 0, + v8::ConstructorBehavior::kThrow)); + generated_HTMLDialogElement_template->PrototypeTemplate()->Set( + js_string(isolate, "close"), + v8::FunctionTemplate::New( + isolate, dialog_close, v8::Local(), + generated_HTMLDialogElement_signature, 0, + v8::ConstructorBehavior::kThrow)); + generated_HTMLDialogElement_template->PrototypeTemplate()->Set( + js_string(isolate, "requestClose"), + v8::FunctionTemplate::New( + isolate, dialog_request_close, v8::Local(), + generated_HTMLDialogElement_signature, 0, + v8::ConstructorBehavior::kThrow)); + html_dialog_element_template.Reset(isolate, generated_HTMLDialogElement_template); + auto generated_HTMLFormElement_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_HTMLFormElement_template->SetClassName(js_string(isolate, "HTMLFormElement")); generated_HTMLFormElement_template->SetInterfaceName(js_string(isolate, "HTMLFormElement")); + generated_HTMLFormElement_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "HTMLFormElement"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_HTMLFormElement_template->Inherit(generated_HTMLElement_template); generated_HTMLFormElement_template->InstanceTemplate()->SetInternalFieldCount(2); generated_HTMLFormElement_template->InstanceTemplate()->SetHandler( @@ -4029,6 +4270,9 @@ void install_generated_dom_templates(v8::Local) auto generated_HTMLSelectElement_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_HTMLSelectElement_template->SetClassName(js_string(isolate, "HTMLSelectElement")); generated_HTMLSelectElement_template->SetInterfaceName(js_string(isolate, "HTMLSelectElement")); + generated_HTMLSelectElement_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "HTMLSelectElement"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_HTMLSelectElement_template->Inherit(generated_HTMLElement_template); generated_HTMLSelectElement_template->InstanceTemplate()->SetInternalFieldCount(2); generated_HTMLSelectElement_template->InstanceTemplate()->SetHandler( @@ -4055,6 +4299,9 @@ void install_generated_dom_templates(v8::Local) auto generated_HTMLScriptElement_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_HTMLScriptElement_template->SetClassName(js_string(isolate, "HTMLScriptElement")); generated_HTMLScriptElement_template->SetInterfaceName(js_string(isolate, "HTMLScriptElement")); + generated_HTMLScriptElement_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "HTMLScriptElement"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_HTMLScriptElement_template->Inherit(generated_HTMLElement_template); generated_HTMLScriptElement_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_HTMLScriptElement_signature = @@ -4079,6 +4326,9 @@ void install_generated_dom_templates(v8::Local) auto generated_HTMLSlotElement_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_HTMLSlotElement_template->SetClassName(js_string(isolate, "HTMLSlotElement")); generated_HTMLSlotElement_template->SetInterfaceName(js_string(isolate, "HTMLSlotElement")); + generated_HTMLSlotElement_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "HTMLSlotElement"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_HTMLSlotElement_template->Inherit(generated_HTMLElement_template); generated_HTMLSlotElement_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_HTMLSlotElement_signature = @@ -4115,6 +4365,9 @@ void install_generated_dom_templates(v8::Local) auto generated_HTMLStyleElement_template = v8::FunctionTemplate::New(isolate, illegal_dom_constructor); generated_HTMLStyleElement_template->SetClassName(js_string(isolate, "HTMLStyleElement")); generated_HTMLStyleElement_template->SetInterfaceName(js_string(isolate, "HTMLStyleElement")); + generated_HTMLStyleElement_template->PrototypeTemplate()->Set( + v8::Symbol::GetToStringTag(isolate), js_string(isolate, "HTMLStyleElement"), + static_cast(v8::ReadOnly | v8::DontEnum)); generated_HTMLStyleElement_template->Inherit(generated_HTMLElement_template); generated_HTMLStyleElement_template->InstanceTemplate()->SetInternalFieldCount(2); auto generated_HTMLStyleElement_signature = @@ -4224,6 +4477,10 @@ void install_generated_dom_constructors( ->GetFunction(local_context).ToLocalChecked(); global->Set(local_context, js_string(isolate, "Element"), generated_Element_constructor).Check(); + auto generated_SVGElement_constructor = svg_element_template.Get(isolate) + ->GetFunction(local_context).ToLocalChecked(); + global->Set(local_context, js_string(isolate, "SVGElement"), + generated_SVGElement_constructor).Check(); auto generated_HTMLElement_constructor = html_element_template.Get(isolate) ->GetFunction(local_context).ToLocalChecked(); global->Set(local_context, js_string(isolate, "HTMLElement"), @@ -4232,6 +4489,10 @@ void install_generated_dom_constructors( ->GetFunction(local_context).ToLocalChecked(); global->Set(local_context, js_string(isolate, "HTMLTableCellElement"), generated_HTMLTableCellElement_constructor).Check(); + auto generated_HTMLDialogElement_constructor = html_dialog_element_template.Get(isolate) + ->GetFunction(local_context).ToLocalChecked(); + global->Set(local_context, js_string(isolate, "HTMLDialogElement"), + generated_HTMLDialogElement_constructor).Check(); auto generated_HTMLFormElement_constructor = html_form_element_template.Get(isolate) ->GetFunction(local_context).ToLocalChecked(); global->Set(local_context, js_string(isolate, "HTMLFormElement"), @@ -4264,8 +4525,6 @@ void install_generated_dom_constructors( generated_HTMLElement_constructor).Check(); global->Set(local_context, js_string(isolate, "HTMLParagraphElement"), generated_HTMLElement_constructor).Check(); - global->Set(local_context, js_string(isolate, "SVGElement"), - generated_Element_constructor).Check(); global->Set(local_context, js_string(isolate, "Window"), generated_EventTarget_constructor).Check(); } diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/angle_context.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/angle_context.h new file mode 100644 index 000000000..e41838b34 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/angle_context.h @@ -0,0 +1,116 @@ +#pragma once +#include "angle_display.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace webscene::graphics { +// Distinguish recoverable context loss from stale handles and execution bugs. +class angle_context_lost : public std::runtime_error { +public: + angle_context_lost() : std::runtime_error("ANGLE context is lost") {} +}; +// A context retains its display owner's lease. The display service, not each +// context, initializes/terminates EGL; destroying one context cannot terminate +// the display used by another engine/context. +class angle_context { + const std::thread::id thread_ = std::this_thread::get_id(); + std::shared_ptr display_lease_; + EGLDisplay display_; + EGLContext context_{EGL_NO_CONTEXT}; + EGLSurface surface_{EGL_NO_SURFACE}; + bool lost_{}; + PFNGLGETGRAPHICSRESETSTATUSEXTPROC reset_status_{}; + void check_thread() const { + if (std::this_thread::get_id()!=thread_) + throw std::logic_error("ANGLE context requires its execution thread"); + } +public: + angle_context(std::shared_ptr display_lease, EGLConfig config, EGLint major) + : display_lease_(std::move(display_lease)), + display_(display_lease_ ? display_lease_->get() : EGL_NO_DISPLAY) { + if (!display_lease_ || display_==EGL_NO_DISPLAY || (major!=2 && major!=3)) + throw std::invalid_argument("ANGLE context requires a display lease and ES version"); + const auto* extensions=eglQueryString(display_,EGL_EXTENSIONS); + if(!extensions || (std::string(" ")+extensions+" ").find(" EGL_EXT_create_context_robustness ")==std::string::npos) + throw std::runtime_error("ANGLE context reset notification is required"); + reset_status_=reinterpret_cast(eglGetProcAddress("glGetGraphicsResetStatusEXT")); + if(!reset_status_) throw std::runtime_error("ANGLE reset status entry point missing"); + if (!eglBindAPI(EGL_OPENGL_ES_API)) throw std::runtime_error("Cannot bind ANGLE ES API"); + const EGLint surface_attributes[]={EGL_WIDTH,1,EGL_HEIGHT,1,EGL_NONE}; + surface_=eglCreatePbufferSurface(display_,config,surface_attributes); + if (surface_==EGL_NO_SURFACE) throw std::runtime_error("Cannot create ANGLE backing surface"); + const EGLint attributes[]={EGL_CONTEXT_CLIENT_VERSION,major, + EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE,EGL_TRUE, + EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE,EGL_TRUE, + EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT,EGL_LOSE_CONTEXT_ON_RESET_EXT,EGL_NONE}; + context_=eglCreateContext(display_,config,EGL_NO_CONTEXT,attributes); + if (context_==EGL_NO_CONTEXT) { + eglDestroySurface(display_,surface_); + throw std::runtime_error("Cannot create isolated WebGL-compatible ANGLE context"); + } + } + angle_context(const angle_context&)=delete; + angle_context& operator=(const angle_context&)=delete; + ~angle_context() { + if (std::this_thread::get_id()!=thread_) std::terminate(); + if (eglGetCurrentContext()==context_) + if (!eglMakeCurrent(display_,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)) std::terminate(); + eglDestroyContext(display_,context_); + eglDestroySurface(display_,surface_); + } + bool is_lost() const { check_thread(); return lost_; } + // Only query while this context is current. Loss is sticky across scopes. + bool poll_loss() { + check_thread(); + if(eglGetCurrentContext()!=context_) throw std::logic_error("ANGLE loss query requires current context"); + lost_=lost_ || reset_status_()!=GL_NO_ERROR; + return lost_; + } + class scope { + angle_context& owner_; + EGLDisplay previous_display_; + EGLContext previous_context_; + EGLSurface previous_draw_,previous_read_; + void restore() noexcept { + const auto display=previous_display_==EGL_NO_DISPLAY ? owner_.display_ : previous_display_; + if(previous_context_==owner_.context_ && owner_.lost_) { + if(!eglMakeCurrent(display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)) std::terminate(); + return; + } + if(!eglMakeCurrent(display,previous_draw_,previous_read_,previous_context_)) { + // A device-wide reset can invalidate the previous context too. + if(eglGetError()!=EGL_CONTEXT_LOST || !eglMakeCurrent(display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)) + std::terminate(); + } + } + public: + explicit scope(angle_context& owner) : owner_(owner) { + owner_.check_thread(); + if(owner_.lost_) throw angle_context_lost(); + previous_display_=eglGetCurrentDisplay(); + previous_context_=eglGetCurrentContext(); + previous_draw_=eglGetCurrentSurface(EGL_DRAW); + previous_read_=eglGetCurrentSurface(EGL_READ); + if (!eglMakeCurrent(owner_.display_,owner_.surface_,owner_.surface_,owner_.context_)) { + if(eglGetError()==EGL_CONTEXT_LOST) { owner_.lost_=true; throw angle_context_lost(); } + throw std::runtime_error("Cannot activate ANGLE context"); + } + if(owner_.poll_loss()) { restore(); throw angle_context_lost(); } + } + scope(const scope&)=delete; + scope& operator=(const scope&)=delete; + ~scope() { + if (std::this_thread::get_id()!=owner_.thread_) std::terminate(); + if(eglGetCurrentContext()==owner_.context_) owner_.poll_loss(); + restore(); + } + }; +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/angle_display.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/angle_display.h new file mode 100644 index 000000000..4e90ef116 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/angle_display.h @@ -0,0 +1,59 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace webscene::graphics { +// EGL initialization is display-wide, not reference-counted per engine. Serialize +// acquisition and final termination, including the interval after the last lease +// expires, so another engine cannot initialize a display just before it is killed. +class angle_display { + struct registry { + std::mutex mutex; + std::unordered_map references; + }; + std::shared_ptr registry_; + EGLDisplay display_; + bool registered_{}; + angle_display(std::shared_ptr state,EGLDisplay display) + : registry_(std::move(state)),display_(display) {} +public: + angle_display(const angle_display&)=delete; + angle_display& operator=(const angle_display&)=delete; + ~angle_display() { + if (!registered_) return; + std::lock_guard lock(registry_->mutex); + auto item=registry_->references.find(display_); + if (item==registry_->references.end()) std::terminate(); + if (--item->second==0) { + eglTerminate(display_); + registry_->references.erase(item); + } + } + static std::shared_ptr acquire(EGLint backend) { + static auto state=std::make_shared(); + auto get_display=reinterpret_cast( + eglGetProcAddress("eglGetPlatformDisplayEXT")); + if (!get_display) throw std::runtime_error("ANGLE platform display entry point missing"); + const EGLint attributes[]={EGL_PLATFORM_ANGLE_TYPE_ANGLE,backend, + EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,EGL_NONE}; + std::lock_guard lock(state->mutex); + auto display=get_display(EGL_PLATFORM_ANGLE_ANGLE,nullptr,attributes); + if (display==EGL_NO_DISPLAY) throw std::runtime_error("ANGLE hardware display unavailable"); + auto lease=std::shared_ptr(new angle_display(state,display)); + auto [item,inserted]=state->references.try_emplace(display,0); + if (inserted && !eglInitialize(display,nullptr,nullptr)) { + state->references.erase(item); + throw std::runtime_error("ANGLE hardware display initialization failed"); + } + ++item->second; + lease->registered_=true; + return lease; + } + EGLDisplay get() const noexcept { return display_; } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/canvas_backing.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/canvas_backing.h new file mode 100644 index 000000000..9bf04ed94 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/canvas_backing.h @@ -0,0 +1,48 @@ +#pragma once +#include "resource_table.h" + +namespace webscene::graphics { +enum class canvas_context_mode : uint32_t { none, two_d, webgl1, webgl2, webgpu, media }; +// Canvas identity is independent of DOM attachment, scene acknowledgement and +// native image allocation. This metadata owns no pixels or backend pointers. +class canvas_backing { + const uint64_t identity_=new_owner_token(); + canvas_context_mode mode_{}; + uint32_t width_=300,height_=150; + uint64_t allocation_generation_=1,content_serial_{},content_floor_{}; +public: + canvas_backing()=default; + canvas_backing(const canvas_backing&)=delete; + canvas_backing& operator=(const canvas_backing&)=delete; + uint64_t identity() const noexcept { return identity_; } + canvas_context_mode mode() const noexcept { return mode_; } + uint32_t width() const noexcept { return width_; } + uint32_t height() const noexcept { return height_; } + uint64_t allocation_generation() const noexcept { return allocation_generation_; } + uint64_t content_serial() const noexcept { return content_serial_; } + bool accepts_completed_content(uint64_t serial) const noexcept { + return serial>=content_floor_ && serial<=content_serial_; + } + bool claim_context(canvas_context_mode mode) noexcept { + if (mode==canvas_context_mode::none || static_cast(mode)>static_cast(canvas_context_mode::media)) return false; + if (mode_!=canvas_context_mode::none && mode_!=mode) return false; + mode_=mode; + return true; + } + void publish_content() { + if (content_serial_==UINT64_MAX) throw std::overflow_error("canvas content serial exhausted"); + ++content_serial_; + } + // Bitmap reset changes content even at the same dimensions. Only storage + // dimension changes advance the allocation generation; CSS size is separate. + void reset_bitmap(uint32_t width,uint32_t height) { + const bool changed=width!=width_ || height!=height_; + if (content_serial_==UINT64_MAX || (changed && allocation_generation_==UINT64_MAX)) + throw std::overflow_error("canvas backing version exhausted"); + width_=width; height_=height; + if (changed) ++allocation_generation_; + ++content_serial_; + content_floor_=content_serial_; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/command_channel.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/command_channel.h new file mode 100644 index 000000000..0bdd8f857 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/command_channel.h @@ -0,0 +1,36 @@ +#pragma once +#include "completion_mailbox.h" +#include "work_queue.h" +#include + +namespace webscene::graphics { +class graphics_service; +// Internal native dispatch ABI. Arguments are values/generation handles only; +// the upload span owns all transient input. Dispatchers are static native code, +// never V8 callbacks, and must report validation failures without throwing. +struct graphics_command { + using arguments=std::array; + void (*execute)(graphics_service&,std::span,const arguments&) noexcept{}; + arguments values{}; +}; +// A producer endpoint may outlive the engine. It has no engine pointer and stops +// admission on close; finalizers/driver threads can safely retain this endpoint. +class command_channel { + work_queue queue_; + std::shared_ptr wake_; +public: + command_channel(size_t capacity,size_t upload_limit,std::shared_ptr wake) + : queue_(capacity,upload_limit),wake_(std::move(wake)) {} + enqueue_result enqueue(graphics_command command,std::span upload={}) { + if (!command.execute) throw std::invalid_argument("native graphics dispatcher is required"); + const auto result=queue_.try_push(command,upload); + if (result==enqueue_result::accepted && wake_) wake_->signal(); + return result; + } + queue_metrics metrics() const { return queue_.metrics(); } +private: + friend class graphics_service; + template bool consume_one(Execute execute) { return queue_.consume_one(execute); } + void close() { queue_.close(); } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/completion_mailbox.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/completion_mailbox.h new file mode 100644 index 000000000..6e0f90d1a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/completion_mailbox.h @@ -0,0 +1,187 @@ +#pragma once +#include "resource_table.h" +#include +#include +#include +#include + +namespace webscene::graphics { +enum class completion_status { success, failed, cancelled, device_lost }; +struct completion_record { + uint64_t operation{}; + resource_owner owner{}; + completion_status status{}; +}; +struct completion_ticket { uint64_t mailbox{}; uint64_t generation{}; size_t slot{}; }; +// Wake implementations may only signal an engine task queue, never enter V8. +// Shared ownership lets late driver callbacks finish without touching a dead engine. +struct completion_wake { + virtual ~completion_wake() = default; + virtual void signal() noexcept = 0; +}; +struct completion_metrics { + size_t pending{},ready{},high_water{},native_pending{},occupied{}; + uint64_t admitted{},delivered{},rejected_publications{},saturated_reservations{}; + uint64_t latency_samples{},total_latency_ns{},max_latency_ns{}; +}; +class completion_mailbox { + enum class state { free, pending, ready }; + struct slot { + state phase{state::free}; + uint64_t generation{}; + bool native_pending{}; + bool requires_polling{true}; + completion_record record{}; + std::chrono::steady_clock::time_point admitted_at{}; + }; + const uint64_t identity_ = new_owner_token(); + const std::thread::id engine_thread_ = std::this_thread::get_id(); + mutable std::mutex mutex_; + std::vector slots_; + std::vector ready_; + size_t head_{}; + size_t count_{}; + size_t pending_{}; + size_t native_pending_{}; + size_t occupied_{}; + bool closed_{}; + const bool measure_latency_; + completion_metrics metrics_{}; + std::shared_ptr wake_; + void check_engine() const { + if (std::this_thread::get_id() != engine_thread_) + throw std::logic_error("graphics completion delivery requires engine thread"); + } + void make_ready(size_t index) { + auto& item = slots_[index]; + if (item.phase != state::ready) { + if (item.phase == state::pending) --pending_; + ready_[(head_ + count_) % ready_.size()] = index; + ++count_; + item.phase = state::ready; + } + } +public: + completion_mailbox(size_t capacity, std::shared_ptr wake, bool measure_latency=false) + : slots_(capacity), ready_(capacity), measure_latency_(measure_latency), wake_(std::move(wake)) { + if (!capacity) throw std::invalid_argument("completion capacity must be positive"); + } + // Reserve BEFORE issuing an asynchronous backend operation. Every admitted + // operation owns a completion slot, so driver callbacks can never overflow. + std::optional reserve(uint64_t operation, resource_owner owner, bool requires_polling=true) { + check_engine(); + std::lock_guard lock(mutex_); + if (closed_) return {}; + for (size_t i = 0; i < slots_.size(); ++i) { + auto& item = slots_[i]; + if (item.phase == state::free && !item.native_pending && item.generation != UINT64_MAX) { + ++item.generation; + item.phase = state::pending; + ++pending_; + item.native_pending=true; + item.requires_polling=requires_polling; + ++native_pending_; + ++occupied_; + ++metrics_.admitted; + metrics_.high_water=std::max(metrics_.high_water,occupied_); + if (measure_latency_) item.admitted_at=std::chrono::steady_clock::now(); + item.record = {operation, owner, completion_status::success}; + return completion_ticket{identity_, item.generation, i}; + } + } + ++metrics_.saturated_reservations; + return {}; + } + // Called exactly once by the operation's native completion, even after + // logical cancellation. Retirement frees a cancelled slot for safe reuse. + bool publish(completion_ticket ticket, completion_status status) { + std::shared_ptr wake; + bool accepted=false; + { + std::lock_guard lock(mutex_); + if (ticket.mailbox != identity_ || ticket.slot >= slots_.size()) { + ++metrics_.rejected_publications; return false; + } + auto& item = slots_[ticket.slot]; + if (item.generation != ticket.generation || !item.native_pending) { + ++metrics_.rejected_publications; return false; + } + item.native_pending=false; + --native_pending_; + if (item.phase==state::free) --occupied_; + if (!closed_ && item.phase==state::pending) { + item.record.status=status; + make_ready(ticket.slot); + accepted=true; + } else ++metrics_.rejected_publications; + // Also wake capacity waiters when a cancelled operation retires. + wake=wake_; + } + if (wake) wake->signal(); + return accepted; + } + void cancel_owner(resource_owner owner, completion_status status = completion_status::cancelled) { + check_engine(); + std::lock_guard lock(mutex_); + for (size_t i = 0; i < slots_.size(); ++i) { + auto& item = slots_[i]; + if (item.phase != state::free && item.record.owner == owner) { + item.record.status = status; + make_ready(i); + } + } + } + void close() { + check_engine(); + std::lock_guard lock(mutex_); + closed_ = true; + wake_.reset(); + for (size_t i = 0; i < slots_.size(); ++i) + if (slots_[i].phase != state::free) { + slots_[i].record.status = completion_status::cancelled; + make_ready(i); + } + } + completion_metrics metrics() const { + std::lock_guard lock(mutex_); + auto result=metrics_; + result.pending=pending_; result.ready=count_; + result.native_pending=native_pending_; result.occupied=occupied_; + return result; + } + bool has_pending() const { std::lock_guard lock(mutex_); return pending_ != 0 || native_pending_ != 0; } + // Spontaneous lifetime callbacks need capacity and wake delivery, but must + // not cause a one-millisecond poll for the entire lifetime of a device. + bool has_pollable_pending() const { + std::lock_guard lock(mutex_); + return std::any_of(slots_.begin(),slots_.end(),[](const auto& item){ + return item.requires_polling && (item.phase==state::pending || item.native_pending); + }); + } + bool has_ready() const { std::lock_guard lock(mutex_); return count_ != 0; } + template bool drain_one(Deliver deliver) { + check_engine(); + completion_record record; + { + std::lock_guard lock(mutex_); + if (!count_) return false; + auto& item = slots_[ready_[head_]]; + record = item.record; + ++metrics_.delivered; + if (measure_latency_) { + const auto elapsed=static_cast(std::chrono::duration_cast( + std::chrono::steady_clock::now()-item.admitted_at).count()); + ++metrics_.latency_samples; + metrics_.total_latency_ns+=elapsed; + metrics_.max_latency_ns=std::max(metrics_.max_latency_ns,elapsed); + } + item.phase = state::free; + if (!item.native_pending) --occupied_; + head_ = (head_ + 1) % ready_.size(); + --count_; + } + deliver(record); // Only this engine-thread scope may resolve JS promises. + return true; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d11_scene_consumer.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d11_scene_consumer.h new file mode 100644 index 000000000..a4c56c754 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d11_scene_consumer.h @@ -0,0 +1,75 @@ +#pragma once +#if defined(_WIN32) +#include "image_lease_abi.h" +#include "d3d12_canvas_images.h" +#include + +namespace webscene::graphics { +// Borrows the image consumer; its caller keeps that lease until poll reports +// completion. Owns every opened texture/fence and the actual host context. +// Ordinary use performs no CPU pixel transfer and no CPU GPU-completion wait. +class d3d11_scene_consumer { + Microsoft::WRL::ComPtr device_; + Microsoft::WRL::ComPtr context_; + Microsoft::WRL::ComPtr texture_; + std::vector> waits_; + Microsoft::WRL::ComPtr completion_; + bool sealed_=false; +public: + ID3D11Texture2D* texture() const noexcept {return texture_.Get();} + static HRESULT create(webscene_gpu_image_consumer_v3* image,ID3D11Device* host, + std::unique_ptr& output) { + if(!image||!host||output)return E_INVALIDARG; + adapter_luid adapter; + auto status=query_adapter_luid(host,adapter);if(FAILED(status))return status; + const auto& source=d3d12_canvas_images::resolve(image->value,adapter); + auto candidate=std::make_unique(); + status=host->QueryInterface(IID_PPV_ARGS(&candidate->device_));if(FAILED(status))return status; + Microsoft::WRL::ComPtr immediate; + host->GetImmediateContext(&immediate); + status=immediate.As(&candidate->context_);if(FAILED(status))return status; + status=candidate->device_->OpenSharedResource1(source.borrowed_handle(),IID_PPV_ARGS(&candidate->texture_)); + if(FAILED(status))return status; + D3D11_TEXTURE2D_DESC desc{};candidate->texture_->GetDesc(&desc); + const auto metadata=image->value.describe(); + if(desc.Width!=metadata.width||desc.Height!=metadata.height||desc.SampleDesc.Count!=1 + ||desc.ArraySize!=1||desc.MipLevels!=1||desc.Format!=dxgi_color_format(metadata.format) + ||!(desc.BindFlags&D3D11_BIND_SHADER_RESOURCE))return DXGI_ERROR_UNSUPPORTED; + status=candidate->device_->CreateFence(0,D3D11_FENCE_FLAG_NONE,IID_PPV_ARGS(&candidate->completion_)); + if(FAILED(status))return status; + std::vector values; + if(image->dependencies) { + const auto count=image->dependencies->count(); + candidate->waits_.reserve(count);values.reserve(count); + for(size_t index=0;indexdependencies->dxgi_fence(index,handle,value)||!win32_nt_handle_ops::valid(handle))return E_INVALIDARG; + Microsoft::WRL::ComPtr fence; + status=candidate->device_->OpenSharedFence(handle,IID_PPV_ARGS(&fence));if(FAILED(status))return status; + candidate->waits_.push_back(std::move(fence));values.push_back(value); + } + } + // Stage every import before changing queue state. A failed enqueue must + // never be retried as if the earlier waits had not already happened. + for(size_t index=0;indexcontext_->Wait(candidate->waits_[index].Get(),values[index]); + if(FAILED(status))return status; + } + output=std::move(candidate);return S_OK; + } + HRESULT seal() { + if(sealed_)return S_OK; + const auto status=context_->Signal(completion_.Get(),1); + if(FAILED(status))return status; + context_->Flush();sealed_=true;return S_OK; + } + HRESULT poll() const { + if(!sealed_)return E_PENDING; + const auto status=device_->GetDeviceRemovedReason();if(FAILED(status))return status; + const auto value=completion_->GetCompletedValue(); + if(value==UINT64_MAX)return DXGI_ERROR_DEVICE_REMOVED; + return value>=1?S_OK:S_FALSE; + } +}; +} +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_canvas_images.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_canvas_images.h new file mode 100644 index 000000000..e7fca4a37 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_canvas_images.h @@ -0,0 +1,99 @@ +#pragma once +#include "d3d12_shared_color.h" +#include "owned_image_pool.h" +#include +#if defined(_WIN32) +namespace webscene::graphics { +// Engine-thread allocator. Every outstanding lease anchors the native storage, +// including after the canvas owner is disposed. GPU completion remains explicit. +class d3d12_canvas_images { + struct storage final : image_provider_lifetime { + image_provider_kind kind() const noexcept override { return image_provider_kind::d3d12; } + struct slot { std::unique_ptr color; image_metadata metadata{}; }; + Microsoft::WRL::ComPtr device; + std::array slots; + std::mutex mutex; + uint64_t bytes{}; + explicit storage(ID3D12Device* value):device(value) {} + }; + std::shared_ptr storage_; + owned_image_pool pool_; + uint64_t limit_; + const std::thread::id thread_=std::this_thread::get_id(); + void check_thread() const { + if (thread_!=std::this_thread::get_id()) throw std::logic_error("D3D12 image allocation requires owner thread"); + } +public: + struct frame { + owned_image_pool::producer producer; + const d3d12_shared_color* color; // Borrowed through producer, never independently retained. + image_metadata metadata; + }; + d3d12_canvas_images(ID3D12Device* device,uint64_t budget,size_t tickets=128, + std::shared_ptr wake={}) + :storage_(std::make_shared(device)),pool_(storage_,tickets,std::move(wake),4),limit_(budget) { + if (!device || !budget) throw std::invalid_argument("D3D12 images require device and budget"); + } + std::optional acquire(image_metadata metadata,HRESULT& status) { + check_thread(); status=S_OK; + auto writer=pool_.acquire(); + if (!writer) { status=DXGI_ERROR_WAS_STILL_DRAWING; return {}; } + // Reservations outlive the lock on exceptional unwind, so completion + // wake callbacks cannot re-enter while the storage mutex is held. + std::array,3> idle; + std::lock_guard lock(storage_->mutex); + auto& slot=storage_->slots[writer->slot()]; + const bool reuse=slot.color && slot.metadata.width==metadata.width + && slot.metadata.height==metadata.height && slot.metadata.format==metadata.format; + metadata.allocation=reuse ? slot.metadata.allocation : new_owner_token(); + writer->set_metadata(metadata); + if (!reuse) { + if (slot.color) storage_->bytes-=slot.color->allocation_bytes(); + slot.color.reset(); + status=d3d12_shared_color::create(storage_->device.Get(),metadata,limit_-storage_->bytes,slot.color); + for (auto& reservation:idle) { + if (status!=E_OUTOFMEMORY) break; + auto candidate=pool_.acquire(); + if (!candidate) break; // Busy images cannot be evicted. + reservation.emplace(std::move(*candidate)); + auto& cached=storage_->slots[reservation->slot()]; + if (cached.color) storage_->bytes-=cached.color->allocation_bytes(); + cached.color.reset(); + status=d3d12_shared_color::create(storage_->device.Get(),metadata,limit_-storage_->bytes,slot.color); + } + // Keep reservations until all retries finish, or acquire() could + // select the same empty slot repeatedly instead of the next cache. + for (auto& reservation:idle) if (reservation) reservation->cancel(false); + if (FAILED(status)) { writer->cancel(false); return {}; } + storage_->bytes+=slot.color->allocation_bytes(); + } + slot.metadata=metadata; + return frame{std::move(*writer),slot.color.get(),metadata}; + } + // Read-only native bridge lookup. The caller must keep consumer alive through + // its GPU completion; returning this object neither begins access nor waits. + static const d3d12_shared_color& resolve(const owned_image_pool::consumer& consumer, + adapter_luid adapter) { + const auto metadata=consumer.describe(); + const auto anchor=consumer.provider(); + if (!anchor || anchor->kind()!=image_provider_kind::d3d12 || !adapter.valid) + throw std::invalid_argument("foreign D3D12 image provider"); + const auto provider=std::static_pointer_cast(anchor); + std::lock_guard lock(provider->mutex); + for (const auto& slot:provider->slots) { + if (slot.color && slot.metadata.allocation==metadata.allocation + && slot.metadata.allocation_generation==metadata.allocation_generation + && slot.metadata.content_serial==metadata.content_serial) { + if (!(slot.color->adapter()==adapter)) throw std::invalid_argument("cross-adapter D3D12 image"); + return *slot.color; + } + } + throw std::invalid_argument("D3D12 image allocation unavailable"); + } + uint64_t allocation_bytes() const { check_thread(); return storage_->bytes; } + size_t busy_images() const { return pool_.busy_images(); } + image_lease_pool::occupancy inspect_occupancy() const { return pool_.inspect_occupancy(); } + void close() { pool_.close(); } +}; +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_fence_waits.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_fence_waits.h new file mode 100644 index 000000000..a5757aa1a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_fence_waits.h @@ -0,0 +1,60 @@ +#pragma once +#include "dxgi_device_identity.h" +#include "nt_handle.h" +#include +#include +#include +#if defined(_WIN32) +namespace webscene::graphics { +struct borrowed_dxgi_fence_wait { HANDLE handle{}; uint64_t value{}; }; +// Open every fence before touching the command queue. Retain this owner until +// the consumer's GPU completion; releasing it is not a completion notification. +class d3d12_fence_waits { + Microsoft::WRL::ComPtr queue_; + std::vector> fences_; + std::vector values_; + bool attempted_{}; +public: + d3d12_fence_waits()=default; + d3d12_fence_waits(const d3d12_fence_waits&)=delete; + d3d12_fence_waits& operator=(const d3d12_fence_waits&)=delete; + static HRESULT prepare(ID3D12CommandQueue* queue,adapter_luid expected_adapter, + std::span waits,std::unique_ptr& result) { + if (!queue || result || !expected_adapter.valid) return E_INVALIDARG; + for (const auto& wait:waits) if (!win32_nt_handle_ops::valid(wait.handle)) return E_INVALIDARG; + Microsoft::WRL::ComPtr device; + auto status=queue->GetDevice(IID_PPV_ARGS(&device)); + if (FAILED(status)) return status; + adapter_luid actual; + status=query_adapter_luid(device.Get(),actual); + if (FAILED(status)) return status; + if (!(actual==expected_adapter)) return DXGI_ERROR_UNSUPPORTED; + try { + auto candidate=std::make_unique(); + candidate->queue_=queue; + candidate->fences_.reserve(waits.size()); candidate->values_.reserve(waits.size()); + for (const auto& wait:waits) { + Microsoft::WRL::ComPtr fence; + status=device->OpenSharedHandle(wait.handle,IID_PPV_ARGS(&fence)); + if (FAILED(status)) return status; + candidate->fences_.push_back(std::move(fence)); + candidate->values_.push_back(wait.value); + } + result=std::move(candidate); return S_OK; + } catch (const std::bad_alloc&) { return E_OUTOFMEMORY; } + } + // Enqueues GPU waits, never a CPU event wait or global device-idle wait. + // Serialize with consumer submission on this queue. A failed enqueue may + // leave earlier waits queued; do not submit sampling or retry this batch. + HRESULT enqueue() noexcept { + if (!queue_ || attempted_) return E_UNEXPECTED; + attempted_=true; + for (size_t i=0;iWait(fences_[i].Get(),values_[i]); + if (FAILED(status)) return status; + } + return S_OK; + } +}; +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_shared_color.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_shared_color.h new file mode 100644 index 000000000..cc892853f --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/d3d12_shared_color.h @@ -0,0 +1,67 @@ +#pragma once +#include "dxgi_device_identity.h" +#include "nt_handle.h" +#include +#if defined(_WIN32) +namespace webscene::graphics { +// Allocation owner only. The lease provider must retain this object through all +// producer/consumer GPU completion; destruction does not wait for a queue. +class d3d12_shared_color { + Microsoft::WRL::ComPtr resource_; + owned_nt_handle handle_; + adapter_luid adapter_; + uint64_t allocation_bytes_{}; +public: + d3d12_shared_color()=default; + d3d12_shared_color(const d3d12_shared_color&)=delete; + d3d12_shared_color& operator=(const d3d12_shared_color&)=delete; + ID3D12Resource* resource() const noexcept { return resource_.Get(); } + HANDLE borrowed_handle() const noexcept { return handle_.get(); } + adapter_luid adapter() const noexcept { return adapter_; } + uint64_t allocation_bytes() const noexcept { return allocation_bytes_; } + static HRESULT create(ID3D12Device* device,const image_metadata& image,uint64_t available_bytes, + std::unique_ptr& result) { + // Never implicitly discard a previous allocation which may be in flight. + if (result || !device || !image.width || !image.height) return E_INVALIDARG; + const auto format=dxgi_color_format(image.format); + if (format==DXGI_FORMAT_UNKNOWN) return E_INVALIDARG; + dxgi_endpoint endpoint; + auto status=identify_dxgi_endpoint(device,endpoint); + if (FAILED(status)) return status; + if (!(endpoint.color_formats & (1U<<(static_cast(image.format)-1)))) + return DXGI_ERROR_UNSUPPORTED; + D3D12_RESOURCE_DESC description{}; + description.Dimension=D3D12_RESOURCE_DIMENSION_TEXTURE2D; + description.Width=image.width; description.Height=image.height; + description.DepthOrArraySize=1; description.MipLevels=1; + description.Format=format; description.SampleDesc.Count=1; + description.Layout=D3D12_TEXTURE_LAYOUT_UNKNOWN; + // Dawn's D3D12 shared-texture import and D3D11 cross-device sampling + // require simultaneous-access resources. Explicit fences still order + // producer writes and consumer reads; this flag is not synchronization. + description.Flags=D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET + | D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS; + const auto allocation=device->GetResourceAllocationInfo(0,1,&description); + if (allocation.SizeInBytes==UINT64_MAX || !allocation.SizeInBytes) return E_INVALIDARG; + if (allocation.SizeInBytes>available_bytes) return E_OUTOFMEMORY; + try { + auto candidate=std::make_unique(); + D3D12_HEAP_PROPERTIES heap{}; heap.Type=D3D12_HEAP_TYPE_DEFAULT; + heap.CreationNodeMask=1; heap.VisibleNodeMask=1; + status=device->CreateCommittedResource(&heap,D3D12_HEAP_FLAG_SHARED,&description, + D3D12_RESOURCE_STATE_COMMON,nullptr,IID_PPV_ARGS(&candidate->resource_)); + if (FAILED(status)) return status; + HANDLE handle=nullptr; + status=device->CreateSharedHandle(candidate->resource_.Get(),nullptr,GENERIC_ALL,nullptr,&handle); + if (FAILED(status)) return status; + if (!win32_nt_handle_ops::valid(handle)) return E_FAIL; + candidate->handle_=owned_nt_handle::adopt(handle); + candidate->adapter_=endpoint.adapter; + candidate->allocation_bytes_=allocation.SizeInBytes; + result=std::move(candidate); + return S_OK; + } catch (const std::bad_alloc&) { return E_OUTOFMEMORY; } + } +}; +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_canvas_images.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_canvas_images.h new file mode 100644 index 000000000..fbaa13bbe --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_canvas_images.h @@ -0,0 +1,150 @@ +#pragma once +#include "owned_image_pool.h" +#include + +namespace webscene::graphics { +// Engine-thread allocator. Pool tickets retain native references independently +// of the canvas owner. This does not grant a lease permission to destroy Device. +class dawn_canvas_images { + struct storage final : image_provider_lifetime { + struct slot { wgpu::Texture texture; image_metadata metadata{}; uint64_t bytes{}; }; + mutable std::mutex mutex; + wgpu::Device device; + std::array slots; + uint64_t resident_bytes{},created{}; + explicit storage(wgpu::Device value):device(std::move(value)) {} + }; + const std::thread::id thread_=std::this_thread::get_id(); + std::shared_ptr storage_; + owned_image_pool pool_; + uint64_t byte_limit_; + void check_thread() const { + if (thread_!=std::this_thread::get_id()) throw std::logic_error("image allocation requires engine thread"); + } +public: + struct frame { + owned_image_pool::producer producer; + wgpu::Texture texture; + image_metadata metadata; + }; + enum class submission_status { pending,success,failed }; + struct submitted_frame { + owned_image_pool::retained image; + std::shared_ptr> status; + }; + // Submit only command buffers recorded for this frame/device. Device error + // scopes still report command validation separately from queue completion. + std::optional submit(frame&& input,const wgpu::CommandBuffer& commands, + std::shared_ptr completion={}) { + check_thread(); + if (!commands || !input.producer.belongs_to(storage_.get())) + throw std::invalid_argument("foreign canvas producer or missing commands"); + auto status=std::make_shared>(submission_status::pending); + auto pending=std::make_shared(std::move(input)); + pending->producer.begin(); + auto image=pending->producer.publish(); + if (!image) { + // No GPU work has been submitted. Return admission backpressure + // without leaving a producer outstanding or waking our own retry. + pending->producer.complete(); pending->producer.cancel(false); + return {}; + } + auto queue=storage_->device.GetQueue(); + queue.Submit(1,&commands); + queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowSpontaneous, + [pending,status,completion](wgpu::QueueWorkDoneStatus result,wgpu::StringView) { + pending->producer.complete(); + status->store(result==wgpu::QueueWorkDoneStatus::Success + ? submission_status::success : submission_status::failed,std::memory_order_release); + if (completion) completion->signal(); + }); + return submitted_frame{std::move(*image),std::move(status)}; + } + dawn_canvas_images(wgpu::Device device,uint64_t byte_limit,size_t tickets=128,std::shared_ptr wake={}) + :storage_(std::make_shared(std::move(device))),pool_(storage_,tickets,std::move(wake)),byte_limit_(byte_limit) { + if (!storage_->device || !byte_limit) throw std::invalid_argument("Dawn image storage requires device and budget"); + } + std::optional acquire(image_metadata metadata) { + check_thread(); + wgpu::TextureFormat format; + uint32_t pixel_bytes=4; + switch (metadata.format) { + case image_format::rgba8_unorm: format=wgpu::TextureFormat::RGBA8Unorm; break; + case image_format::bgra8_unorm: format=wgpu::TextureFormat::BGRA8Unorm; break; + case image_format::rgba16_float: format=wgpu::TextureFormat::RGBA16Float; pixel_bytes=8; break; + case image_format::rgba8_srgb: format=wgpu::TextureFormat::RGBA8UnormSrgb; break; + case image_format::bgra8_srgb: format=wgpu::TextureFormat::BGRA8UnormSrgb; break; + default: throw std::invalid_argument("unsupported canvas format"); + } + wgpu::Limits limits{}; + if (storage_->device.GetLimits(&limits)!=wgpu::Status::Success + || !metadata.width || !metadata.height || metadata.width>limits.maxTextureDimension2D + || metadata.height>limits.maxTextureDimension2D) + throw std::invalid_argument("invalid canvas texture dimensions"); + const uint64_t pixels=uint64_t(metadata.width)*metadata.height; + if (pixels>byte_limit_/pixel_bytes) return {}; + const auto bytes=pixels*pixel_bytes; + auto writer=pool_.acquire(); + if (!writer) return {}; + // Reserve idle slots until eviction finishes. Normal rollback is quiet; + // exceptional unwinding releases storage's mutex before wake callbacks. + std::array,2> idle; + std::lock_guard lock(storage_->mutex); + auto& slot=storage_->slots[writer->slot()]; + const bool reuse=slot.texture && slot.metadata.width==metadata.width + && slot.metadata.height==metadata.height && slot.metadata.format==metadata.format; + metadata.allocation=reuse ? slot.metadata.allocation : new_owner_token(); + // Validate all portable fields before changing native storage. + writer->set_metadata(metadata); + for (auto& reservation:idle) { + if (bytes<=byte_limit_-(storage_->resident_bytes-slot.bytes)) break; + auto candidate=pool_.acquire(); + if (!candidate) break; // Retained/submitted images cannot be evicted. + reservation.emplace(std::move(*candidate)); + auto& cached=storage_->slots[reservation->slot()]; + cached.texture=nullptr; + storage_->resident_bytes-=cached.bytes; cached.bytes=0; + } + for (auto& reservation:idle) if (reservation) reservation->cancel(false); + if (bytes>byte_limit_-(storage_->resident_bytes-slot.bytes)) { + writer->cancel(false); return {}; + } + if (!reuse) { + // Only an idle slot can be acquired. Drop its old allocation before + // replacement so this allocator does not temporarily exceed budget. + slot.texture=nullptr; storage_->resident_bytes-=slot.bytes; slot.bytes=0; + wgpu::TextureDescriptor descriptor{}; + descriptor.size={metadata.width,metadata.height,1}; + descriptor.format=format; + descriptor.usage=wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding + | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + slot.texture=storage_->device.CreateTexture(&descriptor); + if (!slot.texture) throw std::runtime_error("Dawn canvas texture creation failed"); + slot.bytes=bytes; storage_->resident_bytes+=bytes; ++storage_->created; + } + slot.metadata=metadata; + return frame{std::move(*writer),slot.texture,metadata}; + } + // Native presenter only. Keep the consumer alive through its GPU fence; + // resolving an object is not synchronization with its producer timeline. + // The caller may use this texture for reading only, never Destroy or writes. + static wgpu::Texture resolve(const owned_image_pool::consumer& consumer,const wgpu::Device& device) { + const auto metadata=consumer.describe(); + const auto provider=std::dynamic_pointer_cast(consumer.provider()); + if (!provider || !device || provider->device.Get()!=device.Get()) + throw std::invalid_argument("foreign Dawn image provider or device"); + std::lock_guard lock(provider->mutex); + for (const auto& slot:provider->slots) { + if (slot.texture && slot.metadata.allocation==metadata.allocation + && slot.metadata.allocation_generation==metadata.allocation_generation + && slot.metadata.content_serial==metadata.content_serial) + return slot.texture; + } + throw std::invalid_argument("Dawn image allocation unavailable"); + } + uint64_t resident_bytes() const { check_thread(); return storage_->resident_bytes; } + uint64_t created_images() const { check_thread(); return storage_->created; } + size_t busy_images() const { return pool_.busy_images(); } + void close() { pool_.close(); } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_device.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_device.h new file mode 100644 index 000000000..224778f5e --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_device.h @@ -0,0 +1,424 @@ +#pragma once +#include "completion_mailbox.h" +#include "resource_table.h" +#include +#include + +namespace webscene::graphics { +// One logical WebGPU device. Adapter/device references remain native and belong +// to the engine thread; callback captures retain only the completion mailbox. +struct device_loss_signal { + struct snapshot { wgpu::DeviceLostReason reason;std::string message; }; + std::atomic lost{}; + std::shared_ptr wake; +private: + mutable std::mutex mutex_; + std::optional result_; + std::shared_ptr mailbox_; + std::optional ticket_; +public: + explicit device_loss_signal(std::shared_ptr value) : wake(std::move(value)) {} + std::optional result() const {std::lock_guard lock(mutex_);return result_;} + void subscribe(std::shared_ptr mailbox,completion_ticket ticket) { + bool ready; + { + std::lock_guard lock(mutex_); + if(mailbox_)throw std::logic_error("Device loss already subscribed"); + mailbox_=mailbox;ticket_=ticket;ready=result_.has_value(); + } + if(ready)mailbox->publish(ticket,completion_status::success); + } + void publish(wgpu::DeviceLostReason reason,wgpu::StringView message) { + std::shared_ptr mailbox;std::optional ticket; + { + std::lock_guard lock(mutex_); + if(result_)return; + snapshot value{reason,{}}; + constexpr size_t limit=1024*1024; + size_t length=message.length; + if(length==WGPU_STRLEN)length=message.data?strnlen(message.data,limit):0; + // Diagnostics are bounded; loss must still be delivered if a driver + // supplies an oversized message or allocation fails. + try {if(message.data)value.message.assign(message.data,std::min(length,limit));}catch(...){} + result_=std::move(value);mailbox=mailbox_;ticket=ticket_; + if(reason!=wgpu::DeviceLostReason::Destroyed&&reason!=wgpu::DeviceLostReason::CallbackCancelled) + lost.store(true,std::memory_order_release); + } + if(mailbox&&ticket)mailbox->publish(*ticket,completion_status::success); + if(wake)wake->signal(); + } + static void configure(wgpu::DeviceDescriptor& descriptor,std::shared_ptr signal) { + if (!signal) throw std::invalid_argument("device loss signal is required"); + descriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous, + [signal](const wgpu::Device&,wgpu::DeviceLostReason reason,wgpu::StringView message) { + signal->publish(reason,message); + }); + } +}; + +class dawn_device { + const std::thread::id thread_=std::this_thread::get_id(); + const resource_owner owner_; + std::shared_ptr mailbox_; + wgpu::Adapter adapter_; + wgpu::Device device_; + bool closed_{}; + bool lost_{}; + std::shared_ptr loss_; + resource_table buffers_; + size_t active_buffer_scopes_{}; + resource_table shaders_; + resource_table samplers_; + size_t active_shader_scopes_{}; + resource_table bind_groups_; + size_t active_bind_group_scopes_{}; + resource_table bind_group_layouts_; + size_t active_bind_group_layout_scopes_{}; + resource_table pipeline_layouts_; + size_t active_pipeline_layout_scopes_{}; + resource_table render_pipelines_; + resource_table compute_pipelines_; + size_t active_render_pipeline_scopes_{}; + size_t active_compute_pipeline_scopes_{}; + resource_table textures_; + resource_table texture_views_; + size_t active_texture_scopes_{},active_texture_view_scopes_{}; + resource_table command_encoders_; + resource_table render_passes_; + resource_table compute_passes_; + resource_table command_buffers_; + size_t active_command_scopes_{}; + void check_thread() const { + if (std::this_thread::get_id()!=thread_) + throw std::logic_error("Dawn device requires its engine thread"); + } +public: + dawn_device(uint64_t engine,std::shared_ptr mailbox, + wgpu::Adapter adapter,wgpu::Device device,std::shared_ptr loss={},size_t buffer_capacity=1024,size_t shader_capacity=1024,size_t render_pipeline_capacity=1024,size_t texture_capacity=1024,size_t texture_view_capacity=4096,size_t command_capacity=1024) + : owner_{engine,new_owner_token(),0},mailbox_(std::move(mailbox)), + adapter_(std::move(adapter)),device_(std::move(device)),loss_(std::move(loss)),buffers_(buffer_capacity,owner_),shaders_(shader_capacity,owner_),samplers_(shader_capacity,owner_),bind_groups_(render_pipeline_capacity,owner_),bind_group_layouts_(render_pipeline_capacity,owner_),pipeline_layouts_(render_pipeline_capacity,owner_),render_pipelines_(render_pipeline_capacity,owner_),compute_pipelines_(render_pipeline_capacity,owner_),textures_(texture_capacity,owner_),texture_views_(texture_view_capacity,owner_),command_encoders_(command_capacity,owner_),render_passes_(command_capacity,owner_),compute_passes_(command_capacity,owner_),command_buffers_(command_capacity,owner_) { + if (!engine || !mailbox_ || !adapter_ || !device_) + throw std::invalid_argument("Dawn device requires native ownership"); + } + dawn_device(const dawn_device&)=delete; + dawn_device& operator=(const dawn_device&)=delete; + ~dawn_device() { if (std::this_thread::get_id()!=thread_) std::terminate(); close(); } + std::shared_ptr loss_signal() const {check_thread();return loss_;} + resource_owner owner() const { check_thread(); return owner_; } + const wgpu::Adapter& adapter() const { check_thread(); return adapter_; } + const wgpu::Device& native() const { + check_thread(); + if (closed_ || lost_ || (loss_ && loss_->lost.load(std::memory_order_acquire))) + throw std::logic_error("Dawn device is closed or lost"); + return device_; + } + // Internal native descriptor entry point. Browser descriptor validation and + // error-object handling must precede this call in the JavaScript binding. + resource_handle create_buffer(const wgpu::BufferDescriptor& descriptor) { + const auto& device=native(); + if (!buffers_.can_insert()) throw std::length_error("graphics buffer limit reached"); + auto buffer=device.CreateBuffer(&descriptor); + if (!buffer) throw std::runtime_error("Dawn did not return a buffer"); + return buffers_.insert(owner_,std::make_unique(std::move(buffer))); + } + template void with_buffer(resource_handle handle,Execute execute) { + check_thread(); + const auto& buffer=buffers_.get(handle,owner_); + struct guard { + size_t& count; + explicit guard(size_t& value) : count(value) { ++count; } + ~guard() { --count; } + } scope(active_buffer_scopes_); + execute(buffer); + } + // WebGPU destroy invalidates the native allocation, but the wrapper remains + // valid for metadata and repeated destroy calls until it is itself released. + void destroy_buffer(resource_handle handle) { + check_thread(); + if (active_buffer_scopes_) throw std::logic_error("Cannot destroy buffers during execution"); + buffers_.get(handle,owner_).Destroy(); + } + // Wrapper collection releases only this reference. Dawn/queued operations + // retain their own native references; collection must never call Destroy. + void release_buffer(resource_handle handle) { + check_thread(); + if (active_buffer_scopes_) throw std::logic_error("Cannot release buffers during execution"); + buffers_.destroy(handle,owner_); + } + size_t live_buffers() const { check_thread(); return buffers_.resident_count(); } + resource_handle create_shader_module(const wgpu::ShaderModuleDescriptor& descriptor) { + const auto& device=native(); + if(!shaders_.can_insert())throw std::length_error("Graphics shader-module capacity exhausted"); + auto shader=device.CreateShaderModule(&descriptor); + if(!shader)throw std::runtime_error("Dawn did not return a shader module"); + return shaders_.insert(owner_,std::make_unique(std::move(shader))); + } + template void with_shader_module(resource_handle handle,Execute execute) { + check_thread(); + const auto& shader=shaders_.get(handle,owner_); + struct guard { size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;} } scope(active_shader_scopes_); + execute(shader); + } + void release_shader_module(resource_handle handle) { + check_thread(); + if(active_shader_scopes_)throw std::logic_error("Cannot release shader module during execution"); + shaders_.destroy(handle,owner_); + } + size_t live_shader_modules() const {check_thread();return shaders_.resident_count();} + resource_handle create_sampler(const wgpu::SamplerDescriptor& descriptor){ + const auto& device=native();return samplers_.insert(owner_,std::make_unique(device.CreateSampler(&descriptor))); + } + template void with_sampler(resource_handle handle,Execute execute){ + check_thread();execute(samplers_.get(handle,owner_)); + } + void release_sampler(resource_handle handle){check_thread();samplers_.destroy(handle,owner_);} + resource_handle adopt_render_pipeline(wgpu::RenderPipeline value) { + native();return render_pipelines_.insert(owner_,std::make_unique(std::move(value))); + } + resource_handle adopt_compute_pipeline(wgpu::ComputePipeline value) { + native();return compute_pipelines_.insert(owner_,std::make_unique(std::move(value))); + } + resource_handle adopt_bind_group_layout(wgpu::BindGroupLayout value) { + native();return bind_group_layouts_.insert(owner_,std::make_unique(std::move(value))); + } + resource_handle create_render_pipeline(const wgpu::RenderPipelineDescriptor& descriptor) { + const auto& device=native(); + if(!render_pipelines_.can_insert())throw std::length_error("Graphics render-pipeline capacity exhausted"); + auto pipeline=device.CreateRenderPipeline(&descriptor); + if(!pipeline)throw std::runtime_error("Dawn did not return a render pipeline"); + return render_pipelines_.insert(owner_,std::make_unique(std::move(pipeline))); + } + template void with_render_pipeline(resource_handle handle,Execute execute) { + check_thread(); + const auto& pipeline=render_pipelines_.get(handle,owner_); + struct guard { size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;} } scope(active_render_pipeline_scopes_); + execute(pipeline); + } + void release_render_pipeline(resource_handle handle) { + check_thread(); + if(active_render_pipeline_scopes_)throw std::logic_error("Cannot release render pipeline during execution"); + render_pipelines_.destroy(handle,owner_); + } + size_t live_render_pipelines() const {check_thread();return render_pipelines_.resident_count();} + resource_handle create_compute_pipeline(const wgpu::ComputePipelineDescriptor& descriptor) { + const auto& device=native(); + if(!compute_pipelines_.can_insert())throw std::length_error("Graphics compute-pipeline capacity exhausted"); + auto pipeline=device.CreateComputePipeline(&descriptor); + if(!pipeline)throw std::runtime_error("Dawn did not return a compute pipeline"); + return compute_pipelines_.insert(owner_,std::make_unique(std::move(pipeline))); + } + template void with_compute_pipeline(resource_handle handle,Execute execute) { + check_thread(); + const auto& pipeline=compute_pipelines_.get(handle,owner_); + struct guard { size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;} } scope(active_compute_pipeline_scopes_); + execute(pipeline); + } + void release_compute_pipeline(resource_handle handle) { + check_thread(); + if(active_compute_pipeline_scopes_)throw std::logic_error("Cannot release compute pipeline during execution"); + compute_pipelines_.destroy(handle,owner_); + } + size_t live_compute_pipelines() const {check_thread();return compute_pipelines_.resident_count();} + resource_handle create_bind_group_layout(const wgpu::BindGroupLayoutDescriptor& descriptor) { + const auto& device=native(); + if(!bind_group_layouts_.can_insert())throw std::length_error("Graphics bind_group_layout capacity exhausted"); + auto pipeline=device.CreateBindGroupLayout(&descriptor); + if(!pipeline)throw std::runtime_error("Dawn did not return a bind_group_layout"); + return bind_group_layouts_.insert(owner_,std::make_unique(std::move(pipeline))); + } + template void with_bind_group_layout(resource_handle handle,Execute execute) { + check_thread(); + const auto& pipeline=bind_group_layouts_.get(handle,owner_); + struct guard { size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;} } scope(active_bind_group_layout_scopes_); + execute(pipeline); + } + void release_bind_group_layout(resource_handle handle) { + check_thread(); + if(active_bind_group_layout_scopes_)throw std::logic_error("Cannot release bind_group_layout during execution"); + bind_group_layouts_.destroy(handle,owner_); + } + size_t live_bind_group_layouts() const {check_thread();return bind_group_layouts_.resident_count();} + resource_handle create_bind_group(const wgpu::BindGroupDescriptor& descriptor) { + const auto& device=native(); + if(!bind_groups_.can_insert())throw std::length_error("Graphics bind_group capacity exhausted"); + auto pipeline=device.CreateBindGroup(&descriptor); + if(!pipeline)throw std::runtime_error("Dawn did not return a bind_group"); + return bind_groups_.insert(owner_,std::make_unique(std::move(pipeline))); + } + template void with_bind_group(resource_handle handle,Execute execute) { + check_thread(); + const auto& pipeline=bind_groups_.get(handle,owner_); + struct guard { size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;} } scope(active_bind_group_scopes_); + execute(pipeline); + } + void release_bind_group(resource_handle handle) { + check_thread(); + if(active_bind_group_scopes_)throw std::logic_error("Cannot release bind_group during execution"); + bind_groups_.destroy(handle,owner_); + } + size_t live_bind_groups() const {check_thread();return bind_groups_.resident_count();} + resource_handle create_pipeline_layout(const wgpu::PipelineLayoutDescriptor& descriptor) { + const auto& device=native(); + if(!pipeline_layouts_.can_insert())throw std::length_error("Graphics pipeline_layout capacity exhausted"); + auto pipeline=device.CreatePipelineLayout(&descriptor); + if(!pipeline)throw std::runtime_error("Dawn did not return a pipeline_layout"); + return pipeline_layouts_.insert(owner_,std::make_unique(std::move(pipeline))); + } + template void with_pipeline_layout(resource_handle handle,Execute execute) { + check_thread(); + const auto& pipeline=pipeline_layouts_.get(handle,owner_); + struct guard { size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;} } scope(active_pipeline_layout_scopes_); + execute(pipeline); + } + void release_pipeline_layout(resource_handle handle) { + check_thread(); + if(active_pipeline_layout_scopes_)throw std::logic_error("Cannot release pipeline_layout during execution"); + pipeline_layouts_.destroy(handle,owner_); + } + size_t live_pipeline_layouts() const {check_thread();return pipeline_layouts_.resident_count();} + resource_handle create_texture(const wgpu::TextureDescriptor& descriptor) { + const auto& device=native(); + if(!textures_.can_insert())throw std::length_error("Graphics texture capacity exhausted"); + auto texture=device.CreateTexture(&descriptor); + if(!texture)throw std::runtime_error("Dawn did not return a texture"); + return textures_.insert(owner_,std::make_unique(std::move(texture))); + } + // Host-only adoption of a texture imported/created on source_device. The + // importer must supply the true source device; JavaScript cannot call this. + // No texture allocation or pixel transfer occurs at this boundary. + resource_handle adopt_texture(const wgpu::Device& source_device,wgpu::Texture texture) { + const auto& device=native(); + if(!texture || source_device.Get()!=device.Get())throw std::invalid_argument("Imported texture requires its owning device"); + if(!textures_.can_insert())throw std::length_error("Graphics texture capacity exhausted"); + return textures_.insert(owner_,std::make_unique(std::move(texture))); + } + template void with_texture(resource_handle handle,Execute execute) { + check_thread(); + const auto& texture=textures_.get(handle,owner_); + struct guard { size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;} } scope(active_texture_scopes_); + execute(texture); + } + void release_texture(resource_handle handle) { + check_thread(); + if(active_texture_scopes_)throw std::logic_error("Cannot release texture during execution"); + textures_.destroy(handle,owner_); + } + size_t live_textures() const {check_thread();return textures_.resident_count();} + resource_handle create_texture_view(resource_handle texture,const wgpu::TextureViewDescriptor& descriptor) { + native(); + if(!texture_views_.can_insert())throw std::length_error("Texture view capacity exhausted"); + auto view=textures_.get(texture,owner_).CreateView(&descriptor); + if(!view)throw std::runtime_error("Dawn did not return a texture view"); + return texture_views_.insert(owner_,std::make_unique(std::move(view))); + } + template void with_texture_view(resource_handle handle,Execute execute) { + check_thread();const auto& view=texture_views_.get(handle,owner_); + struct guard {size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;}} scope(active_texture_view_scopes_); + execute(view); + } + void release_texture_view(resource_handle handle) { + check_thread();if(active_texture_view_scopes_)throw std::logic_error("Cannot release a borrowed texture view"); + texture_views_.destroy(handle,owner_); + } + void destroy_texture(resource_handle handle) { + check_thread();if(active_texture_scopes_ || active_texture_view_scopes_)throw std::logic_error("Cannot destroy a borrowed texture"); + textures_.get(handle,owner_).Destroy(); + } + size_t live_texture_views() const {check_thread();return texture_views_.resident_count();} + resource_handle create_command_encoder(const wgpu::CommandEncoderDescriptor& descriptor) { + const auto& device=native();if(!command_encoders_.can_insert())throw std::length_error("Command encoder capacity exhausted"); + auto encoder=device.CreateCommandEncoder(&descriptor);if(!encoder)throw std::runtime_error("Dawn did not return a command encoder"); + return command_encoders_.insert(owner_,std::make_unique(std::move(encoder))); + } + resource_handle begin_render_pass(resource_handle encoder,const wgpu::RenderPassDescriptor& descriptor) { + native();if(!render_passes_.can_insert())throw std::length_error("Render pass capacity exhausted"); + auto pass=command_encoders_.get(encoder,owner_).BeginRenderPass(&descriptor);if(!pass)throw std::runtime_error("Dawn did not return a render pass"); + return render_passes_.insert(owner_,std::make_unique(std::move(pass))); + } + resource_handle begin_compute_pass(resource_handle encoder,const wgpu::ComputePassDescriptor& descriptor) { + native();if(!compute_passes_.can_insert())throw std::length_error("Compute pass capacity exhausted"); + auto pass=command_encoders_.get(encoder,owner_).BeginComputePass(&descriptor);if(!pass)throw std::runtime_error("Dawn did not return a compute pass"); + return compute_passes_.insert(owner_,std::make_unique(std::move(pass))); + } + resource_handle finish_command_encoder(resource_handle encoder,const wgpu::CommandBufferDescriptor& descriptor) { + native();if(!command_buffers_.can_insert())throw std::length_error("Command buffer capacity exhausted"); + auto command=command_encoders_.get(encoder,owner_).Finish(&descriptor);if(!command)throw std::runtime_error("Dawn did not return a command buffer"); + return command_buffers_.insert(owner_,std::make_unique(std::move(command))); + } + template void with_command_encoder(resource_handle handle,Execute execute) { + check_thread();const auto& resource=command_encoders_.get(handle,owner_); + struct guard {size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;}} scope(active_command_scopes_); + execute(resource); + } + void release_command_encoder(resource_handle handle) { + check_thread();if(active_command_scopes_)throw std::logic_error("Cannot release command resources during execution"); + command_encoders_.destroy(handle,owner_); + } + size_t live_command_encoders() const {check_thread();return command_encoders_.resident_count();} + template void with_render_pass(resource_handle handle,Execute execute) { + check_thread();const auto& resource=render_passes_.get(handle,owner_); + struct guard {size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;}} scope(active_command_scopes_); + execute(resource); + } + void release_render_pass(resource_handle handle) { + check_thread();if(active_command_scopes_)throw std::logic_error("Cannot release command resources during execution"); + render_passes_.destroy(handle,owner_); + } + size_t live_render_passes() const {check_thread();return render_passes_.resident_count();} + template void with_compute_pass(resource_handle handle,Execute execute) { + check_thread();const auto& resource=compute_passes_.get(handle,owner_); + struct guard {size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;}} scope(active_command_scopes_); + execute(resource); + } + void release_compute_pass(resource_handle handle) { + check_thread();if(active_command_scopes_)throw std::logic_error("Cannot release command resources during execution"); + compute_passes_.destroy(handle,owner_); + } + size_t live_compute_passes() const {check_thread();return compute_passes_.resident_count();} + template void with_command_buffer(resource_handle handle,Execute execute) { + check_thread();const auto& resource=command_buffers_.get(handle,owner_); + struct guard {size_t& count;explicit guard(size_t& value):count(value){++count;}~guard(){--count;}} scope(active_command_scopes_); + execute(resource); + } + void release_command_buffer(resource_handle handle) { + check_thread();if(active_command_scopes_)throw std::logic_error("Cannot release command resources during execution"); + command_buffers_.destroy(handle,owner_); + } + size_t live_command_buffers() const {check_thread();return command_buffers_.resident_count();} + bool loss_pending() const { + check_thread(); + return !closed_ && !lost_ && loss_ && loss_->lost.load(std::memory_order_acquire); + } + void process_loss() { + check_thread(); + if (!loss_pending()) return; + lost_=true; + mailbox_->cancel_owner(owner_,completion_status::device_lost); + } + void close() { + check_thread(); + if (closed_) return; + if (active_bind_group_scopes_ || active_pipeline_layout_scopes_ || active_bind_group_layout_scopes_ || active_buffer_scopes_ || active_shader_scopes_ || active_render_pipeline_scopes_ || active_compute_pipeline_scopes_ || active_texture_scopes_ || active_texture_view_scopes_ || active_command_scopes_) throw std::logic_error("Cannot close device during resource execution"); + process_loss(); + closed_=true; + // Logical cancellation is independent of physical GPU completion. + // Dawn retains submitted native resources until its backend is safe; + // higher-level submission tables still require their completion fences. + if (!lost_) mailbox_->cancel_owner(owner_); + device_.Destroy(); + render_passes_.destroy_owner(owner_); + compute_passes_.destroy_owner(owner_); + command_encoders_.destroy_owner(owner_); + command_buffers_.destroy_owner(owner_); + buffers_.destroy_owner(owner_); + shaders_.destroy_owner(owner_);samplers_.destroy_owner(owner_); + render_pipelines_.destroy_owner(owner_); + compute_pipelines_.destroy_owner(owner_); + pipeline_layouts_.destroy_owner(owner_); + bind_groups_.destroy_owner(owner_); + bind_group_layouts_.destroy_owner(owner_); + texture_views_.destroy_owner(owner_); + textures_.destroy_owner(owner_); + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_canvas_host.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_canvas_host.h new file mode 100644 index 000000000..95776cf4e --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_canvas_host.h @@ -0,0 +1,103 @@ +#pragma once +#include "dawn_dxgi_submission.h" +#include +#include "windows_gpu_adapter.h" +#if defined(_WIN32) +namespace webscene::graphics { +// Engine-thread canvas provider. The host drains completed publications into the +// scene and applies canvas generation/serial filtering there. Four pending +// retirements bound storage and callback ownership during presenter backpressure. +class dawn_dxgi_canvas_host final { + struct active_frame { + d3d12_canvas_images::frame frame; + wgpu::Device device; + std::shared_ptr shared; + std::shared_ptr device_lifetime; + }; + const std::thread::id thread_=std::this_thread::get_id(); + Microsoft::WRL::ComPtr allocator_=windows_canvas_device(); + d3d12_canvas_images images_; + std::unique_ptr active_; + std::array,4> pending_{}; + size_t active_slot_=0; + std::weak_ptr latest_submission_; + std::shared_ptr wake_; + void check_thread()const { + if(thread_!=std::this_thread::get_id())throw std::logic_error("Canvas provider requires its engine thread"); + } + void clear_retired() { + for(auto& item:pending_)if(item) { + auto state=item->state(); + if(state==dawn_dxgi_submission::status::failed||state==dawn_dxgi_submission::status::discarded|| + state==dawn_dxgi_submission::status::consumed)item.reset(); + } + } +public: + explicit dawn_dxgi_canvas_host(uint64_t budget,std::shared_ptr wake={}) + :images_(allocator_.Get(),budget,128,wake),wake_(std::move(wake)) {} + dawn_dxgi_canvas_host(const dawn_dxgi_canvas_host&)=delete; + dawn_dxgi_canvas_host& operator=(const dawn_dxgi_canvas_host&)=delete; + ~dawn_dxgi_canvas_host() { + // The GPUCanvasContext must retire before destroying its provider. + // Dropping an active imported frame could recycle submitted storage. + if(active_)std::terminate(); + } + wgpu::Texture acquire(image_metadata metadata,const wgpu::Device& device, + const wgpu::TextureDescriptor& descriptor,std::shared_ptr device_lifetime={}) { + check_thread();if(active_)throw std::logic_error("Canvas already owns a current frame"); + clear_retired();size_t slot=0;while(slot(active_frame{std::move(*frame),device,shared,std::move(device_lifetime)}); + if(shared->begin(false)!=dxgi_access_status::success)return {}; + active_=std::move(next);active_slot_=slot; + return shared->texture(); + } + void retire(const wgpu::Texture& texture,bool present) { + check_thread(); + if(!active_||texture.Get()!=active_->shared->texture().Get()) + throw std::invalid_argument("Canvas retirement requires its current texture"); + pending_[active_slot_]=dawn_dxgi_submission::publish_submitted(std::move(active_->frame), + active_->device,active_->shared,active_->device_lifetime,wake_,present); + latest_submission_=present ? pending_[active_slot_] : std::weak_ptr{}; + active_.reset(); + } + // Capture at the rendering-opportunity boundary, before draining ready + // outputs. The weak lookup adds no hidden image retention; the returned + // ticket owns its exact allocation independently of later provider work. + std::unique_ptr capture_latest_submission() { + check_thread(); + if(active_)throw std::logic_error("End the current GPU opportunity before capturing its output"); + auto submission=latest_submission_.lock(); + return submission ? submission->capture_snapshot() : nullptr; + } + std::optional take_ready() { + check_thread();clear_retired(); + for(auto& item:pending_)if(item&&item->state()==dawn_dxgi_submission::status::ready) { + auto image=item->take_ready();item.reset();return image; + } + return {}; + } + bool has_completed_retirements()const { + check_thread(); + for(const auto& item:pending_)if(item&&item->state()!=dawn_dxgi_submission::status::pending)return true; + return false; + } + bool idle() { + check_thread();clear_retired();if(active_)return false; + for(const auto& item:pending_)if(item)return false; + return true; + } + bool can_acquire() { + check_thread();clear_retired(); + if(active_||images_.busy_images()>=4)return false; + return std::any_of(pending_.begin(),pending_.end(),[](const auto& item){return !item;}); + } + image_lease_pool::occupancy inspect_occupancy() const { return images_.inspect_occupancy(); } + size_t busy_images()const {check_thread();return images_.busy_images();} +}; +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_canvas_texture.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_canvas_texture.h new file mode 100644 index 000000000..ffad24f7c --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_canvas_texture.h @@ -0,0 +1,20 @@ +#pragma once +#include "dawn_dxgi_image.h" +#include "d3d12_canvas_images.h" +#if defined(_WIN32) +namespace webscene::graphics { +inline std::shared_ptr import_dawn_dxgi_canvas_texture( + const d3d12_canvas_images::frame& frame,const wgpu::Device& device, + const wgpu::TextureDescriptor& descriptor,ID3D12Device* allocator) { + dxgi_endpoint endpoint; + if (FAILED(identify_dxgi_endpoint(allocator,endpoint)))return {}; + // Alpha is a presentation interpretation of these four-channel formats. + endpoint.alpha_modes=3; + endpoint.shared_fence=device.HasFeature(wgpu::FeatureName::SharedFenceDXGISharedHandle); + std::unique_ptr image; + if(dawn_dxgi_image::import(device,frame.color->borrowed_handle(),endpoint,endpoint, + frame.metadata,descriptor.usage,image)!=dxgi_import_status::success)return {}; + return std::shared_ptr(std::move(image)); +} +} +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_fences.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_fences.h new file mode 100644 index 000000000..e5814b3b2 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_fences.h @@ -0,0 +1,100 @@ +#pragma once +#include "nt_handle.h" +#include +#include +#include +#include + +namespace webscene::graphics { +enum class dxgi_fence_status { + success,invalid_argument,unsupported_fence,handle_failure,out_of_memory,missing_device_feature,import_failure +}; +// ExportInfo returns a borrowed handle owned by the Dawn fence. Duplicate every +// handle before releasing EndAccessState; never close the borrowed export. +// Ops injection tests ownership/rollback without pretending to test a Windows GPU. +template struct dxgi_fence_wait { + unique_nt_handle handle; + uint64_t value{}; +}; +template dxgi_fence_status duplicate_dxgi_fences( + std::span fences,std::span values, + std::vector>& result,Export export_handle) { + result.clear(); + if (fences.size()!=values.size()) return dxgi_fence_status::invalid_argument; + try { + std::vector> candidate; + candidate.reserve(fences.size()); + for (size_t i=0;i::duplicate(borrowed),values[i]}); + } + result=std::move(candidate); + return dxgi_fence_status::success; + } catch (const std::bad_alloc&) { return dxgi_fence_status::out_of_memory; } + catch (const std::system_error&) { return dxgi_fence_status::handle_failure; } +} +// The caller keeps borrowed NT handles alive until this function returns. Dawn's +// pinned D3D11/D3D12 imports duplicate/open them; imported fences then own their +// lifetime independently. No ownership of the supplied handles is transferred. +struct imported_dxgi_fences { + std::vector fences; + std::vector values; +}; +inline dxgi_fence_status import_dxgi_fences(const wgpu::Device& device, + std::span handles,std::span values,imported_dxgi_fences& result) { + result={}; + if (!device || handles.size()!=values.size()) return dxgi_fence_status::invalid_argument; + for (auto handle:handles) { + if (!handle || handle==reinterpret_cast(~uintptr_t{0})) + return dxgi_fence_status::invalid_argument; + } + if (!device.HasFeature(wgpu::FeatureName::SharedFenceDXGISharedHandle)) + return dxgi_fence_status::missing_device_feature; + try { + imported_dxgi_fences candidate; + candidate.fences.reserve(handles.size()); + candidate.values.assign(values.begin(),values.end()); + for (auto handle:handles) { + wgpu::SharedFenceDXGISharedHandleDescriptor dxgi{}; dxgi.handle=handle; + wgpu::SharedFenceDescriptor descriptor{}; descriptor.nextInChain=&dxgi; + auto fence=device.ImportSharedFence(&descriptor); + if (!fence) return dxgi_fence_status::import_failure; + // Dawn can return a non-null error object. Verify the native type + // instead of treating pointer existence as successful import. + wgpu::SharedFenceExportInfo info{}; + fence.ExportInfo(&info); + if (info.type!=wgpu::SharedFenceType::DXGISharedHandle) + return dxgi_fence_status::import_failure; + candidate.fences.push_back(std::move(fence)); + } + result=std::move(candidate); + return dxgi_fence_status::success; + } catch (const std::bad_alloc&) { return dxgi_fence_status::out_of_memory; } +} +#if defined(_WIN32) +using owned_dxgi_fence_wait=dxgi_fence_wait; +inline dxgi_fence_status export_dxgi_fences(const wgpu::SharedTextureMemoryEndAccessState& state, + std::vector& result) { + if ((state.fenceCount && !state.fences) || (state.signaledValueCount && !state.signaledValues)) { + result.clear(); return dxgi_fence_status::invalid_argument; + } + return duplicate_dxgi_fences( + {state.fences,state.fenceCount},{state.signaledValues,state.signaledValueCount},result, + [](const wgpu::SharedFence& fence,HANDLE& handle) { + if (!fence) return false; + wgpu::SharedFenceExportInfo info{}; + fence.ExportInfo(&info); + if (info.type!=wgpu::SharedFenceType::DXGISharedHandle) return false; + wgpu::SharedFenceDXGISharedHandleExportInfo dxgi{}; + info.nextInChain=&dxgi; + fence.ExportInfo(&info); + handle=dxgi.handle; + return info.type==wgpu::SharedFenceType::DXGISharedHandle; + }); +} +#endif +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_image.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_image.h new file mode 100644 index 000000000..d53cd36da --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_image.h @@ -0,0 +1,181 @@ +#pragma once +#include "dxgi_bridge_contract.h" +#include "nt_handle.h" +#include "dawn_dxgi_fences.h" +#include +#include +#include +#include + +namespace webscene::graphics { +enum class dxgi_import_status { + success,unsupported_platform,invalid_argument,unsupported_pairing, + missing_device_feature,handle_failure,import_failure,property_mismatch,out_of_memory +}; +enum class dxgi_access_status { success,invalid_state,invalid_fences,native_failure,device_lost,fence_export_failure,fence_import_failure }; +// Thread-confined import/access owner. EndAccess is a fence handoff, not CPU/GPU +// completion; the external consumer must wait on every returned fence/value. +class dawn_dxgi_image { + struct handle_anchor { virtual ~handle_anchor()=default; }; +#if defined(_WIN32) + struct windows_handle final : handle_anchor { + owned_nt_handle value; + explicit windows_handle(owned_nt_handle owned):value(std::move(owned)) {} + }; +#endif + std::shared_ptr handle_; // Last destroyed, after Dawn objects. + wgpu::Device device_; + wgpu::SharedTextureMemory memory_; + wgpu::Texture texture_; + wgpu::SharedTextureMemoryProperties properties_{}; + const std::thread::id thread_=std::this_thread::get_id(); + bool active_{},failed_{}; + void* source_handle_{}; // Identity only; the owned duplicate anchors access. + void check_thread() const { + if (thread_!=std::this_thread::get_id()) throw std::logic_error("DXGI access requires its owner thread"); + } + static wgpu::TextureFormat native_format(image_format format) { + switch (format) { + case image_format::rgba8_unorm: return wgpu::TextureFormat::RGBA8Unorm; + case image_format::bgra8_unorm: return wgpu::TextureFormat::BGRA8Unorm; + case image_format::rgba16_float: return wgpu::TextureFormat::RGBA16Float; + case image_format::rgba8_srgb: return wgpu::TextureFormat::RGBA8UnormSrgb; + case image_format::bgra8_srgb: return wgpu::TextureFormat::BGRA8UnormSrgb; + } + return wgpu::TextureFormat::Undefined; + } + dxgi_import_status initialize(const wgpu::Device& device,void* handle,const image_metadata& image, + dxgi_sync synchronization,wgpu::TextureUsage usage) { + device_=device; source_handle_=handle; + wgpu::SharedTextureMemoryDXGISharedHandleDescriptor dxgi{}; + dxgi.handle=handle; dxgi.useKeyedMutex=synchronization==dxgi_sync::keyed_mutex; + wgpu::SharedTextureMemoryDescriptor descriptor{}; descriptor.nextInChain=&dxgi; + memory_=device.ImportSharedTextureMemory(&descriptor); + if (!memory_ || memory_.GetProperties(&properties_)!=wgpu::Status::Success || memory_.IsDeviceLost()) + return dxgi_import_status::import_failure; + if (properties_.size.width!=image.width || properties_.size.height!=image.height + || properties_.size.depthOrArrayLayers!=1 || properties_.format!=native_format(image.format) + || (properties_.usage & usage)!=usage) + return dxgi_import_status::property_mismatch; + wgpu::TextureDescriptor texture{}; + texture.size=properties_.size; texture.format=properties_.format; texture.usage=usage; + texture_=memory_.CreateTexture(&texture); + return texture_ ? dxgi_import_status::success : dxgi_import_status::import_failure; + } +public: + bool matches(const wgpu::Device& device,void* handle) const noexcept { + return device_.Get()==device.Get() && source_handle_==handle; + } + bool expire_texture() { + check_thread(); + if(active_ || failed_ || !texture_)return false; + texture_.Destroy();return true; + } + dawn_dxgi_image()=default; + dawn_dxgi_image(const dawn_dxgi_image&)=delete; + dawn_dxgi_image& operator=(const dawn_dxgi_image&)=delete; + ~dawn_dxgi_image() { if (active_) std::terminate(); } + const wgpu::SharedTextureMemoryProperties& properties() const noexcept { return properties_; } + dxgi_access_status begin(bool initialized,std::span fences={}, + std::span values={}) { + check_thread(); + if (!memory_ || !texture_ || active_ || failed_) return dxgi_access_status::invalid_state; + if (fences.size()!=values.size()) return dxgi_access_status::invalid_fences; + for (const auto& fence:fences) if (!fence) return dxgi_access_status::invalid_fences; + if (memory_.IsDeviceLost()) return dxgi_access_status::device_lost; + wgpu::SharedTextureMemoryBeginAccessDescriptor descriptor{}; + descriptor.initialized=initialized; + descriptor.fenceCount=fences.size(); descriptor.fences=fences.data(); + descriptor.signaledValueCount=values.size(); descriptor.signaledValues=values.data(); + if (memory_.BeginAccess(texture_,&descriptor)!=wgpu::Status::Success) { + // Native access state can change before backend setup fails. Reject + // reuse of an ambiguous allocation rather than retrying its access. + failed_=true; return dxgi_access_status::native_failure; + } + active_=true; return dxgi_access_status::success; + } + dxgi_access_status begin_shared_fences(bool initialized,std::span handles, + std::span values) { + check_thread(); + if (!memory_ || !texture_ || active_ || failed_) return dxgi_access_status::invalid_state; + if (handles.size()!=values.size()) return dxgi_access_status::invalid_fences; + if (handles.empty()) return begin(initialized); + imported_dxgi_fences waits; + if (import_dxgi_fences(device_,handles,values,waits)!=dxgi_fence_status::success) + return dxgi_access_status::fence_import_failure; + // BeginAccess retains the imported fences for the native texture access. + // The temporary vectors do not need to survive the interval. + return begin(initialized,waits.fences,waits.values); + } + const wgpu::Texture& texture() const { + check_thread(); + if (!active_) throw std::logic_error("DXGI texture access has not begun"); + return texture_; + } + dxgi_access_status end(wgpu::SharedTextureMemoryEndAccessState& handoff) { + check_thread(); + if (!memory_ || !active_) return dxgi_access_status::invalid_state; + wgpu::SharedTextureMemoryEndAccessState result; + const auto status=memory_.EndAccess(texture_,&result); + // Dawn may end access before failing to export a fence. Never retry an + // ambiguous handoff or expose this allocation for further sampling. + active_=false; + if (status!=wgpu::Status::Success) { + failed_=true; handoff=wgpu::SharedTextureMemoryEndAccessState{}; + return memory_.IsDeviceLost() ? dxgi_access_status::device_lost : dxgi_access_status::native_failure; + } + handoff=std::move(result); return dxgi_access_status::success; + } +#if defined(_WIN32) + // Own all outgoing handles before Dawn's temporary end state is freed. A + // failed export invalidates this allocation: presenting without its waits + // would race producer work. The caller must discard the external image. + dxgi_access_status end_owned(std::vector& waits,bool& initialized) { + check_thread(); + waits.clear(); initialized=false; + wgpu::SharedTextureMemoryEndAccessState state; + const auto status=end(state); + if (status!=dxgi_access_status::success) return status; + if (export_dxgi_fences(state,waits)!=dxgi_fence_status::success) { + failed_=true; return dxgi_access_status::fence_export_failure; + } + initialized=state.initialized; + return dxgi_access_status::success; + } +#endif + // Device removal cannot yield a valid fence handoff. Permit cleanup only + // after Dawn confirms loss; callers must invalidate the external image. + bool abandon_lost_device() { + check_thread(); + if (!memory_ || !memory_.IsDeviceLost()) return false; + active_=false; failed_=true; return true; + } + static dxgi_import_status import(const wgpu::Device& device,void* borrowed_nt_handle, + const dxgi_endpoint& producer,const dxgi_endpoint& consumer,const image_metadata& image, + wgpu::TextureUsage usage,std::unique_ptr& result) { + result.reset(); +#if !defined(_WIN32) + return dxgi_import_status::unsupported_platform; +#else + if (!device || !win32_nt_handle_ops::valid(borrowed_nt_handle) || usage==wgpu::TextureUsage::None) + return dxgi_import_status::invalid_argument; + const auto choice=choose_dxgi_bridge(producer,consumer,image); + if (choice.status!=dxgi_bridge_status::supported) return dxgi_import_status::unsupported_pairing; + if (!device.HasFeature(wgpu::FeatureName::SharedTextureMemoryDXGISharedHandle) + || (choice.synchronization==dxgi_sync::shared_fence + && !device.HasFeature(wgpu::FeatureName::SharedFenceDXGISharedHandle))) + return dxgi_import_status::missing_device_feature; + try { + auto owned=std::make_unique(); + auto handle=std::make_shared(owned_nt_handle::duplicate(borrowed_nt_handle)); + owned->handle_=handle; + const auto status=owned->initialize(device,handle->value.get(),image,choice.synchronization,usage); + if (status!=dxgi_import_status::success) return status; + owned->source_handle_=borrowed_nt_handle; + result=std::move(owned); return dxgi_import_status::success; + } catch (const std::bad_alloc&) { return dxgi_import_status::out_of_memory; } + catch (const std::system_error&) { return dxgi_import_status::handle_failure; } +#endif + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_scene_snapshot.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_scene_snapshot.h new file mode 100644 index 000000000..cbfc90e5c --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_scene_snapshot.h @@ -0,0 +1,59 @@ +#pragma once +#include "dawn_dxgi_submission.h" +#include "image_lease_abi.h" +#if defined(_WIN32) +namespace webscene::graphics { +class dawn_dxgi_scene_snapshot final : public webscene_gpu_image_snapshot { + class dependencies final : public webscene_gpu_producer_dependencies { + std::shared_ptr ticket_; + std::vector waits_; + public: + explicit dependencies(std::shared_ptr ticket):ticket_(std::move(ticket)) { + if(export_dxgi_fences(ticket_->producer_handoff(),waits_)!=dxgi_fence_status::success) + throw std::runtime_error("DXGI producer fence export failed"); + } + size_t count() const noexcept override { return waits_.size(); } + bool metal_event(size_t,void*& event,uint64_t& value) const override { event=nullptr;value=0;return false; } + bool dxgi_fence(size_t index,void*& handle,uint64_t& value) const override { + handle=nullptr;value=0; + if(index>=waits_.size())return false; + handle=waits_[index].handle.get();value=waits_[index].value;return true; + } + }; + std::shared_ptr ticket_; + const image_metadata metadata_; + std::optional ready_; + std::shared_ptr resolved_; + std::shared_ptr resolve_image(bool early) { + if(resolved_)return resolved_; + auto producer=std::make_shared(ticket_); + if(!ready_) { + auto image=early?ticket_->take_for_gpu_wait():ticket_->take_ready(); + if(!image)return {}; + ready_.emplace(std::move(*image)); + } + resolved_=std::make_shared(std::move(*ready_),std::move(producer),early); + ready_.reset();return resolved_; + } +public: + explicit dawn_dxgi_scene_snapshot(std::unique_ptr ticket) + :ticket_(std::move(ticket)),metadata_(ticket_->describe()) {} + image_metadata describe() const override { return metadata_; } + status state() const override { + switch(ticket_->state()) { + case dawn_dxgi_submission::status::pending:return status::pending; + case dawn_dxgi_submission::status::ready: + case dawn_dxgi_submission::status::consumed:return status::ready; + default:return status::failed; + } + } + std::shared_ptr resolve() override { + return state()==status::ready?resolve_image(false):nullptr; + } + std::shared_ptr resolve_with_gpu_waits() override { + if(auto image=resolve())return image; + return ticket_->can_enqueue_gpu_wait()?resolve_image(true):nullptr; + } +}; +} +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_submission.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_submission.h new file mode 100644 index 000000000..5f4f04895 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_dxgi_submission.h @@ -0,0 +1,145 @@ +#pragma once +#include "dawn_dxgi_canvas_texture.h" +#include "d3d12_canvas_images.h" +#include "producer_completion_gate.h" +#include +#if defined(_WIN32) +namespace webscene::graphics { +// Nonblocking producer handoff. Completed consumers use take_ready(); capable +// GPU consumers may take a validated image with all producer dependencies. +class dawn_dxgi_submission final : public std::enable_shared_from_this { +public: + enum class status { pending, ready, failed, consumed, discarded }; +private: + mutable std::mutex mutex_; + status status_=status::pending; + std::optional image_; + wgpu::Future future_{}, validation_future_{}; + producer_completion_gate completion_; + // Own EndAccess fences and timeline values beyond the submitting stack. + // Snapshot ownership also keeps these dependencies alive for a GPU consumer. + wgpu::SharedTextureMemoryEndAccessState handoff_; + bool present_=true, handoff_valid_=false; + void finish(bool queue,bool valid,const std::shared_ptr& wake) { + bool notify=false; + { + std::lock_guard lock(mutex_); + if (completion_.finish(queue ? producer_completion_gate::phase::queue + : producer_completion_gate::phase::validation,valid)) { + const auto succeeded=completion_.state()==producer_completion_gate::result::success; + status_=succeeded ? (present_ ? status::ready : status::discarded) : status::failed; + if (!succeeded) image_.reset(); + notify=true; + } + notify=notify || (!queue && present_ && handoff_valid_ && completion_.validated_for_gpu_wait()); + } + if (notify && wake) wake->signal(); + } +public: + status state() const { std::lock_guard lock(mutex_); return status_; } + std::optional take_ready() { + std::lock_guard lock(mutex_); + if (status_!=status::ready) return {}; + status_=status::consumed; + return std::move(image_); + } + // A captured CPU scene owns an exact output independently of the provider's + // destructive ready queue. Metadata is available while pending. Early image + // transfer requires validation and a consumer that encodes GPU dependencies. + class snapshot final { + std::shared_ptr submission_; + std::optional image_; + friend class dawn_dxgi_submission; + snapshot(std::shared_ptr submission, + owned_image_pool::retained image) + : submission_(std::move(submission)),image_(std::move(image)) {} + public: + snapshot(const snapshot&)=delete; + snapshot& operator=(const snapshot&)=delete; + image_metadata describe() const { + if(!image_)throw std::logic_error("Captured image already transferred"); + return image_->describe(); + } + status state() const { return submission_->state(); } + // Immutable after publish_submitted/submit returns. Does not certify image + // readiness; an asynchronous consumer must encode all dependencies. + const wgpu::SharedTextureMemoryEndAccessState& producer_handoff() const noexcept { + return submission_->handoff_; + } + bool can_enqueue_gpu_wait() const { + std::lock_guard lock(submission_->mutex_); + const auto& handoff=submission_->handoff_; + return submission_->present_ && submission_->handoff_valid_ + && submission_->completion_.validated_for_gpu_wait() + && submission_->status_!=status::failed && submission_->status_!=status::discarded + && handoff.initialized && handoff.fenceCount>0 + && handoff.fenceCount==handoff.signaledValueCount; + } + std::optional take_for_gpu_wait() { + if(!can_enqueue_gpu_wait() || !image_) return {}; + auto result=std::move(image_);image_.reset();return result; + } + std::optional take_ready() { + const auto current=state(); + if(current!=status::ready&¤t!=status::consumed)return {}; + if(!image_)return {}; + auto result=std::move(image_); + image_.reset(); + return result; + } + }; + std::unique_ptr capture_snapshot() { + std::lock_guard lock(mutex_); + if(!image_ || status_==status::failed || status_==status::discarded + || status_==status::consumed)return {}; + auto retained=image_->retain(); + if(!retained)return {}; // Retention pressure does not expose a partial scene. + return std::unique_ptr(new snapshot(shared_from_this(),std::move(*retained))); + } + // Exposed for diagnostic waits only. Ordinary callers poll state or use wake. + wgpu::Future completion_future() const noexcept { return future_; } + wgpu::Future validation_future() const noexcept { return validation_future_; } + // Handoff for application work already submitted on this device's queue. + // No command is resubmitted. Even publication backpressure must retain the + // frame until queue completion because its allocation may already be in use. + // present=false retires resize/unconfigure work without exposing an image; + // discarded contents need not be initialized, but completion is still required. + static std::shared_ptr publish_submitted( + d3d12_canvas_images::frame&& frame,const wgpu::Device& device, + std::shared_ptr shared,std::shared_ptr device_lifetime={}, + std::shared_ptr wake={},bool present=true) { + if(!device||!frame.color||!shared||!shared->matches(device,frame.color->borrowed_handle())) + throw std::invalid_argument("Submitted image must match its device and native allocation"); + auto result=std::make_shared(); + auto pending=std::make_shared(std::move(frame)); + pending->producer.begin(); + result->present_=present; + if(present) { + auto image=pending->producer.publish(); + if(!image)result->completion_.reject(); + if(image)result->image_.emplace(std::move(*image)); + } + // Scope only host handoff operations, never application JS recording or + // its error-scope stack. Undefined image contents cannot be presented. + device.PushErrorScope(wgpu::ErrorFilter::Validation); + wgpu::SharedTextureMemoryEndAccessState end; + const bool ended=shared->end(end)==dxgi_access_status::success; + const bool expired=ended&&shared->expire_texture(); + const bool valid=expired&&(!present||end.initialized); + result->handoff_valid_=valid; + result->handoff_=std::move(end); + result->future_=device.GetQueue().OnSubmittedWorkDone(wgpu::CallbackMode::AllowSpontaneous, + [result,pending,shared,device,device_lifetime,wake,valid,present](wgpu::QueueWorkDoneStatus status,wgpu::StringView) { + pending->producer.complete(); + if(!present)pending->producer.cancel(); + result->finish(true,valid&&status==wgpu::QueueWorkDoneStatus::Success,wake); + }); + result->validation_future_=device.PopErrorScope(wgpu::CallbackMode::AllowSpontaneous, + [result,device,device_lifetime,wake](wgpu::PopErrorScopeStatus status,wgpu::ErrorType error,wgpu::StringView) { + result->finish(false,status==wgpu::PopErrorScopeStatus::Success&&error==wgpu::ErrorType::NoError,wake); + }); + return result; + } +}; +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_event_service.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_event_service.h new file mode 100644 index 000000000..551ca2b49 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_event_service.h @@ -0,0 +1,48 @@ +#pragma once +#include "completion_mailbox.h" +#include + +namespace webscene::graphics { +// Construct lazily on the engine thread. ProcessEvents and completion delivery +// are independent of presentation/RAF. Backend callbacks publish native records; +// only pump() invokes the engine's promise-delivery function. +class dawn_event_service { + const std::thread::id engine_thread_ = std::this_thread::get_id(); + std::shared_ptr completions_; + wgpu::Instance instance_; + bool closed_{}; + void check_thread() const { + if (std::this_thread::get_id() != engine_thread_) + throw std::logic_error("Dawn event service requires engine thread"); + } +public: + dawn_event_service(size_t completion_capacity, std::shared_ptr wake, bool measure_latency=false) + : completions_(std::make_shared(completion_capacity, std::move(wake), measure_latency)), + instance_(wgpu::CreateInstance()) { + if (!instance_) throw std::runtime_error("Dawn instance creation failed"); + } + ~dawn_event_service() { + if (std::this_thread::get_id() != engine_thread_) std::terminate(); + // Late native callbacks keep the mailbox alive but cannot deliver JS. + completions_->close(); + } + dawn_event_service(const dawn_event_service&) = delete; + dawn_event_service& operator=(const dawn_event_service&) = delete; + const wgpu::Instance& instance() const { + check_thread(); + if (closed_) throw std::logic_error("Dawn event service is closed"); + return instance_; + } + std::shared_ptr completions() const { check_thread(); return completions_; } + template size_t pump(Deliver deliver, size_t budget = 64) { + check_thread(); + if (!closed_) instance_.ProcessEvents(); + size_t delivered = 0; + while (delivered < budget && completions_->drain_one(deliver)) ++delivered; + return delivered; + } + // Call before discarding the JS context, then drain cancellation records on + // its engine thread. Closing admission does not imply GPU submission finish. + void close() { check_thread(); closed_ = true; completions_->close(); } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_iosurface_canvas_host.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_iosurface_canvas_host.h new file mode 100644 index 000000000..d49beb85e --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_iosurface_canvas_host.h @@ -0,0 +1,102 @@ +#pragma once +#include "dawn_iosurface_submission.h" +#include +#if defined(__APPLE__) +namespace webscene::graphics { +// Engine-thread canvas provider. The host drains completed publications into the +// scene and applies canvas generation/serial filtering there. Three pending +// retirements bound storage and callback ownership during presenter backpressure. +class dawn_iosurface_canvas_host final { + struct active_frame { + iosurface_canvas_images::frame frame; + wgpu::Device device; + std::shared_ptr shared; + std::shared_ptr device_lifetime; + }; + const std::thread::id thread_=std::this_thread::get_id(); + iosurface_canvas_images images_; + std::unique_ptr active_; + std::array,3> pending_{}; + size_t active_slot_=0; + std::weak_ptr latest_submission_; + std::shared_ptr wake_; + void check_thread()const { + if(thread_!=std::this_thread::get_id())throw std::logic_error("Canvas provider requires its engine thread"); + } + void clear_retired() { + for(auto& item:pending_)if(item) { + auto state=item->state(); + if(state==dawn_iosurface_submission::status::failed||state==dawn_iosurface_submission::status::discarded|| + state==dawn_iosurface_submission::status::consumed)item.reset(); + } + } +public: + explicit dawn_iosurface_canvas_host(uint64_t budget,std::shared_ptr wake={}) + :images_(budget),wake_(std::move(wake)) {} + dawn_iosurface_canvas_host(const dawn_iosurface_canvas_host&)=delete; + dawn_iosurface_canvas_host& operator=(const dawn_iosurface_canvas_host&)=delete; + ~dawn_iosurface_canvas_host() { + // The GPUCanvasContext must retire before destroying its provider. + // Dropping an active imported frame could recycle submitted storage. + if(active_)std::terminate(); + } + wgpu::Texture acquire(image_metadata metadata,const wgpu::Device& device, + const wgpu::TextureDescriptor& descriptor,std::shared_ptr device_lifetime={}) { + check_thread();if(active_)throw std::logic_error("Canvas already owns a current frame"); + clear_retired();size_t slot=0;while(slot(active_frame{std::move(*frame),device,shared,std::move(device_lifetime)}); + wgpu::SharedTextureMemoryBeginAccessDescriptor access{};access.initialized=false; + if(!shared->begin(access))return {}; + active_=std::move(next);active_slot_=slot; + return shared->texture(); + } + void retire(const wgpu::Texture& texture,bool present) { + check_thread(); + if(!active_||texture.Get()!=active_->shared->texture().Get()) + throw std::invalid_argument("Canvas retirement requires its current texture"); + pending_[active_slot_]=dawn_iosurface_submission::publish_submitted(std::move(active_->frame), + active_->device,active_->shared,active_->device_lifetime,wake_,present); + latest_submission_=present ? pending_[active_slot_] : std::weak_ptr{}; + active_.reset(); + } + // Capture at the rendering-opportunity boundary, before draining ready + // outputs. The weak lookup adds no hidden image retention; the returned + // ticket owns its exact allocation independently of later provider work. + std::unique_ptr capture_latest_submission() { + check_thread(); + if(active_)throw std::logic_error("End the current GPU opportunity before capturing its output"); + auto submission=latest_submission_.lock(); + return submission ? submission->capture_snapshot() : nullptr; + } + std::optional take_ready() { + check_thread();clear_retired(); + for(auto& item:pending_)if(item&&item->state()==dawn_iosurface_submission::status::ready) { + auto image=item->take_ready();item.reset();return image; + } + return {}; + } + bool has_completed_retirements()const { + check_thread(); + for(const auto& item:pending_)if(item&&item->state()!=dawn_iosurface_submission::status::pending)return true; + return false; + } + bool idle() { + check_thread();clear_retired();if(active_)return false; + for(const auto& item:pending_)if(item)return false; + return true; + } + bool can_acquire() { + check_thread();clear_retired(); + if(active_||images_.busy_images()>=3)return false; + return std::any_of(pending_.begin(),pending_.end(),[](const auto& item){return !item;}); + } + image_lease_pool::occupancy inspect_occupancy() const { return images_.inspect_occupancy(); } + size_t busy_images()const {check_thread();return images_.busy_images();} +}; +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_iosurface_canvas_texture.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_iosurface_canvas_texture.h new file mode 100644 index 000000000..09308d914 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_iosurface_canvas_texture.h @@ -0,0 +1,24 @@ +#pragma once +#include "dawn_shared_image.h" +#include "iosurface_canvas_images.h" +#if defined(__APPLE__) +namespace webscene::graphics { +// Import the exact pool allocation using the application's canvas descriptor. +// The caller keeps the frame reservation and hands both objects to submission +// retirement after application work. No new pixel storage, pixel copy or queue +// submission occurs; Dawn creates a texture object over the shared storage. +inline std::shared_ptr import_dawn_iosurface_canvas_texture( + const iosurface_canvas_images::frame& frame,const wgpu::Device& device, + const wgpu::TextureDescriptor& description) { + if(!frame.color||frame.metadata.format!=image_format::bgra8_unorm|| + description.format!=wgpu::TextureFormat::BGRA8Unorm|| + description.size.width!=frame.metadata.width||description.size.height!=frame.metadata.height) + throw std::invalid_argument("Canvas texture descriptor must match its IOSurface allocation"); + auto surface=frame.color->borrowed_handle(); + std::shared_ptr owner(const_cast(CFRetain(surface)),[](void* value){CFRelease(value);}); + wgpu::SharedTextureMemoryIOSurfaceDescriptor io{};io.ioSurface=surface; + wgpu::SharedTextureMemoryDescriptor import{};import.nextInChain=&io; + return dawn_shared_image::import(device,import,description,std::move(owner)); +} +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_iosurface_submission.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_iosurface_submission.h new file mode 100644 index 000000000..72c8ea35e --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_iosurface_submission.h @@ -0,0 +1,213 @@ +#pragma once +#include "dawn_iosurface_canvas_texture.h" +#include "iosurface_canvas_images.h" +#include "producer_completion_gate.h" +#include +#if defined(__APPLE__) +namespace webscene::graphics { +// Nonblocking producer handoff. Completed consumers use take_ready(); capable +// GPU consumers may take a validated image with all producer dependencies. +class dawn_iosurface_submission final : public std::enable_shared_from_this { +public: + enum class status { pending, ready, failed, consumed, discarded }; +private: + mutable std::mutex mutex_; + status status_=status::pending; + std::optional image_; + wgpu::Future future_{}, validation_future_{}; + producer_completion_gate completion_; + // Own EndAccess fences and timeline values beyond the submitting stack. + // Snapshot ownership also keeps these dependencies alive for a GPU consumer. + wgpu::SharedTextureMemoryEndAccessState handoff_; + bool present_=true, handoff_valid_=false; + void finish(bool queue,bool valid,const std::shared_ptr& wake) { + bool notify=false; + { + std::lock_guard lock(mutex_); + if (completion_.finish(queue ? producer_completion_gate::phase::queue + : producer_completion_gate::phase::validation,valid)) { + const auto succeeded=completion_.state()==producer_completion_gate::result::success; + status_=succeeded ? (present_ ? status::ready : status::discarded) : status::failed; + if (!succeeded) image_.reset(); + notify=true; + } + notify=notify || (!queue && present_ && handoff_valid_ && completion_.validated_for_gpu_wait()); + } + if (notify && wake) wake->signal(); + } +public: + status state() const { std::lock_guard lock(mutex_); return status_; } + std::optional take_ready() { + std::lock_guard lock(mutex_); + if (status_!=status::ready) return {}; + status_=status::consumed; + return std::move(image_); + } + // A captured CPU scene owns an exact output independently of the provider's + // destructive ready queue. Metadata is available while pending. Early image + // transfer requires validation and a consumer that encodes GPU dependencies. + class snapshot final { + std::shared_ptr submission_; + std::optional image_; + friend class dawn_iosurface_submission; + snapshot(std::shared_ptr submission, + owned_image_pool::retained image) + : submission_(std::move(submission)),image_(std::move(image)) {} + public: + snapshot(const snapshot&)=delete; + snapshot& operator=(const snapshot&)=delete; + image_metadata describe() const { + if(!image_)throw std::logic_error("Captured image already transferred"); + return image_->describe(); + } + status state() const { return submission_->state(); } + // Immutable after publish_submitted/submit returns. Does not certify image + // readiness; an asynchronous consumer must encode all dependencies. + const wgpu::SharedTextureMemoryEndAccessState& producer_handoff() const noexcept { + return submission_->handoff_; + } + bool can_enqueue_gpu_wait() const { + std::lock_guard lock(submission_->mutex_); + const auto& handoff=submission_->handoff_; + return submission_->present_ && submission_->handoff_valid_ + && submission_->completion_.validated_for_gpu_wait() + && submission_->status_!=status::failed && submission_->status_!=status::discarded + && handoff.initialized && handoff.fenceCount>0 + && handoff.fenceCount==handoff.signaledValueCount; + } + std::optional take_for_gpu_wait() { + if(!can_enqueue_gpu_wait() || !image_) return {}; + auto result=std::move(image_);image_.reset();return result; + } + std::optional take_ready() { + const auto current=state(); + if(current!=status::ready&¤t!=status::consumed)return {}; + if(!image_)return {}; + auto result=std::move(image_); + image_.reset(); + return result; + } + }; + std::unique_ptr capture_snapshot() { + std::lock_guard lock(mutex_); + if(!image_ || status_==status::failed || status_==status::discarded + || status_==status::consumed)return {}; + auto retained=image_->retain(); + if(!retained)return {}; // Retention pressure does not expose a partial scene. + return std::unique_ptr(new snapshot(shared_from_this(),std::move(*retained))); + } + // Exposed for diagnostic waits only. Ordinary callers poll state or use wake. + wgpu::Future completion_future() const noexcept { return future_; } + wgpu::Future validation_future() const noexcept { return validation_future_; } + // Handoff for application work already submitted on this device's queue. + // No command is resubmitted. Even publication backpressure must retain the + // frame until queue completion because its allocation may already be in use. + // present=false retires resize/unconfigure work without exposing an image; + // discarded contents need not be initialized, but completion is still required. + static std::shared_ptr publish_submitted( + iosurface_canvas_images::frame&& frame,const wgpu::Device& device, + std::shared_ptr shared,std::shared_ptr device_lifetime={}, + std::shared_ptr wake={},bool present=true) { + if(!device||!frame.color||!shared||!shared->matches(device,frame.color->borrowed_handle())) + throw std::invalid_argument("Submitted image must match its device and native allocation"); + auto result=std::make_shared(); + auto pending=std::make_shared(std::move(frame)); + pending->producer.begin(); + result->present_=present; + if(present) { + auto image=pending->producer.publish(); + if(!image)result->completion_.reject(); + if(image)result->image_.emplace(std::move(*image)); + } + // Scope only host handoff operations, never application JS recording or + // its error-scope stack. Undefined image contents cannot be presented. + device.PushErrorScope(wgpu::ErrorFilter::Validation); + wgpu::SharedTextureMemoryEndAccessState end; + const bool ended=shared->end(end); + const bool expired=ended&&shared->expire_texture(); + const bool valid=expired&&(!present||end.initialized); + result->handoff_valid_=valid; + result->handoff_=std::move(end); + result->future_=device.GetQueue().OnSubmittedWorkDone(wgpu::CallbackMode::AllowSpontaneous, + [result,pending,shared,device,device_lifetime,wake,valid,present](wgpu::QueueWorkDoneStatus status,wgpu::StringView) { + pending->producer.complete(); + if(!present)pending->producer.cancel(); + result->finish(true,valid&&status==wgpu::QueueWorkDoneStatus::Success,wake); + }); + result->validation_future_=device.PopErrorScope(wgpu::CallbackMode::AllowSpontaneous, + [result,device,device_lifetime,wake](wgpu::PopErrorScopeStatus status,wgpu::ErrorType error,wgpu::StringView) { + result->finish(false,status==wgpu::PopErrorScopeStatus::Success&&error==wgpu::ErrorType::NoError,wake); + }); + return result; + } + // Invoke on the device/producer owner thread. The recorder may only encode; + // it must not submit, alter error scopes, or let the borrowed texture escape. + // Validation and queue completion must both succeed before publication. + using recorder=std::function; + static std::shared_ptr submit( + iosurface_canvas_images::frame&& frame,const wgpu::Device& device, + const recorder& record,std::shared_ptr device_lifetime={}, + std::shared_ptr wake={}) { + if (!device || !record || !frame.color) + throw std::invalid_argument("Dawn IOSurface submission requires device, frame and recorder"); + // Balance the validation scope even when import/recording rejects work. + struct validation_scope { + wgpu::Device device; + bool popped=false; + explicit validation_scope(wgpu::Device value):device(std::move(value)) { + device.PushErrorScope(wgpu::ErrorFilter::Validation); + } + ~validation_scope() { + if (!popped) device.PopErrorScope(wgpu::CallbackMode::AllowSpontaneous, + [](wgpu::PopErrorScopeStatus,wgpu::ErrorType,wgpu::StringView) {}); + } + } scope(device); + auto pending=std::make_shared(std::move(frame)); + wgpu::TextureDescriptor texture{}; + texture.dimension=wgpu::TextureDimension::e2D; + texture.size={pending->metadata.width,pending->metadata.height,1}; + texture.format=wgpu::TextureFormat::BGRA8Unorm; + texture.usage=wgpu::TextureUsage::RenderAttachment; + auto shared=import_dawn_iosurface_canvas_texture(*pending,device,texture); + if (!shared) return {}; + wgpu::SharedTextureMemoryBeginAccessDescriptor access{}; + access.initialized=false; // Recorder must initialize every presented pixel. + if (!shared->begin(access)) return {}; + wgpu::CommandBuffer command; + try { command=record(shared->texture()); } + catch (...) { wgpu::SharedTextureMemoryEndAccessState end; shared->end(end); throw; } + if (!command) { wgpu::SharedTextureMemoryEndAccessState end; shared->end(end); return {}; } + auto result=std::make_shared(); + pending->producer.begin(); + auto image=pending->producer.publish(); + if (!image) { + wgpu::SharedTextureMemoryEndAccessState end; shared->end(end); + pending->producer.complete(); + return {}; + } + result->image_.emplace(std::move(*image)); + auto queue=device.GetQueue(); + queue.Submit(1,&command); + wgpu::SharedTextureMemoryEndAccessState end; + const bool ended=shared->end(end); + const bool expired=ended && shared->expire_texture(); + result->handoff_valid_=ended && expired && end.initialized; + result->handoff_=std::move(end); + result->future_=queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowSpontaneous, + [result,pending,shared,device,device_lifetime,wake,ended,expired] + (wgpu::QueueWorkDoneStatus completed,wgpu::StringView) { + pending->producer.complete(); + result->finish(true,ended && expired && completed==wgpu::QueueWorkDoneStatus::Success,wake); + }); + scope.popped=true; + result->validation_future_=device.PopErrorScope(wgpu::CallbackMode::AllowSpontaneous, + [result,device,device_lifetime,wake](wgpu::PopErrorScopeStatus completed, + wgpu::ErrorType error,wgpu::StringView) { + result->finish(false,completed==wgpu::PopErrorScopeStatus::Success && + error==wgpu::ErrorType::NoError,wake); + }); + return result; + } +}; +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_metal_producer_wait.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_metal_producer_wait.h new file mode 100644 index 000000000..8cd66e38e --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_metal_producer_wait.h @@ -0,0 +1,30 @@ +#pragma once +#if defined(__APPLE__) && defined(__OBJC__) +#include "metal_producer_wait.h" +#include +#include + +namespace webscene::graphics { +// Export every Dawn dependency before touching the consumer queue. Keep the +// originating handoff/snapshot alive through submission and image retirement. +inline id submit_dawn_metal_producer_waits( + id queue, std::span fences, + std::span values) { + if(!queue || fences.empty() || fences.size()!=values.size()) return nil; + std::vector dependencies; + dependencies.reserve(fences.size()); + for(size_t i=0;i)metal.sharedEvent,values[i]}); + } + return submit_metal_producer_waits(queue,dependencies); +} +} +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_scene_image_snapshot.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_scene_image_snapshot.h new file mode 100644 index 000000000..9b6543eb9 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_scene_image_snapshot.h @@ -0,0 +1,73 @@ +#pragma once +#include "dawn_iosurface_submission.h" +#include "image_lease_abi.h" +#if defined(__APPLE__) +namespace webscene::graphics { +class dawn_scene_image_snapshot final : public webscene_gpu_image_snapshot { + class dependencies final : public webscene_gpu_producer_dependencies { + std::shared_ptr ticket_; + public: + explicit dependencies(std::shared_ptr ticket):ticket_(std::move(ticket)) {} + size_t count() const noexcept override { return ticket_->producer_handoff().fenceCount; } + bool metal_event(size_t index,void*& event,uint64_t& value) const override { + event=nullptr; value=0; + const auto& handoff=ticket_->producer_handoff(); + if(index>=handoff.fenceCount || handoff.fenceCount!=handoff.signaledValueCount) return false; + wgpu::SharedFenceExportInfo type; handoff.fences[index].ExportInfo(&type); + if(type.type!=wgpu::SharedFenceType::MTLSharedEvent) return false; + wgpu::SharedFenceMTLSharedEventExportInfo metal; + wgpu::SharedFenceExportInfo info;info.nextInChain=&metal; + handoff.fences[index].ExportInfo(&info); + event=metal.sharedEvent;value=handoff.signaledValues[index];return event!=nullptr; + } + }; + std::shared_ptr ticket_; + const image_metadata metadata_; + std::optional ready_; + std::shared_ptr resolved_; +public: + explicit dawn_scene_image_snapshot(std::unique_ptr ticket) + :ticket_(std::move(ticket)),metadata_(ticket_->describe()) {} + image_metadata describe() const override { return metadata_; } + status state() const override { + switch(ticket_->state()) { + case dawn_iosurface_submission::status::pending:return status::pending; + case dawn_iosurface_submission::status::ready: + case dawn_iosurface_submission::status::consumed:return status::ready; + default:return status::failed; + } + } + std::shared_ptr resolve() override { + if(state()!=status::ready)return {}; + if(resolved_)return resolved_; + if(!ready_) { + auto image=ticket_->take_ready(); + if(!image)return {}; + ready_.emplace(std::move(*image)); + } + // Keep ownership in ready_ if allocation throws; resolution is retryable. + auto producer=std::make_shared(ticket_); + resolved_=std::make_shared(std::move(*ready_),std::move(producer)); + ready_.reset(); + return resolved_; + } + std::shared_ptr resolve_with_gpu_waits() override { + if(auto completed=resolve())return completed; + if(!ticket_->can_enqueue_gpu_wait())return {}; + if(resolved_)return resolved_; + auto producer=std::make_shared(ticket_); + for(size_t i=0;icount();++i) { + void* event=nullptr;uint64_t value=0; + if(!producer->metal_event(i,event,value))return {}; + } + if(!ready_) { + auto image=ticket_->take_for_gpu_wait(); + if(!image)return {}; + ready_.emplace(std::move(*image)); + } + resolved_=std::make_shared(std::move(*ready_),std::move(producer),true); + ready_.reset();return resolved_; + } +}; +} +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_shared_image.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_shared_image.h new file mode 100644 index 000000000..cceab2c55 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dawn_shared_image.h @@ -0,0 +1,72 @@ +#pragma once +#include +#include + +namespace webscene::graphics { +// Cross-platform Dawn shared-memory ownership/access boundary. Platform code +// supplies the import descriptor and native allocation owner. The caller must +// retain this object through GPU completion; EndAccess is not a completion wait. +class dawn_shared_image final { + std::shared_ptr native_owner_; + wgpu::Device device_; + wgpu::SharedTextureMemory memory_; + wgpu::Texture texture_; + bool active_=false, failed_=false, expired_=false; + dawn_shared_image(wgpu::Device device,std::shared_ptr owner,wgpu::SharedTextureMemory memory, + wgpu::Texture texture) + :native_owner_(std::move(owner)),device_(std::move(device)),memory_(std::move(memory)),texture_(std::move(texture)) {} +public: + dawn_shared_image(const dawn_shared_image&)=delete; + dawn_shared_image& operator=(const dawn_shared_image&)=delete; + static std::shared_ptr import( + const wgpu::Device& device,const wgpu::SharedTextureMemoryDescriptor& import, + const wgpu::TextureDescriptor& description,std::shared_ptr owner) { + if (!device || !owner || description.dimension!=wgpu::TextureDimension::e2D || + !description.size.width || !description.size.height || + description.size.depthOrArrayLayers!=1 || description.sampleCount!=1 || + description.mipLevelCount!=1) return {}; + auto memory=device.ImportSharedTextureMemory(&import); + wgpu::SharedTextureMemoryProperties properties{}; + if (!memory || memory.GetProperties(&properties)!=wgpu::Status::Success || + properties.format!=description.format || + properties.size.width!=description.size.width || + properties.size.height!=description.size.height || + properties.size.depthOrArrayLayers!=1 || + (properties.usage & description.usage)!=description.usage) return {}; + auto texture=memory.CreateTexture(&description); + if (!texture) return {}; + return std::shared_ptr( + new dawn_shared_image(device,std::move(owner),std::move(memory),std::move(texture))); + } + bool matches(const wgpu::Device& device,const void* allocation)const noexcept { + return device.Get()==device_.Get()&&allocation==native_owner_.get(); + } + const wgpu::Texture& texture() const noexcept { return texture_; } + bool begin(const wgpu::SharedTextureMemoryBeginAccessDescriptor& access) { + if (active_ || failed_ || expired_) return false; + if (memory_.BeginAccess(texture_,&access)!=wgpu::Status::Success) { + failed_=true; + return false; + } + active_=true; + return true; + } + // Expire this frame's WebGPU texture object after EndAccess, before its + // native allocation can be recycled. Submitted work retains its resources; + // a JavaScript reference to the old texture cannot write a later frame. + bool expire_texture() { + if(active_ || failed_)return false; + if(!expired_){texture_.Destroy();expired_=true;} + return true; + } + bool end(wgpu::SharedTextureMemoryEndAccessState& handoff) { + if (!active_ || failed_) return false; + if (memory_.EndAccess(texture_,&handoff)!=wgpu::Status::Success) { + failed_=true; // Access ownership is uncertain; never offer reuse. + return false; + } + active_=false; + return true; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dxgi_bridge_contract.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dxgi_bridge_contract.h new file mode 100644 index 000000000..60a6cac45 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dxgi_bridge_contract.h @@ -0,0 +1,49 @@ +#pragma once +#include "image_metadata.h" + +namespace webscene::graphics { +struct adapter_luid { + uint32_t low{}; int32_t high{}; bool valid{}; + bool operator==(const adapter_luid&) const = default; +}; +enum class dxgi_api { d3d11,d3d12 }; +enum class dxgi_sync { none,shared_fence,keyed_mutex }; +enum class dxgi_bridge_status { + supported,unknown_adapter,cross_adapter,unsupported_format,unsupported_alpha, + needs_resolve,unsupported_synchronization,invalid_dimensions,unsupported_api +}; +struct dxgi_endpoint { + adapter_luid adapter; + dxgi_api api{}; + uint32_t color_formats{}; // Bit (image_format - 1). + uint32_t alpha_modes{}; // Bit (image_alpha - 1). + bool shared_fence{},keyed_mutex{}; +}; +struct dxgi_bridge_choice { dxgi_bridge_status status; dxgi_sync synchronization{dxgi_sync::none}; }; +// Capability policy only. Endpoint capabilities must come from native device +// queries; this function does not claim an import or a synchronization pass. +inline dxgi_bridge_choice choose_dxgi_bridge(const dxgi_endpoint& producer,const dxgi_endpoint& consumer, + const image_metadata& image,uint32_t samples=1) noexcept { + if ((producer.api!=dxgi_api::d3d11 && producer.api!=dxgi_api::d3d12) + || (consumer.api!=dxgi_api::d3d11 && consumer.api!=dxgi_api::d3d12)) + return {dxgi_bridge_status::unsupported_api}; + if (!producer.adapter.valid || !consumer.adapter.valid) return {dxgi_bridge_status::unknown_adapter}; + if (!(producer.adapter==consumer.adapter)) return {dxgi_bridge_status::cross_adapter}; + if (!image.width || !image.height) return {dxgi_bridge_status::invalid_dimensions}; + if (samples!=1) return {dxgi_bridge_status::needs_resolve}; + const auto format=static_cast(image.format); + if (format<1 || format>5 || !(producer.color_formats & consumer.color_formats & (1U<<(format-1)))) + return {dxgi_bridge_status::unsupported_format}; + const auto alpha=static_cast(image.alpha); + if (alpha<1 || alpha>3 || !(producer.alpha_modes & consumer.alpha_modes & (1U<<(alpha-1)))) + return {dxgi_bridge_status::unsupported_alpha}; + if (producer.shared_fence && consumer.shared_fence) + return {dxgi_bridge_status::supported,dxgi_sync::shared_fence}; + // Keyed-mutex support for D3D12 combinations requires a separately proven + // bridge. Do not silently substitute it for shared-fence synchronization. + if (producer.api==dxgi_api::d3d11 && consumer.api==dxgi_api::d3d11 + && producer.keyed_mutex && consumer.keyed_mutex) + return {dxgi_bridge_status::supported,dxgi_sync::keyed_mutex}; + return {dxgi_bridge_status::unsupported_synchronization}; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/dxgi_device_identity.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/dxgi_device_identity.h new file mode 100644 index 000000000..bb0a6608a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/dxgi_device_identity.h @@ -0,0 +1,108 @@ +#pragma once +#include "dxgi_bridge_contract.h" +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include +#include + +namespace webscene::graphics { +// LUIDs identify adapters within the current system boot, not persistent hardware +// identities. Query the actual devices; vendor/device IDs are not sufficient. +inline HRESULT query_adapter_luid(ID3D11Device* device,adapter_luid& result) noexcept { + result={}; + if (!device) return E_INVALIDARG; + HRESULT status=device->GetDeviceRemovedReason(); + if (FAILED(status)) return status; + Microsoft::WRL::ComPtr dxgi; + status=device->QueryInterface(IID_PPV_ARGS(&dxgi)); + if (FAILED(status)) return status; + Microsoft::WRL::ComPtr adapter; + status=dxgi->GetAdapter(&adapter); + if (FAILED(status)) return status; + DXGI_ADAPTER_DESC description{}; + status=adapter->GetDesc(&description); + if (FAILED(status)) return status; + result={description.AdapterLuid.LowPart,description.AdapterLuid.HighPart,true}; + return S_OK; +} +inline HRESULT query_adapter_luid(ID3D12Device* device,adapter_luid& result) noexcept { + result={}; + if (!device) return E_INVALIDARG; + const auto status=device->GetDeviceRemovedReason(); + if (FAILED(status)) return status; + const auto luid=device->GetAdapterLuid(); + result={luid.LowPart,luid.HighPart,true}; + return S_OK; +} +inline DXGI_FORMAT dxgi_color_format(image_format format) noexcept { + switch (format) { + case image_format::rgba8_unorm: return DXGI_FORMAT_R8G8B8A8_UNORM; + case image_format::bgra8_unorm: return DXGI_FORMAT_B8G8R8A8_UNORM; + case image_format::rgba16_float: return DXGI_FORMAT_R16G16B16A16_FLOAT; + case image_format::rgba8_srgb: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + case image_format::bgra8_srgb: return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + } + return DXGI_FORMAT_UNKNOWN; +} +// Candidate color formats only: the allocation/import path must still validate +// sharing flags, view compatibility, alpha convention and synchronization. +inline HRESULT query_dxgi_color_formats(ID3D11Device* device,uint32_t& result) noexcept { + result=0; + if (!device) return E_INVALIDARG; + uint32_t candidate=0; + constexpr UINT required=D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_RENDER_TARGET + | D3D11_FORMAT_SUPPORT_SHADER_SAMPLE; + for (uint32_t format=1;format<=5;++format) { + UINT support=0; + const auto status=device->CheckFormatSupport(dxgi_color_format(static_cast(format)),&support); + if (FAILED(status)) return status; + if ((support & required)==required) candidate|=1U<<(format-1); + } + const auto status=device->GetDeviceRemovedReason(); + if (FAILED(status)) return status; + result=candidate; return S_OK; +} +inline HRESULT query_dxgi_color_formats(ID3D12Device* device,uint32_t& result) noexcept { + result=0; + if (!device) return E_INVALIDARG; + uint32_t candidate=0; + constexpr UINT required=D3D12_FORMAT_SUPPORT1_TEXTURE2D | D3D12_FORMAT_SUPPORT1_RENDER_TARGET + | D3D12_FORMAT_SUPPORT1_SHADER_SAMPLE; + for (uint32_t format=1;format<=5;++format) { + D3D12_FEATURE_DATA_FORMAT_SUPPORT support{}; + support.Format=dxgi_color_format(static_cast(format)); + const auto status=device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT,&support,sizeof(support)); + if (FAILED(status)) return status; + if ((static_cast(support.Support1) & required)==required) candidate|=1U<<(format-1); + } + const auto status=device->GetDeviceRemovedReason(); + if (FAILED(status)) return status; + result=candidate; return S_OK; +} +// Do not retain stale endpoint identity on a failed device query. Other endpoint +// fields still require allocation/import, alpha and synchronization qualification. +inline HRESULT identify_dxgi_endpoint(ID3D11Device* device,dxgi_endpoint& endpoint) noexcept { + endpoint={}; endpoint.api=dxgi_api::d3d11; + auto status=query_adapter_luid(device,endpoint.adapter); + if (FAILED(status)) return status; + status=query_dxgi_color_formats(device,endpoint.color_formats); + if (FAILED(status)) endpoint.adapter={}; + return status; +} +inline HRESULT identify_dxgi_endpoint(ID3D12Device* device,dxgi_endpoint& endpoint) noexcept { + endpoint={}; endpoint.api=dxgi_api::d3d12; + auto status=query_adapter_luid(device,endpoint.adapter); + if (FAILED(status)) return status; + status=query_dxgi_color_formats(device,endpoint.color_formats); + if (FAILED(status)) endpoint.adapter={}; + return status; +} +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/engine_wake.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/engine_wake.h new file mode 100644 index 000000000..c5baa4641 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/engine_wake.h @@ -0,0 +1,29 @@ +#pragma once +#include "completion_mailbox.h" +#include +#include + +namespace webscene::graphics { +// Contains no engine pointer. A backend may retain this signal after the engine +// is destroyed; the last reference simply releases an unused condition variable. +class engine_wake final : public completion_wake { + std::mutex mutex_; + std::condition_variable changed_; + bool pending_{}; +public: + void signal() noexcept override { + { + std::lock_guard lock(mutex_); + pending_ = true; + } + changed_.notify_one(); + } + template + bool wait_for(std::chrono::milliseconds duration, Predicate ready) { + std::unique_lock lock(mutex_); + const bool signalled = changed_.wait_for(lock, duration, [&] { return pending_ || ready(); }); + pending_ = false; + return signalled; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/graphics_service.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/graphics_service.h new file mode 100644 index 000000000..25457fbb8 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/graphics_service.h @@ -0,0 +1,417 @@ +#pragma once +#ifndef WEBSCENE_GRAPHICS_ENABLE_ANGLE +#define WEBSCENE_GRAPHICS_ENABLE_ANGLE 1 +#endif +#if WEBSCENE_GRAPHICS_ENABLE_ANGLE +#include "angle_context.h" +#endif +#include "dawn_event_service.h" +#include "dawn_device.h" +#include "command_channel.h" +#include "release_channel.h" +#include +#include + +namespace webscene::graphics { +// One instance per engine, created on its worker. This is native API state only; +// framework-owned Skia contexts and presenter textures never enter this service. +struct graphics_metrics { + size_t live_devices{},live_contexts{}; + completion_metrics completions{}; + queue_metrics commands{}; + size_t release_registrations{}; + size_t live_adapters{}; +}; +class graphics_service { + const std::thread::id thread_ = std::this_thread::get_id(); + const resource_owner owner_{new_owner_token(),new_owner_token(),0}; + std::shared_ptr wake_; + const size_t completion_capacity_; + const bool measure_latency_; +#if WEBSCENE_GRAPHICS_ENABLE_ANGLE + resource_table contexts_; +#endif + resource_table devices_; + resource_table adapters_; + std::unique_ptr dawn_; + bool closed_{}; + std::chrono::steady_clock::time_point next_event_poll_{}; + size_t active_context_scopes_{}; + size_t active_device_scopes_{}; + size_t active_adapter_scopes_{}; + bool executing_commands_{}; + bool pumping_{}; + std::shared_ptr commands_; + std::shared_ptr releases_; + uint64_t executed_command_serial_{}; + void check_thread() const { + if (std::this_thread::get_id()!=thread_) + throw std::logic_error("graphics service requires its engine worker"); + } + void check_open() const { + check_thread(); + if (closed_) throw std::logic_error("graphics service is closed"); + } +public: + graphics_service(std::shared_ptr wake,size_t context_capacity=64,size_t completion_capacity=256,bool measure_latency=false) + : wake_(std::move(wake)),completion_capacity_(completion_capacity),measure_latency_(measure_latency), +#if WEBSCENE_GRAPHICS_ENABLE_ANGLE + contexts_(context_capacity,owner_), +#endif + devices_(context_capacity,owner_),adapters_(context_capacity,owner_) {} + graphics_service(const graphics_service&)=delete; + graphics_service& operator=(const graphics_service&)=delete; + ~graphics_service() { + if (std::this_thread::get_id()!=thread_) std::terminate(); + close(); + } + uint64_t engine_identity() const noexcept { return owner_.engine; } + bool dawn_initialized() const { check_thread(); return dawn_!=nullptr; } + dawn_event_service& dawn() { + check_open(); + if (!dawn_) dawn_=std::make_unique(completion_capacity_,wake_,measure_latency_); + return *dawn_; + } + // Internal discovery completion hook. Only adapters discovered through this + // engine's Dawn instance may be adopted. Async device requests must copy the + // native reference during with_adapter, never retain its borrowed address. + resource_handle adopt_adapter(wgpu::Adapter adapter) { + check_open(); + if (!adapter) throw std::invalid_argument("Cannot adopt a null adapter"); + return adapters_.insert(owner_,std::make_unique(std::move(adapter))); + } + template void with_adapter(resource_handle handle,Execute execute) { + check_open(); + const auto& adapter=adapters_.get(handle,owner_); + struct guard { + size_t& count; + explicit guard(size_t& value) : count(value) { ++count; } + ~guard() { --count; } + } scope(active_adapter_scopes_); + execute(adapter); + } + void destroy_adapter(resource_handle handle) { + check_open(); + if (active_adapter_scopes_) throw std::logic_error("Cannot destroy adapters during execution"); + adapters_.destroy(handle,owner_); + } + size_t live_adapters() const { check_thread(); return adapters_.resident_count(); } + // Internal request-device completion hook: pass a freshly created device + // from this service's instance exactly once, with its originating adapter. + resource_handle adopt_device(wgpu::Adapter adapter,wgpu::Device device,std::shared_ptr loss={},size_t buffer_capacity=1024,size_t shader_capacity=1024,size_t render_pipeline_capacity=1024,size_t texture_capacity=1024,size_t texture_view_capacity=4096,size_t command_capacity=1024) { + check_open(); + return devices_.insert(owner_,std::make_unique( + owner_.engine,dawn().completions(),std::move(adapter),std::move(device),std::move(loss),buffer_capacity,shader_capacity,render_pipeline_capacity,texture_capacity,texture_view_capacity,command_capacity)); + } + template void with_device(resource_handle handle,Execute execute) { + check_open(); + auto& device=devices_.get(handle,owner_); + struct guard { + size_t& count; + explicit guard(size_t& value) : count(value) { ++count; } + ~guard() { --count; } + } scope(active_device_scopes_); + execute(device); + } + void destroy_device(resource_handle handle) { + check_open(); + if (active_device_scopes_) throw std::logic_error("Cannot destroy devices during execution"); + devices_.destroy(handle,owner_); + } + size_t live_devices() const { check_thread(); return devices_.resident_count(); } +#if WEBSCENE_GRAPHICS_ENABLE_ANGLE + resource_handle create_angle_context(EGLint backend,EGLint major) { + check_open(); + auto display=angle_display::acquire(backend); + const EGLint attributes[]={EGL_SURFACE_TYPE,EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE,major==2 ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES3_BIT,EGL_NONE}; + EGLConfig config{}; EGLint count{}; + if (!eglChooseConfig(display->get(),attributes,&config,1,&count) || count!=1) + throw std::runtime_error("ANGLE context configuration unavailable"); + return contexts_.insert(owner_,std::make_unique(display,config,major)); + } + template void with_angle_context(resource_handle handle,Execute execute) { + check_open(); + auto& context=contexts_.get(handle,owner_); + angle_context::scope scope(context); + struct execution_guard { + size_t& count; + explicit execution_guard(size_t& value) : count(value) { ++count; } + ~execution_guard() { --count; } + } guard(active_context_scopes_); + execute(); + // Report a reset during execution to the command's completion path; + // successful callback return alone does not imply a usable context. + if(context.poll_loss()) throw angle_context_lost(); + } + // Called by the execution thread after queued context operations have drained. + void destroy_angle_context(resource_handle handle) { + check_open(); + if (active_context_scopes_) throw std::logic_error("Cannot destroy ANGLE contexts during execution"); + contexts_.destroy(handle,owner_); + } +#endif + // Finalizers enqueue these value-only records through a retained endpoint. + // A full queue requires retry/retention by the caller; it is not a release. + static graphics_command deferred_buffer_release(resource_handle device,resource_handle buffer) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_buffer({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,buffer.table,buffer.generation,buffer.slot}}; + } + static graphics_command deferred_shader_module_release(resource_handle device,resource_handle shader) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_shader_module({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,shader.table,shader.generation,shader.slot}}; + } + static graphics_command deferred_render_pipeline_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_render_pipeline({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_sampler_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_sampler({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_compute_pipeline_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_compute_pipeline({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_bind_group_layout_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_bind_group_layout({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_bind_group_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_bind_group({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_pipeline_layout_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_pipeline_layout({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_texture_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_texture({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_texture_view_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_texture_view({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_command_encoder_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_command_encoder({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_render_pass_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_render_pass({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_compute_pass_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_compute_pass({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_command_buffer_release(resource_handle device,resource_handle pipeline) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { + service.with_device({args[0],args[1],static_cast(args[2])},[&](auto& owner) { + owner.release_command_buffer({args[3],args[4],static_cast(args[5])}); + }); + } catch (const std::invalid_argument&) { /* Device or wrapper already released. */ } + },{device.table,device.generation,device.slot,pipeline.table,pipeline.generation,pipeline.slot}}; + } + static graphics_command deferred_adapter_release(resource_handle handle) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { service.destroy_adapter({args[0],args[1],static_cast(args[2])}); } + catch (const std::invalid_argument&) { /* Already explicitly destroyed. */ } + },{handle.table,handle.generation,handle.slot}}; + } + static graphics_command deferred_device_release(resource_handle handle) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { service.destroy_device({args[0],args[1],static_cast(args[2])}); } + catch (const std::invalid_argument&) { /* Already explicitly destroyed. */ } + },{handle.table,handle.generation,handle.slot}}; + } +#if WEBSCENE_GRAPHICS_ENABLE_ANGLE + static graphics_command deferred_context_release(resource_handle handle) noexcept { + return {[](graphics_service& service,std::span,const graphics_command::arguments& args) noexcept { + try { service.destroy_angle_context({args[0],args[1],static_cast(args[2])}); } + catch (const std::invalid_argument&) { /* Already explicitly destroyed. */ } + },{handle.table,handle.generation,handle.slot}}; + } +#endif + // Create lazily on the engine thread; other threads retain only the channel. + std::shared_ptr command_endpoint(size_t capacity=256,size_t upload_limit=65536) { + check_open(); + if (!commands_) commands_=std::make_shared(capacity,upload_limit,wake_); + return commands_; + } + std::shared_ptr release_endpoint(size_t capacity=256) { + check_open(); + if (!releases_) releases_=std::make_shared(capacity,command_endpoint(),wake_); + return releases_; + } + size_t drain_commands(size_t budget=64) { + check_thread(); + if (!commands_) return 0; + if (executing_commands_) throw std::logic_error("graphics command dispatch is not reentrant"); + struct guard { + bool& active; + explicit guard(bool& value) : active(value) { active=true; } + ~guard() { active=false; } + } scope(executing_commands_); + size_t count=0; + while (countconsume_one([&](const auto& command,auto upload,uint64_t serial) { + command.execute(*this,upload,command.values); + executed_command_serial_=serial; + })) ++count; + size_t released=0; + while (releases_ && releasedconsume_one(executed_command_serial_,[&](const auto& command) { + command.execute(*this,{},command.values); + })) ++released; + return count; + } + // Pressure recovery may release collected wrappers without dispatching new + // commands or entering JavaScript completion delivery. Accepted uses that + // have not executed still prevent release through their serial barrier. + size_t drain_completed_releases(size_t budget=256) { + check_thread(); + if(executing_commands_)throw std::logic_error("Release drain is not reentrant"); + size_t count=0; + while(releases_&&countconsume_one(executed_command_serial_,[&](const auto& command){ + command.execute(*this,{},command.values); + }))++count; + return count; + } + template size_t pump(Deliver deliver,size_t budget=64) { + check_thread(); + if (pumping_) throw std::logic_error("graphics completion pumping is not reentrant"); + struct pump_guard { + bool& active; + explicit pump_guard(bool& value) : active(value) { active=true; } + ~pump_guard() { active=false; } + } scope(pumping_); + devices_.visit_live([](auto& device) { device.process_loss(); }); + drain_commands(budget); + if (!dawn_) return 0; + next_event_poll_=std::chrono::steady_clock::now()+std::chrono::milliseconds(1); + return dawn_->pump(deliver,budget); + } + bool has_ready_work() const { + check_thread(); + if (devices_.any_live([](const auto& device) { return device.loss_pending(); })) return true; + if (commands_ && commands_->metrics().depth) return true; + if (releases_ && releases_->has_ready(executed_command_serial_)) return true; + return dawn_ && (dawn_->completions()->has_ready() + || (!closed_ && dawn_->completions()->has_pollable_pending() + && std::chrono::steady_clock::now()>=next_event_poll_)); + } + std::chrono::milliseconds recommended_idle_wait(std::chrono::milliseconds maximum) const { + check_thread(); + if (devices_.any_live([](const auto& device) { return device.loss_pending(); })) return std::chrono::milliseconds::zero(); + if (commands_ && commands_->metrics().depth) return std::chrono::milliseconds::zero(); + if (releases_ && releases_->has_ready(executed_command_serial_)) return std::chrono::milliseconds::zero(); + if (!dawn_) return maximum; + // Cancellation delivery remains runnable after admission closes. + if (dawn_->completions()->has_ready()) return std::chrono::milliseconds::zero(); + // Only outstanding native operations require ProcessEvents polling. + if (closed_ || !dawn_->completions()->has_pollable_pending()) return maximum; + const auto now=std::chrono::steady_clock::now(); + if (now>=next_event_poll_) + return std::chrono::milliseconds::zero(); + return std::min(maximum,std::chrono::ceil(next_event_poll_-now)); + } + graphics_metrics metrics() const { + check_thread(); + return {devices_.resident_count(),live_contexts(), + dawn_ ? dawn_->completions()->metrics() : completion_metrics{}, + commands_ ? commands_->metrics() : queue_metrics{}, + releases_ ? releases_->occupied() : 0,adapters_.resident_count()}; + } + size_t live_contexts() const { + check_thread(); +#if WEBSCENE_GRAPHICS_ENABLE_ANGLE + return contexts_.resident_count(); +#else + return 0; +#endif + } + void close() { + check_thread(); + if (closed_) return; + if (active_context_scopes_ || active_device_scopes_ || active_adapter_scopes_ || executing_commands_ || pumping_) + throw std::logic_error("Cannot close graphics service during native execution"); + if (releases_) releases_->close(); + if (commands_) { + commands_->close(); + drain_commands(std::numeric_limits::max()); + } + closed_=true; + devices_.destroy_owner(owner_); + adapters_.destroy_owner(owner_); + if (dawn_) dawn_->close(); +#if WEBSCENE_GRAPHICS_ENABLE_ANGLE + contexts_.destroy_owner(owner_); +#endif + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/image_lease_abi.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/image_lease_abi.h new file mode 100644 index 000000000..65ab61c80 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/image_lease_abi.h @@ -0,0 +1,50 @@ +#pragma once +#include "owned_image_pool.h" +#include "../webscene_native_engine.h" +// Backend-owned completion dependencies. Native pointers remain borrowed from +// this retained owner and must never be exposed to JavaScript. +struct webscene_gpu_producer_dependencies { + virtual ~webscene_gpu_producer_dependencies()=default; + virtual size_t count() const noexcept=0; + virtual bool metal_event(size_t index,void*& event,uint64_t& value) const=0; + virtual bool dxgi_fence(size_t,void*& handle,uint64_t& value) const { handle=nullptr;value=0;return false; } +}; +// Native-only implementation of the opaque C handles. Never expose to JS. +struct webscene_gpu_image_lease_v3 { + webscene::graphics::owned_image_pool::retained value; + std::shared_ptr dependencies; + const bool requires_producer_wait; + explicit webscene_gpu_image_lease_v3(webscene::graphics::owned_image_pool::retained image, + std::shared_ptr producer={},bool requires_wait=false) + : value(std::move(image)),dependencies(std::move(producer)),requires_producer_wait(requires_wait) {} +}; +struct webscene_gpu_image_consumer_v3 { + webscene::graphics::owned_image_pool::consumer value; + std::shared_ptr dependencies; + explicit webscene_gpu_image_consumer_v3(webscene::graphics::owned_image_pool::consumer image, + std::shared_ptr producer={}) + : value(std::move(image)),dependencies(std::move(producer)) {} +}; + +// Engine-thread-only dependency for an immutable scene capture. Backends own +// completion synchronization; neither this interface nor metadata expose a +// consumer handle while producer work is pending. This is not a C ABI change. +struct webscene_gpu_image_snapshot { + enum class status { pending, ready, failed }; + virtual ~webscene_gpu_image_snapshot()=default; + virtual webscene::graphics::image_metadata describe() const=0; + virtual status state() const=0; + virtual std::shared_ptr resolve()=0; + virtual std::shared_ptr resolve_with_gpu_waits() { return resolve(); } +}; + +// A submitted opportunity whose exact output could not be retained must remain +// an explicit failed dependency. Absence would incorrectly reuse an older image. +struct webscene_failed_gpu_image_snapshot final : webscene_gpu_image_snapshot { + const webscene::graphics::image_metadata metadata; + explicit webscene_failed_gpu_image_snapshot(webscene::graphics::image_metadata value) + : metadata(value) {} + webscene::graphics::image_metadata describe() const override { return metadata; } + status state() const override { return status::failed; } + std::shared_ptr resolve() override { return {}; } +}; diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/image_lease_pool.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/image_lease_pool.h new file mode 100644 index 000000000..09989e39b --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/image_lease_pool.h @@ -0,0 +1,204 @@ +#pragma once +#include "resource_table.h" +#include "image_metadata.h" +#include "completion_mailbox.h" +#include +#include +#include + +namespace webscene::graphics { +struct image_write_token { uint64_t pool{},generation{}; uint32_t slot{}; }; +struct image_lease_token { uint64_t pool{},generation{}; uint32_t index{}; }; +// Bounded image slots, independent of scene publication counts. This class owns +// lifetime metadata only; the backend retains allocations until slots are idle. +class image_lease_pool { + enum class phase { idle,writing,published }; + enum class lease_kind { free,retained,consumer }; + struct image_slot { + phase state{}; + uint64_t generation{}; + size_t retained{},consumers{}; + bool producer_done{}; + bool producer_started{}; + bool metadata_set{}; + image_metadata metadata{}; + }; + struct lease_slot { + lease_kind kind{}; + uint64_t generation{}; + uint32_t image{}; + }; + const uint64_t identity_=new_owner_token(); + mutable std::mutex mutex_; + std::vector images_; + std::vector leases_; + bool closed_{}; + std::shared_ptr wake_; + image_slot& image(image_write_token token) { + if (token.pool!=identity_ || token.slot>=images_.size()) throw std::invalid_argument("foreign image writer"); + auto& item=images_[token.slot]; + if (item.state==phase::idle || item.generation!=token.generation) throw std::invalid_argument("stale image writer"); + return item; + } + lease_slot& lease(image_lease_token token,lease_kind kind) { + if (token.pool!=identity_ || token.index>=leases_.size()) throw std::invalid_argument("foreign image lease"); + auto& item=leases_[token.index]; + if (item.kind!=kind || item.generation!=token.generation) throw std::invalid_argument("stale or wrong-kind image lease"); + return item; + } + std::optional allocate(uint32_t image,lease_kind kind) { + for (uint32_t i=0;isignal(); } +public: + explicit image_lease_pool(size_t lease_capacity=128,std::shared_ptr wake={},size_t image_capacity=3) : wake_(std::move(wake)) { + if (!lease_capacity || lease_capacity>UINT32_MAX) throw std::invalid_argument("invalid image lease capacity"); + if (!image_capacity || image_capacity>4) throw std::invalid_argument("invalid image slot capacity"); + images_.resize(image_capacity); + leases_.resize(lease_capacity); + } + std::optional acquire_write() { + std::lock_guard lock(mutex_); + if (closed_) return {}; + for (uint32_t i=0;i(metadata.format)<1 || static_cast(metadata.format)>5 + || static_cast(metadata.alpha)<1 || static_cast(metadata.alpha)>3 + || static_cast(metadata.color_space)<1 || static_cast(metadata.color_space)>2 + || static_cast(metadata.orientation)<1 || static_cast(metadata.orientation)>2) + throw std::invalid_argument("invalid portable image metadata"); + // Different frame slots must not alias the same busy physical image. + // Re-presenting unchanged pixels uses retain(), not another writer. + for (const auto& other:images_) + if (&other!=&item && other.state!=phase::idle && other.metadata_set + && other.metadata.allocation==metadata.allocation) + throw std::invalid_argument("image allocation already has an active frame"); + item.metadata=metadata; item.metadata_set=true; + } + image_metadata describe(image_lease_token token) const { + std::lock_guard lock(mutex_); + if (token.pool!=identity_ || token.index>=leases_.size()) throw std::invalid_argument("foreign image lease"); + const auto& owned=leases_[token.index]; + if (owned.kind==lease_kind::free || owned.generation!=token.generation) + throw std::invalid_argument("stale image lease"); + return images_[owned.image].metadata; + } + // Call before submitting any backend work against this image. Producer + // completion is required even if the frame is abandoned before publication. + void begin_producer(image_write_token writer) { + std::lock_guard lock(mutex_); + auto& item=image(writer); + if (item.state!=phase::writing || item.producer_started || !item.metadata_set) + throw std::invalid_argument("invalid producer submission"); + item.producer_started=true; + } + // On capacity exhaustion the writer stays reserved; the caller retries. + std::optional publish(image_write_token writer) { + std::lock_guard lock(mutex_); + auto& item=image(writer); + if (item.state!=phase::writing) throw std::invalid_argument("image already published"); + if (!item.metadata_set || !item.producer_started) throw std::invalid_argument("image publication requires metadata and producer submission"); + auto token=allocate(writer.slot,lease_kind::retained); + if (!token) return {}; + item.state=phase::published; item.retained=1; + return token; + } + // Cancel before submission, or after an abandoned producer has completed. + void cancel_write(image_write_token writer,bool notify_capacity=true) { + std::unique_lock lock(mutex_); + auto& item=image(writer); + if (item.state!=phase::writing) throw std::invalid_argument("cannot cancel published image"); + if (item.producer_started && !item.producer_done) throw std::invalid_argument("producer still uses cancelled image"); + item.state=phase::idle; + lock.unlock(); if (notify_capacity) signal_capacity(); + } + std::optional retain(image_lease_token source) { + std::lock_guard lock(mutex_); + const auto index=lease(source,lease_kind::retained).image; + auto result=allocate(index,lease_kind::retained); + if (result) ++images_[index].retained; + return result; + } + // Registration may precede producer completion. The presenter must enqueue + // the producer's GPU wait before sampling; no CPU wait is required here. + std::optional begin_consumer(image_lease_token source) { + std::lock_guard lock(mutex_); + const auto index=lease(source,lease_kind::retained).image; + auto result=allocate(index,lease_kind::consumer); + if (result) ++images_[index].consumers; + return result; + } + void release(image_lease_token token) { + std::unique_lock lock(mutex_); + auto& owned=lease(token,lease_kind::retained); + auto& item=images_[owned.image]; + owned.kind=lease_kind::free; --item.retained; recycle(item); + lock.unlock(); signal_capacity(); // A ticket is available even if the image remains busy. + } + void finish_consumer(image_lease_token token) { + std::unique_lock lock(mutex_); + auto& owned=lease(token,lease_kind::consumer); + auto& item=images_[owned.image]; + owned.kind=lease_kind::free; --item.consumers; recycle(item); + lock.unlock(); signal_capacity(); + } + void finish_producer(image_write_token writer) { + std::unique_lock lock(mutex_); + auto& item=image(writer); + if (!item.producer_started || item.producer_done) throw std::invalid_argument("invalid producer completion"); + item.producer_done=true; + const bool available=recycle(item); + lock.unlock(); + if (available) signal_capacity(); + } + // Stop new frames. Existing retained scenes can still be redrawn/released. + void close() { std::lock_guard lock(mutex_); closed_=true; } + struct occupancy { + size_t busy{},producer_pending{},retained{},consumer_pending{}; + }; + occupancy inspect_occupancy() const { + std::lock_guard lock(mutex_); + occupancy result; + for(const auto& item:images_) { + if(item.state==phase::idle)continue; + ++result.busy; + result.producer_pending+=item.producer_started&&!item.producer_done; + result.retained+=item.retained!=0; + result.consumer_pending+=item.consumers!=0; + } + return result; + } + size_t busy_images() const { + std::lock_guard lock(mutex_); + size_t count=0; + for (const auto& item:images_) count+=item.state!=phase::idle; + return count; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/image_metadata.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/image_metadata.h new file mode 100644 index 000000000..17167523d --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/image_metadata.h @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace webscene::graphics { +enum class image_format : uint32_t { rgba8_unorm=1,bgra8_unorm,rgba16_float,rgba8_srgb,bgra8_srgb }; +enum class image_alpha : uint32_t { opaque=1,premultiplied,straight }; +enum class image_color_space : uint32_t { srgb=1,display_p3 }; +enum class image_orientation : uint32_t { top_left=1,bottom_left }; +// Portable values only. Allocation and readiness identities resolve through the +// native provider, never by casting an integer to a texture/Skia pointer. +struct image_metadata { + uint64_t canvas{},allocation{},allocation_generation{},content_serial{}; + uint64_t producer_timeline{},producer_value{}; + uint32_t width{},height{}; + image_format format{image_format::rgba8_unorm}; + image_alpha alpha{image_alpha::premultiplied}; + image_color_space color_space{image_color_space::srgb}; + image_orientation orientation{image_orientation::top_left}; +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/iosurface_canvas_images.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/iosurface_canvas_images.h new file mode 100644 index 000000000..d7408cd32 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/iosurface_canvas_images.h @@ -0,0 +1,94 @@ +#pragma once +#include "iosurface_color.h" +#include "owned_image_pool.h" +#include +#if defined(__APPLE__) +namespace webscene::graphics { +// IOSurface storage behind the shared versioned lease contract. Producer and +// consumer GPU completion are supplied by the Dawn/ANGLE/presenter integrations. +class iosurface_canvas_images { + struct storage final : image_provider_lifetime { + image_provider_kind kind() const noexcept override { return image_provider_kind::iosurface; } + struct slot { std::shared_ptr color; image_metadata metadata{}; }; + std::array slots; + std::mutex mutex; + uint64_t bytes{}; + }; + std::shared_ptr storage_=std::make_shared(); + owned_image_pool pool_; + uint64_t limit_; + const std::thread::id thread_=std::this_thread::get_id(); +public: + struct frame { + owned_image_pool::producer producer; + const iosurface_color* color; // Borrowed through the producer lease. + image_metadata metadata; + }; + explicit iosurface_canvas_images(uint64_t budget,std::shared_ptr wake={}) + :pool_(storage_,128,std::move(wake)),limit_(budget) { + if (!budget) throw std::invalid_argument("IOSurface pool requires a budget"); + } + std::optional acquire(image_metadata metadata) { + if (thread_!=std::this_thread::get_id()) + throw std::logic_error("IOSurface allocation requires owner thread"); + if (metadata.format!=image_format::bgra8_unorm) + throw std::invalid_argument("IOSurface pool requires negotiated BGRA8"); + auto writer=pool_.acquire(); + if (!writer) return {}; + std::lock_guard lock(storage_->mutex); + auto& slot=storage_->slots[writer->slot()]; + const bool reuse=slot.color && slot.metadata.width==metadata.width && + slot.metadata.height==metadata.height; + metadata.allocation=reuse ? slot.metadata.allocation : new_owner_token(); + writer->set_metadata(metadata); + if (!reuse) { + if (slot.color) storage_->bytes-=slot.color->allocation_bytes(); + slot.color.reset(); + slot.color=iosurface_color::create_bgra8(metadata.width,metadata.height,limit_-storage_->bytes); + if (!slot.color) { writer->cancel(false); return {}; } + storage_->bytes+=slot.color->allocation_bytes(); + } + slot.metadata=metadata; + return frame{std::move(*writer),slot.color.get(),metadata}; + } + // Adopt an already decoded immutable surface. Three slots bound outstanding + // frames; a busy slot remains owned until every retained scene/GPU consumer + // finishes. No readback, upload or pixel copy occurs here. + std::optional adopt(image_metadata metadata, + std::shared_ptr color) { + if (thread_!=std::this_thread::get_id()) throw std::logic_error("Image publication requires owner thread"); + if (!color || metadata.format!=image_format::bgra8_unorm || + IOSurfaceGetWidth(color->borrowed_handle())!=metadata.width || + IOSurfaceGetHeight(color->borrowed_handle())!=metadata.height) + throw std::invalid_argument("Decoded image metadata mismatch"); + if(color->allocation_bytes()>limit_)throw std::length_error("Decoded surface exceeds image budget"); + auto writer=pool_.acquire(); if (!writer) return {}; + { + std::lock_guard lock(storage_->mutex); + auto& slot=storage_->slots[writer->slot()]; + auto previous=slot.color ? slot.color->allocation_bytes() : 0; + if (color->allocation_bytes()>limit_-(storage_->bytes-previous)) {writer->cancel(false);return {};} + storage_->bytes=storage_->bytes-previous+color->allocation_bytes(); + metadata.allocation=new_owner_token(); writer->set_metadata(metadata); + slot.color=std::move(color); slot.metadata=metadata; + } + writer->begin(); writer->complete(); return writer->publish(); + } + static const iosurface_color& resolve(const owned_image_pool::consumer& consumer) { + const auto metadata=consumer.describe(); + const auto anchor=consumer.provider(); + if (anchor->kind()!=image_provider_kind::iosurface) + throw std::invalid_argument("Foreign IOSurface lease provider"); + const auto provider=std::static_pointer_cast(anchor); + std::lock_guard lock(provider->mutex); + for (const auto& slot:provider->slots) + if (slot.color && slot.metadata.allocation==metadata.allocation && + slot.metadata.allocation_generation==metadata.allocation_generation && + slot.metadata.content_serial==metadata.content_serial) return *slot.color; + throw std::invalid_argument("IOSurface generation unavailable"); + } + image_lease_pool::occupancy inspect_occupancy() const { return pool_.inspect_occupancy(); } + size_t busy_images() const { return pool_.busy_images(); } +}; +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/iosurface_color.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/iosurface_color.h new file mode 100644 index 000000000..245425442 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/iosurface_color.h @@ -0,0 +1,78 @@ +#pragma once +#if defined(__APPLE__) +#include +#include +#include +#include +#include +#include + +namespace webscene::graphics { +// Native allocation ownership only. Callers retain this owner through producer +// and consumer GPU completion; neither retain nor destruction synchronizes GPU work. +class iosurface_color final { + IOSurfaceRef surface_=nullptr; + std::shared_ptr external_owner_; + explicit iosurface_color(IOSurfaceRef surface):surface_(surface) {} +public: + ~iosurface_color() { if (surface_) CFRelease(surface_); } + iosurface_color(const iosurface_color&)=delete; + iosurface_color& operator=(const iosurface_color&)=delete; + IOSurfaceRef borrowed_handle() const noexcept { return surface_; } + size_t allocation_bytes() const noexcept { return IOSurfaceGetAllocSize(surface_); } + + // Keep the decoder's CVPixelBuffer lease, not merely its IOSurface. The + // decoder pool may recycle pixels as soon as the pixel buffer is released. + static std::shared_ptr adopt_bgra8(CVPixelBufferRef pixel, std::shared_ptr owner) { + if (!pixel || !owner || CVPixelBufferGetPixelFormatType(pixel)!=kCVPixelFormatType_32BGRA) return {}; + auto surface=CVPixelBufferGetIOSurface(pixel); + if (!surface) return {}; + auto result=std::shared_ptr(new iosurface_color(nullptr)); + CFRetain(surface); result->surface_=surface; result->external_owner_=std::move(owner); + return result; + } + + // BGRA8 is the negotiated Dawn/Metal-to-CGL diagnostic format. Other formats + // require explicit negotiation, not reinterpretation of these storage bytes. + static std::shared_ptr create_bgra8(uint32_t width,uint32_t height,uint64_t available_bytes) { + if (!width || !height || width>uint32_t(std::numeric_limits::max()/4) || + height>uint32_t(std::numeric_limits::max())) return {}; + const uint64_t row=IOSurfaceAlignProperty(kIOSurfaceBytesPerRow,uint64_t(width)*4); + const auto page=static_cast(getpagesize()); + if (!row || !page || row>uint64_t(INT64_MAX)/height) return {}; + const uint64_t raw=row*height; + if (raw>uint64_t(INT64_MAX)-page+1) return {}; + const uint64_t allocation=((raw+page-1)/page)*page; + if (allocation>available_bytes) return {}; + auto dictionary=CFDictionaryCreateMutable(nullptr,0,&kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (!dictionary) return {}; + bool valid=true; + auto add=[&](CFStringRef key,int64_t value) { + auto number=CFNumberCreate(nullptr,kCFNumberSInt64Type,&value); + if (!number) { valid=false; return; } + CFDictionarySetValue(dictionary,key,number); + CFRelease(number); + }; + add(kIOSurfaceWidth,static_cast(width)); + add(kIOSurfaceHeight,static_cast(height)); + add(kIOSurfaceBytesPerElement,4); + add(kIOSurfaceBytesPerRow,static_cast(row)); + add(kIOSurfaceAllocSize,static_cast(allocation)); + add(kIOSurfacePixelFormat,kCVPixelFormatType_32BGRA); + auto surface=valid ? IOSurfaceCreate(dictionary) : nullptr; + CFRelease(dictionary); + if (!surface) return {}; + if (IOSurfaceGetAllocSize(surface)>available_bytes) { + CFRelease(surface); + return {}; + } + // Take ownership before allocating the shared control block. + std::unique_ptr owner; + try { owner.reset(new iosurface_color(surface)); } + catch (...) { CFRelease(surface); throw; } + return std::shared_ptr(std::move(owner)); + } +}; +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/metal_producer_wait.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/metal_producer_wait.h new file mode 100644 index 000000000..3fb5dd06d --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/metal_producer_wait.h @@ -0,0 +1,31 @@ +#pragma once +#if defined(__APPLE__) && defined(__OBJC__) +#import +#include +#include + +namespace webscene::graphics { +struct metal_producer_dependency { + id event; + uint64_t value; +}; +// Caller holds the host queue lease. All later Skia submissions must use this +// same queue; dependencies and image ownership remain retained by the scene. +// Returning a command buffer certifies submission, never GPU completion. +inline id submit_metal_producer_waits( + id queue, std::span dependencies) { + if(!queue || dependencies.empty()) return nil; + // Validate the entire list before encoding anything into the host queue. + // Shared events can synchronize across devices; do not require device identity. + for(const auto& dependency:dependencies) + if(!dependency.event) return nil; + id barrier=[queue commandBuffer]; + if(!barrier) return nil; + barrier.label=@"WebScene producer dependencies"; + for(const auto& dependency:dependencies) + [barrier encodeWaitForEvent:dependency.event value:dependency.value]; + [barrier commit]; + return barrier; +} +} +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/nt_handle.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/nt_handle.h new file mode 100644 index 000000000..89e6ac4ea --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/nt_handle.h @@ -0,0 +1,68 @@ +#pragma once +#include +#include +#if defined(_WIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#endif + +namespace webscene::graphics { +// Ops supplies the native duplication/close primitives; the same ownership +// implementation can be exercised without requiring a Windows GPU runner. +template class unique_nt_handle { +public: + using handle_type=typename Ops::handle_type; +private: + handle_type value_=Ops::empty(); + explicit unique_nt_handle(handle_type owned):value_(owned) {} +public: + unique_nt_handle()=default; + unique_nt_handle(const unique_nt_handle&)=delete; + unique_nt_handle& operator=(const unique_nt_handle&)=delete; + unique_nt_handle(unique_nt_handle&& other) noexcept :value_(other.release()) {} + unique_nt_handle& operator=(unique_nt_handle&& other) noexcept { + if (this!=&other) { reset(); value_=other.release(); } + return *this; + } + ~unique_nt_handle() { reset(); } + static unique_nt_handle adopt(handle_type owned) { + if (!Ops::valid(owned)) throw std::invalid_argument("invalid owned NT handle"); + return unique_nt_handle(owned); + } + static unique_nt_handle duplicate(handle_type borrowed) { + if (!Ops::valid(borrowed)) throw std::invalid_argument("invalid borrowed NT handle"); + return adopt(Ops::duplicate(borrowed)); + } + handle_type get() const noexcept { return value_; } + explicit operator bool() const noexcept { return Ops::valid(value_); } + handle_type release() noexcept { return std::exchange(value_,Ops::empty()); } + void reset() noexcept { + const auto owned=release(); + if (Ops::valid(owned)) Ops::close(owned); + } +}; +#if defined(_WIN32) +struct win32_nt_handle_ops { + using handle_type=HANDLE; + static HANDLE empty() noexcept { return nullptr; } + static bool valid(HANDLE value) noexcept { return value && value!=INVALID_HANDLE_VALUE; } + static void close(HANDLE value) noexcept { ::CloseHandle(value); } + static HANDLE duplicate(HANDLE borrowed) { + HANDLE result=nullptr; + if (!::DuplicateHandle(::GetCurrentProcess(),borrowed,::GetCurrentProcess(),&result, + 0,FALSE,DUPLICATE_SAME_ACCESS)) + throw std::system_error(static_cast(::GetLastError()),std::system_category(),"DuplicateHandle"); + return result; + } +}; +// NT handles only; legacy IDXGIResource::GetSharedHandle values are not owned +// CloseHandle-compatible handles and must never be passed to this wrapper. +using owned_nt_handle=unique_nt_handle; +#endif +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/owned_image_pool.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/owned_image_pool.h new file mode 100644 index 000000000..b7291235c --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/owned_image_pool.h @@ -0,0 +1,140 @@ +#pragma once +#include "image_lease_pool.h" +#include +#include + +namespace webscene::graphics { +// Native provider lifetime anchor. Its destructor must be safe on a completion +// thread (thread-affine GPU destruction must be dispatched by the provider). +// No native pointer is exported through portable image metadata. +enum class image_provider_kind { generic, iosurface, d3d12 }; +struct image_provider_lifetime { + virtual image_provider_kind kind() const noexcept { return image_provider_kind::generic; } + virtual ~image_provider_lifetime() = default; +}; +class owned_image_pool { + struct state { + std::shared_ptr provider; + image_lease_pool pool; + state(std::shared_ptr p,size_t capacity,std::shared_ptr wake,size_t image_capacity) + : provider(std::move(p)),pool(capacity,std::move(wake),image_capacity) { + if (!provider) throw std::invalid_argument("image provider required"); + } + }; + std::shared_ptr state_; +public: + class consumer { + std::shared_ptr state_; + image_lease_token token_{}; + friend class retained; + public: + consumer(std::shared_ptr s,image_lease_token token):state_(std::move(s)),token_(token) {} + consumer(const consumer&)=delete; + consumer& operator=(const consumer&)=delete; + consumer(consumer&&)=default; + consumer& operator=(consumer&&)=delete; + // Destroying a CPU wrapper is not evidence of GPU completion. + ~consumer() { if (state_) std::terminate(); } + image_metadata describe() const { + if (!state_) throw std::invalid_argument("completed image consumer"); + return state_->pool.describe(token_); + } + std::shared_ptr provider() const { + if (!state_) throw std::invalid_argument("completed image consumer"); + return state_->provider; + } + void complete() { + if (!state_) throw std::invalid_argument("duplicate image completion"); + state_->pool.finish_consumer(token_); state_.reset(); + } + }; + class retained { + std::shared_ptr state_; + image_lease_token token_{}; + public: + retained(std::shared_ptr s,image_lease_token token):state_(std::move(s)),token_(token) {} + retained(const retained&)=delete; + retained& operator=(const retained&)=delete; + retained(retained&&)=default; + retained& operator=(retained&&)=delete; + ~retained() { if (state_) state_->pool.release(token_); } + image_metadata describe() const { + if (!state_) throw std::invalid_argument("moved image reference"); + return state_->pool.describe(token_); + } + std::optional retain() const { + if (!state_) throw std::invalid_argument("moved image reference"); + auto token=state_->pool.retain(token_); + if (!token) return {}; + return retained(state_,*token); + } + std::optional begin_consumer() const { + if (!state_) throw std::invalid_argument("moved image reference"); + auto token=state_->pool.begin_consumer(token_); + if (!token) return {}; + return consumer(state_,*token); + } + }; + class producer { + std::shared_ptr state_; + image_write_token token_{}; + bool started_{},completed_{},published_{}; + public: + producer(std::shared_ptr s,image_write_token token):state_(std::move(s)),token_(token) {} + producer(const producer&)=delete; + producer& operator=(const producer&)=delete; + producer(producer&&)=default; + producer& operator=(producer&&)=delete; + ~producer() { + if (!state_) return; + if (started_ && !completed_) std::terminate(); + if (!published_) state_->pool.cancel_write(token_); + } + // Allocator-internal temporary reservations may cancel quietly to avoid + // waking themselves for capacity they just borrowed and restored. + void cancel(bool notify_capacity=true) { + if (!state_) throw std::invalid_argument("moved image producer"); + state_->pool.cancel_write(token_,notify_capacity); state_.reset(); + } + bool belongs_to(const image_provider_lifetime* provider) const noexcept { + return state_ && state_->provider.get()==provider; + } + uint32_t slot() const { + if (!state_) throw std::invalid_argument("moved image producer"); + return token_.slot; + } + void set_metadata(const image_metadata& metadata) { + if (!state_) throw std::invalid_argument("moved image producer"); + state_->pool.set_metadata(token_,metadata); + } + void begin() { + if (!state_) throw std::invalid_argument("moved image producer"); + state_->pool.begin_producer(token_); started_=true; + } + std::optional publish() { + if (!state_) throw std::invalid_argument("moved image producer"); + auto token=state_->pool.publish(token_); + if (!token) return {}; + published_=true; return retained(state_,*token); + } + void complete() { + if (!state_) throw std::invalid_argument("moved image producer"); + state_->pool.finish_producer(token_); completed_=true; + } + }; + explicit owned_image_pool(std::shared_ptr provider,size_t capacity=128, + std::shared_ptr wake={},size_t image_capacity=3) + : state_(std::make_shared(std::move(provider),capacity,std::move(wake),image_capacity)) {} + owned_image_pool(const owned_image_pool&)=delete; + owned_image_pool& operator=(const owned_image_pool&)=delete; + ~owned_image_pool() { close(); } + std::optional acquire() { + auto token=state_->pool.acquire_write(); + if (!token) return {}; + return producer(state_,*token); + } + void close() { state_->pool.close(); } + image_lease_pool::occupancy inspect_occupancy() const { return state_->pool.inspect_occupancy(); } + size_t busy_images() const { return state_->pool.busy_images(); } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/platform_webgpu_canvas.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/platform_webgpu_canvas.h new file mode 100644 index 000000000..b0c9fafde --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/platform_webgpu_canvas.h @@ -0,0 +1,25 @@ +#pragma once +#include "webgpu_canvas_interop.h" +#if defined(__APPLE__) +#include "dawn_scene_image_snapshot.h" +#include "v8_webgpu_iosurface_canvas_host.h" +namespace webscene::graphics { +using platform_dawn_canvas_host=dawn_iosurface_canvas_host; +using platform_dawn_scene_snapshot=dawn_scene_image_snapshot; +inline constexpr auto platform_canvas_interop=webgpu_canvas_interop::iosurface; +inline constexpr auto platform_canvas_backend=wgpu::BackendType::Metal; +inline auto make_platform_webgpu_canvas_host(std::shared_ptr provider, + std::function metadata) {return make_iosurface_webgpu_canvas_host(std::move(provider),std::move(metadata));} +} +#elif defined(_WIN32) +#include "dawn_dxgi_scene_snapshot.h" +#include "v8_webgpu_dxgi_canvas_host.h" +namespace webscene::graphics { +using platform_dawn_canvas_host=dawn_dxgi_canvas_host; +using platform_dawn_scene_snapshot=dawn_dxgi_scene_snapshot; +inline constexpr auto platform_canvas_interop=webgpu_canvas_interop::dxgi; +inline constexpr auto platform_canvas_backend=wgpu::BackendType::D3D12; +inline auto make_platform_webgpu_canvas_host(std::shared_ptr provider, + std::function metadata) {return make_dxgi_webgpu_canvas_host(std::move(provider),std::move(metadata));} +} +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/producer_completion_gate.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/producer_completion_gate.h new file mode 100644 index 000000000..db479a7eb --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/producer_completion_gate.h @@ -0,0 +1,30 @@ +#pragma once +#include +#include + +namespace webscene::graphics { +// Access is serialized by the submission owner. Failure still waits for both +// signals: validation failure alone does not certify that GPU writes have ended. +class producer_completion_gate final { +public: + enum class phase { queue, validation }; + enum class result { pending, success, failure }; +private: + std::array completed_{}; + bool valid_=true; + result result_=result::pending; +public: + void reject() noexcept { valid_=false; } + result state() const noexcept { return result_; } + bool validated_for_gpu_wait() const noexcept { return completed_[1] && valid_; } + bool finish(phase source,bool valid) noexcept { + const auto index=static_cast(source); + if(completed_[index])return false; + completed_[index]=true; + valid_ &= valid; + if(!completed_[0]||!completed_[1])return false; + result_=valid_ ? result::success : result::failure; + return true; + } +}; +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/release_channel.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/release_channel.h new file mode 100644 index 000000000..330bb0b94 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/release_channel.h @@ -0,0 +1,86 @@ +#pragma once +#include "command_channel.h" + +namespace webscene::graphics { +struct release_ticket { uint64_t channel{},generation{}; size_t slot{}; }; +// Reserve one slot BEFORE exposing a wrapper to GC. Publication never allocates +// or competes for command-queue capacity. The engine releases after the command +// prefix accepted when the finalizer ran; later commands cannot starve release. +class release_channel { + enum class state { free,reserved,ready }; + struct slot { state phase{}; uint64_t generation{},after{}; graphics_command command{}; }; + const uint64_t identity_=new_owner_token(); + const std::thread::id thread_=std::this_thread::get_id(); + mutable std::mutex mutex_; + std::vector slots_; + std::shared_ptr commands_; + std::shared_ptr wake_; + bool closed_{}; + size_t occupied_{}; + void check_thread() const { + if (std::this_thread::get_id()!=thread_) throw std::logic_error("release reservation requires engine thread"); + } +public: + release_channel(size_t capacity,std::shared_ptr commands,std::shared_ptr wake) + : slots_(capacity),commands_(std::move(commands)),wake_(std::move(wake)) { + if (!capacity || !commands_) throw std::invalid_argument("release channel requires bounded storage and command ordering"); + } + std::optional reserve(graphics_command command) { + check_thread(); + if (!command.execute) throw std::invalid_argument("release dispatcher is required"); + std::lock_guard lock(mutex_); + if (closed_) return {}; + for (size_t i=0;imetrics().accepted; + { + std::lock_guard lock(mutex_); + if (closed_ || ticket.channel!=identity_ || ticket.slot>=slots_.size()) return false; + auto& item=slots_[ticket.slot]; + if (item.generation!=ticket.generation || item.phase!=state::reserved) return false; + item.after=after; item.phase=state::ready; + } + if (wake_) wake_->signal(); + return true; + } + size_t occupied() const { std::lock_guard lock(mutex_); return occupied_; } +private: + friend class graphics_service; + bool has_ready(uint64_t completed) const { + std::lock_guard lock(mutex_); + return std::any_of(slots_.begin(),slots_.end(),[&](const auto& item) { + return item.phase==state::ready && item.after<=completed; + }); + } + template bool consume_one(uint64_t completed,Execute execute) { + check_thread(); + graphics_command command; + { + std::lock_guard lock(mutex_); + auto found=std::find_if(slots_.begin(),slots_.end(),[&](const auto& item) { + return item.phase==state::ready && item.after<=completed; + }); + if (found==slots_.end()) return false; + command=found->command; found->phase=state::free; --occupied_; + } + execute(command); + return true; + } + void close() { + check_thread(); + std::lock_guard lock(mutex_); + closed_=true; + // Unpublished registrations are owned by engine-wide resource teardown. + for (auto& item:slots_) if (item.phase==state::reserved) { item.phase=state::free; --occupied_; } + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/resource_table.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/resource_table.h new file mode 100644 index 000000000..eadd2a889 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/resource_table.h @@ -0,0 +1,185 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace webscene::graphics { + +// Tokens identify owners across engines as well as within one resource table. +inline uint64_t new_owner_token() +{ + static std::atomic next{1}; + auto value = next.load(std::memory_order_relaxed); + for (;;) { + if (value == std::numeric_limits::max()) + throw std::overflow_error("graphics owner identity exhausted"); + if (next.compare_exchange_weak(value, value + 1, std::memory_order_relaxed)) + return value; + } +} + +struct resource_owner { + uint64_t engine; + uint64_t device; + uint64_t context; + bool operator==(const resource_owner&) const = default; +}; + +template struct resource_handle { + uint64_t table{}; + uint64_t generation{}; + uint32_t slot{}; +}; + +// Confined to its graphics execution thread. T's destructor therefore also runs +// on that thread. Finalizers and driver callbacks must enqueue release/complete +// requests, never call this table directly. A pointer returned by get() is only +// borrowed for the current synchronous execution scope. +template class resource_table { + struct entry { + std::unique_ptr value; + resource_owner owner{}; + uint64_t generation{1}; + uint64_t last_submission{}; + bool destroyed{}; + }; + const uint64_t identity_ = new_owner_token(); + const std::thread::id thread_ = std::this_thread::get_id(); + const size_t capacity_; + const resource_owner owner_; + std::vector entries_; + uint64_t completed_{}; + + void check_thread() const + { + if (std::this_thread::get_id() != thread_) + throw std::logic_error("graphics resource accessed from wrong thread"); + } + entry& resolve(resource_handle handle, resource_owner owner) + { + check_thread(); + if (handle.table != identity_ || handle.slot >= entries_.size()) + throw std::invalid_argument("foreign graphics resource handle"); + auto& item = entries_[handle.slot]; + if (!item.value || item.destroyed || item.generation != handle.generation + || item.owner != owner) + throw std::invalid_argument("stale or wrong-owner graphics resource handle"); + return item; + } + void release_ready() + { + for (auto& item : entries_) { + if (item.value && item.destroyed && item.last_submission <= completed_) + item.value.reset(); + } + } +public: + explicit resource_table(size_t capacity, resource_owner owner) : capacity_(capacity), owner_(owner) + { + if (!owner.engine || !owner.device) + throw std::invalid_argument("graphics table requires a device owner"); + if (capacity > std::numeric_limits::max()) + throw std::invalid_argument("graphics table capacity too large"); + entries_.reserve(capacity); + } + resource_table(const resource_table&) = delete; + resource_table& operator=(const resource_table&) = delete; + ~resource_table() + { + // Teardown must drain the GPU before destroying this execution scope. + // Silent release here would turn a shutdown bug into GPU use-after-free. + if (std::this_thread::get_id() != thread_) std::terminate(); + for (const auto& item : entries_) + if (item.value && item.last_submission > completed_) std::terminate(); + } + // Owner-thread admission check before expensive native allocation. This is + // not a reservation: callers must not reenter/mutate the table before insert. + bool can_insert() const { + check_thread(); + if (entries_.size()::max()) return true; + return false; + } + // Validate before transferring ownership. In particular a wrong-thread + // call must leave the caller's pointer intact for release on its owner. + resource_handle insert(resource_owner owner, std::unique_ptr&& value) + { + check_thread(); + if (!value || owner != owner_) + throw std::invalid_argument("graphics resource requires an owner and value"); + for (uint32_t i = 0; i < entries_.size(); ++i) { + auto& item = entries_[i]; + if (!item.value && item.generation < std::numeric_limits::max()) { + ++item.generation; + item.owner = owner; + item.destroyed = false; + item.last_submission = 0; + item.value = std::move(value); + return {identity_, item.generation, i}; + } + } + if (entries_.size() == capacity_) throw std::length_error("graphics resource limit reached"); + entries_.push_back({std::move(value), owner}); + return {identity_, 1, static_cast(entries_.size() - 1)}; + } + T& get(resource_handle handle, resource_owner owner) { return *resolve(handle, owner).value; } + void mark_used(resource_handle handle, resource_owner owner, uint64_t submission) + { + auto& item = resolve(handle, owner); + if (submission <= completed_ || submission < item.last_submission) + throw std::invalid_argument("graphics submission serial is not monotonic"); + item.last_submission = submission; + } + void destroy(resource_handle handle, resource_owner owner) + { + resolve(handle, owner).destroyed = true; + release_ready(); + } + void destroy_owner(resource_owner owner) + { + check_thread(); + for (auto& item : entries_) + if (item.value && item.owner == owner) item.destroyed = true; + release_ready(); + } + // Each table belongs to exactly one device/context submission timeline. + // completion is a contiguous completed prefix for that queue only. + void complete(uint64_t submission) + { + check_thread(); + if (submission < completed_) throw std::invalid_argument("graphics completion moved backwards"); + completed_ = submission; + release_ready(); + } + // Engine-internal visitors must not mutate this table during traversal. + template void visit_live(Visit visit) { + check_thread(); + for (auto& item:entries_) if (item.value && !item.destroyed) visit(*item.value); + } + template bool any_live(Predicate predicate) const { + check_thread(); + for (const auto& item:entries_) if (item.value && !item.destroyed && predicate(*item.value)) return true; + return false; + } + size_t resident_count() const + { + check_thread(); + size_t count = 0; + for (const auto& item : entries_) count += item.value != nullptr; + return count; + } + size_t deferred_count() const + { + check_thread(); + size_t count = 0; + for (const auto& item : entries_) count += item.value && item.destroyed; + return count; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_release_registry.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_release_registry.h new file mode 100644 index 000000000..68493488a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_release_registry.h @@ -0,0 +1,65 @@ +#pragma once +#include "release_channel.h" +#include + +namespace webscene::graphics { +// Binding-owned registry, destroyed in the owning isolate scope before isolate +// disposal. It holds weak wrappers only; all release capacity is reserved before +// a wrapper is exposed to JavaScript. No GPU API runs from either GC callback. +class v8_release_registry { + struct entry { + v8::Global wrapper; + std::shared_ptr releases; + release_ticket ticket; + bool published{}; + }; + v8::Isolate* isolate_; + const std::thread::id thread_=std::this_thread::get_id(); + std::shared_ptr releases_; + std::vector> entries_; + static void second_pass(const v8::WeakCallbackInfo& info) { + auto* item=info.GetParameter(); + item->releases->publish(item->ticket); + item->published=true; + } + static void first_pass(const v8::WeakCallbackInfo& info) { + info.GetParameter()->wrapper.Reset(); + info.SetSecondPassCallback(second_pass); + } + void check_scope() const { + if (std::this_thread::get_id()!=thread_ || v8::Isolate::GetCurrent()!=isolate_) + throw std::logic_error("graphics weak handles require the owning isolate scope"); + } +public: + v8_release_registry(v8::Isolate* isolate,std::shared_ptr releases,size_t capacity) + : isolate_(isolate),releases_(std::move(releases)),entries_(capacity) { + if (!isolate_ || !releases_ || !capacity) throw std::invalid_argument("invalid graphics wrapper registry"); + check_scope(); + } + ~v8_release_registry() { + check_scope(); + for (auto& item:entries_) if (item) { + item->wrapper.Reset(); + if (!item->published) item->releases->publish(item->ticket); + } + } + // False means capacity is unavailable: do not expose the wrapper. Its native + // resource remains the binding caller's responsibility until attach succeeds. + bool attach(v8::Local wrapper,graphics_command release) { + check_scope(); + if (wrapper.IsEmpty()) throw std::invalid_argument("graphics wrapper is empty"); + auto found=std::find_if(entries_.begin(),entries_.end(),[](const auto& item) { + return !item || item->published; + }); + if (found==entries_.end()) return false; + auto item=std::make_unique(); + auto ticket=releases_->reserve(release); + if (!ticket) return false; + item->releases=releases_; item->ticket=*ticket; + item->wrapper.Reset(isolate_,wrapper); + item->wrapper.SetWeak(item.get(),first_pass,v8::WeakCallbackType::kParameter); + *found=std::move(item); + return true; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapter_info.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapter_info.h new file mode 100644 index 000000000..eb486db3a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapter_info.h @@ -0,0 +1,51 @@ +#pragma once +#include "webgpu_adapter_info.h" +#include +#include +namespace webscene::graphics { +class v8_webgpu_adapter_info { + alignas(void*) static inline char brand_{}; + v8::Isolate* isolate_; + v8::Global realm_; + v8::Global instance_; + v8::Global prototype_; + static void get(const v8::FunctionCallbackInfo& info) { + auto object=info.This(); + if(object->InternalFieldCount()!=2 || !object->GetInternalField(0)->IsValue() + || !object->GetInternalField(0).As()->IsExternal() + || object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_) { + info.GetIsolate()->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8Literal(info.GetIsolate(),"Illegal GPUAdapterInfo receiver")));return; + } + v8::Local value; + if(object->GetInternalField(1).As()->Get(info.GetIsolate()->GetCurrentContext(),info.Data().As()->Value()).ToLocal(&value))info.GetReturnValue().Set(value); + } +public: + v8_webgpu_adapter_info(v8::Isolate* isolate,v8::Local context):isolate_(isolate) { + realm_.Reset(isolate,context); + auto instance=v8::ObjectTemplate::New(isolate);instance->SetInternalFieldCount(2);instance_.Reset(isolate,instance); + auto prototype=v8::ObjectTemplate::New(isolate); + constexpr std::array names{"vendor","architecture","device","description","subgroupMinSize","subgroupMaxSize","isFallbackAdapter"}; + for(uint32_t i=0;iSetAccessorProperty(v8::String::NewFromUtf8(isolate,names[i]).ToLocalChecked(),v8::FunctionTemplate::New(isolate,get,v8::Integer::NewFromUnsigned(isolate,i))); + prototype->Set(v8::Symbol::GetToStringTag(isolate),v8::String::NewFromUtf8Literal(isolate,"GPUAdapterInfo"),static_cast(v8::ReadOnly|v8::DontEnum)); + prototype_.Reset(isolate,prototype->NewInstance(context).ToLocalChecked()); + } + v8_webgpu_adapter_info(const v8_webgpu_adapter_info&)=delete; + v8_webgpu_adapter_info& operator=(const v8_webgpu_adapter_info&)=delete; + v8::MaybeLocal create(v8::Local context,const webgpu_adapter_info& data) { + if(v8::Isolate::GetCurrent()!=isolate_ || realm_.Get(isolate_)!=context)throw std::logic_error("Adapter info belongs to another realm"); + auto values=v8::Array::New(isolate_,7); + std::array strings{&data.vendor,&data.architecture,&data.device,&data.description}; + for(uint32_t i=0;i value; + if(!v8::String::NewFromUtf8(isolate_,strings[i]->data(),v8::NewStringType::kNormal,static_cast(strings[i]->size())).ToLocal(&value) + || !values->Set(context,i,value).FromMaybe(false))return {}; + } + if(!values->Set(context,4,v8::Integer::NewFromUnsigned(isolate_,data.subgroup_min_size)).FromMaybe(false) + || !values->Set(context,5,v8::Integer::NewFromUnsigned(isolate_,data.subgroup_max_size)).FromMaybe(false) + || !values->Set(context,6,v8::Boolean::New(isolate_,data.is_fallback_adapter)).FromMaybe(false))return {}; + v8::Local object; + if(!instance_.Get(isolate_)->NewInstance(context).ToLocal(&object) || !object->SetPrototype(context,prototype_.Get(isolate_)).FromMaybe(false))return {}; + object->SetInternalField(0,v8::External::New(isolate_,&brand_,v8::kExternalPointerTypeTagDefault));object->SetInternalField(1,values);return object; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapter_options.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapter_options.h new file mode 100644 index 000000000..c55cca82c --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapter_options.h @@ -0,0 +1,56 @@ +#pragma once +#include +#include "webgpu_adapter_options.h" +#include +#include + +namespace webscene::graphics { +inline bool read_webgpu_adapter_options(v8::Isolate* isolate,v8::Local context, + v8::Local input,webgpu_adapter_options& output) { + const auto string=[&](const char* value) { return v8::String::NewFromUtf8(isolate,value).ToLocalChecked(); }; + const auto fail=[&](const char* message) { + isolate->ThrowException(v8::Exception::TypeError(string(message))); + return false; + }; + webgpu_adapter_options converted; + if (input->IsNullOrUndefined()) { output=std::move(converted); return true; } + if (!input->IsObject()) return fail("GPURequestAdapterOptions must be a dictionary."); + auto dictionary=input.As(); + const auto get=[&](const char* name,v8::Local& value) { + return dictionary->Get(context,string(name)).ToLocal(&value); + }; + const auto text=[&](v8::Local value,std::string& result) { + v8::Local converted_string; + if (!value->ToString(context).ToLocal(&converted_string)) return false; + v8::String::Utf8Value bytes(isolate,converted_string); + if (!*bytes) return false; + result.assign(*bytes,bytes.length()); return true; + }; + // WebIDL dictionary members are accessed lexicographically, not in IDL + // declaration order. Preserve getter/coercion exceptions and stop at once. + v8::Local value; + if (!get("featureLevel",value)) return false; + if (!value->IsUndefined()) { + v8::Local level; + if (!value->ToString(context).ToLocal(&level)) return false; + v8::String::Value units(isolate,level); + if (!*units && units.length()) return false; + if (units.length()) converted.feature_level.assign(reinterpret_cast(*units),units.length()); + else converted.feature_level.clear(); + } + if (!get("forceFallbackAdapter",value)) return false; + if (!value->IsUndefined()) converted.force_fallback_adapter=value->BooleanValue(isolate); + if (!get("powerPreference",value)) return false; + if (!value->IsUndefined()) { + std::string preference; + if (!text(value,preference)) return false; + if (preference=="low-power") converted.power_preference=wgpu::PowerPreference::LowPower; + else if (preference=="high-performance") converted.power_preference=wgpu::PowerPreference::HighPerformance; + else return fail("Invalid GPUPowerPreference."); + } + if (!get("xrCompatible",value)) return false; + if (!value->IsUndefined()) converted.xr_compatible=value->BooleanValue(isolate); + output=std::move(converted); // No partially converted native request on failure. + return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapter_request.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapter_request.h new file mode 100644 index 000000000..769671c0d --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapter_request.h @@ -0,0 +1,129 @@ +#pragma once +#include "webgpu_adapter_options.h" +#include "completion_mailbox.h" +#include +#if defined(_WIN32) +#include "windows_gpu_adapter.h" +#endif + +namespace webscene::graphics { +// Engine-owned promise state. Driver callbacks capture only native_result and +// the mailbox, never V8 handles or this object. The wrapper registry supplies +// the GPUAdapter object factory when the completion reaches the engine thread. +class v8_webgpu_adapter_request final { + struct native_result { + std::mutex mutex; + wgpu::Adapter adapter; + bool abandoned=false; + }; + const std::thread::id thread_=std::this_thread::get_id(); + v8::Isolate* const isolate_; + const resource_owner owner_; + const uint64_t operation_; + v8::Global realm_; + v8::Global resolver_; + std::shared_ptr native_=std::make_shared(); + void check_thread() const { + if (std::this_thread::get_id()!=thread_) throw std::logic_error("Adapter promise requires the engine thread"); + } + v8_webgpu_adapter_request(v8::Isolate* isolate,resource_owner owner,uint64_t operation) + :isolate_(isolate),owner_(owner),operation_(operation) {} +public: + ~v8_webgpu_adapter_request() { + if (std::this_thread::get_id()!=thread_) std::terminate(); + std::lock_guard lock(native_->mutex); + native_->abandoned=true; native_->adapter=nullptr; + } + v8_webgpu_adapter_request(const v8_webgpu_adapter_request&)=delete; + v8_webgpu_adapter_request& operator=(const v8_webgpu_adapter_request&)=delete; + bool pending() const { check_thread(); return !resolver_.IsEmpty(); } + bool cancel(v8::Local context) { + check_thread(); + if (resolver_.IsEmpty()) return false; + if (v8::Isolate::GetCurrent()!=isolate_ || realm_.Get(isolate_)!=context) + throw std::logic_error("Adapter cancellation belongs to another realm"); + auto resolver=resolver_.Get(isolate_); resolver_.Reset(); realm_.Reset(); + { std::lock_guard lock(native_->mutex); native_->abandoned=true; native_->adapter=nullptr; } + return resolver->Resolve(context,v8::Null(isolate_)).FromMaybe(false); + } + static std::unique_ptr start(v8::Isolate* isolate, + v8::Local context,const webgpu_adapter_options& requested, + const wgpu::Instance& instance,std::shared_ptr mailbox, + resource_owner owner,uint64_t operation,wgpu::BackendType host_backend, + v8::Local& promise) { + if (!instance || !mailbox || !operation) throw std::invalid_argument("Adapter request lacks native ownership"); + auto result=std::unique_ptr(new v8_webgpu_adapter_request(isolate,owner,operation)); + v8::Local resolver; + if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return {}; + promise=resolver->GetPromise(); + auto options=make_dawn_adapter_options(requested,host_backend); +#if defined(_WIN32) + // Pinned Dawn RequestAdapterOptionsLUID ABI, using the C entry point + // only. No dependency on unexported Dawn-native C++ constructors. + struct luid_options : wgpu::ChainedStruct { LUID adapterLUID{}; } luid; + if(options && host_backend==wgpu::BackendType::D3D12) { + luid.sType=wgpu::SType::RequestAdapterOptionsLUID; + luid.adapterLUID=windows_gpu_adapter_luid();options->nextInChain=&luid; + } +#endif + auto ticket=options ? mailbox->reserve(operation,owner) : std::nullopt; + if (!ticket) { + if (resolver->Resolve(context,v8::Null(isolate)).IsNothing()) return {}; + return result; + } + result->realm_.Reset(isolate,context); result->resolver_.Reset(isolate,resolver); + auto native=result->native_; + instance.RequestAdapter(&*options,wgpu::CallbackMode::AllowSpontaneous, + [native,mailbox,ticket=*ticket](wgpu::RequestAdapterStatus status,wgpu::Adapter adapter,wgpu::StringView) { + { + std::lock_guard lock(native->mutex); + if (!native->abandoned && status==wgpu::RequestAdapterStatus::Success) + native->adapter=std::move(adapter); + } + if (!mailbox->publish(ticket,status==wgpu::RequestAdapterStatus::Success + ? completion_status::success : completion_status::failed)) { + std::lock_guard lock(native->mutex); + native->adapter=nullptr; + } + }); + return result; + } + template + bool complete(v8::Isolate* isolate,v8::Local context, + completion_record record,WrapAdapter wrap) { + check_thread(); + if (record.operation!=operation_ || record.owner!=owner_ || resolver_.IsEmpty()) return false; + if (isolate!=isolate_) throw std::logic_error("Adapter completion belongs to another isolate"); + if (realm_.Get(isolate)!=context) throw std::logic_error("Adapter completion belongs to another realm"); + wgpu::Adapter adapter; + { + std::lock_guard lock(native_->mutex); + native_->abandoned=true; + if (record.status==completion_status::success) adapter=std::move(native_->adapter); + native_->adapter=nullptr; + } + auto resolver=resolver_.Get(isolate); + // Consume the completion before calling the factory: allocation failure + // must not leave a permanently pending request or allow duplicate wrapping. + resolver_.Reset(); realm_.Reset(); + v8::Local value=v8::Null(isolate); + v8::Local failure; + { + v8::TryCatch caught(isolate); + try { + if (adapter) { + v8::MaybeLocal wrapped=wrap(std::move(adapter)); + if (!wrapped.ToLocal(&value) && !caught.HasCaught()) + failure=v8::Exception::Error(v8::String::NewFromUtf8Literal(isolate,"GPUAdapter wrapper creation failed")); + } + } catch (const std::exception&) { + failure=v8::Exception::Error(v8::String::NewFromUtf8Literal(isolate,"GPUAdapter wrapper creation failed")); + } + if (caught.HasTerminated()) return false; + if (caught.HasCaught()) failure=caught.Exception(); + } + if (!failure.IsEmpty()) return resolver->Reject(context,failure).FromMaybe(false); + return resolver->Resolve(context,value).FromMaybe(false); + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapters.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapters.h new file mode 100644 index 000000000..6e860c4b7 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_adapters.h @@ -0,0 +1,229 @@ +#pragma once +#include "graphics_service.h" +#include "v8_webgpu_devices.h" +#include "v8_webgpu_device_request.h" +#include +#include "v8_webgpu_supported_features.h" +#include "webgpu_feature_names.h" +#include "v8_webgpu_limits.h" +#include "v8_webgpu_adapter_info.h" + +namespace webscene::graphics { +// Realm-owned adapters and asynchronous device requests. No global is installed +// here; secure discovery, expired adapters and loss integration remain separate. +class v8_webgpu_adapters { + webgpu_canvas_interop interop_; + struct entry { + v8::Global wrapper; + v8::Global features_key; + v8::Global limits_key; + v8::Global info_key; + graphics_service* service{}; + resource_handle adapter; + std::shared_ptr releases; + release_ticket ticket; + bool published{},consumed{}; + v8_webgpu_adapters* registry{}; + }; + struct pending_request { + entry* adapter{}; + v8::Global keep_alive; + std::unique_ptr bridge; + }; + std::list> requests_; + v8_webgpu_devices& devices_; + v8::Global dom_exception_; + alignas(void*) static inline char brand_{}; + v8::Isolate* isolate_; + const std::thread::id thread_=std::this_thread::get_id(); + v8::Global realm_; + v8::Global instance_; + v8::Global prototype_; + v8_webgpu_supported_features features_factory_; + v8_webgpu_limits limits_factory_; + v8_webgpu_adapter_info info_factory_; + std::vector> entries_; + static void fail(v8::Isolate* isolate,const char* message) { + isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate,message).ToLocalChecked())); + } + static entry* receiver(const v8::FunctionCallbackInfo& info) { + auto object=info.This(); + if (object->InternalFieldCount()!=2 || !object->GetInternalField(0)->IsValue() + || !object->GetInternalField(0).As()->IsExternal() + || object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_) { + fail(info.GetIsolate(),"Illegal GPUAdapter receiver"); return nullptr; + } + auto* item=static_cast(object->GetAlignedPointerFromInternalField(1,v8::kEmbedderDataTypeTagDefault)); + if (!item) fail(info.GetIsolate(),"GPUAdapter realm has been released"); + return item; + } + static void request_device(const v8::FunctionCallbackInfo& info) { + auto* isolate=info.GetIsolate(); auto context=isolate->GetCurrentContext(); + v8::Local failure; + v8::Local promise; + { + v8::TryCatch caught(isolate); + try { + if (auto* item=receiver(info)) { + auto* registry=item->registry; + auto operation=std::make_unique(); + auto* current=operation.get(); current->adapter=item; + current->keep_alive.Reset(isolate,info.This()); + registry->requests_.push_back(std::move(operation)); + try { + current->bridge=v8_webgpu_device_request::start_checked(isolate,context,info[0],[&] { + auto* refreshed=receiver(info); + if (!refreshed) return std::pair{wgpu::Adapter{},true}; + wgpu::Adapter adapter; + refreshed->service->with_adapter(refreshed->adapter,[&](const auto& native) { adapter=native; }); + return std::pair{std::move(adapter),refreshed->consumed}; + },item->service->dawn().completions(),{item->service->engine_identity(),new_owner_token(),0},new_owner_token(), + registry->dom_exception_.Get(isolate),promise,registry->interop_); + if (current->bridge && current->bridge->pending()) item->consumed=true; + } catch (...) { + registry->requests_.remove_if([&](const auto& value) { return value.get()==current; }); + throw; + } + if (!current->bridge || !current->bridge->pending()) + registry->requests_.remove_if([&](const auto& value) { return value.get()==current; }); + } + } catch (const std::exception&) { + failure=v8::Exception::Error(v8::String::NewFromUtf8Literal(isolate,"GPUAdapter device request failed")); + } + if (caught.HasTerminated()) return; + if (caught.HasCaught()) failure=caught.Exception(); + } + if (!failure.IsEmpty()) { + v8::Local resolver; + if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return; + if (!resolver->Reject(context,failure).FromMaybe(false)) return; + promise=resolver->GetPromise(); + } + if (!promise.IsEmpty()) info.GetReturnValue().Set(promise); + } + static void adapter_info(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); if (!item) return; + v8::Local value; + if (info.This()->GetPrivate(info.GetIsolate()->GetCurrentContext(),item->info_key.Get(info.GetIsolate())).ToLocal(&value)) + info.GetReturnValue().Set(value); + } + static void limits(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); if (!item) return; + v8::Local value; + if (info.This()->GetPrivate(info.GetIsolate()->GetCurrentContext(),item->limits_key.Get(info.GetIsolate())).ToLocal(&value)) + info.GetReturnValue().Set(value); + } + static void features(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); if (!item) return; + v8::Local value; + if (info.This()->GetPrivate(info.GetIsolate()->GetCurrentContext(),item->features_key.Get(info.GetIsolate())).ToLocal(&value)) + info.GetReturnValue().Set(value); + } + static void first_pass(const v8::WeakCallbackInfo& info) { + info.GetParameter()->wrapper.Reset(); info.SetSecondPassCallback(second_pass); + } + static void second_pass(const v8::WeakCallbackInfo& info) { + auto* item=info.GetParameter(); item->releases->publish(item->ticket); item->published=true; + } + void check_scope() const { + if (std::this_thread::get_id()!=thread_ || v8::Isolate::GetCurrent()!=isolate_) + throw std::logic_error("GPUAdapter wrappers require their owning isolate scope"); + } +public: + v8_webgpu_adapters(v8::Isolate* isolate,v8::Local context, + v8_webgpu_devices& devices,v8::Local dom_exception,size_t capacity=64,webgpu_canvas_interop interop=webgpu_canvas_interop::none) + :interop_(interop),devices_(devices),isolate_(isolate),features_factory_(isolate,context),limits_factory_(isolate,context),info_factory_(isolate,context),entries_(capacity) { + check_scope(); + if (dom_exception.IsEmpty()) throw std::invalid_argument("Trusted DOMException is required"); + dom_exception_.Reset(isolate,dom_exception); + realm_.Reset(isolate,context); + auto instance=v8::ObjectTemplate::New(isolate); instance->SetInternalFieldCount(2); instance_.Reset(isolate,instance); + auto prototype=v8::ObjectTemplate::New(isolate); + prototype->Set(isolate,"requestDevice",v8::FunctionTemplate::New(isolate,request_device)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"info"),v8::FunctionTemplate::New(isolate,adapter_info)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"limits"),v8::FunctionTemplate::New(isolate,limits)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"features"),v8::FunctionTemplate::New(isolate,features)); + prototype_.Reset(isolate,prototype->NewInstance(context).ToLocalChecked()); + } + v8_webgpu_adapters(const v8_webgpu_adapters&)=delete; + v8_webgpu_adapters& operator=(const v8_webgpu_adapters&)=delete; + ~v8_webgpu_adapters() { + check_scope(); + // Invalidate every receiver before cancellation can construct exceptions + // or invoke user code through a host-supplied exception constructor. + for (auto& item:entries_) if (item && !item->wrapper.IsEmpty()) + item->wrapper.Get(isolate_)->SetAlignedPointerInInternalField(1,nullptr,v8::kEmbedderDataTypeTagDefault); + for (auto& request:requests_) if (request->bridge) request->bridge->cancel(realm_.Get(isolate_)); + requests_.clear(); + for (auto& item:entries_) if (item) { + item->wrapper.Reset(); + if (!item->published) item->releases->publish(item->ticket); + } + } + // Device registry must outlive this adapter registry. Completed devices have + // independent ownership and do not retain their originating adapter wrapper. + bool complete(completion_record record) { + check_scope(); + auto context=realm_.Get(isolate_); + for (auto it=requests_.begin();it!=requests_.end();++it) { + auto* request=it->get(); + if (!request->bridge) continue; + if (!request->bridge->complete(isolate_,context,record,[&](wgpu::Device native) -> v8::MaybeLocal { + auto* item=request->adapter; + wgpu::Adapter adapter; + item->service->with_adapter(item->adapter,[&](const auto& value) { adapter=value; }); + auto handle=item->service->adopt_device(std::move(adapter),std::move(native),request->bridge->loss_signal()); + try { + v8::Local wrapper; + if (devices_.wrap(context,*item->service,handle,request->bridge->label(),request->bridge->queue_label()).ToLocal(&wrapper)) return wrapper; + } catch (...) { item->service->destroy_device(handle); throw; } + item->service->destroy_device(handle); return {}; + })) continue; + requests_.erase(it); return true; + } + return false; + } + // Caller retains ownership until a non-empty wrapper is returned. + v8::MaybeLocal wrap(v8::Local context,graphics_service& service,resource_handle adapter) { + check_scope(); + if (realm_.Get(isolate_)!=context) throw std::logic_error("GPUAdapter belongs to another realm"); + service.with_adapter(adapter,[](auto&) {}); + for (const auto& item:entries_) if (item && !item->published && item->service==&service + && item->adapter.table==adapter.table && item->adapter.generation==adapter.generation && item->adapter.slot==adapter.slot) + throw std::invalid_argument("GPUAdapter handle is already wrapped"); + auto found=std::find_if(entries_.begin(),entries_.end(),[](const auto& item) { return !item || item->published; }); + if (found==entries_.end()) return {}; + v8::Local wrapper; + if (!instance_.Get(isolate_)->NewInstance(context).ToLocal(&wrapper) + || !wrapper->SetPrototype(context,prototype_.Get(isolate_)).FromMaybe(false)) return {}; + auto item=std::make_unique(); + item->registry=this; + item->service=&service; item->adapter=adapter; item->releases=service.release_endpoint(); + item->features_key.Reset(isolate_,v8::Private::New(isolate_)); + std::vector names; + service.with_adapter(adapter,[&](const auto& native) { names=webgpu_supported_feature_names(native); }); + v8::Local snapshot; + if (!features_factory_.create(context,names).ToLocal(&snapshot) + || !wrapper->SetPrivate(context,item->features_key.Get(isolate_),snapshot).FromMaybe(false)) return {}; + item->limits_key.Reset(isolate_,v8::Private::New(isolate_)); + v8::MaybeLocal limit_snapshot; + service.with_adapter(adapter,[&](const auto& native) { limit_snapshot=limits_factory_.create(context,native); }); + v8::Local limit_object; + if(!limit_snapshot.ToLocal(&limit_object) || !wrapper->SetPrivate(context,item->limits_key.Get(isolate_),limit_object).FromMaybe(false))return {}; + webgpu_adapter_info metadata; + service.with_adapter(adapter,[&](const auto& native) { metadata=read_webgpu_adapter_info(native); }); + item->info_key.Reset(isolate_,v8::Private::New(isolate_)); + v8::Local info_object; + if(!info_factory_.create(context,metadata).ToLocal(&info_object) + || !wrapper->SetPrivate(context,item->info_key.Get(isolate_),info_object).FromMaybe(false))return {}; + auto ticket=item->releases->reserve(graphics_service::deferred_adapter_release(adapter)); + if (!ticket) return {}; + item->ticket=*ticket; + wrapper->SetInternalField(0,v8::External::New(isolate_,&brand_,v8::kExternalPointerTypeTagDefault)); + wrapper->SetAlignedPointerInInternalField(1,item.get(),v8::kEmbedderDataTypeTagDefault); + item->wrapper.Reset(isolate_,wrapper); item->wrapper.SetWeak(item.get(),first_pass,v8::WeakCallbackType::kParameter); + *found=std::move(item); + return wrapper; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_async_pipelines.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_async_pipelines.h new file mode 100644 index 000000000..316382ec5 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_async_pipelines.h @@ -0,0 +1,115 @@ +#pragma once +#include "v8_webgpu_compute_pipelines.h" +#include "v8_webgpu_render_pipelines.h" +namespace webscene::graphics { +// Driver callbacks own only native references and completion storage. V8 wrapping +// and promise resolution happen through the existing engine completion mailbox. +class v8_webgpu_async_pipelines { + struct native_result { + wgpu::RenderPipeline render; + wgpu::ComputePipeline compute; + wgpu::CreatePipelineAsyncStatus status{}; + std::string message; + }; + struct request { + uint64_t operation{}; + std::shared_ptr mailbox; + std::shared_ptr result; + graphics_service* service{}; + resource_handle device; + v8_webgpu_render_pipelines* renders{}; + v8_webgpu_compute_pipelines* computes{}; + std::string label; + v8::Global context; + v8::Global parent; + v8::Global resolver; + }; + v8::Isolate* isolate_; + resource_owner owner_{new_owner_token(),new_owner_token(),new_owner_token()}; + std::vector> pending_; + static v8::Local text(v8::Isolate* isolate,const std::string& s){ + return v8::String::NewFromUtf8(isolate,s.data(),v8::NewStringType::kNormal,static_cast(s.size())).ToLocalChecked(); + } +public: + explicit v8_webgpu_async_pipelines(v8::Isolate* isolate):isolate_(isolate){} + ~v8_webgpu_async_pipelines(){ + for(auto& p:pending_) { + p->mailbox->cancel_owner(owner_); + auto context=p->context.Get(isolate_);v8::Context::Scope scope(context); + p->resolver.Get(isolate_)->Reject(context,v8::Exception::Error(text(isolate_,"Pipeline creation cancelled"))).FromMaybe(false); + } + } + template + void start(v8::Local context,v8::Local parent, + v8::Local resolver,graphics_service& service, + resource_handle device,v8_webgpu_render_pipelines& renders, + v8_webgpu_compute_pipelines& computes,const Descriptor& descriptor,const std::string& label) { + if(pending_.size()>=256)throw std::length_error("Async pipeline capacity exhausted"); + auto p=std::make_unique(); + p->operation=new_owner_token();p->mailbox=service.dawn().completions();p->result=std::make_shared(); + p->service=&service;p->device=device;p->renders=&renders;p->computes=&computes;p->label=label; + p->context.Reset(isolate_,context);p->parent.Reset(isolate_,parent);p->resolver.Reset(isolate_,resolver); + pending_.reserve(pending_.size()+1); + auto ticket=p->mailbox->reserve(p->operation,owner_); + if(!ticket)throw std::length_error("Pipeline completion capacity exhausted"); + auto mailbox=p->mailbox;auto result=p->result; + pending_.push_back(std::move(p)); + using Pipeline=std::conditional_t,wgpu::RenderPipeline,wgpu::ComputePipeline>; + auto callback=[mailbox,result,ticket=*ticket](wgpu::CreatePipelineAsyncStatus status,Pipeline pipeline,wgpu::StringView message){ + auto completion=completion_status::failed; + try { + result->status=status; + if constexpr(std::is_same_v)result->render=std::move(pipeline); + else result->compute=std::move(pipeline); + const auto length=message.length==WGPU_STRLEN?(message.data?strnlen(message.data,1024*1024):0):std::min(message.length,1024*1024); + if(message.data)result->message.assign(message.data,length); + if(status==wgpu::CreatePipelineAsyncStatus::Success)completion=completion_status::success; + }catch(...){} + mailbox->publish(ticket,completion); + }; + try { + service.with_device(device,[&](auto& owned){ + if constexpr(std::is_same_v) + owned.native().CreateRenderPipelineAsync(&descriptor,wgpu::CallbackMode::AllowSpontaneous,callback); + else owned.native().CreateComputePipelineAsync(&descriptor,wgpu::CallbackMode::AllowSpontaneous,callback); + }); + } catch(...) { + mailbox->publish(*ticket,completion_status::failed); + throw; + } + } + bool complete(completion_record record) { + if(record.owner!=owner_)return false; + auto found=std::find_if(pending_.begin(),pending_.end(),[&](auto& p){return p->operation==record.operation;}); + if(found==pending_.end())return true; + auto p=std::move(*found);pending_.erase(found); + auto context=p->context.Get(isolate_);v8::Context::Scope scope(context); + auto resolver=p->resolver.Get(isolate_); + try { + if(record.status!=completion_status::success)throw std::runtime_error(p->result->message.empty()?"Pipeline creation failed":p->result->message); + v8::Local wrapper; + p->service->with_device(p->device,[&](auto& device){ + if(p->result->render){ + auto handle=device.adopt_render_pipeline(std::move(p->result->render)); + try { + if(!p->renders->wrap(context,*p->service,p->device,handle,p->parent.Get(isolate_),p->label).ToLocal(&wrapper))throw std::runtime_error("Render pipeline wrapper capacity exhausted"); + }catch(...){device.release_render_pipeline(handle);throw;} + }else{ + auto handle=device.adopt_compute_pipeline(std::move(p->result->compute)); + try { + if(!p->computes->wrap(context,*p->service,p->device,handle,p->parent.Get(isolate_),p->label).ToLocal(&wrapper))throw std::runtime_error("Compute pipeline wrapper capacity exhausted"); + }catch(...){device.release_compute_pipeline(handle);throw;} + } + }); + resolver->Resolve(context,wrapper).FromMaybe(false); + }catch(const std::exception& e){ + auto error=v8::Exception::Error(text(isolate_,e.what())).As(); + error->Set(context,text(isolate_,"name"),text(isolate_,"GPUPipelineError")).Check(); + error->Set(context,text(isolate_,"reason"),text(isolate_, + p->result->status==wgpu::CreatePipelineAsyncStatus::ValidationError?"validation":"internal")).Check(); + resolver->Reject(context,error).FromMaybe(false); + } + return true; + } +}; +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_group_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_group_descriptor.h new file mode 100644 index 000000000..20935f33f --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_group_descriptor.h @@ -0,0 +1,57 @@ +#pragma once +#include "v8_webgpu_vertex_state.h" +#include "v8_webgpu_buffers.h" +#include "v8_webgpu_samplers.h" +#include "v8_webgpu_textures.h" +#include "v8_webgpu_bind_group_layouts.h" +namespace webscene::graphics { +struct webgpu_bind_group_descriptor { + std::string label; + wgpu::BindGroupLayout layout; + std::vector entries; + template void with_native(Execute execute) const { + wgpu::BindGroupDescriptor descriptor{}; + descriptor.label=wgpu::StringView(label.data(),label.size()); + descriptor.layout=layout;descriptor.entryCount=entries.size();descriptor.entries=entries.data(); + execute(descriptor); + } +}; +inline bool read_webgpu_bind_group_descriptor(v8::Isolate* isolate,v8::Local context, + v8::Local input,webgpu_bind_group_descriptor& output) { + webgpu_state_reader reader(isolate,context,input);webgpu_bind_group_descriptor converted; + v8::Local value; + if(!reader.get("label",value))return false; + if(!value->IsUndefined()) { + v8::Local text;if(!value->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false; + converted.label.assign(*bytes,bytes.length()); + } + if(!reader.get("entries",value)||!read_webgpu_sequence(isolate,context,value,[&](auto item){ + webgpu_state_reader r(isolate,context,item);wgpu::BindGroupEntry entry{}; + if(!r.uint32("binding",entry.binding,true))return false; + v8::Local resource;if(!r.get("resource",resource))return false; + // Interface union arms precede dictionary conversion. Native references + // retain resources across subsequent descriptor getters and GC. + if(v8_webgpu_buffers::is_instance(resource)) + entry.buffer=v8_webgpu_buffers::native_reference(resource); + else if(v8_webgpu_samplers::is_instance(resource)) + entry.sampler=v8_webgpu_samplers::native_reference(resource); + else if(v8_webgpu_texture_views::is_instance(resource)) + entry.textureView=v8_webgpu_texture_views::native_reference(resource); + else if(v8_webgpu_textures::is_instance(resource)) + entry.textureView=v8_webgpu_textures::native_reference(resource).CreateView(); + else { + webgpu_state_reader binding(isolate,context,resource); + v8::Local buffer;if(!binding.get("buffer",buffer))return false; + if(!v8_webgpu_buffers::is_instance(buffer))return reader.fail("GPUBufferBinding requires a GPUBuffer"); + entry.buffer=v8_webgpu_buffers::native_reference(buffer); + if(!binding.uint64("offset",entry.offset)||!binding.uint64("size",entry.size))return false; + } + converted.entries.push_back(std::move(entry));return true; + }))return false; + if(!reader.get("layout",value))return false; + try {converted.layout=v8_webgpu_bind_group_layouts::native_reference(value);} + catch(const std::invalid_argument&) {return reader.fail("Bind group requires a GPUBindGroupLayout");} + output=std::move(converted);return true; +} +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_group_layout_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_group_layout_descriptor.h new file mode 100644 index 000000000..1f8c0dc68 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_group_layout_descriptor.h @@ -0,0 +1,77 @@ +#pragma once +#include "v8_webgpu_vertex_state.h" +namespace webscene::graphics { +struct webgpu_bind_group_layout_descriptor { + struct entry { wgpu::BindGroupLayoutEntry value{};bool external{}; }; + std::string label; + std::vector entries; + template void with_native(Execute execute)const { + std::vector values;values.reserve(entries.size()); + std::vector external(entries.size()); + for(size_t i=0;i context, + v8::Local input,webgpu_bind_group_layout_descriptor& output) { + webgpu_state_reader reader(isolate,context,input);webgpu_bind_group_layout_descriptor converted; + v8::Local value; + if(!reader.get("label",value))return false; + if(!value->IsUndefined()) { + v8::Local text;if(!value->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false; + converted.label.assign(*bytes,bytes.length()); + } + if(!reader.get("entries",value)||!read_webgpu_sequence(isolate,context,value,[&](auto item){ + webgpu_state_reader entry_reader(isolate,context,item); + webgpu_bind_group_layout_descriptor::entry entry;v8::Local nested; + if(!entry_reader.uint32("binding",entry.value.binding,true))return false; + if(!entry_reader.get("buffer",nested))return false; + if(!nested->IsUndefined()) { + webgpu_state_reader r(isolate,context,nested); + entry.value.buffer.type=wgpu::BufferBindingType::Uniform; + if(!r.boolean("hasDynamicOffset",entry.value.buffer.hasDynamicOffset) + ||!r.uint64("minBindingSize",entry.value.buffer.minBindingSize) + ||!r.enumeration("type",entry.value.buffer.type))return false; + } + if(!entry_reader.get("externalTexture",nested))return false; + if(!nested->IsUndefined()) { + if(!nested->IsNull()&&!nested->IsObject())return reader.fail("External texture layout must be a dictionary"); + entry.external=true; + } + if(!entry_reader.get("sampler",nested))return false; + if(!nested->IsUndefined()) { + webgpu_state_reader r(isolate,context,nested);entry.value.sampler.type=wgpu::SamplerBindingType::Filtering; + if(!r.enumeration("type",entry.value.sampler.type))return false; + } + if(!entry_reader.get("storageTexture",nested))return false; + if(!nested->IsUndefined()) { + webgpu_state_reader r(isolate,context,nested); + entry.value.storageTexture.access=wgpu::StorageTextureAccess::WriteOnly; + entry.value.storageTexture.viewDimension=wgpu::TextureViewDimension::e2D; + if(!r.enumeration("access",entry.value.storageTexture.access) + ||!r.enumeration("format",entry.value.storageTexture.format,true) + ||!r.enumeration("viewDimension",entry.value.storageTexture.viewDimension))return false; + } + if(!entry_reader.get("texture",nested))return false; + if(!nested->IsUndefined()) { + webgpu_state_reader r(isolate,context,nested); + entry.value.texture.sampleType=wgpu::TextureSampleType::Float; + entry.value.texture.viewDimension=wgpu::TextureViewDimension::e2D; + if(!r.boolean("multisampled",entry.value.texture.multisampled) + ||!r.enumeration("sampleType",entry.value.texture.sampleType) + ||!r.enumeration("viewDimension",entry.value.texture.viewDimension))return false; + } + uint32_t visibility=0;if(!entry_reader.uint32("visibility",visibility,true))return false; + entry.value.visibility=static_cast(visibility); + converted.entries.push_back(std::move(entry));return true; + }))return false; + output=std::move(converted);return true; +} +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_group_layouts.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_group_layouts.h new file mode 100644 index 000000000..99534b4f5 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_group_layouts.h @@ -0,0 +1,15 @@ +#pragma once +#include "v8_webgpu_labeled_resources.h" +namespace webscene::graphics { +struct v8_webgpu_bind_group_layouts_traits { + using native_type=wgpu::BindGroupLayout; + static constexpr const char* name="GPUBindGroupLayout"; + template static void with(dawn_device& device,resource_handle handle,Execute execute) { + device.with_bind_group_layout(handle,std::move(execute)); + } + static graphics_command release(resource_handle device,resource_handle handle) noexcept { + return graphics_service::deferred_bind_group_layout_release(device,handle); + } +}; +using v8_webgpu_bind_group_layouts=v8_webgpu_labeled_resources; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_groups.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_groups.h new file mode 100644 index 000000000..44b4daf02 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_bind_groups.h @@ -0,0 +1,15 @@ +#pragma once +#include "v8_webgpu_labeled_resources.h" +namespace webscene::graphics { +struct v8_webgpu_bind_groups_traits { + using native_type=wgpu::BindGroup; + static constexpr const char* name="GPUBindGroup"; + template static void with(dawn_device& device,resource_handle handle,Execute execute) { + device.with_bind_group(handle,std::move(execute)); + } + static graphics_command release(resource_handle device,resource_handle handle) noexcept { + return graphics_service::deferred_bind_group_release(device,handle); + } +}; +using v8_webgpu_bind_groups=v8_webgpu_labeled_resources; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_buffer_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_buffer_descriptor.h new file mode 100644 index 000000000..64c3e053d --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_buffer_descriptor.h @@ -0,0 +1,58 @@ +#pragma once +#include +#include "webgpu_buffer_descriptor.h" +#include +#include +#include + +namespace webscene::graphics { +// Dictionary conversion only. Usage combinations, alignment, device limits and +// allocation errors remain WebGPU/Dawn validation, not WebIDL TypeErrors. +inline bool read_webgpu_buffer_descriptor(v8::Isolate* isolate,v8::Local context, + v8::Local input,webgpu_buffer_descriptor& output) { + const auto string=[&](const char* value) { return v8::String::NewFromUtf8(isolate,value).ToLocalChecked(); }; + const auto fail=[&](const char* message) { + isolate->ThrowException(v8::Exception::TypeError(string(message))); + return false; + }; + if (!input->IsNullOrUndefined() && !input->IsObject()) return fail("GPUBufferDescriptor must be a dictionary."); + webgpu_buffer_descriptor converted; + const auto get=[&](const char* name,v8::Local& value) { + if (input->IsNullOrUndefined()) { value=v8::Undefined(isolate); return true; } + return input.As()->Get(context,string(name)).ToLocal(&value); + }; + const auto integer=[&](v8::Local value,double maximum,uint64_t& result) { + v8::Local number; + if (!value->ToNumber(context).ToLocal(&number)) return false; + const double truncated=std::trunc(number->Value()); + if (!std::isfinite(truncated) || truncated<0 || truncated>maximum) + return fail("GPU buffer integer is outside its WebIDL range."); + result=static_cast(truncated); + return true; + }; + v8::Local value; + // Inherited dictionary members first, then lexicographic own members. + if (!get("label",value)) return false; + if (!value->IsUndefined()) { + v8::Local label; + if (!value->ToString(context).ToLocal(&label)) return false; + // V8 UTF-8 conversion replaces lone UTF-16 surrogates with U+FFFD, + // implementing USVString while preserving embedded NUL via byte length. + v8::String::Utf8Value bytes(isolate,label); + if (!*bytes) return false; + converted.label.assign(*bytes,bytes.length()); + } + if (!get("mappedAtCreation",value)) return false; + if (!value->IsUndefined()) converted.mapped_at_creation=value->BooleanValue(isolate); + if (!get("size",value)) return false; + if (value->IsUndefined()) return fail("GPUBufferDescriptor.size is required."); + if (!integer(value,9007199254740991.0,converted.size)) return false; + if (!get("usage",value)) return false; + if (value->IsUndefined()) return fail("GPUBufferDescriptor.usage is required."); + uint64_t usage{}; + if (!integer(value,4294967295.0,usage)) return false; + converted.usage=static_cast(usage); + output=std::move(converted); + return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_buffers.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_buffers.h new file mode 100644 index 000000000..8f016ede0 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_buffers.h @@ -0,0 +1,438 @@ +#pragma once +#include "graphics_service.h" +#include "v8_webgpu_mapped_ranges.h" +#include "v8_webgpu_map_request.h" +#include "v8_webgpu_buffer_descriptor.h" +#include +#include +#include +#include +#include + +namespace webscene::graphics { +// Realm-owned buffer wrappers. This internal factory does not install a public +// GPUBuffer constructor. Destroy the registry in its isolate before the service. +class v8_webgpu_buffers { + struct map_operation; + struct entry { + v8::Global wrapper; + graphics_service* service; + resource_handle device; + resource_handle buffer; + std::shared_ptr releases; + release_ticket ticket; + bool published{}; + std::string label; + uint64_t size{}; + uint32_t usage{}; + std::unique_ptr mapping; + v8::Global dom_exception; + v8_webgpu_buffers* registry{}; + map_operation* pending_map{}; + std::vector read_mapping; + }; + struct map_operation { + std::unique_ptr request; + entry* target{}; + uint64_t offset{},length{}; + bool read{}; + }; + std::list> maps_; + static void cancel_map(entry& item) { + if (!item.pending_map) return; + auto* operation=item.pending_map; + item.pending_map=nullptr; operation->target=nullptr; + operation->request->cancel(); + } + alignas(void*) static inline const char brand_{}; + v8::Isolate* isolate_; + const std::thread::id thread_=std::this_thread::get_id(); + v8::Global realm_; + v8::Global instance_; + v8::Global prototype_; + v8::Global dom_exception_; + std::vector> entries_; + static void error(v8::Isolate* isolate,const char* message) { + isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate,message).ToLocalChecked())); + } + static entry* receiver(const v8::FunctionCallbackInfo& info) { + auto object=info.This(); + if (object->InternalFieldCount()!=2 || !object->GetInternalField(0)->IsValue() + || !object->GetInternalField(0).As()->IsExternal() + || object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_) { + error(info.GetIsolate(),"Illegal GPUBuffer receiver"); return nullptr; + } + auto* item=static_cast(object->GetAlignedPointerFromInternalField(1,v8::kEmbedderDataTypeTagDefault)); + if (!item) error(info.GetIsolate(),"GPUBuffer realm has been released"); + return item; + } + template static void access(const v8::FunctionCallbackInfo& info,Execute execute) { + auto* item=receiver(info); + if (!item) return; + try { item->service->with_device(item->device,[&](auto& device) { execute(device,item->buffer); }); } + catch (const std::exception&) { error(info.GetIsolate(),"GPUBuffer native ownership is unavailable"); } + } + static void size(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); + if (item) info.GetReturnValue().Set(v8::Number::New(info.GetIsolate(),static_cast(item->size))); + } + static void usage(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); + if (item) info.GetReturnValue().Set(v8::Integer::NewFromUnsigned(info.GetIsolate(),item->usage)); + } + static void label(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); + if (!item) return; + v8::Local text; + if (v8::String::NewFromUtf8(info.GetIsolate(),item->label.data(),v8::NewStringType::kNormal, + static_cast(item->label.size())).ToLocal(&text)) info.GetReturnValue().Set(text); + } + static void set_label(const v8::FunctionCallbackInfo& info) { + if (!receiver(info)) return; // Brand check precedes user conversion. + auto* isolate=info.GetIsolate(); + v8::Local text; + if (!info[0]->ToString(isolate->GetCurrentContext()).ToLocal(&text)) return; + v8::String::Utf8Value bytes(isolate,text); // USVString replaces lone surrogates. + if (!*bytes) return; + // ToString may execute arbitrary JS. Reacquire after conversion instead + // of keeping a pointer to an entry that teardown could have freed. + auto* item=receiver(info); + if (!item) return; + try { + std::string converted(*bytes,bytes.length()); + item->service->with_device(item->device,[&](auto& device) { + device.with_buffer(item->buffer,[&](const auto& buffer) { + buffer.SetLabel(wgpu::StringView(converted.data(),converted.size())); + }); + }); + item->label=std::move(converted); + } catch (const std::exception&) { error(isolate,"GPUBuffer label update failed"); } + } + static void map_state(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); + if (!item) return; + const char* state=item->pending_map ? "pending" : item->mapping ? "mapped" : "unmapped"; + info.GetReturnValue().Set(v8::String::NewFromUtf8(info.GetIsolate(),state).ToLocalChecked()); + } + static void map_async_impl(const v8::FunctionCallbackInfo& info) { + if (!receiver(info)) return; + auto* isolate=info.GetIsolate(); auto context=isolate->GetCurrentContext(); + const auto number=[&](v8::Local value,double maximum,uint64_t& result) { + v8::Local numeric; + if (!value->ToNumber(context).ToLocal(&numeric)) return false; + auto integer=std::trunc(numeric->Value()); + if (!std::isfinite(integer) || integer<0 || integer>maximum) { + error(isolate,"Map argument is outside its WebIDL range"); return false; + } + result=static_cast(integer); return true; + }; + uint64_t mode=0,offset=0,length=0; + if (!info.Length()) { error(isolate,"mapAsync requires a mode"); return; } + if (!number(info[0],4294967295.0,mode)) return; + if (!info[1]->IsUndefined() && !number(info[1],9007199254740991.0,offset)) return; + bool has_length=!info[2]->IsUndefined(); + if (has_length && !number(info[2],9007199254740991.0,length)) return; + auto* item=receiver(info); + if (!item) return; + item->service->with_device(item->device,[&](auto& device) { + if (item->pending_map || item->mapping) { + device.native().InjectError(wgpu::ErrorType::Validation,"Buffer is already mapped or mapping"); + operation_error(info); // Outer callback converts this to a rejected promise. + return; + } + wgpu::Buffer native; + device.with_buffer(item->buffer,[&](const auto& buffer) { + native=buffer; + if (!has_length) { auto size=buffer.GetSize(); length=offset(); + operation->target=item; operation->offset=offset; operation->length=length; operation->read=mode==1; + operation->request=std::make_unique(isolate,context,info.This(), + item->dom_exception.Get(isolate),std::move(native),device.owner(),new_owner_token()); + auto* pending=operation.get(); + item->registry->maps_.push_back(std::move(operation)); + item->pending_map=pending; + // Unknown browser flags must never opt into a future native extension. + auto native_mode=mode==1 ? wgpu::MapMode::Read : mode==2 ? wgpu::MapMode::Write : wgpu::MapMode::None; + v8::Local promise; + try { + if (pending->request->start(item->service->dawn().completions(),native_mode,offset,length).ToLocal(&promise)) + info.GetReturnValue().Set(promise); + } catch (...) { + item->pending_map=nullptr; pending->target=nullptr; throw; + } + if (!pending->request->pending()) { + item->pending_map=nullptr; + auto& maps=item->registry->maps_; + maps.erase(std::find_if(maps.begin(),maps.end(),[&](const auto& value) { return value.get()==pending; })); + // No native request was admitted; other reentrant requests remain intact. + } + }); + } + static void map_async(const v8::FunctionCallbackInfo& info) { + auto* isolate=info.GetIsolate(); auto context=isolate->GetCurrentContext(); + v8::Local failure; + { + v8::TryCatch caught(isolate); + try { map_async_impl(info); } + catch (const std::exception&) { error(isolate,"Buffer mapping could not be started"); } + if (caught.HasTerminated()) return; + if (caught.HasCaught()) failure=caught.Exception(); + } + if (!failure.IsEmpty()) { + v8::Local resolver; + if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return; + if (resolver->Reject(context,failure).FromMaybe(false)) info.GetReturnValue().Set(resolver->GetPromise()); + } + } + static void operation_error(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); + if (!item) return; + auto* isolate=info.GetIsolate(); + v8::Local args[]{v8::String::NewFromUtf8Literal(isolate,"Invalid mapped buffer range"), + v8::String::NewFromUtf8Literal(isolate,"OperationError")}; + v8::Local exception; + if (item->dom_exception.Get(isolate)->NewInstance(isolate->GetCurrentContext(),2,args).ToLocal(&exception)) + isolate->ThrowException(exception); + } + static void get_mapped_range(const v8::FunctionCallbackInfo& info) { + if (!receiver(info)) return; + auto* isolate=info.GetIsolate(); auto context=isolate->GetCurrentContext(); + const auto number=[&](v8::Local value,uint64_t& result) { + v8::Local numeric; + if (!value->ToNumber(context).ToLocal(&numeric)) return false; + const auto integer=std::trunc(numeric->Value()); + if (!std::isfinite(integer) || integer<0 || integer>9007199254740991.0) { + error(isolate,"Mapped range integer is outside its WebIDL range"); return false; + } + result=static_cast(integer); return true; + }; + uint64_t offset=0,length=0; + if (!info[0]->IsUndefined() && !number(info[0],offset)) return; + const bool has_length=!info[1]->IsUndefined(); + if (has_length && !number(info[1],length)) return; + auto* item=receiver(info); // Conversion can execute JS, including unmap. + if (!item) return; + try { + if (!item->mapping) { operation_error(info); return; } + if (!has_length) item->service->with_device(item->device,[&](auto& device) { + device.with_buffer(item->buffer,[&](const auto& buffer) { + auto size=buffer.GetSize(); length=offset view; + if (item->mapping->create(context,offset,length).ToLocal(&view)) info.GetReturnValue().Set(view); + } catch (const std::invalid_argument&) { operation_error(info); } + catch (const std::exception&) { error(isolate,"Mapped buffer ownership is unavailable"); } + } + static void unmap(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); + if (!item) return; + access(info,[&](auto& device,auto handle) { + cancel_map(*item); + if (item->mapping) { item->mapping->detach(); item->mapping.reset(); } + device.with_buffer(handle,[](const auto& buffer) { buffer.Unmap(); }); + }); + } + static void destroy(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); + if (!item) return; + access(info,[&](auto& device,auto handle) { + cancel_map(*item); + if (item->mapping) { item->mapping->detach(); item->mapping.reset(); } + device.destroy_buffer(handle); + }); + } + static void first_pass(const v8::WeakCallbackInfo& info) { + info.GetParameter()->wrapper.Reset(); info.SetSecondPassCallback(second_pass); + } + static void second_pass(const v8::WeakCallbackInfo& info) { + auto* item=info.GetParameter(); + item->releases->publish(item->ticket); item->published=true; + } + void check_scope() const { + if (std::this_thread::get_id()!=thread_ || v8::Isolate::GetCurrent()!=isolate_) + throw std::logic_error("GPUBuffer wrappers require their owning isolate scope"); + } +public: + static bool is_instance(v8::Local value) { + if(!value->IsObject())return false; + auto object=value.As(); + return object->InternalFieldCount()==2 && object->GetInternalField(0)->IsValue() + && object->GetInternalField(0).As()->IsExternal() + && object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)==&brand_; + } + static wgpu::Buffer native_reference(v8::Local value) { + if (!value->IsObject()) throw std::invalid_argument("GPU resource object required"); + auto object=value.As(); + if (object->InternalFieldCount()!=2 || !object->GetInternalField(0)->IsValue() + || !object->GetInternalField(0).As()->IsExternal() + || object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_) + throw std::invalid_argument("Incorrect GPU resource interface"); + auto* item=static_cast(object->GetAlignedPointerFromInternalField(1,v8::kEmbedderDataTypeTagDefault)); + if (!item) throw std::invalid_argument("GPU resource realm has been released"); + wgpu::Buffer result; + item->service->with_device(item->device,[&](auto& owned) { + owned.with_buffer(item->buffer,[&](const auto& native) { result=native; }); + }); + return result; + } + v8_webgpu_buffers(v8::Isolate* isolate,v8::Local context,size_t capacity,v8::Local dom_exception) + :isolate_(isolate),entries_(capacity) { + check_scope(); + if (dom_exception.IsEmpty()) throw std::invalid_argument("Trusted DOMException constructor is required"); + dom_exception_.Reset(isolate,dom_exception); + realm_.Reset(isolate,context); + auto instance=v8::ObjectTemplate::New(isolate); instance->SetInternalFieldCount(2); + instance_.Reset(isolate,instance); + auto prototype=v8::ObjectTemplate::New(isolate); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"size"),v8::FunctionTemplate::New(isolate,size)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"usage"),v8::FunctionTemplate::New(isolate,usage)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"mapState"),v8::FunctionTemplate::New(isolate,map_state)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"label"),v8::FunctionTemplate::New(isolate,label),v8::FunctionTemplate::New(isolate,set_label)); + auto map_method=v8::FunctionTemplate::New(isolate,map_async); map_method->SetLength(1); + prototype->Set(isolate,"mapAsync",map_method); + prototype->Set(isolate,"getMappedRange",v8::FunctionTemplate::New(isolate,get_mapped_range)); + prototype->Set(isolate,"unmap",v8::FunctionTemplate::New(isolate,unmap)); + prototype->Set(isolate,"destroy",v8::FunctionTemplate::New(isolate,destroy)); + prototype_.Reset(isolate,prototype->NewInstance(context).ToLocalChecked()); + } + v8_webgpu_buffers(const v8_webgpu_buffers&)=delete; + v8_webgpu_buffers& operator=(const v8_webgpu_buffers&)=delete; + ~v8_webgpu_buffers() { + check_scope(); + for (auto& item:entries_) if (item) { + if (!item->wrapper.IsEmpty()) item->wrapper.Get(isolate_)->SetAlignedPointerInInternalField(1,nullptr,v8::kEmbedderDataTypeTagDefault); + cancel_map(*item); + item->mapping.reset(); // Detach before releasing native storage. + item->wrapper.Reset(); + if (!item->published) item->releases->publish(item->ticket); + } + } + // Route graphics completion records here before unrelated operation handlers. + bool complete(completion_record record) { + check_scope(); + for (auto it=maps_.begin();it!=maps_.end();++it) { + auto& operation=**it; + if (operation.request->complete(record,[&](const auto& buffer,auto wrapper) { + auto* item=operation.target; + if (!item) throw std::logic_error("Canceled mapping cannot attach"); + void* data=nullptr; + if (operation.read) { + auto* source=buffer.GetConstMappedRange(operation.offset,operation.length); + if (!source && operation.length) throw std::runtime_error("Read mapping unavailable"); + item->read_mapping.resize(static_cast(operation.length)); + if (operation.length) std::memcpy(item->read_mapping.data(),source,static_cast(operation.length)); + data=item->read_mapping.data(); // JS writes to READ views must be discarded. + } else data=buffer.GetMappedRange(operation.offset,operation.length); + item->mapping=std::make_unique(isolate_,realm_.Get(isolate_),wrapper, + data,operation.offset,operation.length); + })) { + if (operation.target) operation.target->pending_map=nullptr; + maps_.erase(it); + return true; + } + } + return false; + } + // Binding lifecycle hook: invoke before destroying the corresponding native + // device, and during engine-thread device-loss delivery before JS can run. + // It does not destroy devices or release wrappers, and is idempotent. + size_t detach_device(graphics_service& service,resource_handle device) { + check_scope(); + size_t detached=0; + for (auto& item:entries_) if (item && item->service==&service + && item->device.table==device.table && item->device.generation==device.generation + && item->device.slot==device.slot) { + cancel_map(*item); + if (item->mapping) { item->mapping.reset(); ++detached; } + } + return detached; + } + // GPUDevice binding entry point: converts the JS descriptor, creates native + // storage, and rolls back the native handle if wrapper registration fails. + v8::MaybeLocal create(v8::Local context,graphics_service& service, + resource_handle device,v8::Local descriptor) { + check_scope(); + if (realm_.Get(isolate_)!=context) throw std::logic_error("Buffer creation belongs to another realm"); + webgpu_buffer_descriptor converted; + if (!read_webgpu_buffer_descriptor(isolate_,context,descriptor,converted)) return {}; + if (converted.mapped_at_creation && converted.size%4) { + isolate_->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate_,"Mapped buffer size must be a multiple of four"))); + return {}; + } + if (std::none_of(entries_.begin(),entries_.end(),[](const auto& item) { return !item || item->published; })) { + isolate_->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate_,"Buffer wrapper capacity exhausted"))); + return {}; + } + auto native=make_dawn_buffer_descriptor(converted); + if (!native) { + // None is invalid in both standards and Dawn: it forces Dawn's + // validation/error-buffer path without enabling a private usage bit. + // The wrapper retains the original browser usage value below. + native=wgpu::BufferDescriptor{}; + native->label=wgpu::StringView(converted.label.data(),converted.label.size()); + native->size=converted.size; native->mappedAtCreation=converted.mapped_at_creation; + native->usage=wgpu::BufferUsage::None; + } + resource_handle handle; + service.with_device(device,[&](auto& owner) { handle=owner.create_buffer(*native); }); + v8::TryCatch caught(isolate_); + try { + v8::Local wrapper; + if (wrap(context,service,device,handle,converted.label,converted.usage).ToLocal(&wrapper)) return wrapper; + } catch (...) { + service.with_device(device,[&](auto& owner) { owner.release_buffer(handle); }); + throw; + } + service.with_device(device,[&](auto& owner) { owner.release_buffer(handle); }); + if (caught.HasCaught()) { caught.ReThrow(); return {}; } + isolate_->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate_,"Buffer wrapper registration failed"))); + caught.ReThrow(); + return {}; + } + // Ownership transfers only on success. Caller releases the native handle if + // allocation/registration fails. No native operation runs in GC callbacks. + v8::MaybeLocal wrap(v8::Local context,graphics_service& service, + resource_handle device,resource_handle buffer,std::string initial_label={},std::optional browser_usage={}) { + check_scope(); + if (realm_.Get(isolate_)!=context) throw std::logic_error("GPUBuffer wrapper belongs to another realm"); + service.with_device(device,[&](auto& owner) { owner.with_buffer(buffer,[](const auto&) {}); }); + for (const auto& item:entries_) if (item && !item->published + && item->service==&service && item->device.table==device.table + && item->device.generation==device.generation && item->device.slot==device.slot + && item->buffer.table==buffer.table && item->buffer.generation==buffer.generation + && item->buffer.slot==buffer.slot) + throw std::invalid_argument("GPUBuffer handle is already wrapped"); + auto found=std::find_if(entries_.begin(),entries_.end(),[](const auto& item) { return !item || item->published; }); + if (found==entries_.end()) return {}; + v8::Local object; + if (!instance_.Get(isolate_)->NewInstance(context).ToLocal(&object) + || !object->SetPrototype(context,prototype_.Get(isolate_)).FromMaybe(false)) return {}; + auto item=std::make_unique(); + item->registry=this; + item->label=std::move(initial_label); + item->service=&service; item->device=device; item->buffer=buffer; item->releases=service.release_endpoint(); + item->dom_exception.Reset(isolate_,dom_exception_.Get(isolate_)); + // This factory currently accepts only unmapped buffers or a complete + // mapped-at-creation region. mapAsync will attach its selected subrange. + service.with_device(device,[&](auto& owner) { owner.with_buffer(buffer,[&](const auto& native) { + item->size=native.GetSize(); item->usage=browser_usage.value_or(static_cast(native.GetUsage())); + if (native.GetMapState()==wgpu::BufferMapState::Mapped) { + auto size=native.GetSize(); + auto* data=native.GetMappedRange(0,size); + item->mapping=std::make_unique(isolate_,context,object,data,0,size); + } + }); }); + auto ticket=item->releases->reserve(graphics_service::deferred_buffer_release(device,buffer)); + if (!ticket) return {}; + item->ticket=*ticket; + object->SetInternalField(0,v8::External::New(isolate_,const_cast(&brand_),v8::kExternalPointerTypeTagDefault)); + object->SetAlignedPointerInInternalField(1,item.get(),v8::kEmbedderDataTypeTagDefault); + item->wrapper.Reset(isolate_,object); + item->wrapper.SetWeak(item.get(),first_pass,v8::WeakCallbackType::kParameter); + *found=std::move(item); + return object; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_canvas_configuration.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_canvas_configuration.h new file mode 100644 index 000000000..a4a58af01 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_canvas_configuration.h @@ -0,0 +1,42 @@ +#pragma once +#include "v8_webgpu_vertex_state.h" +namespace webscene::graphics { +// Conversion only: presentation capability negotiation and configure's device +// validation are separate. Preserve requested color/HDR modes without silently +// substituting an SDR or sRGB canvas. +struct webgpu_canvas_configuration { + wgpu::Device device; + wgpu::TextureFormat format=wgpu::TextureFormat::Undefined; + uint32_t usage=0x10; + std::vector view_formats; + std::string alpha_mode="opaque",color_space="srgb",tone_mapping="standard"; +}; +template bool read_webgpu_canvas_configuration(v8::Isolate* isolate,v8::Local context, + v8::Local input,webgpu_canvas_configuration& output,ResolveDevice resolve_device) { + webgpu_state_reader reader(isolate,context,input);webgpu_canvas_configuration converted;v8::Local value; + const auto enumeration=[&](webgpu_state_reader& dictionary,const char* name,std::string& output,std::initializer_list allowed) { + v8::Local value;if(!dictionary.get(name,value))return false;if(value->IsUndefined())return true; + v8::Local text;if(!value->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false; + std::string converted(*bytes,bytes.length()); + if(std::find(allowed.begin(),allowed.end(),converted)==allowed.end())return dictionary.fail("Invalid canvas configuration enum"); + output=std::move(converted);return true; + }; + if(!enumeration(reader,"alphaMode",converted.alpha_mode,{"opaque","premultiplied"}) + // PredefinedColorSpace is imported from the pinned @webref/idl html.idl. + ||!enumeration(reader,"colorSpace",converted.color_space,{"srgb","srgb-linear","display-p3","display-p3-linear"}) + ||!reader.get("device",value))return false; + try{converted.device=resolve_device(value);}catch(const std::exception&){return reader.fail("Canvas configuration requires a live GPUDevice");} + if(!converted.device)return reader.fail("Canvas configuration requires a live GPUDevice"); + if(!reader.enumeration("format",converted.format,true)||!reader.get("toneMapping",value))return false; + webgpu_state_reader tone(isolate,context,value); + if(!enumeration(tone,"mode",converted.tone_mapping,{"standard","extended"})||!reader.uint32("usage",converted.usage)||!reader.get("viewFormats",value))return false; + if(!value->IsUndefined()&&!read_webgpu_sequence(isolate,context,value,[&](auto input) { + v8::Local text;if(!input->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false; + for(const auto& [name,native]:webgpu_enum_names::values)if(name==std::string_view(*bytes,bytes.length())){converted.view_formats.push_back(native);return true;} + return reader.fail("Invalid canvas view format"); + }))return false; + output=std::move(converted);return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_canvas_context.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_canvas_context.h new file mode 100644 index 000000000..ed8f0e231 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_canvas_context.h @@ -0,0 +1,109 @@ +#pragma once +#include "v8_webgpu_devices.h" +#include "webgpu_canvas_texture_descriptor.h" +#include +#include +namespace webscene::graphics { +// Host callbacks run on the engine thread, invoke no JavaScript, and must retain +// imported allocations through GPU completion. They never implement CPU copies. +struct webgpu_canvas_host { + std::function validate; + std::function acquire; + std::function retire; + std::function invalidate; +}; +class v8_webgpu_canvas_context { + alignas(void*) static inline char brand_{}; + v8::Isolate* isolate_;const std::thread::id thread_=std::this_thread::get_id(); + v8::Global realm_;v8::Global wrapper_,canvas_,device_,current_; + v8::Global dom_exception_; + std::optional configuration_; + wgpu::Texture native_current_;webgpu_canvas_host host_;uint32_t width_,height_; + void check_scope()const{if(std::this_thread::get_id()!=thread_||v8::Isolate::GetCurrent()!=isolate_)throw std::logic_error("Canvas context requires its owning isolate scope");} + static void fail(v8::Isolate* isolate,const char* text){isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate,text).ToLocalChecked()));} + static v8_webgpu_canvas_context* receiver(const v8::FunctionCallbackInfo& info) { + auto object=info.This();if(object->InternalFieldCount()!=2||!object->GetInternalField(0)->IsValue()||!object->GetInternalField(0).As()->IsExternal() + ||object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_){fail(info.GetIsolate(),"Illegal GPUCanvasContext receiver");return nullptr;} + auto* self=static_cast(object->GetAlignedPointerFromInternalField(1,v8::kEmbedderDataTypeTagDefault)); + if(!self)fail(info.GetIsolate(),"GPUCanvasContext realm has been released");return self; + } + void exception(const char* name,const char* message) { + auto context=realm_.Get(isolate_);v8::Local args[]={v8::String::NewFromUtf8(isolate_,message).ToLocalChecked(),v8::String::NewFromUtf8(isolate_,name).ToLocalChecked()}; + v8::Local error;if(dom_exception_.Get(isolate_)->NewInstance(context,2,args).ToLocal(&error))isolate_->ThrowException(error); + } + static void canvas(const v8::FunctionCallbackInfo& info){auto* self=receiver(info);if(self)info.GetReturnValue().Set(self->canvas_.Get(info.GetIsolate()));} + static void configure(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + try { + webgpu_canvas_configuration converted;v8::Local device; + if(!read_webgpu_canvas_configuration(isolate,context,info[0],converted,[&](auto value){auto native=v8_webgpu_devices::native_reference(value);device=value.template As();return native;}))return; + auto* self=receiver(info);if(!self)return; + // Host validation includes required format features and presenter + // capabilities before the configuration is committed. + self->host_.validate(converted);validate_webgpu_canvas_format_usage(converted); + self->end_frame(false);if(self->host_.invalidate)self->host_.invalidate();self->configuration_=std::move(converted);self->device_.Reset(isolate,device); + }catch(const std::invalid_argument& e){fail(isolate,e.what());} + catch(const std::exception&){auto* self=receiver(info);if(self)self->exception("OperationError","Canvas configuration failed");} + } + static void unconfigure(const v8::FunctionCallbackInfo& info) { + auto* self=receiver(info);if(!self)return; + try{self->end_frame(false);self->configuration_.reset();self->device_.Reset();if(self->host_.invalidate)self->host_.invalidate();} + catch(const std::exception&){self->exception("OperationError","Canvas retirement failed");} + } + static void current(const v8::FunctionCallbackInfo& info) { + auto* self=receiver(info);if(!self)return; + if(!self->configuration_){self->exception("InvalidStateError","Canvas context is not configured");return;} + if(!self->current_.IsEmpty()){info.GetReturnValue().Set(self->current_.Get(info.GetIsolate()));return;} + try { + auto descriptor=webgpu_canvas_texture_descriptor(*self->configuration_,self->width_,self->height_); + auto texture=self->host_.acquire(*self->configuration_,descriptor); + if(!texture){self->exception("OperationError","Canvas texture acquisition unavailable");return;} + v8::Local wrapper; + try { + if(!v8_webgpu_devices::adopt_canvas_texture(self->realm_.Get(self->isolate_),self->device_.Get(self->isolate_),self->configuration_->device,texture,descriptor).ToLocal(&wrapper)) { + self->host_.retire(texture,false);self->exception("OperationError","Canvas texture wrapping failed");return; + } + }catch(...){self->host_.retire(texture,false);throw;} + self->native_current_=std::move(texture);self->current_.Reset(self->isolate_,wrapper);info.GetReturnValue().Set(wrapper); + }catch(const std::exception&){self->exception("OperationError","Canvas texture acquisition failed");} + } + static void get_configuration(const v8::FunctionCallbackInfo& info) { + auto* self=receiver(info);if(!self)return;if(!self->configuration_){info.GetReturnValue().SetNull();return;} + auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext();const auto& config=*self->configuration_; + auto output=v8::Object::New(isolate);auto formats=v8::Array::New(isolate,static_cast(config.view_formats.size()));auto tone=v8::Object::New(isolate); + const auto string=[&](std::string_view text){return v8::String::NewFromUtf8(isolate,text.data(),v8::NewStringType::kNormal,static_cast(text.size())).ToLocalChecked();}; + const auto format=[&](wgpu::TextureFormat value){for(const auto& [name,native]:webgpu_enum_names::values)if(native==value)return string(name);return string("");}; + for(uint32_t i=0;iSet(context,i,format(config.view_formats[i])).FromMaybe(false))return; + if(!tone->Set(context,string("mode"),string(config.tone_mapping)).FromMaybe(false))return; + const auto set=[&](const char* name,v8::Local value){return output->Set(context,string(name),value).FromMaybe(false);}; + if(!set("device",self->device_.Get(isolate))||!set("format",format(config.format))||!set("usage",v8::Integer::NewFromUnsigned(isolate,config.usage)) + ||!set("viewFormats",formats)||!set("alphaMode",string(config.alpha_mode))||!set("colorSpace",string(config.color_space))||!set("toneMapping",tone))return; + info.GetReturnValue().Set(output); + } +public: + v8_webgpu_canvas_context(const v8_webgpu_canvas_context&)=delete; + v8_webgpu_canvas_context& operator=(const v8_webgpu_canvas_context&)=delete; + v8_webgpu_canvas_context(v8_webgpu_canvas_context&&)=delete; + v8_webgpu_canvas_context& operator=(v8_webgpu_canvas_context&&)=delete; + v8_webgpu_canvas_context(v8::Isolate* isolate,v8::Local context,v8::Local canvas, + v8::Local dom_exception,uint32_t width,uint32_t height,webgpu_canvas_host host) + :isolate_(isolate),host_(std::move(host)),width_(width),height_(height) { + check_scope();if(!host_.validate||!host_.acquire||!host_.retire||dom_exception.IsEmpty())throw std::invalid_argument("Canvas host and exception constructor required"); + realm_.Reset(isolate,context);canvas_.Reset(isolate,canvas);dom_exception_.Reset(isolate,dom_exception); + auto instance=v8::ObjectTemplate::New(isolate);instance->SetInternalFieldCount(2);auto prototype=v8::ObjectTemplate::New(isolate); + prototype->Set(v8::Symbol::GetToStringTag(isolate),v8::String::NewFromUtf8Literal(isolate,"GPUCanvasContext"),static_cast(v8::ReadOnly|v8::DontEnum)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"canvas"),v8::FunctionTemplate::New(isolate,canvas_getter)); + for(auto [name,callback,length]:{std::tuple{"configure",configure,1},std::tuple{"unconfigure",unconfigure,0},std::tuple{"getConfiguration",get_configuration,0},std::tuple{"getCurrentTexture",current,0}}){auto fn=v8::FunctionTemplate::New(isolate,callback);fn->SetLength(length);prototype->Set(isolate,name,fn);} + auto object=instance->NewInstance(context).ToLocalChecked();if(!object->SetPrototype(context,prototype->NewInstance(context).ToLocalChecked()).FromMaybe(false))throw std::runtime_error("Canvas prototype initialization failed"); + object->SetInternalField(0,v8::External::New(isolate,&brand_,v8::kExternalPointerTypeTagDefault));object->SetAlignedPointerInInternalField(1,this,v8::kEmbedderDataTypeTagDefault);wrapper_.Reset(isolate,object); + } + ~v8_webgpu_canvas_context(){check_scope();wrapper_.Get(isolate_)->SetAlignedPointerInInternalField(1,nullptr,v8::kEmbedderDataTypeTagDefault);end_frame(false);} + v8::Local object()const{check_scope();return wrapper_.Get(isolate_);} + bool is_configured()const noexcept{return configuration_.has_value();} + bool has_current_texture()const noexcept{return !current_.IsEmpty();} + void end_frame(bool present){check_scope();if(current_.IsEmpty())return;host_.retire(native_current_,present);current_.Reset();native_current_=nullptr;} + void resize(uint32_t width,uint32_t height){check_scope();end_frame(false);width_=width;height_=height;} +private: + static void canvas_getter(const v8::FunctionCallbackInfo& info){canvas(info);} +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_command_buffers.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_command_buffers.h new file mode 100644 index 000000000..dcc123e10 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_command_buffers.h @@ -0,0 +1,15 @@ +#pragma once +#include "v8_webgpu_labeled_resources.h" +namespace webscene::graphics { +struct v8_webgpu_command_buffers_traits { + using native_type=wgpu::CommandBuffer; + static constexpr const char* name="GPUCommandBuffer"; + template static void with(dawn_device& device,resource_handle handle,Execute execute) { + device.with_command_buffer(handle,std::move(execute)); + } + static graphics_command release(resource_handle device,resource_handle handle) noexcept { + return graphics_service::deferred_command_buffer_release(device,handle); + } +}; +using v8_webgpu_command_buffers=v8_webgpu_labeled_resources; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_command_encoders.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_command_encoders.h new file mode 100644 index 000000000..0c4eb3f81 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_command_encoders.h @@ -0,0 +1,124 @@ +#pragma once +#include "v8_webgpu_command_buffers.h" +#include "v8_webgpu_object_descriptor.h" +#include "v8_webgpu_render_pass_descriptor.h" +#include "v8_webgpu_render_passes.h" +#include "v8_webgpu_compute_passes.h" +#include "v8_webgpu_copy_descriptor.h" +namespace webscene::graphics { +struct v8_webgpu_command_encoders_traits { + using native_type=wgpu::CommandEncoder;static constexpr const char* name="GPUCommandEncoder"; + template static void with(dawn_device& device,resource_handle handle,Execute execute){device.with_command_encoder(handle,std::move(execute));} + static graphics_command release(resource_handle device,resource_handle handle)noexcept{return graphics_service::deferred_command_encoder_release(device,handle);} +}; +class v8_webgpu_command_encoders:public v8_webgpu_labeled_resources { + using base=v8_webgpu_labeled_resources; + v8_webgpu_command_buffers commands_; + v8_webgpu_render_passes passes_; + v8_webgpu_compute_passes compute_passes_; + static void begin_render_pass(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + try { + webgpu_render_pass_descriptor descriptor; + if(!read_webgpu_render_pass_descriptor(isolate,context,info[0],descriptor))return; + if(!descriptor.valid_shapes()){fail(isolate,"GPUColor sequence must have four elements");return;} + auto* item=receiver(info);if(!item)return;auto* registry=static_cast(item->registry); + resource_handle pass; + descriptor.with_native([&](const auto& native){item->service->with_device(item->device,[&](auto& owned){pass=owned.begin_render_pass(item->resource,native);});}); + v8::Local wrapper; + try { + if(!registry->passes_.wrap(context,*item->service,item->device,pass,info.This(),descriptor.label).ToLocal(&wrapper)) { + item->service->with_device(item->device,[&](auto& owned){owned.release_render_pass(pass);});fail(isolate,"Render pass wrapper capacity exhausted");return; + } + }catch(...){item->service->with_device(item->device,[&](auto& owned){owned.release_render_pass(pass);});throw;} + info.GetReturnValue().Set(wrapper); + }catch(const std::exception&){fail(isolate,"Render pass creation failed");} + } + static void begin_compute_pass(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + try { + std::string label;if(!read_webgpu_object_label(isolate,context,info[0],label))return; + auto* item=receiver(info);if(!item)return;auto* registry=static_cast(item->registry); + wgpu::ComputePassDescriptor descriptor{};descriptor.label=wgpu::StringView(label.data(),label.size()); + resource_handle pass; + item->service->with_device(item->device,[&](auto& owned){pass=owned.begin_compute_pass(item->resource,descriptor);}); + v8::Local wrapper; + try { + if(!registry->compute_passes_.wrap(context,*item->service,item->device,pass,info.This(),label).ToLocal(&wrapper)){ + item->service->with_device(item->device,[&](auto& owned){owned.release_compute_pass(pass);});fail(isolate,"Compute pass capacity exhausted");return; + } + }catch(...){item->service->with_device(item->device,[&](auto& owned){owned.release_compute_pass(pass);});throw;} + info.GetReturnValue().Set(wrapper); + }catch(const std::exception&){fail(isolate,"Compute pass creation failed");} + } + template static void copy_texture(const v8::FunctionCallbackInfo& info){ + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(info.Length()<3){fail(isolate,"Texture copy requires source, destination and extent");return;} + try{ + wgpu::TexelCopyTextureInfo source{},destination{};wgpu::TexelCopyBufferInfo buffer{};wgpu::Extent3D size{}; + if constexpr(Kind==2){if(!read_copy_buffer(isolate,context,info[0],buffer)||!read_copy_texture(isolate,context,info[1],destination))return;} + else { + if(!read_copy_texture(isolate,context,info[0],source))return; + if constexpr(Kind==0){if(!read_copy_texture(isolate,context,info[1],destination))return;} + else if(!read_copy_buffer(isolate,context,info[1],buffer))return; + } + if(!read_copy_extent(isolate,context,info[2],size))return; + auto* item=receiver(info);if(!item)return; + item->service->with_device(item->device,[&](auto& owned){owned.with_command_encoder(item->resource,[&](const auto& encoder){ + if constexpr(Kind==0)encoder.CopyTextureToTexture(&source,&destination,&size); + else if constexpr(Kind==1)encoder.CopyTextureToBuffer(&source,&buffer,&size); + else encoder.CopyBufferToTexture(&buffer,&destination,&size); + });}); + }catch(const std::exception&){fail(isolate,"Texture copy ownership unavailable");} + } + static bool copy_size(v8::Isolate* isolate,v8::Local context,v8::Local value,uint64_t& size){ + v8::Local number;if(!value->ToNumber(context).ToLocal(&number))return false; + auto n=std::trunc(number->Value());if(!std::isfinite(n)||n<0||n>9007199254740991.0){fail(isolate,"Invalid buffer copy size");return false;} + size=static_cast(n);return true; + } + template static void copy_buffer(const v8::FunctionCallbackInfo& info){ + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(info.Length()<(Clear?1:5)){fail(isolate,"Missing buffer copy arguments");return;} + try{ + auto source=v8_webgpu_buffers::native_reference(info[0]);wgpu::Buffer dest; + uint64_t sourceOffset=0,destOffset=0,size=wgpu::kWholeSize; + if(!info[1]->IsUndefined()&&!copy_size(isolate,context,info[1],sourceOffset))return; + if constexpr(Clear){if(!info[2]->IsUndefined()&&!copy_size(isolate,context,info[2],size))return;} + else{dest=v8_webgpu_buffers::native_reference(info[2]);if(!copy_size(isolate,context,info[3],destOffset)||!copy_size(isolate,context,info[4],size))return;} + auto* item=receiver(info);if(!item)return; + item->service->with_device(item->device,[&](auto& owned){owned.with_command_encoder(item->resource,[&](const auto& encoder){ + if constexpr(Clear)encoder.ClearBuffer(source,sourceOffset,size); + else encoder.CopyBufferToBuffer(source,sourceOffset,dest,destOffset,size); + });}); + }catch(const std::exception&){fail(isolate,"Buffer copy ownership unavailable");} + } + static void finish(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + try { + std::string label;if(!read_webgpu_object_label(isolate,context,info[0],label))return; + auto* item=receiver(info);if(!item)return;auto* registry=static_cast(item->registry); + wgpu::CommandBufferDescriptor descriptor{};descriptor.label=wgpu::StringView(label.data(),label.size()); + resource_handle command; + item->service->with_device(item->device,[&](auto& owned){command=owned.finish_command_encoder(item->resource,descriptor);}); + v8::Local wrapper; + try { + if(!registry->commands_.wrap(context,*item->service,item->device,command,info.This(),label).ToLocal(&wrapper)) { + item->service->with_device(item->device,[&](auto& owned){owned.release_command_buffer(command);}); + fail(isolate,"Command buffer wrapper capacity exhausted");return; + } + }catch(...){item->service->with_device(item->device,[&](auto& owned){owned.release_command_buffer(command);});throw;} + info.GetReturnValue().Set(wrapper); + }catch(const std::exception&){fail(isolate,"Command encoder finish failed");} + } +public: + v8_webgpu_command_encoders(v8::Isolate* isolate,v8::Local context,size_t capacity=1024,size_t command_capacity=1024) + :base(isolate,context,capacity),commands_(isolate,context,command_capacity),passes_(isolate,context,capacity),compute_passes_(isolate,context,capacity) { + prototype_.Get(isolate)->Set(context,v8::String::NewFromUtf8Literal(isolate,"beginComputePass"),v8::Function::New(context,begin_compute_pass).ToLocalChecked()).Check(); + for(auto [name,callback]:{std::pair{"copyTextureToTexture",copy_texture<0>},std::pair{"copyTextureToBuffer",copy_texture<1>},std::pair{"copyBufferToTexture",copy_texture<2>},std::pair{"copyBufferToBuffer",copy_buffer},std::pair{"clearBuffer",copy_buffer}}) + prototype_.Get(isolate)->Set(context,v8::String::NewFromUtf8(isolate,name).ToLocalChecked(),v8::Function::New(context,callback).ToLocalChecked()).Check(); + if(!prototype_.Get(isolate)->Set(context,v8::String::NewFromUtf8Literal(isolate,"beginRenderPass"),v8::Function::New(context,begin_render_pass,{},1).ToLocalChecked()).FromMaybe(false))throw std::runtime_error("Render pass method initialization failed"); + if(!prototype_.Get(isolate)->Set(context,v8::String::NewFromUtf8Literal(isolate,"finish"),v8::Function::New(context,finish,{},0).ToLocalChecked()).FromMaybe(false)) + throw std::runtime_error("Command encoder prototype initialization failed"); + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_compute_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_compute_descriptor.h new file mode 100644 index 000000000..bcd2363bb --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_compute_descriptor.h @@ -0,0 +1,38 @@ +#pragma once +#include "v8_webgpu_render_descriptor.h" +#include "v8_webgpu_pipeline_layouts.h" +namespace webscene::graphics { +struct webgpu_compute_descriptor { + std::string label; + wgpu::PipelineLayout layout; + webgpu_programmable_stage stage; + template void with_native(Execute execute) const { + auto constants=stage.native_constants(); + wgpu::ComputePipelineDescriptor descriptor{}; + descriptor.label=wgpu::StringView(label.data(),label.size());descriptor.layout=layout; + descriptor.compute.module=stage.module; + if(stage.entry_point)descriptor.compute.entryPoint=wgpu::StringView(stage.entry_point->data(),stage.entry_point->size()); + descriptor.compute.constantCount=constants.size();descriptor.compute.constants=constants.data(); + execute(descriptor); + } +}; +inline bool read_webgpu_compute_descriptor(v8::Isolate* isolate,v8::Local context, + v8::Local input,webgpu_compute_descriptor& output) { + webgpu_state_reader reader(isolate,context,input);webgpu_compute_descriptor result; + v8::Local value; + if(!reader.get("label",value))return false; + if(!value->IsUndefined()){ + v8::Local label;if(!value->ToString(context).ToLocal(&label))return false; + v8::String::Utf8Value text(isolate,label);if(!*text)return false;result.label.assign(*text,text.length()); + } + if(!reader.get("layout",value))return false; + if(v8_webgpu_pipeline_layouts::is_instance(value))result.layout=v8_webgpu_pipeline_layouts::native_reference(value); + else { + v8::Local mode;if(!value->ToString(context).ToLocal(&mode))return false; + v8::String::Utf8Value text(isolate,mode); + if(!*text||std::string_view(*text,text.length())!="auto")return reader.fail("Pipeline layout must be auto or GPUPipelineLayout"); + } + if(!reader.get("compute",value)||!read_webgpu_programmable_stage(isolate,context,value,result.stage))return false; + output=std::move(result);return true; +} +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_compute_passes.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_compute_passes.h new file mode 100644 index 000000000..02e60f9e1 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_compute_passes.h @@ -0,0 +1,100 @@ +#pragma once +#include "v8_webgpu_compute_pipelines.h" +#include "v8_webgpu_bind_groups.h" +#include "v8_webgpu_buffers.h" +#include "v8_webgpu_vertex_state.h" +#include +#include +#include +namespace webscene::graphics { +struct v8_webgpu_compute_passes_traits { + using native_type=wgpu::ComputePassEncoder;static constexpr const char* name="GPUComputePassEncoder"; + template static void with(dawn_device& device,resource_handle handle,Execute execute){device.with_compute_pass(handle,std::move(execute));} + static graphics_command release(resource_handle device,resource_handle handle)noexcept{return graphics_service::deferred_compute_pass_release(device,handle);} +}; +class v8_webgpu_compute_passes:public v8_webgpu_labeled_resources { + using base=v8_webgpu_labeled_resources; + static void set_pipeline(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return; + try { + auto pipeline=v8_webgpu_compute_pipelines::native_reference(info[0]);auto* item=receiver(info);if(!item)return; + item->service->with_device(item->device,[&](auto& owned){owned.with_compute_pass(item->resource,[&](const auto& pass){pass.SetPipeline(pipeline);});}); + }catch(const std::exception&){fail(info.GetIsolate(),"setPipeline requires a live GPUComputePipeline");} + } + static bool unsigned_value(v8::Isolate* isolate,v8::Local context,v8::Local value,uint64_t maximum,uint64_t& output) { + v8::Local number;if(!value->ToNumber(context).ToLocal(&number))return false; + double n=std::trunc(number->Value()); + if(!std::isfinite(n)||n<0||n>static_cast(maximum)){fail(isolate,"Binding argument is outside its unsigned range");return false;} + output=static_cast(n);return true; + } + static std::shared_ptr offset_backing(v8::Isolate* isolate,v8::Local value) { + if(!value->IsUint32Array()){fail(isolate,"Dynamic offsets require Uint32Array");return {};} + v8::Local buffer=value.As()->Buffer(); + std::shared_ptr backing; + if(buffer->IsSharedArrayBuffer())backing=buffer.As()->GetBackingStore(); + else { + if(buffer.As()->WasDetached()){fail(isolate,"Dynamic offsets are detached");return {};} + backing=buffer.As()->GetBackingStore(); + } + if(backing->IsResizableByUserJavaScript()){fail(isolate,"Resizable dynamic offsets are not allowed");return {};} + return backing; + } + static void set_bind_group(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(info.Length()<2||info.Length()==4){fail(isolate,"setBindGroup requires two or three arguments, or five for a typed range");return;} + try { + uint64_t index=0;if(!unsigned_value(isolate,context,info[0],UINT32_MAX,index))return; + wgpu::BindGroup group; + if(!info[1]->IsNullOrUndefined())group=v8_webgpu_bind_groups::native_reference(info[1]); + std::vector offsets; + if(info.Length()>=5) { + auto backing=offset_backing(isolate,info[2]);if(!backing)return; + uint64_t start=0,count=0; + if(!unsigned_value(isolate,context,info[3],9007199254740991ULL,start) + ||!unsigned_value(isolate,context,info[4],UINT32_MAX,count))return; + backing=offset_backing(isolate,info[2]);if(!backing)return; + auto array=info[2].As(); + if(start>array->Length()||count>array->Length()-start) { + isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate,"Dynamic offset range exceeds its array")));return; + } + offsets.resize(static_cast(count)); + if(count) { + auto* values=reinterpret_cast(static_cast(backing->Data())+array->ByteOffset())+start; + for(size_t i=0;iIsShared()?std::atomic_ref(values[i]).load(std::memory_order_relaxed):values[i]; + } + }else if(!info[2]->IsUndefined()) { + if(!read_webgpu_sequence(isolate,context,info[2],[&](auto value){ + uint64_t offset=0;if(!unsigned_value(isolate,context,value,UINT32_MAX,offset))return false; + offsets.push_back(static_cast(offset));return true; + }))return; + } + auto* item=receiver(info);if(!item)return; + item->service->with_device(item->device,[&](auto& owned){owned.with_compute_pass(item->resource,[&](const auto& pass){ + pass.SetBindGroup(static_cast(index),group,offsets.size(),offsets.data()); + });}); + }catch(const std::exception&){fail(isolate,"setBindGroup requires live native ownership");} + } + static void dispatch(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(!info.Length()){fail(isolate,"dispatchWorkgroups requires x");return;} + uint64_t args[]={0,1,1}; + for(int i=0;i<3;++i)if(!info[i]->IsUndefined()&&!unsigned_value(isolate,context,info[i],UINT32_MAX,args[i]))return; + auto* item=receiver(info);if(!item)return; + try{item->service->with_device(item->device,[&](auto& owned){owned.with_compute_pass(item->resource,[&](const auto& pass){ + pass.DispatchWorkgroups(static_cast(args[0]),static_cast(args[1]),static_cast(args[2])); + });});}catch(const std::exception&){fail(isolate,"Compute pass ownership unavailable");} + } + static void end(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info);if(!item)return; + try{item->service->with_device(item->device,[&](auto& owned){owned.with_compute_pass(item->resource,[](const auto& pass){pass.End();});});} + catch(const std::exception&){fail(info.GetIsolate(),"Compute pass ownership unavailable");} + } +public: + v8_webgpu_compute_passes(v8::Isolate* isolate,v8::Local context,size_t capacity=1024):base(isolate,context,capacity) { + auto prototype=prototype_.Get(isolate); + for(auto [name,callback,length]:{std::tuple{"setPipeline",set_pipeline,1},std::tuple{"setBindGroup",set_bind_group,2},std::tuple{"dispatchWorkgroups",dispatch,1},std::tuple{"end",end,0}}) { + if(!prototype->Set(context,v8::String::NewFromUtf8(isolate,name).ToLocalChecked(),v8::Function::New(context,callback,{},length).ToLocalChecked()).FromMaybe(false))throw std::runtime_error("Compute pass prototype initialization failed"); + } + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_compute_pipelines.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_compute_pipelines.h new file mode 100644 index 000000000..fcd440ac6 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_compute_pipelines.h @@ -0,0 +1,15 @@ +#pragma once +#include "v8_webgpu_pipeline_resources.h" +namespace webscene::graphics { +struct v8_webgpu_compute_pipelines_traits { + using native_type=wgpu::ComputePipeline; + static constexpr const char* name="GPUComputePipeline"; + template static void with(dawn_device& device,resource_handle handle,Execute execute) { + device.with_compute_pipeline(handle,std::move(execute)); + } + static graphics_command release(resource_handle device,resource_handle handle) noexcept { + return graphics_service::deferred_compute_pipeline_release(device,handle); + } +}; +using v8_webgpu_compute_pipelines=v8_webgpu_pipeline_resources; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_constants.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_constants.h new file mode 100644 index 000000000..a9f857da9 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_constants.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include +namespace webscene::graphics { +inline constexpr const char* webgpu_flag_namespaces[]{ + "GPUBufferUsage","GPUTextureUsage","GPUMapMode","GPUShaderStage","GPUColorWrite" +}; +inline bool install_webgpu_flag_namespaces(v8::Isolate* isolate,v8::Local context) { + auto text=[&](const char* value){return v8::String::NewFromUtf8(isolate,value).ToLocalChecked();}; + auto define=[&](const char* name,std::initializer_list> constants) { + auto object=v8::Object::New(isolate); + for(auto [key,value]:constants) + if(!object->DefineOwnProperty(context,text(key),v8::Integer::NewFromUnsigned(isolate,value), + static_cast(v8::ReadOnly|v8::DontDelete)).FromMaybe(false))return false; + if(!object->DefineOwnProperty(context,v8::Symbol::GetToStringTag(isolate),text(name), + static_cast(v8::ReadOnly|v8::DontEnum)).FromMaybe(false))return false; + return context->Global()->DefineOwnProperty(context,text(name),object,v8::DontEnum).FromMaybe(false); + }; + return define("GPUBufferUsage",{{"MAP_READ",1},{"MAP_WRITE",2},{"COPY_SRC",4},{"COPY_DST",8}, + {"INDEX",16},{"VERTEX",32},{"UNIFORM",64},{"STORAGE",128},{"INDIRECT",256},{"QUERY_RESOLVE",512}}) + &&define("GPUTextureUsage",{{"COPY_SRC",1},{"COPY_DST",2},{"TEXTURE_BINDING",4},{"STORAGE_BINDING",8},{"RENDER_ATTACHMENT",16},{"TRANSIENT_ATTACHMENT",32}}) + &&define("GPUMapMode",{{"READ",1},{"WRITE",2}}) + &&define("GPUShaderStage",{{"VERTEX",1},{"FRAGMENT",2},{"COMPUTE",4}}) + &&define("GPUColorWrite",{{"RED",1},{"GREEN",2},{"BLUE",4},{"ALPHA",8},{"ALL",15}}); +} +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_copy_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_copy_descriptor.h new file mode 100644 index 000000000..1ea55723d --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_copy_descriptor.h @@ -0,0 +1,48 @@ +#pragma once +#include "v8_webgpu_textures.h" +#include "v8_webgpu_buffers.h" +namespace webscene::graphics { +inline bool read_copy_coordinates(v8::Isolate* isolate,v8::Local context, + v8::Local value,uint32_t (&coordinates)[3],bool extent){ + webgpu_state_reader reader(isolate,context,value); + v8::Local iterator; + if(value->IsObject()&&!value.As()->Get(context,v8::Symbol::GetIterator(isolate)).ToLocal(&iterator))return false; + if(!iterator.IsEmpty()&&!iterator->IsNullOrUndefined()){ + size_t count=0; + if(!read_webgpu_sequence(isolate,context,value,[&](auto item){ + if(count>=3)return reader.fail("Too many copy coordinates"); + v8::Local number;if(!item->ToNumber(context).ToLocal(&number))return false; + auto n=std::trunc(number->Value());if(!std::isfinite(n)||n<0||n>UINT32_MAX)return reader.fail("Invalid copy coordinate"); + coordinates[count++]=static_cast(n);return true; + },iterator))return false; + return !extent||count>0||reader.fail("Copy extent requires width"); + } + return reader.uint32(extent?"width":"x",coordinates[0],extent) + &&reader.uint32(extent?"height":"y",coordinates[1]) + &&reader.uint32(extent?"depthOrArrayLayers":"z",coordinates[2]); +} +inline bool read_copy_extent(v8::Isolate* isolate,v8::Local context,v8::Local value,wgpu::Extent3D& extent){ + uint32_t coords[]={0,1,1};if(!read_copy_coordinates(isolate,context,value,coords,true))return false; + extent={coords[0],coords[1],coords[2]};return true; +} +inline bool read_copy_texture(v8::Isolate* isolate,v8::Local context,v8::Local value,wgpu::TexelCopyTextureInfo& copy){ + webgpu_state_reader reader(isolate,context,value);v8::Local texture,origin; + if(!reader.enumeration("aspect",copy.aspect)||!reader.uint32("mipLevel",copy.mipLevel)||!reader.get("origin",origin))return false; + uint32_t coords[]={0,0,0};if(!origin->IsUndefined()&&!read_copy_coordinates(isolate,context,origin,coords,false))return false; + copy.origin={coords[0],coords[1],coords[2]}; + if(!reader.get("texture",texture))return false; + if(!v8_webgpu_textures::is_instance(texture))return reader.fail("Copy requires GPUTexture"); + copy.texture=v8_webgpu_textures::native_reference(texture);return true; +} +inline bool read_copy_layout(v8::Isolate* isolate,v8::Local context,v8::Local value,wgpu::TexelCopyBufferLayout& layout){ + webgpu_state_reader reader(isolate,context,value); + return reader.uint32("bytesPerRow",layout.bytesPerRow)&&reader.uint64("offset",layout.offset)&&reader.uint32("rowsPerImage",layout.rowsPerImage); +} +inline bool read_copy_buffer(v8::Isolate* isolate,v8::Local context,v8::Local value,wgpu::TexelCopyBufferInfo& copy){ + if(!read_copy_layout(isolate,context,value,copy.layout))return false; + webgpu_state_reader reader(isolate,context,value);v8::Local buffer; + if(!reader.get("buffer",buffer))return false; + if(!v8_webgpu_buffers::is_instance(buffer))return reader.fail("Copy requires GPUBuffer"); + copy.buffer=v8_webgpu_buffers::native_reference(buffer);return true; +} +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_device_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_device_descriptor.h new file mode 100644 index 000000000..04581b8a3 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_device_descriptor.h @@ -0,0 +1,97 @@ +#pragma once +#include "webgpu_device_descriptor.h" +#include +#include +#include + +namespace webscene::graphics { +// WebIDL conversion only: capability/limit checks precede native RequestDevice. +inline bool read_webgpu_device_descriptor(v8::Isolate* isolate,v8::Local context, + v8::Local input,webgpu_device_descriptor& output) { + const auto key=[&](const char* name) { return v8::String::NewFromUtf8(isolate,name).ToLocalChecked(); }; + const auto fail=[&](const char* message) { + isolate->ThrowException(v8::Exception::TypeError(key(message))); return false; + }; + const auto get=[&](v8::Local object,const char* name,v8::Local& value) { + if (object->IsNullOrUndefined()) { value=v8::Undefined(isolate); return true; } + if (!object->IsObject()) return fail("GPU descriptor must be a dictionary"); + return object.As()->Get(context,key(name)).ToLocal(&value); + }; + const auto label=[&](v8::Local dictionary,std::string& result) { + v8::Local value; + if (!get(dictionary,"label",value)) return false; + if (value->IsUndefined()) return true; + v8::Local text; + if (!value->ToString(context).ToLocal(&text)) return false; + v8::String::Utf8Value bytes(isolate,text); + if (!*bytes) return false; + result.assign(*bytes,bytes.length()); return true; + }; + webgpu_device_descriptor converted; + // Inherited label first; then dictionary members in lexicographic order. + if (!label(input,converted.label)) return false; + v8::Local value; + if (!get(input,"defaultQueue",value) || !label(value,converted.queue_label)) return false; + if (!get(input,"requiredFeatures",value)) return false; + if (!value->IsUndefined()) { + if (!value->IsObject()) return fail("requiredFeatures must be an iterable object"); + v8::Local method,iterator_value,next; + if (!value.As()->Get(context,v8::Symbol::GetIterator(isolate)).ToLocal(&method)) return false; + if (!method->IsFunction()) return fail("requiredFeatures is not iterable"); + if (!method.As()->Call(context,value,0,nullptr).ToLocal(&iterator_value)) return false; + if (!iterator_value->IsObject()) return fail("Feature iterator must return an object"); + auto iterator=iterator_value.As(); + if (!iterator->Get(context,key("next")).ToLocal(&next)) return false; + if (!next->IsFunction()) return fail("Feature iterator next is not callable"); + for (;;) { + v8::Local step,done,feature; + if (!next.As()->Call(context,iterator,0,nullptr).ToLocal(&step)) return false; + if (!step->IsObject()) return fail("Feature iterator result must be an object"); + if (!step.As()->Get(context,key("done")).ToLocal(&done)) return false; + if (done->BooleanValue(isolate)) break; + if (!step.As()->Get(context,key("value")).ToLocal(&feature)) return false; + v8::Local text; + if (!feature->ToString(context).ToLocal(&text)) return false; + v8::String::Utf8Value bytes(isolate,text); + if (!*bytes) return false; + auto native=webgpu_feature_from_name(std::string_view(*bytes,bytes.length())); + if (!native) return fail("Unknown GPUFeatureName"); + converted.required_features.push_back(*native); + } + } + if (!get(input,"requiredLimits",value)) return false; + if (!value->IsUndefined()) { + if (!value->IsObject()) return fail("requiredLimits must be a record object"); + auto record=value.As(); + v8::Local keys; + // Snapshot all own keys first. Check each descriptor immediately before + // conversion/get: an earlier getter may change a later property's flags. + if (!record->GetOwnPropertyNames(context,v8::ALL_PROPERTIES,v8::KeyConversionMode::kConvertToString).ToLocal(&keys)) return false; + for (uint32_t i=0;iLength();++i) { + v8::Local property,descriptor,enumerable,limit; + if (!keys->Get(context,i).ToLocal(&property)) return false; + if (!record->GetOwnPropertyDescriptor(context,property.As()).ToLocal(&descriptor)) return false; + if (descriptor->IsUndefined()) continue; + if (!descriptor.As()->Get(context,key("enumerable")).ToLocal(&enumerable)) return false; + if (!enumerable->BooleanValue(isolate)) continue; + v8::Local name; + if (!property->ToString(context).ToLocal(&name)) return false; + v8::String::Value units(isolate,name); + if (!*units) return false; + std::u16string limit_name(reinterpret_cast(*units),units.length()); + if (!record->Get(context,property).ToLocal(&limit)) return false; + std::optional number; + if (!limit->IsUndefined()) { + v8::Local numeric; + if (!limit->ToNumber(context).ToLocal(&numeric)) return false; + const double truncated=std::trunc(numeric->Value()); + if (!std::isfinite(truncated) || truncated<0 || truncated>9007199254740991.0) + return fail("Required limit is outside GPUSize64 range"); + number=static_cast(truncated); + } + converted.required_limits.emplace_back(std::move(limit_name),number); + } + } + output=std::move(converted); return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_device_lost.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_device_lost.h new file mode 100644 index 000000000..b09e81dfa --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_device_lost.h @@ -0,0 +1,80 @@ +#pragma once +#include "dawn_device.h" +#include +namespace webscene::graphics { +class v8_webgpu_device_lost_info { + alignas(void*) static inline char brand_{}; + v8::Isolate* isolate_; + v8::Global instance_; + v8::Global prototype_; + static void illegal(const v8::FunctionCallbackInfo& args) { + args.GetIsolate()->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8Literal(args.GetIsolate(),"Illegal constructor"))); + } + static void get(const v8::FunctionCallbackInfo& args) { + auto object=args.This();auto* isolate=args.GetIsolate(); + if(object->InternalFieldCount()!=3||!object->GetInternalField(0)->IsValue() + ||!object->GetInternalField(0).As()->IsExternal() + ||object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_) { + isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8Literal(isolate,"Illegal GPUDeviceLostInfo receiver")));return; + } + args.GetReturnValue().Set(object->GetInternalField(args.Data().As()->Value()).As()); + } +public: + v8_webgpu_device_lost_info(v8::Isolate* isolate,v8::Local context):isolate_(isolate) { + auto name=v8::String::NewFromUtf8Literal(isolate,"GPUDeviceLostInfo"); + auto type=v8::FunctionTemplate::New(isolate,illegal);type->SetClassName(name); + type->InstanceTemplate()->SetInternalFieldCount(3);instance_.Reset(isolate,type->InstanceTemplate()); + type->PrototypeTemplate()->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"reason"),v8::FunctionTemplate::New(isolate,get,v8::Integer::New(isolate,1))); + type->PrototypeTemplate()->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"message"),v8::FunctionTemplate::New(isolate,get,v8::Integer::New(isolate,2))); + type->PrototypeTemplate()->Set(v8::Symbol::GetToStringTag(isolate),name,static_cast(v8::ReadOnly|v8::DontEnum)); + auto constructor=type->GetFunction(context).ToLocalChecked(); + prototype_.Reset(isolate,constructor->Get(context,v8::String::NewFromUtf8Literal(isolate,"prototype")).ToLocalChecked().As()); + if(!context->Global()->DefineOwnProperty(context,name,constructor,v8::DontEnum).FromMaybe(false))throw std::runtime_error("GPUDeviceLostInfo installation failed"); + } + v8::MaybeLocal create(v8::Local context,const device_loss_signal::snapshot& result) { + v8::Local object;if(!instance_.Get(isolate_)->NewInstance(context).ToLocal(&object))return {}; + if(!object->SetPrototype(context,prototype_.Get(isolate_)).FromMaybe(false))return {}; + v8::Local message; + if(!v8::String::NewFromUtf8(isolate_,result.message.data(),v8::NewStringType::kNormal,static_cast(result.message.size())).ToLocal(&message))return {}; + object->SetInternalField(0,v8::External::New(isolate_,&brand_,v8::kExternalPointerTypeTagDefault)); + object->SetInternalField(1,result.reason==wgpu::DeviceLostReason::Destroyed + ?v8::String::NewFromUtf8Literal(isolate_,"destroyed"):v8::String::NewFromUtf8Literal(isolate_,"unknown")); + object->SetInternalField(2,message);return object; + } +}; +class v8_webgpu_device_lost { + v8::Isolate* isolate_; + v8_webgpu_device_lost_info& info_; + v8::Global context_; + v8::Global resolver_; + std::shared_ptr signal_; + std::shared_ptr mailbox_; + resource_owner owner_{new_owner_token(),new_owner_token(),new_owner_token()}; + uint64_t operation_=new_owner_token(); +public: + v8_webgpu_device_lost(v8::Isolate* isolate,v8::Local context,v8_webgpu_device_lost_info& info, + std::shared_ptr signal,std::shared_ptr mailbox) + :isolate_(isolate),info_(info),signal_(std::move(signal)),mailbox_(std::move(mailbox)) { + auto resolver=v8::Promise::Resolver::New(context).ToLocalChecked(); + context_.Reset(isolate,context);resolver_.Reset(isolate,resolver); + if(signal_) { + auto ticket=mailbox_->reserve(operation_,owner_,false); + if(!ticket)throw std::length_error("Device loss completion capacity exhausted"); + try {signal_->subscribe(mailbox_,*ticket);} + catch(...){mailbox_->publish(*ticket,completion_status::cancelled);throw;} + } + } + ~v8_webgpu_device_lost(){if(signal_)mailbox_->cancel_owner(owner_);} + v8::Local promise()const {return resolver_.Get(isolate_)->GetPromise();} + bool complete(completion_record record) { + if(record.owner!=owner_||record.operation!=operation_)return false; + if(record.status!=completion_status::success)return true; + auto result=signal_->result();if(!result)return true; + auto context=context_.Get(isolate_);v8::Context::Scope scope(context); + v8::Local value; + if(info_.create(context,*result).ToLocal(&value)) + (void)resolver_.Get(isolate_)->Resolve(context,value).FromMaybe(false); + return true; + } +}; +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_device_request.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_device_request.h new file mode 100644 index 000000000..583287ea4 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_device_request.h @@ -0,0 +1,205 @@ +#pragma once +#include +#include "completion_mailbox.h" +#include "dawn_device.h" +#include "v8_webgpu_device_descriptor.h" +#include "webgpu_prepared_device_descriptor.h" +#include + +namespace webscene::graphics { +// Engine-owned requestDevice promise bridge. Descriptors and adapter validity +// must be checked by the browser binding before entry. Driver callbacks capture +// native result storage and the mailbox only; wrapping runs on the engine thread. +class v8_webgpu_device_request final { + struct native_result { + std::mutex mutex; + wgpu::Device device; + bool abandoned=false; + }; + const std::thread::id thread_=std::this_thread::get_id(); + v8::Isolate* const isolate_; + const resource_owner owner_; + const uint64_t operation_; + std::string label_,queue_label_; + std::shared_ptr loss_; + v8::Global realm_; + v8::Global resolver_; + v8::Global dom_exception_; + bool reject(v8::Local context,v8::Local resolver) { + v8::Local failure; + { + v8::TryCatch caught(isolate_); + v8::Local args[]{v8::String::NewFromUtf8Literal(isolate_,"GPUDevice request failed"), + v8::String::NewFromUtf8Literal(isolate_,"OperationError")}; + v8::Local exception; + if (dom_exception_.Get(isolate_)->NewInstance(context,2,args).ToLocal(&exception)) failure=exception; + else if (caught.HasCaught()) failure=caught.Exception(); + if (caught.HasTerminated()) return false; + } + if (failure.IsEmpty()) failure=v8::Exception::Error(v8::String::NewFromUtf8Literal(isolate_,"GPUDevice request failed")); + return resolver->Reject(context,failure).FromMaybe(false); + } + std::shared_ptr native_=std::make_shared(); + void check_thread() const { + if (std::this_thread::get_id()!=thread_) throw std::logic_error("Device promise requires the engine thread"); + } + v8_webgpu_device_request(v8::Isolate* isolate,resource_owner owner,uint64_t operation) + :isolate_(isolate),owner_(owner),operation_(operation) {} +public: + ~v8_webgpu_device_request() { + if (std::this_thread::get_id()!=thread_) std::terminate(); + std::lock_guard lock(native_->mutex); + native_->abandoned=true; native_->device=nullptr; + } + v8_webgpu_device_request(const v8_webgpu_device_request&)=delete; + v8_webgpu_device_request& operator=(const v8_webgpu_device_request&)=delete; + std::shared_ptr loss_signal() const {check_thread();return loss_;} + const std::string& queue_label() const { check_thread(); return queue_label_; } + const std::string& label() const { check_thread(); return label_; } + bool pending() const { check_thread(); return !resolver_.IsEmpty(); } + // Host teardown cancellation. The native callback still owns its mailbox + // ticket and retires independently; cancellation never pretends GPU work + // completed and never permits a later completion to wrap the device. + bool cancel(v8::Local context) { + check_thread(); + if (resolver_.IsEmpty()) return false; + if (v8::Isolate::GetCurrent()!=isolate_ || realm_.Get(isolate_)!=context) + throw std::logic_error("Device cancellation belongs to another realm"); + auto resolver=resolver_.Get(isolate_); + resolver_.Reset(); realm_.Reset(); + { + std::lock_guard lock(native_->mutex); + native_->abandoned=true; native_->device=nullptr; + } + return reject(context,resolver); + } + // ResolveAdapter rechecks native ownership and consumed state after all + // user-controlled descriptor getters/coercions have run. It returns + // pair; callbacks must not hold a borrowed registry entry + // across the conversion phase. + template + static std::unique_ptr start_checked(v8::Isolate* isolate, + v8::Local context,v8::Local input,ResolveAdapter resolve_adapter, + std::shared_ptr mailbox,resource_owner owner,uint64_t operation, + v8::Local dom_exception,v8::Local& promise,webgpu_canvas_interop interop=webgpu_canvas_interop::none) { + if (v8::Isolate::GetCurrent()!=isolate || isolate->GetCurrentContext()!=context) + throw std::logic_error("Device request requires its owning isolate scope"); + if (!mailbox || !operation || dom_exception.IsEmpty()) + throw std::invalid_argument("Device request lacks mailbox or trusted DOMException"); + v8::Local failure; + std::unique_ptr prepared; + wgpu::Adapter adapter; + std::string label,queue_label; + { + v8::TryCatch caught(isolate); + try { + webgpu_device_descriptor converted; + if (read_webgpu_device_descriptor(isolate,context,input,converted)) { + label=converted.label;queue_label=converted.queue_label; + auto state=resolve_adapter(); adapter=std::move(state.first); + webgpu_device_request_error error; + prepared=webgpu_prepared_device_descriptor::prepare(converted,adapter,state.second,error,interop); + if (error==webgpu_device_request_error::unsupported_feature) + failure=v8::Exception::TypeError(v8::String::NewFromUtf8Literal(isolate,"Required WebGPU feature is unavailable")); + } + } catch (const std::exception&) { + failure=v8::Exception::Error(v8::String::NewFromUtf8Literal(isolate,"Device request preparation failed")); + } + if (caught.HasTerminated()) return {}; + if (caught.HasCaught()) failure=caught.Exception(); + } + if (prepared && failure.IsEmpty()) { + auto descriptor=prepared->native(); + auto loss=std::make_shared(nullptr); + device_loss_signal::configure(descriptor,loss); + auto result=start(isolate,context,descriptor,adapter,std::move(mailbox),owner,operation,dom_exception,promise); + if (result) {result->loss_=std::move(loss);result->label_=std::move(label);result->queue_label_=std::move(queue_label);} + return result; + } + v8::Local resolver; + if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return {}; + promise=resolver->GetPromise(); + auto result=std::unique_ptr(new v8_webgpu_device_request(isolate,owner,operation)); + result->dom_exception_.Reset(isolate,dom_exception); + if (failure.IsEmpty()) { + if (!result->reject(context,resolver)) return {}; + } else if (!resolver->Reject(context,failure).FromMaybe(false)) return {}; + return result; + } + static std::unique_ptr start(v8::Isolate* isolate, + v8::Local context,const wgpu::DeviceDescriptor& descriptor, + const wgpu::Adapter& adapter,std::shared_ptr mailbox, + resource_owner owner,uint64_t operation,v8::Local dom_exception, + v8::Local& promise) { + if (v8::Isolate::GetCurrent()!=isolate || isolate->GetCurrentContext()!=context) + throw std::logic_error("Device request requires its owning isolate scope"); + if (!adapter || !mailbox || !operation || dom_exception.IsEmpty()) + throw std::invalid_argument("Device request lacks native ownership or trusted DOMException"); + auto result=std::unique_ptr(new v8_webgpu_device_request(isolate,owner,operation)); + v8::Local resolver; + if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return {}; + promise=resolver->GetPromise(); + result->realm_.Reset(isolate,context); result->resolver_.Reset(isolate,resolver); + result->dom_exception_.Reset(isolate,dom_exception); + auto ticket=mailbox->reserve(operation,owner); + if (!ticket) { + result->resolver_.Reset(); result->realm_.Reset(); + if (!result->reject(context,resolver)) return {}; + return result; + } + auto native=result->native_; + adapter.RequestDevice(&descriptor,wgpu::CallbackMode::AllowSpontaneous, + [native,mailbox,ticket=*ticket](wgpu::RequestDeviceStatus status,wgpu::Device device,wgpu::StringView) { + { + std::lock_guard lock(native->mutex); + if (!native->abandoned && status==wgpu::RequestDeviceStatus::Success) + native->device=std::move(device); + } + if (!mailbox->publish(ticket,status==wgpu::RequestDeviceStatus::Success + ? completion_status::success : completion_status::failed)) { + std::lock_guard lock(native->mutex); + native->device=nullptr; + } + }); + return result; + } + template + bool complete(v8::Isolate* isolate,v8::Local context, + completion_record record,WrapDevice wrap) { + check_thread(); + if (record.operation!=operation_ || record.owner!=owner_ || resolver_.IsEmpty()) return false; + if (isolate!=isolate_) throw std::logic_error("Device completion belongs to another isolate"); + if (realm_.Get(isolate)!=context) throw std::logic_error("Device completion belongs to another realm"); + wgpu::Device device; + { + std::lock_guard lock(native_->mutex); + native_->abandoned=true; + if (record.status==completion_status::success) device=std::move(native_->device); + native_->device=nullptr; + } + auto resolver=resolver_.Get(isolate); + // Consume the completion before calling the factory: allocation failure + // must not leave a permanently pending request or allow duplicate wrapping. + resolver_.Reset(); realm_.Reset(); + if (!device) return reject(context,resolver); + v8::Local value; + v8::Local failure; + { + v8::TryCatch caught(isolate); + try { + if (device) { + v8::MaybeLocal wrapped=wrap(std::move(device)); + if (!wrapped.ToLocal(&value) && !caught.HasCaught()) + failure=v8::Exception::Error(v8::String::NewFromUtf8Literal(isolate,"GPUDevice wrapper creation failed")); + } + } catch (const std::exception&) { + failure=v8::Exception::Error(v8::String::NewFromUtf8Literal(isolate,"GPUDevice wrapper creation failed")); + } + if (caught.HasTerminated()) return false; + if (caught.HasCaught()) failure=caught.Exception(); + } + if (!failure.IsEmpty()) return resolver->Reject(context,failure).FromMaybe(false); + return resolver->Resolve(context,value).FromMaybe(false); + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_devices.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_devices.h new file mode 100644 index 000000000..84e59f4f1 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_devices.h @@ -0,0 +1,653 @@ +#pragma once +#include "v8_webgpu_buffers.h" +#include "v8_webgpu_error_scopes.h" +#include "v8_webgpu_device_lost.h" +#include "v8_webgpu_bind_groups.h" +#include "v8_webgpu_pipeline_layouts.h" +#include "v8_webgpu_pipeline_layout_descriptor.h" +#include "v8_webgpu_bind_group_descriptor.h" +#include "v8_webgpu_bind_group_layouts.h" +#include "v8_webgpu_bind_group_layout_descriptor.h" +#include "v8_webgpu_shaders.h" +#include "v8_webgpu_samplers.h" +#include "v8_webgpu_render_pipelines.h" +#include "v8_webgpu_compute_descriptor.h" +#include "v8_webgpu_async_pipelines.h" +#include "v8_webgpu_textures.h" +#include "v8_webgpu_command_encoders.h" +#include "v8_webgpu_queue.h" +#include "v8_webgpu_render_descriptor.h" +#include "v8_webgpu_shader_descriptor.h" +#include "v8_webgpu_supported_features.h" +#include "webgpu_feature_names.h" +#include "v8_webgpu_limits.h" +#include "v8_webgpu_adapter_info.h" + +namespace webscene::graphics { +// Internal realm-owned device factory. Public discovery, capabilities, queues and +// event/lost promises are separate integration work; no global is installed here. +class v8_webgpu_devices { + struct entry { + v8::Global wrapper; + v8::Global buffer_owner_key; + v8::Global features_key; + v8::Global limits_key; + v8::Global info_key; + v8::Global queue_key; + std::unique_ptr queue; + std::unique_ptr error_scopes; + std::unique_ptr loss; + graphics_service* service{}; + resource_handle device; + std::unique_ptr buffers; + std::unique_ptr shaders; + std::unique_ptr samplers; + std::unique_ptr pipeline_layouts; + std::unique_ptr binding_groups; + std::unique_ptr binding_layouts; + std::unique_ptr pipelines; + std::unique_ptr computes; + std::unique_ptr async_pipelines; + std::unique_ptr textures; + std::unique_ptr encoders; + std::shared_ptr releases; + release_ticket ticket; + bool published{},destroyed{}; + std::string label; + }; + alignas(void*) static inline char brand_{}; + v8::Isolate* isolate_; + const std::thread::id thread_=std::this_thread::get_id(); + v8::Global realm_; + v8::Global dom_exception_; + v8::Global instance_; + v8::Global prototype_; + v8_webgpu_device_lost_info lost_info_factory_; + v8_webgpu_errors errors_factory_; + v8_webgpu_supported_features features_factory_; + v8_webgpu_limits limits_factory_; + v8_webgpu_adapter_info info_factory_; + std::function)> initialize_event_target_; + size_t buffer_capacity_; + std::vector> entries_; + static void fail(v8::Isolate* isolate,const char* message) { + isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate,message).ToLocalChecked())); + } + static entry* receiver(const v8::FunctionCallbackInfo& info) { + auto object=info.This(); + if (object->InternalFieldCount()!=2 || !object->GetInternalField(0)->IsValue() + || !object->GetInternalField(0).As()->IsExternal() + || object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_) { + fail(info.GetIsolate(),"Illegal GPUDevice receiver"); return nullptr; + } + auto* item=static_cast(object->GetAlignedPointerFromInternalField(1,v8::kEmbedderDataTypeTagDefault)); + if (!item) fail(info.GetIsolate(),"GPUDevice realm has been released"); + return item; + } + static void lost(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info);if(item)info.GetReturnValue().Set(item->loss->promise()); + } + static void push_error_scope(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return; + auto* isolate=info.GetIsolate(); + if(info.Length()<1){fail(isolate,"pushErrorScope requires a filter");return;} + v8::Local converted; + if(!info[0]->ToString(isolate->GetCurrentContext()).ToLocal(&converted))return; + v8::String::Utf8Value text(isolate,converted); + if(!*text)return; + const std::string_view value(*text,text.length()); + wgpu::ErrorFilter filter; + if(value=="validation")filter=wgpu::ErrorFilter::Validation; + else if(value=="out-of-memory")filter=wgpu::ErrorFilter::OutOfMemory; + else if(value=="internal")filter=wgpu::ErrorFilter::Internal; + else {fail(isolate,"Invalid GPUErrorFilter");return;} + // Conversion may run user code and retire this realm. + auto* item=receiver(info);if(!item)return; + try { + item->service->with_device(item->device,[&](auto& owned){owned.native().PushErrorScope(filter);}); + }catch(const std::exception&){fail(isolate,"GPU error scope push failed");} + } + static void pop_error_scope(const v8::FunctionCallbackInfo& info) { + auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + v8::Local resolver; + if(!v8::Promise::Resolver::New(context).ToLocal(&resolver))return; + info.GetReturnValue().Set(resolver->GetPromise()); + v8::Local failure; + { + v8::TryCatch caught(isolate); + auto* item=receiver(info); + if(item) { + try { + auto device=native_reference(info.This()); + item->error_scopes->pop(context,info.This(),resolver,std::move(device),item->service->dawn().completions()); + }catch(const std::exception&){fail(isolate,"GPUDevice native ownership unavailable");} + } + if(caught.HasTerminated())return; + if(caught.HasCaught())failure=caught.Exception(); + } + if(!failure.IsEmpty())(void)resolver->Reject(context,failure).FromMaybe(false); + } + static void create_buffer(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); if (!item) return; + auto* isolate=info.GetIsolate(); auto context=isolate->GetCurrentContext(); + if (!info.Length()) { fail(isolate,"createBuffer requires a descriptor"); return; } + try { + v8::Local buffer; + if (!item->buffers->create(context,*item->service,item->device,info[0]).ToLocal(&buffer)) return; + // A reachable buffer must retain its parent device wrapper. Device + // GC must not destroy resources that JavaScript can still access. + if (!buffer->SetPrivate(context,item->buffer_owner_key.Get(isolate),info.This()).FromMaybe(false)) return; + info.GetReturnValue().Set(buffer); + } catch (const std::bad_alloc&) { + isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate,"Buffer allocation failed"))); + } catch (const std::length_error&) { + isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate,"Buffer capacity exhausted"))); + } catch (const std::exception&) { fail(isolate,"GPUDevice native ownership is unavailable"); } + } + static void create_bind_group_layout(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return; + auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(!info.Length()){fail(isolate,"createBindGroupLayout requires a descriptor");return;} + try { + webgpu_bind_group_layout_descriptor descriptor; + if(!read_webgpu_bind_group_layout_descriptor(isolate,context,info[0],descriptor))return; + auto* item=receiver(info);if(!item)return; + resource_handle handle; + descriptor.with_native([&](const auto& native){ + item->service->with_device(item->device,[&](auto& owned){handle=owned.create_bind_group_layout(native);}); + }); + v8::Local wrapper; + try { + if(item->binding_layouts->wrap(context,*item->service,item->device,handle,info.This(),descriptor.label).ToLocal(&wrapper)){ + info.GetReturnValue().Set(wrapper);return; + } + }catch(...){ + item->service->with_device(item->device,[&](auto& owned){owned.release_bind_group_layout(handle);});throw; + } + item->service->with_device(item->device,[&](auto& owned){owned.release_bind_group_layout(handle);}); + fail(isolate,"Bind-group-layout wrapper capacity exhausted"); + }catch(const std::exception&){fail(isolate,"Bind-group-layout creation failed");} + } + static void create_pipeline_layout(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return; + auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(!info.Length()){fail(isolate,"createPipelineLayout requires a descriptor");return;} + try { + webgpu_pipeline_layout_descriptor descriptor; + if(!read_webgpu_pipeline_layout_descriptor(isolate,context,info[0],descriptor))return; + auto* item=receiver(info);if(!item)return; + resource_handle handle; + descriptor.with_native([&](const auto& native){ + item->service->with_device(item->device,[&](auto& owned){handle=owned.create_pipeline_layout(native);}); + }); + v8::Local wrapper; + try { + if(item->pipeline_layouts->wrap(context,*item->service,item->device,handle,info.This(),descriptor.label).ToLocal(&wrapper)){ + info.GetReturnValue().Set(wrapper);return; + } + }catch(...){ + item->service->with_device(item->device,[&](auto& owned){owned.release_pipeline_layout(handle);});throw; + } + item->service->with_device(item->device,[&](auto& owned){owned.release_pipeline_layout(handle);}); + fail(isolate,"Pipeline-layout wrapper capacity exhausted"); + }catch(const std::exception&){fail(isolate,"Pipeline-layout creation failed");} + } + static void create_bind_group(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return; + auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(!info.Length()){fail(isolate,"createBindGroup requires a descriptor");return;} + try { + webgpu_bind_group_descriptor descriptor; + if(!read_webgpu_bind_group_descriptor(isolate,context,info[0],descriptor))return; + auto* item=receiver(info);if(!item)return; + resource_handle handle; + descriptor.with_native([&](const auto& native){ + item->service->with_device(item->device,[&](auto& owned){handle=owned.create_bind_group(native);}); + }); + v8::Local wrapper; + try { + if(item->binding_groups->wrap(context,*item->service,item->device,handle,info.This(),descriptor.label).ToLocal(&wrapper)){ + info.GetReturnValue().Set(wrapper);return; + } + }catch(...){ + item->service->with_device(item->device,[&](auto& owned){owned.release_bind_group(handle);});throw; + } + item->service->with_device(item->device,[&](auto& owned){owned.release_bind_group(handle);}); + fail(isolate,"Bind-group wrapper capacity exhausted"); + }catch(const std::exception&){fail(isolate,"Bind-group creation failed");} + } + static void create_shader(const v8::FunctionCallbackInfo& info) { + if (!receiver(info)) return; + auto* isolate=info.GetIsolate(); auto context=isolate->GetCurrentContext(); + if (!info.Length()) { fail(isolate,"createShaderModule requires a descriptor"); return; } + try { + webgpu_shader_descriptor converted; + // Compilation hints are converted for observable WebIDL effects; + // native shader compilation does not require these optional hints. + if (!read_webgpu_shader_descriptor(isolate,context,info[0],converted, + [](v8::Local value) -> std::optional { + if(!v8_webgpu_pipeline_layouts::is_instance(value))return {}; + return v8_webgpu_pipeline_layouts::native_reference(value); + })) return; + auto* item=receiver(info); if (!item) return; // Coercion can reenter. + wgpu::ShaderSourceWGSL source{}; + source.code=wgpu::StringView(converted.code.data(),converted.code.size()); + wgpu::ShaderModuleDescriptor descriptor{}; + descriptor.nextInChain=&source; + descriptor.label=wgpu::StringView(converted.label.data(),converted.label.size()); + resource_handle shader; + item->service->with_device(item->device,[&](auto& owned) { shader=owned.create_shader_module(descriptor); }); + v8::Local wrapper; + try { + if (!item->shaders->wrap(context,*item->service,item->device,shader,info.This(),converted.label).ToLocal(&wrapper)) { + item->service->with_device(item->device,[&](auto& owned) { owned.release_shader_module(shader); }); + fail(isolate,"Shader wrapper capacity exhausted"); return; + } + } catch (...) { + item->service->with_device(item->device,[&](auto& owned) { owned.release_shader_module(shader); }); + throw; + } + info.GetReturnValue().Set(wrapper); + } catch (const std::bad_alloc&) { + isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate,"Shader allocation failed"))); + } catch (const std::length_error&) { + isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate,"Shader capacity exhausted"))); + } catch (const std::exception&) { fail(isolate,"GPUDevice native shader ownership is unavailable"); } + } + static void create_pipeline(const v8::FunctionCallbackInfo& info) { + if (!receiver(info)) return; + auto* isolate=info.GetIsolate(); auto context=isolate->GetCurrentContext(); + if (!info.Length()) { fail(isolate,"createRenderPipeline requires a descriptor"); return; } + try { + webgpu_render_descriptor converted; + if (!read_webgpu_render_descriptor(isolate,context,info[0],converted, + [](v8::Local value) -> std::optional { + if(!v8_webgpu_pipeline_layouts::is_instance(value))return {}; + return v8_webgpu_pipeline_layouts::native_reference(value); + })) return; + auto* item=receiver(info); if (!item) return; // Coercion can reenter. + resource_handle pipeline; + converted.with_native([&](const auto& descriptor) { + item->service->with_device(item->device,[&](auto& owned) { pipeline=owned.create_render_pipeline(descriptor); }); + }); + v8::Local wrapper; + try { + if (!item->pipelines->wrap(context,*item->service,item->device,pipeline,info.This(),converted.label).ToLocal(&wrapper)) { + item->service->with_device(item->device,[&](auto& owned) { owned.release_render_pipeline(pipeline); }); + fail(isolate,"Pipeline wrapper capacity exhausted"); return; + } + } catch (...) { + item->service->with_device(item->device,[&](auto& owned) { owned.release_render_pipeline(pipeline); }); + throw; + } + info.GetReturnValue().Set(wrapper); + } catch (const std::bad_alloc&) { + isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate,"Pipeline allocation failed"))); + } catch (const std::length_error&) { + isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate,"Pipeline capacity exhausted"))); + } catch (const std::exception&) { fail(isolate,"GPUDevice native pipeline ownership is unavailable"); } + } + static void create_sampler(const v8::FunctionCallbackInfo& info){ + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + try{ + std::string label;if(!read_webgpu_object_label(isolate,context,info[0],label))return; + webgpu_state_reader r(isolate,context,info[0]);wgpu::SamplerDescriptor d{}; + d.label=wgpu::StringView(label.data(),label.size()); + d.addressModeU=d.addressModeV=d.addressModeW=wgpu::AddressMode::ClampToEdge; + d.magFilter=d.minFilter=wgpu::FilterMode::Nearest;d.mipmapFilter=wgpu::MipmapFilterMode::Nearest; + d.lodMinClamp=0;d.lodMaxClamp=32;uint32_t anisotropy=1; + if(!r.enumeration("addressModeU",d.addressModeU)||!r.enumeration("addressModeV",d.addressModeV) + ||!r.enumeration("addressModeW",d.addressModeW)||!r.enumeration("compare",d.compare) + ||!r.floating("lodMaxClamp",d.lodMaxClamp)||!r.floating("lodMinClamp",d.lodMinClamp) + ||!r.enumeration("magFilter",d.magFilter)||!r.uint32("maxAnisotropy",anisotropy) + ||!r.enumeration("minFilter",d.minFilter)||!r.enumeration("mipmapFilter",d.mipmapFilter))return; + if(anisotropy>UINT16_MAX){fail(isolate,"maxAnisotropy exceeds unsigned short");return;}d.maxAnisotropy=static_cast(anisotropy); + auto* item=receiver(info);if(!item)return; + resource_handle handle;item->service->with_device(item->device,[&](auto& device){handle=device.create_sampler(d);}); + v8::Local result; + try{ + if(!item->samplers->wrap(context,*item->service,item->device,handle,info.This(),label).ToLocal(&result)){ + item->service->with_device(item->device,[&](auto& device){device.release_sampler(handle);});fail(isolate,"Sampler capacity exhausted");return;} + }catch(...){item->service->with_device(item->device,[&](auto& device){device.release_sampler(handle);});throw;} + info.GetReturnValue().Set(result); + }catch(const std::exception&){fail(isolate,"Sampler creation failed");} + } + static void create_compute_pipeline(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + try { + webgpu_compute_descriptor converted;if(!read_webgpu_compute_descriptor(isolate,context,info[0],converted))return; + auto* item=receiver(info);if(!item)return; + resource_handle pipeline; + converted.with_native([&](const auto& desc){item->service->with_device(item->device,[&](auto& device){pipeline=device.create_compute_pipeline(desc);});}); + v8::Local result; + try { + if(!item->computes->wrap(context,*item->service,item->device,pipeline,info.This(),converted.label).ToLocal(&result)){ + item->service->with_device(item->device,[&](auto& device){device.release_compute_pipeline(pipeline);});fail(isolate,"Compute pipeline capacity exhausted");return; + } + }catch(...){item->service->with_device(item->device,[&](auto& device){device.release_compute_pipeline(pipeline);});throw;} + info.GetReturnValue().Set(result); + }catch(const std::exception&){fail(isolate,"Compute pipeline creation failed");} + } + template static void create_pipeline_async(const v8::FunctionCallbackInfo& info) { + auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + v8::Local resolver;if(!v8::Promise::Resolver::New(context).ToLocal(&resolver))return; + info.GetReturnValue().Set(resolver->GetPromise()); + v8::TryCatch caught(isolate); + try { + if(!receiver(info)){ + auto error=caught.Exception();caught.Reset();resolver->Reject(context,error).FromMaybe(false);return; + } + auto launch=[&](const auto& converted){ + auto* item=receiver(info);if(!item)return; + converted.with_native([&](const auto& descriptor){ + item->async_pipelines->start(context,info.This(),resolver,*item->service,item->device, + *item->pipelines,*item->computes,descriptor,converted.label); + }); + }; + bool valid; + if constexpr(Compute){ + webgpu_compute_descriptor converted;valid=read_webgpu_compute_descriptor(isolate,context,info[0],converted); + if(valid)launch(converted); + }else{ + webgpu_render_descriptor converted;valid=read_webgpu_render_descriptor(isolate,context,info[0],converted, + [](v8::Local value)->std::optional{ + if(!v8_webgpu_pipeline_layouts::is_instance(value))return {}; + return v8_webgpu_pipeline_layouts::native_reference(value); + }); + if(valid)launch(converted); + } + if(!valid||caught.HasCaught()){ + auto error=caught.HasCaught()?caught.Exception():v8::Exception::TypeError(v8::String::NewFromUtf8Literal(isolate,"Invalid pipeline descriptor")); + caught.Reset();resolver->Reject(context,error).FromMaybe(false); + } + }catch(const std::exception& e){ + caught.Reset();resolver->Reject(context,v8::Exception::Error(v8::String::NewFromUtf8(isolate,e.what()).ToLocalChecked())).FromMaybe(false); + } + } + static void create_texture(const v8::FunctionCallbackInfo& info) { + if (!receiver(info)) return; + auto* isolate=info.GetIsolate(); auto context=isolate->GetCurrentContext(); + if (!info.Length()) { fail(isolate,"createTexture requires a descriptor"); return; } + try { + webgpu_texture_descriptor converted; + if (!read_webgpu_texture_descriptor(isolate,context,info[0],converted)) return; + if (!converted.valid_extent_shape) { fail(isolate,"Texture extent sequence must have one to three elements");return; } + auto* item=receiver(info); if (!item) return; // Coercion can reenter. + resource_handle texture; + converted.with_native([&](const auto& descriptor) { + item->service->with_device(item->device,[&](auto& owned) { texture=owned.create_texture(descriptor); }); + }); + v8::Local wrapper; + try { + if (!item->textures->wrap_texture(context,*item->service,item->device,texture,info.This(),converted).ToLocal(&wrapper)) { + item->service->with_device(item->device,[&](auto& owned) { owned.release_texture(texture); }); + fail(isolate,"Texture wrapper capacity exhausted"); return; + } + } catch (...) { + item->service->with_device(item->device,[&](auto& owned) { owned.release_texture(texture); }); + throw; + } + info.GetReturnValue().Set(wrapper); + } catch (const std::bad_alloc&) { + isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate,"Texture allocation failed"))); + } catch (const std::length_error&) { + isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate,"Texture capacity exhausted"))); + } catch (const std::exception&) { fail(isolate,"GPUDevice native texture ownership is unavailable"); } + } + static void create_encoder(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + try { + std::string label;if(!read_webgpu_object_label(isolate,context,info[0],label))return; + auto* item=receiver(info);if(!item)return; + wgpu::CommandEncoderDescriptor descriptor{};descriptor.label=wgpu::StringView(label.data(),label.size()); + resource_handle encoder; + item->service->with_device(item->device,[&](auto& owned){encoder=owned.create_command_encoder(descriptor);}); + v8::Local wrapper; + try { + if(!item->encoders->wrap(context,*item->service,item->device,encoder,info.This(),label).ToLocal(&wrapper)) { + item->service->with_device(item->device,[&](auto& owned){owned.release_command_encoder(encoder);}); + fail(isolate,"Command encoder wrapper capacity exhausted");return; + } + }catch(...){item->service->with_device(item->device,[&](auto& owned){owned.release_command_encoder(encoder);});throw;} + info.GetReturnValue().Set(wrapper); + }catch(const std::exception&){fail(isolate,"Command encoder creation failed");} + } + static void queue(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info);if(!item)return;v8::Local value; + if(info.This()->GetPrivate(info.GetIsolate()->GetCurrentContext(),item->queue_key.Get(info.GetIsolate())).ToLocal(&value))info.GetReturnValue().Set(value); + } + static void adapter_info(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); if (!item) return; + v8::Local value; + if (info.This()->GetPrivate(info.GetIsolate()->GetCurrentContext(),item->info_key.Get(info.GetIsolate())).ToLocal(&value)) + info.GetReturnValue().Set(value); + } + static void limits(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); if (!item) return; + v8::Local value; + if (info.This()->GetPrivate(info.GetIsolate()->GetCurrentContext(),item->limits_key.Get(info.GetIsolate())).ToLocal(&value)) + info.GetReturnValue().Set(value); + } + static void features(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); if (!item) return; + v8::Local value; + if (info.This()->GetPrivate(info.GetIsolate()->GetCurrentContext(),item->features_key.Get(info.GetIsolate())).ToLocal(&value)) + info.GetReturnValue().Set(value); + } + static void label(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); if (!item) return; + v8::Local value; + if (v8::String::NewFromUtf8(info.GetIsolate(),item->label.data(),v8::NewStringType::kNormal, + static_cast(item->label.size())).ToLocal(&value)) info.GetReturnValue().Set(value); + } + static void set_label(const v8::FunctionCallbackInfo& info) { + if (!receiver(info)) return; + auto* isolate=info.GetIsolate(); + v8::Local value; + if (!info[0]->ToString(isolate->GetCurrentContext()).ToLocal(&value)) return; + v8::String::Utf8Value bytes(isolate,value); + if (!*bytes) return; + auto* item=receiver(info); if (!item) return; + try { + std::string converted(*bytes,bytes.length()); + item->service->with_device(item->device,[&](auto& device) { + device.native().SetLabel(wgpu::StringView(converted.data(),converted.size())); + }); + item->label=std::move(converted); + } catch (const std::exception&) { fail(isolate,"GPUDevice label update failed"); } + } + static void destroy(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); if (!item || item->destroyed) return; + try { + item->buffers->detach_device(*item->service,item->device); + item->service->with_device(item->device,[](auto& device) { device.native().Destroy(); }); + item->destroyed=true; + } catch (const std::exception&) { fail(info.GetIsolate(),"GPUDevice native ownership is unavailable"); } + } + static void first_pass(const v8::WeakCallbackInfo& info) { + info.GetParameter()->wrapper.Reset(); info.SetSecondPassCallback(second_pass); + } + static void second_pass(const v8::WeakCallbackInfo& info) { + auto* item=info.GetParameter(); item->releases->publish(item->ticket); item->published=true; + } + void check_scope() const { + if (std::this_thread::get_id()!=thread_ || v8::Isolate::GetCurrent()!=isolate_) + throw std::logic_error("GPUDevice wrappers require their owning isolate scope"); + } +public: + v8_webgpu_devices(v8::Isolate* isolate,v8::Local context, + v8::Local dom_exception,size_t capacity=64,size_t buffer_capacity=1024, + v8::Local event_target={},std::function)> initialize_event_target={}) + :isolate_(isolate),lost_info_factory_(isolate,context),errors_factory_(isolate,context),features_factory_(isolate,context),limits_factory_(isolate,context),info_factory_(isolate,context),initialize_event_target_(std::move(initialize_event_target)),buffer_capacity_(buffer_capacity),entries_(capacity) { + check_scope(); + if (dom_exception.IsEmpty()) throw std::invalid_argument("Trusted DOMException constructor is required"); + realm_.Reset(isolate,context); dom_exception_.Reset(isolate,dom_exception); + auto type=v8::FunctionTemplate::New(isolate); + if(!event_target.IsEmpty())type->Inherit(event_target); + auto instance=type->InstanceTemplate();instance->SetInternalFieldCount(2);instance_.Reset(isolate,instance); + auto prototype=v8::ObjectTemplate::New(isolate); + auto create=v8::FunctionTemplate::New(isolate,create_buffer); create->SetLength(1); + prototype->Set(isolate,"createBuffer",create); + auto layout_create=v8::FunctionTemplate::New(isolate,create_pipeline_layout);layout_create->SetLength(1); + prototype->Set(isolate,"createPipelineLayout",layout_create); + auto group_create=v8::FunctionTemplate::New(isolate,create_bind_group);group_create->SetLength(1); + prototype->Set(isolate,"createBindGroup",group_create); + auto binding_create=v8::FunctionTemplate::New(isolate,create_bind_group_layout);binding_create->SetLength(1); + prototype->Set(isolate,"createBindGroupLayout",binding_create); + auto pop_scope=v8::FunctionTemplate::New(isolate,pop_error_scope); + prototype->Set(isolate,"popErrorScope",pop_scope); + auto push_scope=v8::FunctionTemplate::New(isolate,push_error_scope);push_scope->SetLength(1); + prototype->Set(isolate,"pushErrorScope",push_scope); + auto shader_create=v8::FunctionTemplate::New(isolate,create_shader); shader_create->SetLength(1); + prototype->Set(isolate,"createShaderModule",shader_create); + prototype->Set(isolate,"createSampler",v8::FunctionTemplate::New(isolate,create_sampler)); + auto pipeline_create=v8::FunctionTemplate::New(isolate,create_pipeline);pipeline_create->SetLength(1); + prototype->Set(isolate,"createRenderPipeline",pipeline_create); + prototype->Set(isolate,"createComputePipeline",v8::FunctionTemplate::New(isolate,create_compute_pipeline)); + prototype->Set(isolate,"createComputePipelineAsync",v8::FunctionTemplate::New(isolate,create_pipeline_async)); + prototype->Set(isolate,"createRenderPipelineAsync",v8::FunctionTemplate::New(isolate,create_pipeline_async)); + auto texture_create=v8::FunctionTemplate::New(isolate,create_texture);texture_create->SetLength(1); + prototype->Set(isolate,"createTexture",texture_create); + auto encoder_create=v8::FunctionTemplate::New(isolate,create_encoder);encoder_create->SetLength(0); + prototype->Set(isolate,"createCommandEncoder",encoder_create); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"label"),v8::FunctionTemplate::New(isolate,label),v8::FunctionTemplate::New(isolate,set_label)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"adapterInfo"),v8::FunctionTemplate::New(isolate,adapter_info)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"limits"),v8::FunctionTemplate::New(isolate,limits)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"features"),v8::FunctionTemplate::New(isolate,features)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"queue"),v8::FunctionTemplate::New(isolate,queue)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"lost"),v8::FunctionTemplate::New(isolate,lost)); + prototype->Set(isolate,"destroy",v8::FunctionTemplate::New(isolate,destroy)); + auto prototype_object=prototype->NewInstance(context).ToLocalChecked(); + if(!event_target.IsEmpty()) { + auto parent=event_target->GetFunction(context).ToLocalChecked()->Get(context,v8::String::NewFromUtf8Literal(isolate,"prototype")).ToLocalChecked(); + if(!prototype_object->SetPrototype(context,parent).FromMaybe(false))throw std::runtime_error("GPUDevice EventTarget inheritance failed"); + } + prototype_.Reset(isolate,prototype_object); + } + v8_webgpu_devices(const v8_webgpu_devices&)=delete; + v8_webgpu_devices& operator=(const v8_webgpu_devices&)=delete; + ~v8_webgpu_devices() { + check_scope(); + for (auto& item:entries_) if (item) { + if (!item->wrapper.IsEmpty()) item->wrapper.Get(isolate_)->SetAlignedPointerInInternalField(1,nullptr,v8::kEmbedderDataTypeTagDefault); + item->loss.reset(); + item->error_scopes.reset(); + item->queue.reset(); + item->encoders.reset(); + item->textures.reset(); + item->async_pipelines.reset(); + item->computes.reset(); + item->pipelines.reset(); + item->pipeline_layouts.reset(); + item->binding_groups.reset(); + item->binding_layouts.reset(); + item->samplers.reset(); + item->shaders.reset(); + item->buffers.reset(); // Invalidate first; cancellation can construct JS exceptions. + item->wrapper.Reset(); + if (!item->published) item->releases->publish(item->ticket); + } + } + bool complete(completion_record record) { + check_scope(); + for (auto& item:entries_) if (item && (item->loss->complete(record)||item->error_scopes->complete(record)||item->buffers->complete(record)||item->shaders->complete(record)||item->async_pipelines->complete(record)||item->queue->complete(record))) return true; + return false; + } +private: + static entry* entry_from_value(v8::Local value) { + if(!value->IsObject())throw std::invalid_argument("GPUDevice object required");auto object=value.As(); + if(object->InternalFieldCount()!=2||!object->GetInternalField(0)->IsValue()||!object->GetInternalField(0).As()->IsExternal() + ||object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_)throw std::invalid_argument("Incorrect GPUDevice interface"); + auto* item=static_cast(object->GetAlignedPointerFromInternalField(1,v8::kEmbedderDataTypeTagDefault)); + if(!item)throw std::invalid_argument("GPUDevice realm has been released"); + return item; + } +public: + static wgpu::Device native_reference(v8::Local value) { + auto* item=entry_from_value(value);wgpu::Device device; + item->service->with_device(item->device,[&](auto& owned){device=owned.native();});return device; + } + // Host canvas bridge. Caller supplies an imported texture from source_device; + // no GPU allocation or copying is performed. Metadata must describe the + // actual facade exposed to JavaScript, not broader host-only capabilities. + static v8::MaybeLocal adopt_canvas_texture(v8::Local context, + v8::Local device_object,const wgpu::Device& source_device, + const wgpu::Texture& texture,const webgpu_texture_descriptor& descriptor) { + auto* item=entry_from_value(device_object); + if(!texture||!descriptor.valid_extent_shape||texture.GetWidth()!=descriptor.size.width + ||texture.GetHeight()!=descriptor.size.height||texture.GetDepthOrArrayLayers()!=descriptor.size.depthOrArrayLayers + ||texture.GetMipLevelCount()!=descriptor.mip_levels||texture.GetSampleCount()!=descriptor.samples + ||texture.GetDimension()!=descriptor.dimension||texture.GetFormat()!=descriptor.format + ||texture.GetUsage()!=static_cast(descriptor.usage)) + throw std::invalid_argument("Canvas texture metadata differs from its native facade"); + resource_handle handle; + item->service->with_device(item->device,[&](auto& owned){handle=owned.adopt_texture(source_device,texture);}); + try { + v8::Local wrapper; + if(item->textures->wrap_texture(context,*item->service,item->device,handle,device_object,descriptor).ToLocal(&wrapper))return wrapper; + }catch(...){item->service->with_device(item->device,[&](auto& owned){owned.release_texture(handle);});throw;} + item->service->with_device(item->device,[&](auto& owned){owned.release_texture(handle);});return {}; + } + // Caller retains ownership until a non-empty wrapper is returned. + v8::MaybeLocal wrap(v8::Local context,graphics_service& service,resource_handle device,std::string initial_label={},std::string queue_label={}) { + check_scope(); + if (realm_.Get(isolate_)!=context) throw std::logic_error("GPUDevice belongs to another realm"); + service.with_device(device,[](auto&) {}); + for (const auto& item:entries_) if (item && !item->published && item->service==&service + && item->device.table==device.table && item->device.generation==device.generation && item->device.slot==device.slot) + throw std::invalid_argument("GPUDevice handle is already wrapped"); + auto found=std::find_if(entries_.begin(),entries_.end(),[](const auto& item) { return !item || item->published; }); + if (found==entries_.end()) return {}; + v8::Local wrapper; + if (!instance_.Get(isolate_)->NewInstance(context).ToLocal(&wrapper) + || !wrapper->SetPrototype(context,prototype_.Get(isolate_)).FromMaybe(false)) return {}; + if(initialize_event_target_&&!initialize_event_target_(wrapper))return {}; + auto item=std::make_unique(); + item->label=std::move(initial_label); + item->service=&service; item->device=device; item->releases=service.release_endpoint(); + service.with_device(device,[&](auto& owned){item->loss=std::make_unique(isolate_,context,lost_info_factory_,owned.loss_signal(),service.dawn().completions());}); + item->error_scopes=std::make_unique(isolate_,errors_factory_,dom_exception_.Get(isolate_)); + item->buffers=std::make_unique(isolate_,context,buffer_capacity_,dom_exception_.Get(isolate_)); + item->pipeline_layouts=std::make_unique(isolate_,context); + item->binding_groups=std::make_unique(isolate_,context); + item->binding_layouts=std::make_unique(isolate_,context); + item->shaders=std::make_unique(isolate_,context); + item->samplers=std::make_unique(isolate_,context); + item->pipelines=std::make_unique(isolate_,context); + item->computes=std::make_unique(isolate_,context); + item->async_pipelines=std::make_unique(isolate_); + item->textures=std::make_unique(isolate_,context); + item->encoders=std::make_unique(isolate_,context); + item->buffer_owner_key.Reset(isolate_,v8::Private::New(isolate_)); + item->features_key.Reset(isolate_,v8::Private::New(isolate_)); + std::vector names; + service.with_device(device,[&](auto& owned) { names=webgpu_supported_feature_names(owned.native()); }); + v8::Local snapshot; + if (!features_factory_.create(context,names).ToLocal(&snapshot) + || !wrapper->SetPrivate(context,item->features_key.Get(isolate_),snapshot).FromMaybe(false)) return {}; + item->limits_key.Reset(isolate_,v8::Private::New(isolate_)); + v8::MaybeLocal limit_snapshot; + service.with_device(device,[&](auto& owned) { limit_snapshot=limits_factory_.create(context,owned.native()); }); + v8::Local limit_object; + if(!limit_snapshot.ToLocal(&limit_object) || !wrapper->SetPrivate(context,item->limits_key.Get(isolate_),limit_object).FromMaybe(false))return {}; + webgpu_adapter_info metadata; + service.with_device(device,[&](const auto& owned) { metadata=read_webgpu_adapter_info(owned.adapter()); }); + item->info_key.Reset(isolate_,v8::Private::New(isolate_)); + v8::Local info_object; + if(!info_factory_.create(context,metadata).ToLocal(&info_object) + || !wrapper->SetPrivate(context,item->info_key.Get(isolate_),info_object).FromMaybe(false))return {}; + item->queue_key.Reset(isolate_,v8::Private::New(isolate_)); + item->queue=std::make_unique(isolate_,service,device,std::move(queue_label),dom_exception_.Get(isolate_)); + v8::Local queue_object; + if(!item->queue->create(context,wrapper).ToLocal(&queue_object)||!wrapper->SetPrivate(context,item->queue_key.Get(isolate_),queue_object).FromMaybe(false))return {}; + auto ticket=item->releases->reserve(graphics_service::deferred_device_release(device)); + if (!ticket) return {}; + item->ticket=*ticket; + wrapper->SetInternalField(0,v8::External::New(isolate_,&brand_,v8::kExternalPointerTypeTagDefault)); + wrapper->SetAlignedPointerInInternalField(1,item.get(),v8::kEmbedderDataTypeTagDefault); + item->wrapper.Reset(isolate_,wrapper); item->wrapper.SetWeak(item.get(),first_pass,v8::WeakCallbackType::kParameter); + *found=std::move(item); + return wrapper; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_discovery.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_discovery.h new file mode 100644 index 000000000..0ee4888e4 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_discovery.h @@ -0,0 +1,135 @@ +#pragma once +#include "v8_webgpu_adapters.h" +#include "v8_webgpu_adapter_request.h" +#include "v8_webgpu_adapter_options.h" +#include "wgsl_language_feature_names.h" +#include +namespace webscene::graphics { +// Internal realm-owned GPU discovery object. The host must apply its secure +// context and graphics policy before installing it. This class installs nothing. +// Adapter/device registries and the graphics service must outlive this object. +class v8_webgpu_discovery { + alignas(void*) static inline char brand_{}; + v8::Isolate* isolate_; + const std::thread::id thread_=std::this_thread::get_id(); + graphics_service& service_; + v8_webgpu_adapters& adapters_; + const wgpu::BackendType backend_; + const wgpu::TextureFormat preferred_format_; + v8::Global realm_; + v8::Global wrapper_; + v8::Global language_key_; + std::list> requests_; + void check_scope() const { + if (std::this_thread::get_id()!=thread_ || v8::Isolate::GetCurrent()!=isolate_) + throw std::logic_error("GPU discovery requires its owner isolate"); + } + static v8_webgpu_discovery* receiver(const v8::FunctionCallbackInfo& info) { + auto object=info.This(); + if (object->InternalFieldCount()==2 && object->GetInternalField(0)->IsValue() + && object->GetInternalField(0).As()->IsExternal() + && object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)==&brand_) { + auto* owner=static_cast(object->GetAlignedPointerFromInternalField(1,v8::kEmbedderDataTypeTagDefault)); + if (owner) return owner; + } + info.GetIsolate()->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8Literal(info.GetIsolate(),"Illegal GPU receiver"))); + return nullptr; + } + static void language_features(const v8::FunctionCallbackInfo& info) { + auto* owner=receiver(info); if (!owner) return; + v8::Local value; + if (info.This()->GetPrivate(info.GetIsolate()->GetCurrentContext(),owner->language_key_.Get(info.GetIsolate())).ToLocal(&value)) + info.GetReturnValue().Set(value); + } + static void preferred_canvas_format(const v8::FunctionCallbackInfo& info) { + auto* owner=receiver(info); if (!owner) return; + const char* format=owner->preferred_format_==wgpu::TextureFormat::BGRA8Unorm?"bgra8unorm":"rgba8unorm"; + info.GetReturnValue().Set(v8::String::NewFromUtf8(info.GetIsolate(),format).ToLocalChecked()); + } + static void request_adapter(const v8::FunctionCallbackInfo& info) { + auto* isolate=info.GetIsolate(); auto context=isolate->GetCurrentContext(); + v8::Local failure; + v8::Local promise; + { + v8::TryCatch caught(isolate); + try { + if (receiver(info)) { + webgpu_adapter_options options; + if (read_webgpu_adapter_options(isolate,context,info[0],options)) { + // Descriptor getters may invalidate the host controller. + if (auto* owner=receiver(info)) { + if (owner->realm_.Get(isolate)!=context) throw std::logic_error("Foreign discovery realm"); + owner->requests_.push_back(nullptr); + auto slot=std::prev(owner->requests_.end()); + try { + *slot=v8_webgpu_adapter_request::start(isolate,context,options,owner->service_.dawn().instance(), + owner->service_.dawn().completions(),{owner->service_.engine_identity(),new_owner_token(),0}, + new_owner_token(),owner->backend_,promise); + } catch (...) { owner->requests_.erase(slot); throw; } + if (!*slot || !(*slot)->pending()) owner->requests_.erase(slot); + } + } + } + } catch (const std::exception&) { + failure=v8::Exception::Error(v8::String::NewFromUtf8Literal(isolate,"GPU adapter discovery failed")); + } + if (caught.HasTerminated()) return; + if (caught.HasCaught()) failure=caught.Exception(); + } + if (!failure.IsEmpty()) { + v8::Local resolver; + if (!v8::Promise::Resolver::New(context).ToLocal(&resolver) || !resolver->Reject(context,failure).FromMaybe(false)) return; + promise=resolver->GetPromise(); + } + if (!promise.IsEmpty()) info.GetReturnValue().Set(promise); + } +public: + v8_webgpu_discovery(v8::Isolate* isolate,v8::Local context,graphics_service& service, + v8_webgpu_adapters& adapters,wgpu::BackendType backend=wgpu::BackendType::Undefined, + wgpu::TextureFormat preferred_format=wgpu::TextureFormat::BGRA8Unorm) + :isolate_(isolate),service_(service),adapters_(adapters),backend_(backend),preferred_format_(preferred_format) { + check_scope(); + if (preferred_format!=wgpu::TextureFormat::BGRA8Unorm && preferred_format!=wgpu::TextureFormat::RGBA8Unorm) + throw std::invalid_argument("Preferred canvas format must be BGRA8Unorm or RGBA8Unorm"); + realm_.Reset(isolate,context); + auto instance=v8::ObjectTemplate::New(isolate); instance->SetInternalFieldCount(2); + auto prototype=v8::ObjectTemplate::New(isolate); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"wgslLanguageFeatures"),v8::FunctionTemplate::New(isolate,language_features)); + prototype->Set(isolate,"getPreferredCanvasFormat",v8::FunctionTemplate::New(isolate,preferred_canvas_format)); + prototype->Set(isolate,"requestAdapter",v8::FunctionTemplate::New(isolate,request_adapter)); + auto wrapper=instance->NewInstance(context).ToLocalChecked(); + wrapper->SetPrototype(context,prototype->NewInstance(context).ToLocalChecked()).Check(); + wrapper->SetInternalField(0,v8::External::New(isolate,&brand_,v8::kExternalPointerTypeTagDefault)); + wrapper->SetAlignedPointerInInternalField(1,this,v8::kEmbedderDataTypeTagDefault); + v8_wgsl_language_features feature_factory(isolate,context); + auto names=supported_wgsl_language_feature_names(service_.dawn().instance()); + auto features=feature_factory.create(context,names).ToLocalChecked(); + language_key_.Reset(isolate,v8::Private::New(isolate)); + wrapper->SetPrivate(context,language_key_.Get(isolate),features).Check(); + wrapper_.Reset(isolate,wrapper); + } + v8_webgpu_discovery(const v8_webgpu_discovery&)=delete; + v8_webgpu_discovery& operator=(const v8_webgpu_discovery&)=delete; + ~v8_webgpu_discovery() { + check_scope(); + wrapper_.Get(isolate_)->SetAlignedPointerInInternalField(1,nullptr,v8::kEmbedderDataTypeTagDefault); + for (auto& request:requests_) if (request) request->cancel(realm_.Get(isolate_)); + } + v8::Local object() const { check_scope(); return wrapper_.Get(isolate_); } + bool complete(completion_record record) { + check_scope(); auto context=realm_.Get(isolate_); + for (auto it=requests_.begin();it!=requests_.end();++it) { + if (!*it || !(*it)->complete(isolate_,context,record,[&](wgpu::Adapter adapter) -> v8::MaybeLocal { + auto handle=service_.adopt_adapter(std::move(adapter)); + try { + v8::Local wrapper; + if (adapters_.wrap(context,service_,handle).ToLocal(&wrapper)) return wrapper; + } catch (...) { service_.destroy_adapter(handle); throw; } + service_.destroy_adapter(handle); return {}; + })) continue; + requests_.erase(it); return true; + } + return false; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_dxgi_canvas_host.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_dxgi_canvas_host.h new file mode 100644 index 000000000..4c5600d9c --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_dxgi_canvas_host.h @@ -0,0 +1,34 @@ +#pragma once +#include "v8_webgpu_canvas_context.h" +#include "dawn_dxgi_canvas_host.h" +#if defined(_WIN32) +namespace webscene::graphics { +// The document supplies canvas/generation/content and producer timeline identities. This +// adapter supplies bitmap and presentation metadata from the configuration. +inline webgpu_canvas_host make_dxgi_webgpu_canvas_host( + std::shared_ptr provider, + std::function next_metadata,std::shared_ptr device_lifetime={}) { + if(!provider||!next_metadata)throw std::invalid_argument("Canvas provider and frame identity source required"); + webgpu_canvas_host result; + result.validate=[](const webgpu_canvas_configuration& config) { + if(config.format!=wgpu::TextureFormat::BGRA8Unorm||config.color_space!="srgb"||config.tone_mapping!="standard") + throw std::invalid_argument("DXGI presenter currently requires BGRA8 sRGB standard tone mapping"); + if(!config.device.HasFeature(wgpu::FeatureName::SharedTextureMemoryDXGISharedHandle)|| + !config.device.HasFeature(wgpu::FeatureName::SharedFenceDXGISharedHandle)) + throw std::invalid_argument("Canvas device lacks native DXGI sharing capabilities"); + }; + result.acquire=[provider,next_metadata=std::move(next_metadata),device_lifetime=std::move(device_lifetime)]( + const webgpu_canvas_configuration& config,const webgpu_texture_descriptor& descriptor) { + auto metadata=next_metadata();metadata.width=descriptor.size.width;metadata.height=descriptor.size.height; + metadata.format=image_format::bgra8_unorm;metadata.color_space=image_color_space::srgb; + metadata.alpha=config.alpha_mode=="opaque"?image_alpha::opaque:image_alpha::premultiplied; + metadata.orientation=image_orientation::top_left; + wgpu::Texture texture; + descriptor.with_native([&](const auto& native){texture=provider->acquire(metadata,config.device,native,device_lifetime);}); + return texture; + }; + result.retire=[provider](const wgpu::Texture& texture,bool present){provider->retire(texture,present);}; + return result; +} +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_error_scopes.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_error_scopes.h new file mode 100644 index 000000000..aa0f3ae67 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_error_scopes.h @@ -0,0 +1,89 @@ +#pragma once +#include "graphics_service.h" +#include +#include "v8_webgpu_errors.h" +namespace webscene::graphics { +class v8_webgpu_error_scopes { + struct result { wgpu::ErrorType type=wgpu::ErrorType::NoError;std::string message; }; + struct request { + uint64_t operation{}; + std::shared_ptr mailbox; + std::shared_ptr value; + v8::Global context; + v8::Global device; + v8::Global resolver; + }; + v8::Isolate* isolate_; + v8_webgpu_errors& errors_; + v8::Global dom_exception_; + resource_owner owner_{new_owner_token(),new_owner_token(),new_owner_token()}; + std::vector> pending_; + void reject(v8::Local context,v8::Local resolver) { + v8::Local args[]{v8::String::NewFromUtf8Literal(isolate_,"GPU error scope unavailable"), + v8::String::NewFromUtf8Literal(isolate_,"OperationError")}; + v8::Local exception; + if(dom_exception_.Get(isolate_)->NewInstance(context,2,args).ToLocal(&exception)) + (void)resolver->Reject(context,exception).FromMaybe(false); + } +public: + v8_webgpu_error_scopes(v8::Isolate* isolate,v8_webgpu_errors& errors,v8::Local dom_exception) + :isolate_(isolate),errors_(errors){dom_exception_.Reset(isolate,dom_exception);} + ~v8_webgpu_error_scopes() { + for(auto& item:pending_) { + item->mailbox->cancel_owner(owner_); + auto context=item->context.Get(isolate_);v8::Context::Scope scope(context); + reject(context,item->resolver.Get(isolate_)); + } + } + void pop(v8::Local context,v8::Local wrapper, + v8::Local resolver,wgpu::Device device,std::shared_ptr mailbox) { + try { + if(pending_.size()>=256)throw std::length_error("Error scope request capacity exhausted"); + auto item=std::make_unique();item->operation=new_owner_token();item->mailbox=mailbox; + item->value=std::make_shared();item->context.Reset(isolate_,context); + item->device.Reset(isolate_,wrapper);item->resolver.Reset(isolate_,resolver); + auto value=item->value;pending_.reserve(pending_.size()+1); + auto ticket=mailbox->reserve(item->operation,owner_); + if(!ticket)throw std::length_error("Error scope completion capacity exhausted"); + pending_.push_back(std::move(item)); + device.PopErrorScope(wgpu::CallbackMode::AllowSpontaneous, + [mailbox,ticket=*ticket,value,device](wgpu::PopErrorScopeStatus status,wgpu::ErrorType type,wgpu::StringView message) { + auto completion=completion_status::failed; + try { + if(status==wgpu::PopErrorScopeStatus::Success) { + constexpr size_t limit=1024*1024; + size_t length=message.length; + if(length==WGPU_STRLEN)length=message.data?strnlen(message.data,limit+1):0; + if(length>limit||(!message.data&&length))throw std::length_error("GPU error message budget exceeded"); + value->type=type;if(length)value->message.assign(message.data,length); + completion=completion_status::success; + } + }catch(...){} + mailbox->publish(ticket,completion); + }); + }catch(const std::exception&){reject(context,resolver);} + } + bool complete(completion_record record) { + if(record.owner!=owner_)return false; + auto found=std::find_if(pending_.begin(),pending_.end(),[&](const auto& item){return item->operation==record.operation;}); + if(found==pending_.end())return true; + auto item=std::move(*found);pending_.erase(found); + auto context=item->context.Get(isolate_);v8::Context::Scope scope(context); + auto resolver=item->resolver.Get(isolate_); + if(record.status!=completion_status::success){reject(context,resolver);return true;} + if(item->value->type==wgpu::ErrorType::NoError){(void)resolver->Resolve(context,v8::Null(isolate_)).FromMaybe(false);return true;} + int kind=0; + switch(item->value->type) { + case wgpu::ErrorType::Validation:kind=1;break; + case wgpu::ErrorType::OutOfMemory:kind=2;break; + case wgpu::ErrorType::Internal:kind=3;break; + default:reject(context,resolver);return true; + } + v8::Local error; + if(errors_.create(context,kind,item->value->message).ToLocal(&error)) + (void)resolver->Resolve(context,error).FromMaybe(false); + else reject(context,resolver); + return true; + } +}; +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_errors.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_errors.h new file mode 100644 index 000000000..da8eae131 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_errors.h @@ -0,0 +1,58 @@ +#pragma once +#include +#include +#include +#include +namespace webscene::graphics { +inline constexpr const char* webgpu_error_names[]{"GPUError","GPUValidationError","GPUOutOfMemoryError","GPUInternalError"}; +class v8_webgpu_errors { + alignas(void*) static inline char brand_{}; + v8::Isolate* isolate_; + std::array,4> constructors_; + static v8::Local text(v8::Isolate* isolate,const char* value) { + return v8::String::NewFromUtf8(isolate,value).ToLocalChecked(); + } + static void construct(const v8::FunctionCallbackInfo& args) { + auto* isolate=args.GetIsolate(); + if(!args.IsConstructCall()||args.Data().As()->Value()==0||args.Length()<1) { + isolate->ThrowException(v8::Exception::TypeError(text(isolate,"Illegal GPUError construction")));return; + } + v8::Local message; + if(!args[0]->ToString(isolate->GetCurrentContext()).ToLocal(&message))return; + args.This()->SetInternalField(0,v8::External::New(isolate,&brand_,v8::kExternalPointerTypeTagDefault)); + args.This()->SetInternalField(1,message); + args.GetReturnValue().Set(args.This()); + } + static void message(const v8::FunctionCallbackInfo& args) { + auto object=args.This();auto* isolate=args.GetIsolate(); + if(object->InternalFieldCount()!=2||!object->GetInternalField(0)->IsValue() + ||!object->GetInternalField(0).As()->IsExternal() + ||object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_) { + isolate->ThrowException(v8::Exception::TypeError(text(isolate,"Illegal GPUError receiver")));return; + } + args.GetReturnValue().Set(object->GetInternalField(1).As()); + } +public: + v8_webgpu_errors(v8::Isolate* isolate,v8::Local context):isolate_(isolate) { + auto base=v8::FunctionTemplate::New(isolate,construct,v8::Integer::New(isolate,0)); + base->SetClassName(text(isolate,webgpu_error_names[0]));base->InstanceTemplate()->SetInternalFieldCount(2); + auto getter=v8::FunctionTemplate::New(isolate,message); + base->PrototypeTemplate()->SetAccessorProperty(text(isolate,"message"),getter); + for(int i=0;i<4;++i) { + auto type=i? v8::FunctionTemplate::New(isolate,construct,v8::Integer::New(isolate,i)):base; + if(i){type->Inherit(base);type->SetLength(1);type->SetClassName(text(isolate,webgpu_error_names[i]));type->InstanceTemplate()->SetInternalFieldCount(2);} + type->PrototypeTemplate()->Set(v8::Symbol::GetToStringTag(isolate),text(isolate,webgpu_error_names[i]), + static_cast(v8::ReadOnly|v8::DontEnum)); + auto constructor=type->GetFunction(context).ToLocalChecked();constructors_[i].Reset(isolate,constructor); + if(!context->Global()->DefineOwnProperty(context,text(isolate,webgpu_error_names[i]),constructor,v8::DontEnum).FromMaybe(false)) + throw std::runtime_error("GPUError interface installation failed"); + } + } + v8::MaybeLocal create(v8::Local context,int kind,const std::string& message) { + v8::Local value; + if(!v8::String::NewFromUtf8(isolate_,message.data(),v8::NewStringType::kNormal,static_cast(message.size())).ToLocal(&value))return {}; + v8::Local args[]{value}; + return constructors_.at(kind).Get(isolate_)->NewInstance(context,1,args); + } +}; +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_iosurface_canvas_host.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_iosurface_canvas_host.h new file mode 100644 index 000000000..da84c8087 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_iosurface_canvas_host.h @@ -0,0 +1,34 @@ +#pragma once +#include "v8_webgpu_canvas_context.h" +#include "dawn_iosurface_canvas_host.h" +#if defined(__APPLE__) +namespace webscene::graphics { +// The document supplies canvas/generation/content and producer timeline identities. This +// adapter supplies bitmap and presentation metadata from the configuration. +inline webgpu_canvas_host make_iosurface_webgpu_canvas_host( + std::shared_ptr provider, + std::function next_metadata,std::shared_ptr device_lifetime={}) { + if(!provider||!next_metadata)throw std::invalid_argument("Canvas provider and frame identity source required"); + webgpu_canvas_host result; + result.validate=[](const webgpu_canvas_configuration& config) { + if(config.format!=wgpu::TextureFormat::BGRA8Unorm||config.color_space!="srgb"||config.tone_mapping!="standard") + throw std::invalid_argument("IOSurface presenter currently requires BGRA8 sRGB standard tone mapping"); + if(!config.device.HasFeature(wgpu::FeatureName::SharedTextureMemoryIOSurface)|| + !config.device.HasFeature(wgpu::FeatureName::SharedFenceMTLSharedEvent)) + throw std::invalid_argument("Canvas device lacks native IOSurface sharing capabilities"); + }; + result.acquire=[provider,next_metadata=std::move(next_metadata),device_lifetime=std::move(device_lifetime)]( + const webgpu_canvas_configuration& config,const webgpu_texture_descriptor& descriptor) { + auto metadata=next_metadata();metadata.width=descriptor.size.width;metadata.height=descriptor.size.height; + metadata.format=image_format::bgra8_unorm;metadata.color_space=image_color_space::srgb; + metadata.alpha=config.alpha_mode=="opaque"?image_alpha::opaque:image_alpha::premultiplied; + metadata.orientation=image_orientation::top_left; + wgpu::Texture texture; + descriptor.with_native([&](const auto& native){texture=provider->acquire(metadata,config.device,native,device_lifetime);}); + return texture; + }; + result.retire=[provider](const wgpu::Texture& texture,bool present){provider->retire(texture,present);}; + return result; +} +} // namespace webscene::graphics +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_labeled_resources.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_labeled_resources.h new file mode 100644 index 000000000..05bb1ba5d --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_labeled_resources.h @@ -0,0 +1,161 @@ +#pragma once +#include "graphics_service.h" +#include +namespace webscene::graphics { +// Shared lifetime machinery for immutable GPU objects with mutable labels. +// Each Traits specialization has a distinct receiver brand and prototype. +template class v8_webgpu_labeled_resources { +protected: + using Native=typename Traits::native_type; + struct entry { + v8_webgpu_labeled_resources* registry{}; + v8::Global wrapper; + v8::Global device_owner_key; + graphics_service* service{}; + resource_handle device; + resource_handle resource; + std::shared_ptr releases; + release_ticket ticket; + bool published{}; + std::string label; + }; + alignas(void*) static inline char brand_{}; + v8::Isolate* isolate_; + const std::thread::id thread_=std::this_thread::get_id(); + v8::Global realm_; + v8::Global instance_; + v8::Global prototype_; + std::vector> entries_; + static void fail(v8::Isolate* isolate,const char* message) { + isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate,message).ToLocalChecked())); + } + static entry* receiver(const v8::FunctionCallbackInfo& info) { + auto object=info.This(); + if (object->InternalFieldCount()!=2 || !object->GetInternalField(0)->IsValue() + || !object->GetInternalField(0).As()->IsExternal() + || object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_) { + fail(info.GetIsolate(),"Illegal GPU resource receiver"); return nullptr; + } + auto* item=static_cast(object->GetAlignedPointerFromInternalField(1,v8::kEmbedderDataTypeTagDefault)); + if (!item) fail(info.GetIsolate(),"GPU resource realm has been released"); + return item; + } + static void label(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info); if (!item) return; + v8::Local value; + if (v8::String::NewFromUtf8(info.GetIsolate(),item->label.data(),v8::NewStringType::kNormal, + static_cast(item->label.size())).ToLocal(&value)) info.GetReturnValue().Set(value); + } + static void set_label(const v8::FunctionCallbackInfo& info) { + if (!receiver(info)) return; + auto* isolate=info.GetIsolate(); + v8::Local value; + if (!info[0]->ToString(isolate->GetCurrentContext()).ToLocal(&value)) return; + v8::String::Utf8Value bytes(isolate,value); + if (!*bytes) return; + auto* item=receiver(info); if (!item) return; + try { + std::string converted(*bytes,bytes.length()); + item->service->with_device(item->device,[&](auto& device) { + Traits::with(device,item->resource,[&](const auto& native) { native.SetLabel(wgpu::StringView(converted.data(),converted.size())); }); + }); + item->label=std::move(converted); + } catch (const std::exception&) { fail(isolate,"GPU resource label update failed"); } + } + static void first_pass(const v8::WeakCallbackInfo& info) { + info.GetParameter()->wrapper.Reset(); info.SetSecondPassCallback(second_pass); + } + static void second_pass(const v8::WeakCallbackInfo& info) { + auto* item=info.GetParameter(); item->releases->publish(item->ticket); item->published=true; + } + void check_scope() const { + if (std::this_thread::get_id()!=thread_ || v8::Isolate::GetCurrent()!=isolate_) + throw std::logic_error("GPU resources require their owning isolate scope"); + } +public: + v8_webgpu_labeled_resources(v8::Isolate* isolate,v8::Local context, + size_t capacity=1024) + :isolate_(isolate),entries_(capacity) { + check_scope();realm_.Reset(isolate,context); + auto instance=v8::ObjectTemplate::New(isolate); instance->SetInternalFieldCount(2); instance_.Reset(isolate,instance); + auto prototype=v8::ObjectTemplate::New(isolate); + prototype->Set(v8::Symbol::GetToStringTag(isolate),v8::String::NewFromUtf8(isolate,Traits::name).ToLocalChecked(),static_cast(v8::ReadOnly|v8::DontEnum)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"label"),v8::FunctionTemplate::New(isolate,label),v8::FunctionTemplate::New(isolate,set_label)); + prototype_.Reset(isolate,prototype->NewInstance(context).ToLocalChecked()); + } + v8_webgpu_labeled_resources(const v8_webgpu_labeled_resources&)=delete; + v8_webgpu_labeled_resources& operator=(const v8_webgpu_labeled_resources&)=delete; + ~v8_webgpu_labeled_resources() { + check_scope(); + for (auto& item:entries_) if (item) { + if (!item->wrapper.IsEmpty()) item->wrapper.Get(isolate_)->SetAlignedPointerInInternalField(1,nullptr,v8::kEmbedderDataTypeTagDefault); + item->wrapper.Reset(); + if (!item->published) item->releases->publish(item->ticket); + } + } + // Returns a retained native reference without invoking JavaScript. A later + // descriptor getter may collect the source wrapper; the converted reference + // stays valid until native descriptor consumption. Cross-device validation + // belongs to Dawn, not WebIDL interface conversion. + static bool is_instance(v8::Local value) { + if(!value->IsObject())return false; + auto object=value.As(); + return object->InternalFieldCount()==2 && object->GetInternalField(0)->IsValue() + && object->GetInternalField(0).As()->IsExternal() + && object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)==&brand_; + } + static Native native_reference(v8::Local value) { + if (!value->IsObject()) throw std::invalid_argument("GPU resource object required"); + auto object=value.As(); + if (object->InternalFieldCount()!=2 || !object->GetInternalField(0)->IsValue() + || !object->GetInternalField(0).As()->IsExternal() + || object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_) + throw std::invalid_argument("Incorrect GPU resource interface"); + auto* item=static_cast(object->GetAlignedPointerFromInternalField(1,v8::kEmbedderDataTypeTagDefault)); + if (!item) throw std::invalid_argument("GPU resource realm has been released"); + Native result; + item->service->with_device(item->device,[&](auto& owned) { + Traits::with(owned,item->resource,[&](const auto& native) { result=native; }); + }); + return result; + } + // Caller retains ownership until a non-empty wrapper is returned. + v8::MaybeLocal wrap(v8::Local context,graphics_service& service,resource_handle device,resource_handle resource,v8::Local parent,std::string initial_label={}) { + check_scope(); + if (realm_.Get(isolate_)!=context) throw std::logic_error("GPU resource belongs to another realm"); + if(parent.IsEmpty())throw std::invalid_argument("GPU resource wrapper requires its parent device"); + service.with_device(device,[&](auto& owned) { Traits::with(owned,resource,[](const auto&) {}); }); + for (const auto& item:entries_) if (item && !item->published && item->service==&service + && item->device.table==device.table && item->device.generation==device.generation && item->device.slot==device.slot + && item->resource.table==resource.table && item->resource.generation==resource.generation && item->resource.slot==resource.slot) + throw std::invalid_argument("GPU resource handle is already wrapped"); + auto found=std::find_if(entries_.begin(),entries_.end(),[](const auto& item) { return !item || item->published; }); + if (found==entries_.end()) return {}; + v8::Local wrapper; + if (!instance_.Get(isolate_)->NewInstance(context).ToLocal(&wrapper) + || !wrapper->SetPrototype(context,prototype_.Get(isolate_)).FromMaybe(false)) return {}; + auto item=std::make_unique(); + item->registry=this; + item->label=std::move(initial_label); + item->service=&service; item->device=device;item->resource=resource; item->releases=service.release_endpoint(); + item->device_owner_key.Reset(isolate_,v8::Private::New(isolate_)); + if(!wrapper->SetPrivate(context,item->device_owner_key.Get(isolate_),parent).FromMaybe(false))return {}; + auto ticket=item->releases->reserve(Traits::release(device,resource)); + if (!ticket) { + // Native lifetime tickets can fill before the small JS wrappers + // create enough heap pressure for V8 to collect them naturally. + // Live JS objects remain rooted; weak callbacks only publish. + isolate_->LowMemoryNotification(); + service.drain_completed_releases(); + ticket=item->releases->reserve(Traits::release(device,resource)); + if(!ticket)return {}; + } + item->ticket=*ticket; + wrapper->SetInternalField(0,v8::External::New(isolate_,&brand_,v8::kExternalPointerTypeTagDefault)); + wrapper->SetAlignedPointerInInternalField(1,item.get(),v8::kEmbedderDataTypeTagDefault); + item->wrapper.Reset(isolate_,wrapper); item->wrapper.SetWeak(item.get(),first_pass,v8::WeakCallbackType::kParameter); + *found=std::move(item); + return wrapper; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_limits.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_limits.h new file mode 100644 index 000000000..889f33507 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_limits.h @@ -0,0 +1,57 @@ +#pragma once +#include "webgpu_limit_names.h" +#include +#include +namespace webscene::graphics { +// Immutable values stay in traced JS storage, independent of native resources. +class v8_webgpu_limits { + alignas(void*) static inline char brand_{}; + v8::Isolate* isolate_; + v8::Global realm_; + v8::Global instance_; + v8::Global prototype_; + static void get(const v8::FunctionCallbackInfo& info) { + auto object=info.This(); + if (object->InternalFieldCount()!=2 || !object->GetInternalField(0)->IsValue() + || !object->GetInternalField(0).As()->IsExternal() + || object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_) { + info.GetIsolate()->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8Literal(info.GetIsolate(),"Illegal GPUSupportedLimits receiver")));return; + } + auto index=info.Data().As()->Value(); + v8::Local value; + if (object->GetInternalField(1).As()->Get(info.GetIsolate()->GetCurrentContext(),index).ToLocal(&value)) info.GetReturnValue().Set(value); + } +public: + v8_webgpu_limits(v8::Isolate* isolate,v8::Local context):isolate_(isolate) { + realm_.Reset(isolate,context); + auto instance=v8::ObjectTemplate::New(isolate);instance->SetInternalFieldCount(2);instance_.Reset(isolate,instance); + auto prototype=v8::ObjectTemplate::New(isolate); + for(uint32_t i=0;i(name.data()),v8::NewStringType::kNormal,static_cast(name.size())).ToLocalChecked(); + prototype->SetAccessorProperty(key,v8::FunctionTemplate::New(isolate,get,v8::Integer::NewFromUnsigned(isolate,i))); + } + prototype->Set(v8::Symbol::GetToStringTag(isolate),v8::String::NewFromUtf8Literal(isolate,"GPUSupportedLimits"),static_cast(v8::ReadOnly|v8::DontEnum)); + prototype_.Reset(isolate,prototype->NewInstance(context).ToLocalChecked()); + } + v8_webgpu_limits(const v8_webgpu_limits&)=delete; + v8_webgpu_limits& operator=(const v8_webgpu_limits&)=delete; + template v8::MaybeLocal create(v8::Local context,const Source& source) { + if(v8::Isolate::GetCurrent()!=isolate_ || realm_.Get(isolate_)!=context)throw std::logic_error("Limits belong to another realm"); + if(!source)throw std::invalid_argument("Limits require a native source"); + wgpu::Limits limits{};wgpu::CompatibilityModeLimits compatibility{};limits.nextInChain=&compatibility; + if(source.GetLimits(&limits)!=wgpu::Status::Success)throw std::runtime_error("Native limits query failed"); + auto values=v8::Array::New(isolate_,static_cast(webgpu_limit_names.size())); + for(uint32_t i=0;i(limit.member); + if(value==(wide?UINT64_MAX:UINT32_MAX))throw std::runtime_error("Native limit is unavailable"); + if(!values->Set(context,i,v8::Number::New(isolate_,static_cast(value))).FromMaybe(false))return {}; + } + v8::Local object; + if(!instance_.Get(isolate_)->NewInstance(context).ToLocal(&object) || !object->SetPrototype(context,prototype_.Get(isolate_)).FromMaybe(false))return {}; + object->SetInternalField(0,v8::External::New(isolate_,&brand_,v8::kExternalPointerTypeTagDefault));object->SetInternalField(1,values); + return object; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_map_request.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_map_request.h new file mode 100644 index 000000000..fe80971ee --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_map_request.h @@ -0,0 +1,119 @@ +#pragma once +#include "completion_mailbox.h" +#include +#include + +namespace webscene::graphics { +// Engine-owned asynchronous mapping bridge. Callers perform WebIDL conversion +// and route completion records here. Native callbacks capture no V8 handles. +class v8_webgpu_map_request { + v8::Isolate* isolate_; + const std::thread::id thread_=std::this_thread::get_id(); + resource_owner owner_; + uint64_t operation_; + bool retired_{},started_{}; + wgpu::Buffer buffer_; + v8::Global realm_; + v8::Global wrapper_; + v8::Global resolver_; + v8::Global dom_exception_; + void check_scope() const { + if (std::this_thread::get_id()!=thread_ || v8::Isolate::GetCurrent()!=isolate_) + throw std::logic_error("Map promise requires its owning isolate scope"); + } + bool reject(const char* name) { + auto context=realm_.Get(isolate_); + auto resolver=resolver_.Get(isolate_); + // Settle ownership before calling the exception factory. Even a damaged + // factory must not leave this request pending or permit recursive cancel. + [[maybe_unused]] auto keep_alive=wrapper_.Get(isolate_); + resolver_.Reset(); wrapper_.Reset(); + v8::Local args[]{v8::String::NewFromUtf8Literal(isolate_,"Buffer mapping failed"), + v8::String::NewFromUtf8(isolate_,name).ToLocalChecked()}; + v8::Local reason; + { + v8::TryCatch caught(isolate_); + v8::Local exception; + if (dom_exception_.Get(isolate_)->NewInstance(context,2,args).ToLocal(&exception)) reason=exception; + else { + if (caught.HasTerminated()) return false; + if (caught.HasCaught()) reason=caught.Exception(); + } + } + if (reason.IsEmpty()) reason=v8::Exception::Error(v8::String::NewFromUtf8Literal(isolate_,"Mapping exception construction failed")); + return resolver->Reject(context,reason).FromMaybe(false); + } + bool reject_allocation() { + auto resolver=resolver_.Get(isolate_); + resolver_.Reset(); wrapper_.Reset(); + return resolver->Reject(realm_.Get(isolate_),v8::Exception::RangeError( + v8::String::NewFromUtf8Literal(isolate_,"Buffer mapping allocation failed"))).FromMaybe(false); + } +public: + v8_webgpu_map_request(v8::Isolate* isolate,v8::Local context, + v8::Local wrapper,v8::Local dom_exception, + wgpu::Buffer buffer,resource_owner owner,uint64_t operation) + :isolate_(isolate),owner_(owner),operation_(operation),buffer_(std::move(buffer)) { + check_scope(); + if (!buffer_ || wrapper.IsEmpty() || dom_exception.IsEmpty() || !operation) + throw std::invalid_argument("Map request lacks ownership"); + realm_.Reset(isolate,context); wrapper_.Reset(isolate,wrapper); dom_exception_.Reset(isolate,dom_exception); + } + v8_webgpu_map_request(const v8_webgpu_map_request&)=delete; + v8_webgpu_map_request& operator=(const v8_webgpu_map_request&)=delete; + ~v8_webgpu_map_request() { + check_scope(); + // Realm teardown must abort the native mapping before dropping its V8 + // references. A canceled/settled old request never unmaps a newer map. + if (!resolver_.IsEmpty()) buffer_.Unmap(); + } + bool pending() const { check_scope(); return !resolver_.IsEmpty(); } + v8::MaybeLocal start(std::shared_ptr mailbox, + wgpu::MapMode mode,uint64_t offset,uint64_t size) { + check_scope(); + if (!mailbox) throw std::invalid_argument("Map completion mailbox is required"); + if (started_ || retired_) throw std::logic_error("Map request already started"); + auto context=realm_.Get(isolate_); + if (isolate_->GetCurrentContext()!=context) throw std::logic_error("Map request belongs to another realm"); + started_=true; + v8::Local resolver; + if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return {}; + auto promise=resolver->GetPromise(); resolver_.Reset(isolate_,resolver); + auto ticket=mailbox->reserve(operation_,owner_); + if (!ticket) { retired_=true; reject("OperationError"); return promise; } + buffer_.MapAsync(mode,offset,size,wgpu::CallbackMode::AllowSpontaneous, + [mailbox,ticket=*ticket,buffer=buffer_](wgpu::MapAsyncStatus status,wgpu::StringView) { + mailbox->publish(ticket,status==wgpu::MapAsyncStatus::Success ? completion_status::success + : status==wgpu::MapAsyncStatus::Error ? completion_status::failed : completion_status::cancelled); + }); + return promise; + } + bool cancel() { + check_scope(); + if (resolver_.IsEmpty()) return false; + if (isolate_->GetCurrentContext()!=realm_.Get(isolate_)) throw std::logic_error("Map cancellation belongs to another realm"); + buffer_.Unmap(); + return reject("AbortError"); + } + // Attach the selected mapping to its wrapper before resolving. The caller + // owns range offsets/mode and must use GetConstMappedRange for READ mappings. + template bool complete(completion_record record,Attach attach) { + check_scope(); + if (record.operation!=operation_ || record.owner!=owner_ || retired_ || !started_) return false; + if (isolate_->GetCurrentContext()!=realm_.Get(isolate_)) throw std::logic_error("Map completion belongs to another realm"); + retired_=true; + if (resolver_.IsEmpty()) return true; // Late callback after cancellation. + if (record.status!=completion_status::success) + return reject(record.status==completion_status::failed ? "OperationError" : "AbortError"); + auto context=realm_.Get(isolate_); + if (isolate_->GetCurrentContext()!=context) throw std::logic_error("Map completion belongs to another realm"); + try { attach(buffer_,wrapper_.Get(isolate_)); } + catch (const std::bad_alloc&) { buffer_.Unmap(); return reject_allocation(); } + catch (const std::length_error&) { buffer_.Unmap(); return reject_allocation(); } + catch (const std::exception&) { buffer_.Unmap(); return reject("OperationError"); } + auto resolver=resolver_.Get(isolate_); + resolver_.Reset(); wrapper_.Reset(); + return resolver->Resolve(context,v8::Undefined(isolate_)).FromMaybe(false); + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_mapped_ranges.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_mapped_ranges.h new file mode 100644 index 000000000..eda738951 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_mapped_ranges.h @@ -0,0 +1,81 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace webscene::graphics { +// Engine/realm-owned views of an already mapped native buffer. Destroy/detach +// this object BEFORE unmapping or destroying native storage. The owner wrapper +// must in turn retain that storage; each reachable ArrayBuffer keeps it alive. +class v8_webgpu_mapped_ranges { + struct range { uint64_t offset,length; v8::Global view; }; + v8::Isolate* isolate_; + const std::thread::id thread_=std::this_thread::get_id(); + v8::Global realm_; + v8::Global owner_; + v8::Global owner_key_; + v8::Global detach_key_; + uint8_t* data_; + uint64_t start_,length_; + bool detached_{}; + std::vector> ranges_; + void check_scope() const { + if (std::this_thread::get_id()!=thread_ || v8::Isolate::GetCurrent()!=isolate_) + throw std::logic_error("Mapped ranges require the owning isolate scope"); + } +public: + v8_webgpu_mapped_ranges(v8::Isolate* isolate,v8::Local context, + v8::Local owner,void* data,uint64_t start,uint64_t length) + :isolate_(isolate),data_(static_cast(data)),start_(start),length_(length) { + check_scope(); + if (owner.IsEmpty() || (!data && length) || length>SIZE_MAX || start>UINT64_MAX-length) + throw std::invalid_argument("Invalid native mapped region"); + realm_.Reset(isolate,context); + owner_.Reset(isolate,owner); owner_.SetWeak(); + owner_key_.Reset(isolate,v8::Private::New(isolate)); + detach_key_.Reset(isolate,v8::Object::New(isolate)); + } + v8_webgpu_mapped_ranges(const v8_webgpu_mapped_ranges&)=delete; + v8_webgpu_mapped_ranges& operator=(const v8_webgpu_mapped_ranges&)=delete; + ~v8_webgpu_mapped_ranges() { detach(); } + v8::MaybeLocal create(v8::Local context,uint64_t offset,uint64_t length) { + check_scope(); + if (realm_.Get(isolate_)!=context) throw std::logic_error("Mapped range belongs to another realm"); + if (detached_ || owner_.IsEmpty() || offset%8 || length%4 || offsetlength_ || length>length_-(offset-start_)) + throw std::invalid_argument("Invalid mapped buffer range"); + // Even collected views reserve their ranges until unmap. Empty ranges + // occupy no bytes and therefore never overlap another range. + for (const auto& prior:ranges_) if (length && prior->length + && offsetoffset+prior->length && prior->offset(); item->offset=offset; item->length=length; + if (ranges_.size()==ranges_.capacity()) + ranges_.reserve(ranges_.empty() ? 8 : ranges_.size()*2); + auto backing=v8::ArrayBuffer::NewBackingStore(length ? data_+(offset-start_) : nullptr, + static_cast(length),[](void*,size_t,void*) {},nullptr); + auto view=v8::ArrayBuffer::New(isolate_,std::move(backing)); + if (!view->SetPrivate(context,owner_key_.Get(isolate_),owner_.Get(isolate_)).FromMaybe(false)) return {}; + view->SetDetachKey(detach_key_.Get(isolate_)); + item->view.Reset(isolate_,view); item->view.SetWeak(); + ranges_.push_back(std::move(item)); + return view; + } + void detach() { + check_scope(); + if (detached_) return; + for (auto& item:ranges_) if (!item->view.IsEmpty()) { + auto view=item->view.Get(isolate_); + if (!view->WasDetached() && !view->Detach(detach_key_.Get(isolate_)).FromMaybe(false)) + throw std::logic_error("Mapped ArrayBuffer detachment failed"); + if (!view->DeletePrivate(realm_.Get(isolate_),owner_key_.Get(isolate_)).FromMaybe(false)) + throw std::logic_error("Mapped ArrayBuffer owner release failed"); + item->view.Reset(); + } + ranges_.clear(); detached_=true; data_=nullptr; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_object_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_object_descriptor.h new file mode 100644 index 000000000..42718bd65 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_object_descriptor.h @@ -0,0 +1,14 @@ +#pragma once +#include "v8_webgpu_render_state.h" +#include +namespace webscene::graphics { +inline bool read_webgpu_object_label(v8::Isolate* isolate,v8::Local context,v8::Local input,std::string& output) { + webgpu_state_reader reader(isolate,context,input);v8::Local value; + if(!reader.get("label",value))return false;std::string converted; + if(!value->IsUndefined()) { + v8::Local text;if(!value->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false;converted.assign(*bytes,bytes.length()); + } + output=std::move(converted);return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_pipeline_layout_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_pipeline_layout_descriptor.h new file mode 100644 index 000000000..3f661c036 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_pipeline_layout_descriptor.h @@ -0,0 +1,34 @@ +#pragma once +#include "v8_webgpu_vertex_state.h" +#include "v8_webgpu_bind_group_layouts.h" +namespace webscene::graphics { +struct webgpu_pipeline_layout_descriptor { + std::string label; + std::vector layouts; + uint32_t immediate_size{}; + template void with_native(Execute execute)const { + wgpu::PipelineLayoutDescriptor descriptor{}; + descriptor.label=wgpu::StringView(label.data(),label.size()); + descriptor.bindGroupLayoutCount=layouts.size();descriptor.bindGroupLayouts=layouts.data(); + descriptor.immediateSize=immediate_size;execute(descriptor); + } +}; +inline bool read_webgpu_pipeline_layout_descriptor(v8::Isolate* isolate,v8::Local context, + v8::Local input,webgpu_pipeline_layout_descriptor& output) { + webgpu_state_reader reader(isolate,context,input);webgpu_pipeline_layout_descriptor converted; + v8::Local value; + if(!reader.get("label",value))return false; + if(!value->IsUndefined()) { + v8::Local text;if(!value->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false; + converted.label.assign(*bytes,bytes.length()); + } + if(!reader.get("bindGroupLayouts",value)||!read_webgpu_sequence(isolate,context,value,[&](auto item){ + if(item->IsNullOrUndefined()){converted.layouts.emplace_back();return true;} + if(!v8_webgpu_bind_group_layouts::is_instance(item))return reader.fail("Expected a nullable GPUBindGroupLayout"); + converted.layouts.push_back(v8_webgpu_bind_group_layouts::native_reference(item));return true; + }))return false; + if(!reader.uint32("immediateSize",converted.immediate_size))return false; + output=std::move(converted);return true; +} +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_pipeline_layouts.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_pipeline_layouts.h new file mode 100644 index 000000000..0f23fb701 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_pipeline_layouts.h @@ -0,0 +1,15 @@ +#pragma once +#include "v8_webgpu_labeled_resources.h" +namespace webscene::graphics { +struct v8_webgpu_pipeline_layouts_traits { + using native_type=wgpu::PipelineLayout; + static constexpr const char* name="GPUPipelineLayout"; + template static void with(dawn_device& device,resource_handle handle,Execute execute) { + device.with_pipeline_layout(handle,std::move(execute)); + } + static graphics_command release(resource_handle device,resource_handle handle) noexcept { + return graphics_service::deferred_pipeline_layout_release(device,handle); + } +}; +using v8_webgpu_pipeline_layouts=v8_webgpu_labeled_resources; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_pipeline_resources.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_pipeline_resources.h new file mode 100644 index 000000000..74b10885c --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_pipeline_resources.h @@ -0,0 +1,41 @@ +#pragma once +#include "v8_webgpu_labeled_resources.h" +#include "v8_webgpu_bind_group_layouts.h" +namespace webscene::graphics { +template class v8_webgpu_pipeline_resources : public v8_webgpu_labeled_resources { + using base=v8_webgpu_labeled_resources; + v8_webgpu_bind_group_layouts layouts_; + static void get_layout(const v8::FunctionCallbackInfo& info) { + auto* item=base::receiver(info);if(!item)return; + auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(!info.Length()){base::fail(isolate,"getBindGroupLayout requires an index");return;} + v8::Local number;if(!info[0]->ToNumber(context).ToLocal(&number))return; + auto n=std::trunc(number->Value()); + if(!std::isfinite(n)||n<0||n>UINT32_MAX){base::fail(isolate,"Invalid bind group index");return;} + item=base::receiver(info);if(!item)return; + auto* registry=static_cast(item->registry); + try { + resource_handle layout; + item->service->with_device(item->device,[&](auto& device){ + Traits::with(device,item->resource,[&](const auto& pipeline){ + layout=device.adopt_bind_group_layout(pipeline.GetBindGroupLayout(static_cast(n))); + }); + }); + v8::Local result; + try { + if(!registry->layouts_.wrap(context,*item->service,item->device,layout,info.This(),"").ToLocal(&result)){ + item->service->with_device(item->device,[&](auto& device){device.release_bind_group_layout(layout);}); + base::fail(isolate,"Bind group layout capacity exhausted");return; + } + }catch(...){item->service->with_device(item->device,[&](auto& device){device.release_bind_group_layout(layout);});throw;} + info.GetReturnValue().Set(result); + }catch(const std::exception&){base::fail(isolate,"Pipeline layout ownership unavailable");} + } +public: + v8_webgpu_pipeline_resources(v8::Isolate* isolate,v8::Local context,size_t capacity=1024) + :base(isolate,context,capacity),layouts_(isolate,context,capacity){ + this->prototype_.Get(isolate)->Set(context,v8::String::NewFromUtf8Literal(isolate,"getBindGroupLayout"), + v8::Function::New(context,get_layout,{},1).ToLocalChecked()).Check(); + } +}; +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_programmable_stage.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_programmable_stage.h new file mode 100644 index 000000000..cc100ee28 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_programmable_stage.h @@ -0,0 +1,71 @@ +#pragma once +#include "v8_webgpu_shaders.h" +#include +#include +namespace webscene::graphics { +struct webgpu_programmable_stage { + wgpu::ShaderModule module; + std::optional entry_point; + std::vector> constants; + // The returned entries borrow key storage. Keep this descriptor alive and + // unchanged until Dawn has consumed the native pipeline descriptor. + std::vector native_constants() const & { + std::vector result;result.reserve(constants.size()); + for(const auto& [key,value]:constants) { + wgpu::ConstantEntry entry{};entry.key=wgpu::StringView(key.data(),key.size());entry.value=value; + result.push_back(entry); + } + return result; + } + std::vector native_constants() const &&=delete; +}; +// Converts inherited GPUProgrammableStage members in WebIDL name order. Derived +// vertex buffers / fragment targets must be converted after this function. +inline bool read_webgpu_programmable_stage(v8::Isolate* isolate,v8::Local context, + v8::Local input,webgpu_programmable_stage& output) { + const auto key=[&](const char* text){return v8::String::NewFromUtf8(isolate,text).ToLocalChecked();}; + const auto fail=[&](const char* text){isolate->ThrowException(v8::Exception::TypeError(key(text)));return false;}; + if(!input->IsNullOrUndefined() && !input->IsObject())return fail("Programmable stage must be a dictionary"); + const auto get=[&](const char* name,v8::Local& value) { + if(input->IsNullOrUndefined()){value=v8::Undefined(isolate);return true;} + return input.As()->Get(context,key(name)).ToLocal(&value); + }; + const auto string=[&](v8::Local value,std::string& result) { + v8::Local text;if(!value->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false; + result.assign(*bytes,bytes.length());return true; + }; + webgpu_programmable_stage converted; + v8::Local value; + if(!get("constants",value))return false; + if(!value->IsUndefined()) { + if(!value->IsObject())return fail("Pipeline constants must be a record object"); + auto record=value.As();v8::Local keys; + if(!record->GetOwnPropertyNames(context,v8::ALL_PROPERTIES,v8::KeyConversionMode::kConvertToString).ToLocal(&keys))return false; + for(uint32_t i=0;iLength();++i) { + v8::Local property,descriptor,enumerable,constant; + if(!keys->Get(context,i).ToLocal(&property))return false; + if(!record->GetOwnPropertyDescriptor(context,property.As()).ToLocal(&descriptor))return false; + if(descriptor->IsUndefined())continue; + if(!descriptor.As()->Get(context,key("enumerable")).ToLocal(&enumerable))return false; + if(!enumerable->BooleanValue(isolate))continue; + std::string name;if(!string(property,name))return false; + if(!record->Get(context,property).ToLocal(&constant))return false; + v8::Local number;if(!constant->ToNumber(context).ToLocal(&number))return false; + if(!std::isfinite(number->Value()))return fail("Pipeline constants must be finite doubles"); + // USVString conversion can collapse distinct UTF-16 property names. + auto existing=std::find_if(converted.constants.begin(),converted.constants.end(),[&](const auto& item){return item.first==name;}); + if(existing==converted.constants.end())converted.constants.emplace_back(std::move(name),number->Value()); + else existing->second=number->Value(); + } + } + if(!get("entryPoint",value))return false; + if(!value->IsUndefined()) { + std::string name;if(!string(value,name))return false;converted.entry_point=std::move(name); + } + if(!get("module",value))return false; + try {converted.module=v8_webgpu_shaders::native_reference(value);} + catch(const std::exception&) {return fail("Programmable stage requires a live GPUShaderModule");} + output=std::move(converted);return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_queue.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_queue.h new file mode 100644 index 000000000..1e0e254c3 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_queue.h @@ -0,0 +1,194 @@ +#pragma once +#include "v8_webgpu_command_buffers.h" +#include "v8_webgpu_buffers.h" +#include "v8_webgpu_copy_descriptor.h" +#include +#include "v8_webgpu_vertex_state.h" +namespace webscene::graphics { +// One queue per internal device wrapper. The queue's traced parent edge keeps +// the device alive; this controller owns no independent native GPU reference. +class v8_webgpu_queue { + alignas(void*) static inline char brand_{}; + const std::thread::id thread_=std::this_thread::get_id(); + v8::Isolate* isolate_;graphics_service& service_;resource_handle device_; + v8::Global wrapper_;v8::Global parent_key_; + std::string label_; + v8::Global dom_exception_; + void check_scope()const{if(std::this_thread::get_id()!=thread_||v8::Isolate::GetCurrent()!=isolate_)throw std::logic_error("GPUQueue requires its owning isolate scope");} + static void fail(v8::Isolate* isolate,const char* text){isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate,text).ToLocalChecked()));} + static v8_webgpu_queue* receiver(const v8::FunctionCallbackInfo& info) { + auto object=info.This(); + if(object->InternalFieldCount()!=2||!object->GetInternalField(0)->IsValue()||!object->GetInternalField(0).As()->IsExternal() + ||object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_){fail(info.GetIsolate(),"Illegal GPUQueue receiver");return nullptr;} + auto* self=static_cast(object->GetAlignedPointerFromInternalField(1,v8::kEmbedderDataTypeTagDefault)); + if(!self)fail(info.GetIsolate(),"GPUQueue realm has been released");return self; + } + static bool size64(v8::Isolate* isolate,v8::Local context,v8::Local input,uint64_t& result) { + v8::Local number;if(!input->ToNumber(context).ToLocal(&number))return false; + double value=std::trunc(number->Value()); + if(!std::isfinite(value)||value<0||value>9007199254740991.0){fail(isolate,"GPUSize64 is outside the safe integer range");return false;} + result=static_cast(value);return true; + } + struct source { + std::shared_ptr backing; + size_t offset{},length{},element_size=1; + }; + static bool buffer_source(v8::Isolate* isolate,v8::Local value,source& result) { + v8::Local buffer=value; + if(value->IsArrayBufferView())buffer=value.As()->Buffer(); + if(buffer->IsSharedArrayBuffer())result.backing=buffer.As()->GetBackingStore(); + else if(buffer->IsArrayBuffer()) { + if(buffer.As()->WasDetached()){fail(isolate,"Buffer source is detached");return false;} + result.backing=buffer.As()->GetBackingStore(); + }else{fail(isolate,"Expected an AllowSharedBufferSource");return false;} + if(result.backing->IsResizableByUserJavaScript()){fail(isolate,"Resizable buffer source is not allowed");return false;} + result.length=result.backing->ByteLength(); + if(value->IsArrayBufferView()) { + auto view=value.As();result.offset=view->ByteOffset();result.length=view->ByteLength(); + } + if(value->IsInt16Array()||value->IsUint16Array()||value->IsFloat16Array())result.element_size=2; + else if(value->IsInt32Array()||value->IsUint32Array()||value->IsFloat32Array())result.element_size=4; + else if(value->IsFloat64Array()||value->IsBigInt64Array()||value->IsBigUint64Array())result.element_size=8; + return true; + } + void operation_error(v8::Local context) { + v8::Local args[]{v8::String::NewFromUtf8Literal(isolate_,"writeBuffer source range is invalid"), + v8::String::NewFromUtf8Literal(isolate_,"OperationError")}; + v8::Local error; + if(dom_exception_.Get(isolate_)->NewInstance(context,2,args).ToLocal(&error))isolate_->ThrowException(error); + } + static void write_buffer(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(info.Length()<3){fail(isolate,"writeBuffer requires buffer, bufferOffset and data");return;} + try { + auto buffer=v8_webgpu_buffers::native_reference(info[0]); + uint64_t destination=0,offset=0,count=0; + if(!size64(isolate,context,info[1],destination))return; + source data;if(!buffer_source(isolate,info[2],data))return; + if(!info[3]->IsUndefined()&&!size64(isolate,context,info[3],offset))return; + bool has_count=!info[4]->IsUndefined(); + if(has_count&&!size64(isolate,context,info[4],count))return; + // Numeric conversions can execute JS, detach data or retire a realm. + data={};if(!buffer_source(isolate,info[2],data))return; + auto* self=receiver(info);if(!self)return; + uint64_t elements=data.length/data.element_size; + if(offset>elements){self->operation_error(context);return;} + if(!has_count)count=elements-offset; + if(count>elements-offset||(count*data.element_size)%4){self->operation_error(context);return;} + const size_t bytes=static_cast(count)*data.element_size; + const size_t start=data.offset+static_cast(offset)*data.element_size; + auto* base=static_cast(data.backing->Data()); + const void* contents=bytes?base+start:nullptr; + std::vector shared_copy; + if(data.backing->IsShared()&&bytes) { + shared_copy.resize(bytes); + for(size_t i=0;i(base[start+i]).load(std::memory_order_relaxed); + contents=shared_copy.data(); + } + self->service_.with_device(self->device_,[&](auto& owned){owned.native().GetQueue().WriteBuffer(buffer,destination,contents,bytes);}); + }catch(const std::bad_alloc&){isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate,"writeBuffer allocation failed")));} + catch(const std::exception&){fail(isolate,"writeBuffer native ownership unavailable");} + } + static void write_texture(const v8::FunctionCallbackInfo& info){ + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(info.Length()<4){fail(isolate,"writeTexture requires destination, data, layout and size");return;} + try{ + wgpu::TexelCopyTextureInfo dest{};wgpu::TexelCopyBufferLayout layout{};wgpu::Extent3D extent{}; + if(!read_copy_texture(isolate,context,info[0],dest)||!read_copy_layout(isolate,context,info[2],layout)||!read_copy_extent(isolate,context,info[3],extent))return; + source data;if(!buffer_source(isolate,info[1],data))return; + auto* self=receiver(info);if(!self)return; + auto* bytes=static_cast(data.backing->Data());if(data.offset)bytes+=data.offset; + std::vector shared; + if(data.backing->IsShared()){shared.resize(data.length);for(size_t i=0;i(bytes[i]).load(std::memory_order_relaxed);bytes=shared.data();} + self->service_.with_device(self->device_,[&](auto& device){device.native().GetQueue().WriteTexture(&dest,bytes,data.length,&layout,&extent);}); + }catch(const std::exception&){fail(isolate,"writeTexture ownership unavailable");} + } + struct work_request{ + uint64_t operation; + std::shared_ptr mailbox; + v8::Global context; + v8::Global queue; + v8::Global resolver; + }; + resource_owner work_owner_{new_owner_token(),new_owner_token(),new_owner_token()}; + std::vector> work_; + static void work_done(const v8::FunctionCallbackInfo& info){ + auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + v8::Local resolver;if(!v8::Promise::Resolver::New(context).ToLocal(&resolver))return; + info.GetReturnValue().Set(resolver->GetPromise());v8::TryCatch caught(isolate); + auto* self=receiver(info); + if(!self){auto error=caught.Exception();caught.Reset();resolver->Reject(context,error).FromMaybe(false);return;} + try{ + if(self->work_.size()>=1024)throw std::length_error("Queue work request capacity exhausted"); + auto p=std::make_unique();p->operation=new_owner_token();p->mailbox=self->service_.dawn().completions(); + p->context.Reset(isolate,context);p->queue.Reset(isolate,info.This());p->resolver.Reset(isolate,resolver); + self->work_.reserve(self->work_.size()+1); + auto ticket=p->mailbox->reserve(p->operation,self->work_owner_);if(!ticket)throw std::length_error("Queue work completion capacity exhausted"); + auto mailbox=p->mailbox;self->work_.push_back(std::move(p)); + try { + self->service_.with_device(self->device_,[&](auto& device){ + device.native().GetQueue().OnSubmittedWorkDone(wgpu::CallbackMode::AllowSpontaneous, + [mailbox,ticket=*ticket](wgpu::QueueWorkDoneStatus status,wgpu::StringView){ + mailbox->publish(ticket,status==wgpu::QueueWorkDoneStatus::Success?completion_status::success:completion_status::failed); + }); + }); + } catch(...) { + mailbox->publish(*ticket,completion_status::failed); + throw; + } + }catch(const std::exception& e){ + caught.Reset();resolver->Reject(context,v8::Exception::Error(v8::String::NewFromUtf8(isolate,e.what()).ToLocalChecked())).FromMaybe(false); + } + } + static void submit(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + try { + std::vector commands; + if(!read_webgpu_sequence(isolate,context,info[0],[&](auto value){commands.push_back(v8_webgpu_command_buffers::native_reference(value));return true;}))return; + auto* self=receiver(info);if(!self)return; + self->service_.with_device(self->device_,[&](auto& owned){owned.native().GetQueue().Submit(commands.size(),commands.data());}); + }catch(const std::exception&){fail(isolate,"GPUQueue submit requires live command buffers and device ownership");} + } + static void label(const v8::FunctionCallbackInfo& info) { + auto* self=receiver(info);if(!self)return;v8::Local value; + if(v8::String::NewFromUtf8(info.GetIsolate(),self->label_.data(),v8::NewStringType::kNormal,static_cast(self->label_.size())).ToLocal(&value))info.GetReturnValue().Set(value); + } + static void set_label(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();v8::Local text; + if(!info[0]->ToString(isolate->GetCurrentContext()).ToLocal(&text))return;v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return; + auto* self=receiver(info);if(!self)return; + try{std::string value(*bytes,bytes.length());self->service_.with_device(self->device_,[&](auto& owned){owned.native().GetQueue().SetLabel(wgpu::StringView(value.data(),value.size()));});self->label_=std::move(value);} + catch(const std::exception&){fail(isolate,"GPUQueue label ownership unavailable");} + } +public: + bool complete(completion_record record){ + check_scope();if(record.owner!=work_owner_)return false; + auto found=std::find_if(work_.begin(),work_.end(),[&](auto& p){return p->operation==record.operation;}); + if(found==work_.end())return true; + auto p=std::move(*found);work_.erase(found);auto context=p->context.Get(isolate_);v8::Context::Scope scope(context); + if(record.status==completion_status::success)p->resolver.Get(isolate_)->Resolve(context,v8::Undefined(isolate_)).FromMaybe(false); + else p->resolver.Get(isolate_)->Reject(context,v8::Exception::Error(v8::String::NewFromUtf8Literal(isolate_,"GPU queue work failed"))).FromMaybe(false); + return true; + } + v8_webgpu_queue(v8::Isolate* isolate,graphics_service& service,resource_handle device,std::string label,v8::Local dom_exception) + :isolate_(isolate),service_(service),device_(device),label_(std::move(label)){check_scope();dom_exception_.Reset(isolate,dom_exception);} + ~v8_webgpu_queue(){for(auto& p:work_)p->mailbox->cancel_owner(work_owner_);check_scope();if(!wrapper_.IsEmpty())wrapper_.Get(isolate_)->SetAlignedPointerInInternalField(1,nullptr,v8::kEmbedderDataTypeTagDefault);wrapper_.Reset();} + v8::MaybeLocal create(v8::Local context,v8::Local parent) { + check_scope(); + auto instance=v8::ObjectTemplate::New(isolate_);instance->SetInternalFieldCount(2); + auto prototype=v8::ObjectTemplate::New(isolate_);auto submit_fn=v8::FunctionTemplate::New(isolate_,submit);submit_fn->SetLength(1); + prototype->Set(isolate_,"submit",submit_fn); + prototype->Set(isolate_,"onSubmittedWorkDone",v8::FunctionTemplate::New(isolate_,work_done)); + prototype->Set(isolate_,"writeTexture",v8::FunctionTemplate::New(isolate_,write_texture)); + auto write=v8::FunctionTemplate::New(isolate_,write_buffer);write->SetLength(3);prototype->Set(isolate_,"writeBuffer",write); + prototype->Set(v8::Symbol::GetToStringTag(isolate_),v8::String::NewFromUtf8Literal(isolate_,"GPUQueue"),static_cast(v8::ReadOnly|v8::DontEnum)); + prototype->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate_,"label"),v8::FunctionTemplate::New(isolate_,label),v8::FunctionTemplate::New(isolate_,set_label)); + v8::Local object,proto; + if(!instance->NewInstance(context).ToLocal(&object)||!prototype->NewInstance(context).ToLocal(&proto)||!object->SetPrototype(context,proto).FromMaybe(false))return {}; + parent_key_.Reset(isolate_,v8::Private::New(isolate_));if(!object->SetPrivate(context,parent_key_.Get(isolate_),parent).FromMaybe(false))return {}; + object->SetInternalField(0,v8::External::New(isolate_,&brand_,v8::kExternalPointerTypeTagDefault));object->SetAlignedPointerInInternalField(1,this,v8::kEmbedderDataTypeTagDefault); + wrapper_.Reset(isolate_,object);wrapper_.SetWeak(this,[](const v8::WeakCallbackInfo& info){info.GetParameter()->wrapper_.Reset();},v8::WeakCallbackType::kParameter); + return object; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_realm.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_realm.h new file mode 100644 index 000000000..705d4e1c9 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_realm.h @@ -0,0 +1,28 @@ +#pragma once +#include "v8_webgpu_discovery.h" +namespace webscene::graphics { +// One ownership/dispatch unit per document realm. The caller decides secure +// context exposure and negotiated interop before installing object() on Navigator. +// Canvas controllers must be destroyed first, and the service must outlive this. +class v8_webgpu_realm final { + v8_webgpu_devices devices_; + v8_webgpu_adapters adapters_; + v8_webgpu_discovery discovery_; +public: + v8_webgpu_realm(v8::Isolate* isolate,v8::Local context, + graphics_service& service,v8::Local dom_exception, + webgpu_canvas_interop interop=webgpu_canvas_interop::none, + wgpu::BackendType backend=wgpu::BackendType::Undefined, + wgpu::TextureFormat preferred_format=wgpu::TextureFormat::BGRA8Unorm, + v8::Local event_target={},std::function)> initialize_event_target={}) + :devices_(isolate,context,dom_exception,64,1024,event_target,std::move(initialize_event_target)), + adapters_(isolate,context,devices_,dom_exception,64,interop), + discovery_(isolate,context,service,adapters_,backend,preferred_format) {} + v8_webgpu_realm(const v8_webgpu_realm&)=delete; + v8_webgpu_realm& operator=(const v8_webgpu_realm&)=delete; + v8::Local object()const {return discovery_.object();} + bool complete(completion_record record) { + return discovery_.complete(record)||adapters_.complete(record)||devices_.complete(record); + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_descriptor.h new file mode 100644 index 000000000..1f27f9bcb --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_descriptor.h @@ -0,0 +1,71 @@ +#pragma once +#include "v8_webgpu_vertex_state.h" +namespace webscene::graphics { +struct webgpu_fragment_state { + webgpu_programmable_stage stage; + std::vector> targets; +}; +struct webgpu_render_descriptor { + std::string label; + wgpu::PipelineLayout layout; + std::optional depth; + std::optional fragment; + wgpu::MultisampleState multisample; + wgpu::PrimitiveState primitive; + webgpu_vertex_state vertex; + // All borrowed strings, nested arrays and blend pointers stay alive for + // this synchronous native call. Native APIs retain their own objects. + template void with_native(Execute execute) const & { + wgpu::RenderPipelineDescriptor descriptor{}; + descriptor.label=wgpu::StringView(label.data(),label.size());descriptor.layout=layout; + descriptor.depthStencil=depth?&*depth:nullptr;descriptor.multisample=multisample;descriptor.primitive=primitive; + auto vertex_constants=vertex.stage.native_constants(); + descriptor.vertex.module=vertex.stage.module; + if(vertex.stage.entry_point)descriptor.vertex.entryPoint=wgpu::StringView(vertex.stage.entry_point->data(),vertex.stage.entry_point->size()); + descriptor.vertex.constantCount=vertex_constants.size();descriptor.vertex.constants=vertex_constants.data(); + std::vector buffers;buffers.reserve(vertex.buffers.size()); + for(const auto& buffer:vertex.buffers)buffers.push_back(buffer?buffer->native():wgpu::VertexBufferLayout{}); + descriptor.vertex.bufferCount=buffers.size();descriptor.vertex.buffers=buffers.data(); + wgpu::FragmentState native_fragment{};std::vector fragment_constants;std::vector targets; + if(fragment) { + fragment_constants=fragment->stage.native_constants();native_fragment.module=fragment->stage.module; + if(fragment->stage.entry_point)native_fragment.entryPoint=wgpu::StringView(fragment->stage.entry_point->data(),fragment->stage.entry_point->size()); + native_fragment.constantCount=fragment_constants.size();native_fragment.constants=fragment_constants.data(); + targets.reserve(fragment->targets.size());for(const auto& target:fragment->targets)targets.push_back(target?target->native():wgpu::ColorTargetState{}); + native_fragment.targetCount=targets.size();native_fragment.targets=targets.data();descriptor.fragment=&native_fragment; + } + execute(descriptor); + } +}; +// ResolveLayout must recognize native-backed GPUPipelineLayout objects without +// user code. An empty optional takes the enum branch of the WebIDL union. +template bool read_webgpu_render_descriptor(v8::Isolate* isolate,v8::Local context,v8::Local input,webgpu_render_descriptor& output,ResolveLayout resolve_layout) { + webgpu_state_reader reader(isolate,context,input);webgpu_render_descriptor converted;v8::Local value; + const auto string=[&](v8::Local input,std::string& output) { + v8::Local text;if(!input->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false;output.assign(*bytes,bytes.length());return true; + }; + if(!reader.get("label",value))return false;if(!value->IsUndefined()&&!string(value,converted.label))return false; + if(!reader.get("layout",value))return false;if(value->IsUndefined())return reader.fail("Pipeline layout is required"); + auto layout=value->IsObject()?resolve_layout(value):std::optional{}; + if(layout) {if(!*layout)return reader.fail("Pipeline layout ownership unavailable");converted.layout=std::move(*layout);} + else {std::string automatic;if(!string(value,automatic))return false;if(automatic!="auto")return reader.fail("Invalid GPUAutoLayoutMode");} + if(!reader.get("depthStencil",value))return false; + if(!value->IsUndefined()) {wgpu::DepthStencilState depth;if(!read_webgpu_depth_stencil(isolate,context,value,depth))return false;converted.depth=depth;} + if(!reader.get("fragment",value))return false; + if(!value->IsUndefined()) { + webgpu_fragment_state fragment;if(!read_webgpu_programmable_stage(isolate,context,value,fragment.stage))return false; + webgpu_state_reader fragment_reader(isolate,context,value);v8::Local targets; + if(!fragment_reader.get("targets",targets)||!read_webgpu_sequence(isolate,context,targets,[&](auto target) { + if(target->IsNullOrUndefined()){fragment.targets.emplace_back(std::nullopt);return true;} + webgpu_color_target color;if(!read_webgpu_color_target(isolate,context,target,color))return false; + fragment.targets.emplace_back(std::move(color));return true; + }))return false; + converted.fragment=std::move(fragment); + } + if(!reader.get("multisample",value)||!read_webgpu_multisample_state(isolate,context,value,converted.multisample))return false; + if(!reader.get("primitive",value)||!read_webgpu_primitive_state(isolate,context,value,converted.primitive))return false; + if(!reader.get("vertex",value)||!read_webgpu_vertex_state(isolate,context,value,converted.vertex))return false; + output=std::move(converted);return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_pass_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_pass_descriptor.h new file mode 100644 index 000000000..0a8418d3e --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_pass_descriptor.h @@ -0,0 +1,99 @@ +#pragma once +#include "v8_webgpu_textures.h" +#include "v8_webgpu_object_descriptor.h" +namespace webscene::graphics { +struct webgpu_attachment_view { + wgpu::Texture texture;wgpu::TextureView view; + wgpu::TextureView native() const {return texture?texture.CreateView():view;} +}; +inline bool read_webgpu_attachment_view(v8::Isolate* isolate,v8::Local input,webgpu_attachment_view& output) { + try {output.view=v8_webgpu_texture_views::native_reference(input);return true;}catch(const std::invalid_argument&){} + try {output.texture=v8_webgpu_textures::native_reference(input);return true;}catch(const std::exception&){} + isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8Literal(isolate,"Attachment requires a GPUTexture or GPUTextureView")));return false; +} +inline bool read_webgpu_color(v8::Isolate* isolate,v8::Local context,v8::Local input,wgpu::Color& output,bool& valid_shape) { + webgpu_state_reader reader(isolate,context,input);wgpu::Color converted{};v8::Local iterator; + const auto number=[&](v8::Local value,double& output) { + v8::Local numeric;if(!value->ToNumber(context).ToLocal(&numeric))return false; + if(!std::isfinite(numeric->Value()))return reader.fail("GPUColor components must be finite doubles");output=numeric->Value();return true; + }; + if(input->IsObject()&&!input.As()->Get(context,v8::Symbol::GetIterator(isolate)).ToLocal(&iterator))return false; + if(!iterator.IsEmpty()&&!iterator->IsNullOrUndefined()) { + size_t count=0;double* components[]={&converted.r,&converted.g,&converted.b,&converted.a}; + if(!read_webgpu_sequence(isolate,context,input,[&](auto value){double n;if(!number(value,n))return false;if(count<4)*components[count]=n;++count;return true;},iterator))return false; + valid_shape=count==4; + }else { + v8::Local value; + for(auto [name,target]:{std::pair{"a",&converted.a},{"b",&converted.b},{"g",&converted.g},{"r",&converted.r}}) { + if(!reader.get(name,value))return false;if(value->IsUndefined())return reader.fail("GPUColor components are required");if(!number(value,*target))return false; + } + valid_shape=true; + } + output=converted;return true; +} +struct webgpu_pass_color { + wgpu::RenderPassColorAttachment state; + webgpu_attachment_view view,resolve; + bool valid_shape=true; +}; +struct webgpu_pass_depth { + wgpu::RenderPassDepthStencilAttachment state; + webgpu_attachment_view view; +}; +struct webgpu_render_pass_descriptor { + std::string label;std::vector> colors; + std::optional depth;uint64_t max_draw_count=50000000; + bool valid_shapes()const{for(const auto& color:colors)if(color&&!color->valid_shape)return false;return true;} + template void with_native(Execute execute) const & { + if(!valid_shapes())throw std::invalid_argument("GPUColor sequence must have four elements"); + std::vector attachments;attachments.reserve(colors.size()); + for(const auto& color:colors) { + wgpu::RenderPassColorAttachment attachment{}; + if(color){attachment=color->state;attachment.view=color->view.native();attachment.resolveTarget=color->resolve.native();} + attachments.push_back(std::move(attachment)); + } + wgpu::RenderPassDescriptor descriptor{};descriptor.label=wgpu::StringView(label.data(),label.size()); + descriptor.colorAttachmentCount=attachments.size();descriptor.colorAttachments=attachments.data(); + wgpu::RenderPassDepthStencilAttachment native_depth{}; + if(depth){native_depth=depth->state;native_depth.view=depth->view.native();descriptor.depthStencilAttachment=&native_depth;} + wgpu::RenderPassMaxDrawCount count{};count.maxDrawCount=max_draw_count;descriptor.nextInChain=&count; + execute(descriptor); + } +}; +inline bool read_webgpu_render_pass_descriptor(v8::Isolate* isolate,v8::Local context,v8::Local input,webgpu_render_pass_descriptor& output) { + webgpu_render_pass_descriptor converted;webgpu_state_reader reader(isolate,context,input);v8::Local value; + if(!read_webgpu_object_label(isolate,context,input,converted.label)||!reader.get("colorAttachments",value))return false; + if(!read_webgpu_sequence(isolate,context,value,[&](auto input) { + if(input->IsNullOrUndefined()){converted.colors.emplace_back(std::nullopt);return true;} + webgpu_state_reader color(isolate,context,input);webgpu_pass_color attachment;v8::Local value;bool depth_slice_provided=false; + if(!color.get("clearValue",value))return false; + if(!value->IsUndefined()&&!read_webgpu_color(isolate,context,value,attachment.state.clearValue,attachment.valid_shape))return false; + if(!color.uint32("depthSlice",attachment.state.depthSlice,false,&depth_slice_provided)||!color.enumeration("loadOp",attachment.state.loadOp,true)||!color.get("resolveTarget",value))return false; + if(!value->IsUndefined()&&!read_webgpu_attachment_view(isolate,value,attachment.resolve))return false; + if(!color.enumeration("storeOp",attachment.state.storeOp,true)||!color.get("view",value)||!read_webgpu_attachment_view(isolate,value,attachment.view))return false; + // Explicit UINT_MAX must remain invalid rather than native omitted. + if(depth_slice_provided&&attachment.state.depthSlice==wgpu::kDepthSliceUndefined)attachment.state.depthSlice=wgpu::kDepthSliceUndefined-1; + converted.colors.emplace_back(std::move(attachment));return true; + }))return false; + if(!reader.get("depthStencilAttachment",value))return false; + if(!value->IsUndefined()) { + webgpu_state_reader depth(isolate,context,value);webgpu_pass_depth attachment; + if(!depth.floating("depthClearValue",attachment.state.depthClearValue)||!depth.enumeration("depthLoadOp",attachment.state.depthLoadOp) + ||!depth.boolean("depthReadOnly",attachment.state.depthReadOnly)||!depth.enumeration("depthStoreOp",attachment.state.depthStoreOp) + ||!depth.uint32("stencilClearValue",attachment.state.stencilClearValue)||!depth.enumeration("stencilLoadOp",attachment.state.stencilLoadOp) + ||!depth.boolean("stencilReadOnly",attachment.state.stencilReadOnly)||!depth.enumeration("stencilStoreOp",attachment.state.stencilStoreOp) + ||!depth.get("view",value)||!read_webgpu_attachment_view(isolate,value,attachment.view))return false; + converted.depth=std::move(attachment); + } + if(!reader.uint64("maxDrawCount",converted.max_draw_count)||!reader.get("occlusionQuerySet",value))return false; + // Query-set wrappers are not exposed yet; do not accept forged interfaces. + if(!value->IsUndefined())return reader.fail("Occlusion queries require a live GPUQuerySet"); + if(!reader.get("timestampWrites",value))return false; + if(!value->IsUndefined()) { + webgpu_state_reader timestamp(isolate,context,value);uint32_t begin=0,end=0; + if(!timestamp.uint32("beginningOfPassWriteIndex",begin)||!timestamp.uint32("endOfPassWriteIndex",end)||!timestamp.get("querySet",value))return false; + return reader.fail("Timestamp writes require a live GPUQuerySet"); + } + output=std::move(converted);return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_passes.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_passes.h new file mode 100644 index 000000000..bc21b8d1a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_passes.h @@ -0,0 +1,119 @@ +#pragma once +#include "v8_webgpu_render_pipelines.h" +#include "v8_webgpu_bind_groups.h" +#include "v8_webgpu_buffers.h" +#include "v8_webgpu_vertex_state.h" +#include +#include +#include +namespace webscene::graphics { +struct v8_webgpu_render_passes_traits { + using native_type=wgpu::RenderPassEncoder;static constexpr const char* name="GPURenderPassEncoder"; + template static void with(dawn_device& device,resource_handle handle,Execute execute){device.with_render_pass(handle,std::move(execute));} + static graphics_command release(resource_handle device,resource_handle handle)noexcept{return graphics_service::deferred_render_pass_release(device,handle);} +}; +class v8_webgpu_render_passes:public v8_webgpu_labeled_resources { + using base=v8_webgpu_labeled_resources; + static void set_pipeline(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return; + try { + auto pipeline=v8_webgpu_render_pipelines::native_reference(info[0]);auto* item=receiver(info);if(!item)return; + item->service->with_device(item->device,[&](auto& owned){owned.with_render_pass(item->resource,[&](const auto& pass){pass.SetPipeline(pipeline);});}); + }catch(const std::exception&){fail(info.GetIsolate(),"setPipeline requires a live GPURenderPipeline");} + } + static bool unsigned_value(v8::Isolate* isolate,v8::Local context,v8::Local value,uint64_t maximum,uint64_t& output) { + v8::Local number;if(!value->ToNumber(context).ToLocal(&number))return false; + double n=std::trunc(number->Value()); + if(!std::isfinite(n)||n<0||n>static_cast(maximum)){fail(isolate,"Binding argument is outside its unsigned range");return false;} + output=static_cast(n);return true; + } + static std::shared_ptr offset_backing(v8::Isolate* isolate,v8::Local value) { + if(!value->IsUint32Array()){fail(isolate,"Dynamic offsets require Uint32Array");return {};} + v8::Local buffer=value.As()->Buffer(); + std::shared_ptr backing; + if(buffer->IsSharedArrayBuffer())backing=buffer.As()->GetBackingStore(); + else { + if(buffer.As()->WasDetached()){fail(isolate,"Dynamic offsets are detached");return {};} + backing=buffer.As()->GetBackingStore(); + } + if(backing->IsResizableByUserJavaScript()){fail(isolate,"Resizable dynamic offsets are not allowed");return {};} + return backing; + } + static void set_bind_group(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(info.Length()<2||info.Length()==4){fail(isolate,"setBindGroup requires two or three arguments, or five for a typed range");return;} + try { + uint64_t index=0;if(!unsigned_value(isolate,context,info[0],UINT32_MAX,index))return; + wgpu::BindGroup group; + if(!info[1]->IsNullOrUndefined())group=v8_webgpu_bind_groups::native_reference(info[1]); + std::vector offsets; + if(info.Length()>=5) { + auto backing=offset_backing(isolate,info[2]);if(!backing)return; + uint64_t start=0,count=0; + if(!unsigned_value(isolate,context,info[3],9007199254740991ULL,start) + ||!unsigned_value(isolate,context,info[4],UINT32_MAX,count))return; + backing=offset_backing(isolate,info[2]);if(!backing)return; + auto array=info[2].As(); + if(start>array->Length()||count>array->Length()-start) { + isolate->ThrowException(v8::Exception::RangeError(v8::String::NewFromUtf8Literal(isolate,"Dynamic offset range exceeds its array")));return; + } + offsets.resize(static_cast(count)); + if(count) { + auto* values=reinterpret_cast(static_cast(backing->Data())+array->ByteOffset())+start; + for(size_t i=0;iIsShared()?std::atomic_ref(values[i]).load(std::memory_order_relaxed):values[i]; + } + }else if(!info[2]->IsUndefined()) { + if(!read_webgpu_sequence(isolate,context,info[2],[&](auto value){ + uint64_t offset=0;if(!unsigned_value(isolate,context,value,UINT32_MAX,offset))return false; + offsets.push_back(static_cast(offset));return true; + }))return; + } + auto* item=receiver(info);if(!item)return; + item->service->with_device(item->device,[&](auto& owned){owned.with_render_pass(item->resource,[&](const auto& pass){ + pass.SetBindGroup(static_cast(index),group,offsets.size(),offsets.data()); + });}); + }catch(const std::exception&){fail(isolate,"setBindGroup requires live native ownership");} + } + static void set_vertex_buffer(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(info.Length()<2){fail(isolate,"setVertexBuffer requires slot and buffer");return;} + try { + uint64_t slot=0,offset=0,size=wgpu::kWholeSize; + if(!unsigned_value(isolate,context,info[0],UINT32_MAX,slot))return; + wgpu::Buffer buffer;if(!info[1]->IsNullOrUndefined())buffer=v8_webgpu_buffers::native_reference(info[1]); + if(!info[2]->IsUndefined()&&!unsigned_value(isolate,context,info[2],9007199254740991ULL,offset))return; + if(!info[3]->IsUndefined()&&!unsigned_value(isolate,context,info[3],9007199254740991ULL,size))return; + auto* item=receiver(info);if(!item)return; + item->service->with_device(item->device,[&](auto& owned){owned.with_render_pass(item->resource,[&](const auto& pass){ + pass.SetVertexBuffer(static_cast(slot),buffer,offset,size); + });}); + }catch(const std::exception&){fail(isolate,"setVertexBuffer requires live native ownership");} + } + static void draw(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + if(!info.Length()){fail(isolate,"draw requires vertexCount");return;} + uint32_t arguments[]={0,1,0,0}; + for(int i=0;i<4;++i) { + if(i&&info[i]->IsUndefined())continue; + v8::Local number;if(!info[i]->ToNumber(context).ToLocal(&number))return; + const double n=std::trunc(number->Value());if(!std::isfinite(n)||n<0||n>4294967295.0){fail(isolate,"Draw argument is outside GPUSize32 range");return;} + arguments[i]=static_cast(n); + } + auto* item=receiver(info);if(!item)return; + try{item->service->with_device(item->device,[&](auto& owned){owned.with_render_pass(item->resource,[&](const auto& pass){pass.Draw(arguments[0],arguments[1],arguments[2],arguments[3]);});});} + catch(const std::exception&){fail(isolate,"Render pass ownership unavailable");} + } + static void end(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info);if(!item)return; + try{item->service->with_device(item->device,[&](auto& owned){owned.with_render_pass(item->resource,[](const auto& pass){pass.End();});});} + catch(const std::exception&){fail(info.GetIsolate(),"Render pass ownership unavailable");} + } +public: + v8_webgpu_render_passes(v8::Isolate* isolate,v8::Local context,size_t capacity=1024):base(isolate,context,capacity) { + auto prototype=prototype_.Get(isolate); + for(auto [name,callback,length]:{std::tuple{"setPipeline",set_pipeline,1},std::tuple{"setBindGroup",set_bind_group,2},std::tuple{"setVertexBuffer",set_vertex_buffer,2},std::tuple{"draw",draw,1},std::tuple{"end",end,0}}) { + if(!prototype->Set(context,v8::String::NewFromUtf8(isolate,name).ToLocalChecked(),v8::Function::New(context,callback,{},length).ToLocalChecked()).FromMaybe(false))throw std::runtime_error("Render pass prototype initialization failed"); + } + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_pipelines.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_pipelines.h new file mode 100644 index 000000000..23154166a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_pipelines.h @@ -0,0 +1,15 @@ +#pragma once +#include "v8_webgpu_pipeline_resources.h" +namespace webscene::graphics { +struct v8_webgpu_render_pipelines_traits { + using native_type=wgpu::RenderPipeline; + static constexpr const char* name="GPURenderPipeline"; + template static void with(dawn_device& device,resource_handle handle,Execute execute) { + device.with_render_pipeline(handle,std::move(execute)); + } + static graphics_command release(resource_handle device,resource_handle handle) noexcept { + return graphics_service::deferred_render_pipeline_release(device,handle); + } +}; +using v8_webgpu_render_pipelines=v8_webgpu_pipeline_resources; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_state.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_state.h new file mode 100644 index 000000000..e6a9f4ffd --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_render_state.h @@ -0,0 +1,134 @@ +#pragma once +#include "webgpu_render_enums.h" +#include +#include +#include +namespace webscene::graphics { +class webgpu_state_reader { + v8::Isolate* isolate_;v8::Local context_;v8::Local input_; +public: + webgpu_state_reader(v8::Isolate* isolate,v8::Local context,v8::Local input) + :isolate_(isolate),context_(context),input_(input){} + bool fail(const char* text) {isolate_->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate_,text).ToLocalChecked()));return false;} + bool get(const char* name,v8::Local& value) { + if(input_->IsNullOrUndefined()){value=v8::Undefined(isolate_);return true;} + if(!input_->IsObject())return fail("Render state must be a dictionary"); + return input_.As()->Get(context_,v8::String::NewFromUtf8(isolate_,name).ToLocalChecked()).ToLocal(&value); + } + template bool enumeration(const char* name,Native& output,bool required=false) { + v8::Local value;if(!get(name,value))return false;if(value->IsUndefined())return required?fail("Required render-state enum is missing"):true; + v8::Local text;if(!value->ToString(context_).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate_,text);if(!*bytes)return false; + const std::string_view key(*bytes,bytes.length()); + for(const auto& [label,native]:webgpu_enum_names::values)if(label==key){output=native;return true;} + return fail("Invalid WebGPU render-state enum"); + } + template bool boolean(const char* name,Boolean& output) { + v8::Local value;if(!get(name,value))return false; + if(!value->IsUndefined())output=value->BooleanValue(isolate_);return true; + } + bool int32(const char* name,int32_t& output) { + v8::Local value;if(!get(name,value))return false;if(value->IsUndefined())return true; + v8::Local number;if(!value->ToNumber(context_).ToLocal(&number))return false; + const double truncated=std::trunc(number->Value()); + if(!std::isfinite(truncated)||truncated<-2147483648.0||truncated>2147483647.0)return fail("Render-state integer is outside long range"); + output=static_cast(truncated);return true; + } + bool floating(const char* name,float& output) { + v8::Local value;if(!get(name,value))return false;if(value->IsUndefined())return true; + v8::Local number;if(!value->ToNumber(context_).ToLocal(&number))return false; + const float converted=static_cast(number->Value()); + if(!std::isfinite(number->Value())||!std::isfinite(converted))return fail("Render-state float must be finite"); + output=converted;return true; + } + bool optional_boolean(const char* name,wgpu::OptionalBool& output) { + v8::Local value;if(!get(name,value))return false; + if(!value->IsUndefined())output=value->BooleanValue(isolate_)?wgpu::OptionalBool::True:wgpu::OptionalBool::False; + return true; + } + bool uint64(const char* name,uint64_t& output,bool required=false) { + v8::Local value;if(!get(name,value))return false; + if(value->IsUndefined())return required?fail("Required integer is missing"):true; + v8::Local number;if(!value->ToNumber(context_).ToLocal(&number))return false; + const double truncated=std::trunc(number->Value()); + if(!std::isfinite(truncated)||truncated<0||truncated>9007199254740991.0)return fail("GPUSize64 is outside the safe integer range"); + output=static_cast(truncated);return true; + } + bool uint32(const char* name,uint32_t& output,bool required=false,bool* provided=nullptr) { + v8::Local value;if(!get(name,value))return false;if(value->IsUndefined())return required?fail("Required integer is missing"):true; + if(provided)*provided=true; + v8::Local number;if(!value->ToNumber(context_).ToLocal(&number))return false; + const double truncated=std::trunc(number->Value()); + if(!std::isfinite(truncated)||truncated<0||truncated>4294967295.0)return fail("Render-state integer is outside unsigned long range"); + output=static_cast(truncated);return true; + } +}; +inline bool read_webgpu_primitive_state(v8::Isolate* isolate,v8::Local context,v8::Local input,wgpu::PrimitiveState& output) { + webgpu_state_reader reader(isolate,context,input);wgpu::PrimitiveState converted{}; + converted.cullMode=wgpu::CullMode::None;converted.frontFace=wgpu::FrontFace::CCW;converted.topology=wgpu::PrimitiveTopology::TriangleList; + if(!reader.enumeration("cullMode",converted.cullMode)||!reader.enumeration("frontFace",converted.frontFace) + ||!reader.enumeration("stripIndexFormat",converted.stripIndexFormat)||!reader.enumeration("topology",converted.topology) + ||!reader.boolean("unclippedDepth",converted.unclippedDepth))return false; + output=converted;return true; +} +inline bool read_webgpu_multisample_state(v8::Isolate* isolate,v8::Local context,v8::Local input,wgpu::MultisampleState& output) { + webgpu_state_reader reader(isolate,context,input);wgpu::MultisampleState converted{};converted.count=1;converted.mask=0xffffffff; + if(!reader.boolean("alphaToCoverageEnabled",converted.alphaToCoverageEnabled)||!reader.uint32("count",converted.count)||!reader.uint32("mask",converted.mask))return false; + output=converted;return true; +} +inline bool read_webgpu_blend_component(v8::Isolate* isolate,v8::Local context,v8::Local input,wgpu::BlendComponent& output) { + webgpu_state_reader reader(isolate,context,input);wgpu::BlendComponent converted{}; + converted.dstFactor=wgpu::BlendFactor::Zero;converted.operation=wgpu::BlendOperation::Add;converted.srcFactor=wgpu::BlendFactor::One; + if(!reader.enumeration("dstFactor",converted.dstFactor)||!reader.enumeration("operation",converted.operation)||!reader.enumeration("srcFactor",converted.srcFactor))return false; + output=converted;return true; +} +inline bool read_webgpu_blend_state(v8::Isolate* isolate,v8::Local context,v8::Local input,wgpu::BlendState& output) { + webgpu_state_reader reader(isolate,context,input);wgpu::BlendState converted{};v8::Local value; + if(!reader.get("alpha",value))return false;if(value->IsUndefined())return reader.fail("Blend alpha is required"); + if(!read_webgpu_blend_component(isolate,context,value,converted.alpha))return false; + if(!reader.get("color",value))return false;if(value->IsUndefined())return reader.fail("Blend color is required"); + if(!read_webgpu_blend_component(isolate,context,value,converted.color))return false; + output=converted;return true; +} +inline bool read_webgpu_stencil_face(v8::Isolate* isolate,v8::Local context,v8::Local input,wgpu::StencilFaceState& output) { + webgpu_state_reader reader(isolate,context,input);wgpu::StencilFaceState converted{}; + converted.compare=wgpu::CompareFunction::Always;converted.depthFailOp=wgpu::StencilOperation::Keep; + converted.failOp=wgpu::StencilOperation::Keep;converted.passOp=wgpu::StencilOperation::Keep; + if(!reader.enumeration("compare",converted.compare)||!reader.enumeration("depthFailOp",converted.depthFailOp) + ||!reader.enumeration("failOp",converted.failOp)||!reader.enumeration("passOp",converted.passOp))return false; + output=converted;return true; +} +inline bool read_webgpu_depth_stencil(v8::Isolate* isolate,v8::Local context,v8::Local input,wgpu::DepthStencilState& output) { + webgpu_state_reader reader(isolate,context,input);wgpu::DepthStencilState converted{};v8::Local value; + if(!reader.int32("depthBias",converted.depthBias)||!reader.floating("depthBiasClamp",converted.depthBiasClamp) + ||!reader.floating("depthBiasSlopeScale",converted.depthBiasSlopeScale)||!reader.enumeration("depthCompare",converted.depthCompare) + ||!reader.optional_boolean("depthWriteEnabled",converted.depthWriteEnabled)||!reader.enumeration("format",converted.format,true))return false; + if(!reader.get("stencilBack",value)||!read_webgpu_stencil_face(isolate,context,value,converted.stencilBack))return false; + if(!reader.get("stencilFront",value)||!read_webgpu_stencil_face(isolate,context,value,converted.stencilFront))return false; + if(!reader.uint32("stencilReadMask",converted.stencilReadMask)||!reader.uint32("stencilWriteMask",converted.stencilWriteMask))return false; + output=converted;return true; +} +struct webgpu_color_target { + wgpu::TextureFormat format=wgpu::TextureFormat::Undefined; + std::optional blend; + uint32_t write_mask=0xf; + // Native pointers borrow this converted descriptor; do not move it until + // the pipeline creation call has consumed the native view. + wgpu::ColorTargetState native() const & { + wgpu::ColorTargetState result{};result.format=format;result.blend=blend?&*blend:nullptr; + // WebGPU defines only four color bits. Keep invalid bits invalid for + // native validation rather than truncating to the known mask. + result.writeMask=static_cast(write_mask);return result; + } + wgpu::ColorTargetState native() const &&=delete; +}; +inline bool read_webgpu_color_target(v8::Isolate* isolate,v8::Local context,v8::Local input,webgpu_color_target& output) { + webgpu_state_reader reader(isolate,context,input);webgpu_color_target converted;v8::Local value; + if(!reader.get("blend",value))return false; + if(!value->IsUndefined()) { + wgpu::BlendState blend{};if(!read_webgpu_blend_state(isolate,context,value,blend))return false;converted.blend=blend; + } + if(!reader.enumeration("format",converted.format,true)||!reader.uint32("writeMask",converted.write_mask))return false; + output=std::move(converted);return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_samplers.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_samplers.h new file mode 100644 index 000000000..61ed3802b --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_samplers.h @@ -0,0 +1,15 @@ +#pragma once +#include "v8_webgpu_labeled_resources.h" +namespace webscene::graphics { +struct v8_webgpu_samplers_traits { + using native_type=wgpu::Sampler; + static constexpr const char* name="GPUSampler"; + template static void with(dawn_device& device,resource_handle handle,Execute execute) { + device.with_sampler(handle,std::move(execute)); + } + static graphics_command release(resource_handle device,resource_handle handle) noexcept { + return graphics_service::deferred_sampler_release(device,handle); + } +}; +using v8_webgpu_samplers=v8_webgpu_labeled_resources; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_shader_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_shader_descriptor.h new file mode 100644 index 000000000..ea3006b30 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_shader_descriptor.h @@ -0,0 +1,80 @@ +#pragma once +#include +#include +#include +#include +#include +namespace webscene::graphics { +struct webgpu_shader_hint { + std::string entry_point; + enum class layout_kind { omitted,automatic,explicit_layout } kind=layout_kind::omitted; + wgpu::PipelineLayout layout; +}; +struct webgpu_shader_descriptor { + std::string label,code; + std::vector hints; +}; +// ResolveLayout recognizes genuine GPUPipelineLayout wrappers, returning an +// empty optional for other values. Unrecognized objects then undergo the string +// branch of the WebIDL union conversion. No user code should run in recognition. +template +inline bool read_webgpu_shader_descriptor(v8::Isolate* isolate,v8::Local context, + v8::Local input,webgpu_shader_descriptor& output,ResolveLayout resolve_layout) { + const auto key=[&](const char* text){return v8::String::NewFromUtf8(isolate,text).ToLocalChecked();}; + const auto fail=[&](const char* text){isolate->ThrowException(v8::Exception::TypeError(key(text)));return false;}; + const auto get=[&](v8::Local dictionary,const char* name,v8::Local& value) { + if(dictionary->IsNullOrUndefined()){value=v8::Undefined(isolate);return true;} + if(!dictionary->IsObject())return fail("Shader descriptor must be a dictionary"); + return dictionary.As()->Get(context,key(name)).ToLocal(&value); + }; + const auto string=[&](v8::Local value,std::string& result) { + v8::Local text;if(!value->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false; + result.assign(*bytes,bytes.length());return true; + }; + webgpu_shader_descriptor converted; + v8::Local value; + if(!get(input,"label",value))return false; + if(!value->IsUndefined() && !string(value,converted.label))return false; + if(!get(input,"code",value))return false; + if(value->IsUndefined())return fail("Shader code is required"); + if(!string(value,converted.code))return false; + if(!get(input,"compilationHints",value))return false; + if(!value->IsUndefined()) { + if(!value->IsObject())return fail("Compilation hints must be an iterable object"); + v8::Local method,iterator,next; + if(!value.As()->Get(context,v8::Symbol::GetIterator(isolate)).ToLocal(&method))return false; + if(!method->IsFunction())return fail("Compilation hints are not iterable"); + if(!method.As()->Call(context,value,0,nullptr).ToLocal(&iterator))return false; + if(!iterator->IsObject())return fail("Hint iterator must return an object"); + if(!iterator.As()->Get(context,key("next")).ToLocal(&next))return false; + if(!next->IsFunction())return fail("Hint iterator next must be callable"); + for(;;) { + v8::Local step,done,hint_value,member; + if(!next.As()->Call(context,iterator,0,nullptr).ToLocal(&step))return false; + if(!step->IsObject())return fail("Hint iterator result must be an object"); + if(!step.As()->Get(context,key("done")).ToLocal(&done))return false; + if(done->BooleanValue(isolate))break; + if(!step.As()->Get(context,key("value")).ToLocal(&hint_value))return false; + webgpu_shader_hint hint; + if(!get(hint_value,"entryPoint",member))return false; + if(member->IsUndefined())return fail("Hint entryPoint is required"); + if(!string(member,hint.entry_point))return false; + if(!get(hint_value,"layout",member))return false; + if(!member->IsUndefined()) { + auto layout=member->IsObject()?resolve_layout(member):std::optional{}; + if(layout) { + if(!*layout)return fail("Pipeline layout native ownership is unavailable"); + hint.kind=webgpu_shader_hint::layout_kind::explicit_layout;hint.layout=std::move(*layout); + } else { + std::string automatic;if(!string(member,automatic))return false; + if(automatic!="auto")return fail("Invalid GPUAutoLayoutMode"); + hint.kind=webgpu_shader_hint::layout_kind::automatic; + } + } + converted.hints.push_back(std::move(hint)); + } + } + output=std::move(converted);return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_shaders.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_shaders.h new file mode 100644 index 000000000..ad7cf57e5 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_shaders.h @@ -0,0 +1,131 @@ +#pragma once +#include "v8_webgpu_labeled_resources.h" +#include "webgpu_compilation_info.h" +namespace webscene::graphics { +struct v8_webgpu_shaders_traits { + using native_type=wgpu::ShaderModule; + static constexpr const char* name="GPUShaderModule"; + template static void with(dawn_device& device,resource_handle handle,Execute execute) { + device.with_shader_module(handle,std::move(execute)); + } + static graphics_command release(resource_handle device,resource_handle handle) noexcept { + return graphics_service::deferred_shader_module_release(device,handle); + } +}; +class v8_webgpu_shaders final : public v8_webgpu_labeled_resources { + using base=v8_webgpu_labeled_resources; + struct native_result { webgpu_compilation_info info; }; + struct request { + uint64_t operation; + std::shared_ptr mailbox; + std::shared_ptr result; + v8::Global context; + v8::Global shader; + v8::Global resolver; + }; + resource_owner owner_{new_owner_token(),new_owner_token(),new_owner_token()}; + std::vector> pending_; + static v8::Local string(v8::Isolate* isolate,const char* value) { + return v8::String::NewFromUtf8(isolate,value).ToLocalChecked(); + } + static void get_info(const v8::FunctionCallbackInfo& args) { + auto* isolate=args.GetIsolate();auto context=isolate->GetCurrentContext(); + v8::Local resolver; + if(!v8::Promise::Resolver::New(context).ToLocal(&resolver))return; + args.GetReturnValue().Set(resolver->GetPromise()); + v8::TryCatch caught(isolate); + auto* item=receiver(args); + if(!item) { + auto reason=caught.Exception();caught.Reset(); + (void)resolver->Reject(context,reason).FromMaybe(false);return; + } + try { + auto* self=static_cast(item->registry); + self->check_scope(); + if(self->pending_.size()>=256)throw std::length_error("Compilation request capacity exhausted"); + auto request_value=std::make_unique(); + request_value->operation=new_owner_token(); + request_value->mailbox=item->service->dawn().completions(); + request_value->result=std::make_shared(); + request_value->context.Reset(isolate,context); + request_value->shader.Reset(isolate,args.This()); + request_value->resolver.Reset(isolate,resolver); + auto mailbox=request_value->mailbox; + auto result=request_value->result; + auto operation=request_value->operation; + auto module=base::native_reference(args.This()); + // Reserve vector storage before admitting a callback. + self->pending_.reserve(self->pending_.size()+1); + auto ticket=mailbox->reserve(operation,self->owner_); + if(!ticket)throw std::length_error("Compilation completion capacity exhausted"); + self->pending_.push_back(std::move(request_value)); + module.GetCompilationInfo(wgpu::CallbackMode::AllowSpontaneous, + [mailbox,ticket=*ticket,result,module](wgpu::CompilationInfoRequestStatus status,const wgpu::CompilationInfo* info) { + auto completion=completion_status::failed; + try { + if(status==wgpu::CompilationInfoRequestStatus::Success&&info) { + result->info=webgpu_compilation_info::copy(*info); + completion=completion_status::success; + } + }catch(...){} + mailbox->publish(ticket,completion); + }); + }catch(const std::exception& error) { + (void)resolver->Reject(context,v8::Exception::Error(string(isolate,error.what()))).FromMaybe(false); + } + } +public: + v8_webgpu_shaders(v8::Isolate* isolate,v8::Local context,size_t capacity=1024) + :base(isolate,context,capacity) { + auto method=v8::Function::New(context,get_info).ToLocalChecked(); + prototype_.Get(isolate)->Set(context,string(isolate,"getCompilationInfo"),method).Check(); + } + ~v8_webgpu_shaders() { + check_scope(); + for(auto& item:pending_) { + item->mailbox->cancel_owner(owner_); + auto context=item->context.Get(isolate_);v8::Context::Scope scope(context); + (void)item->resolver.Get(isolate_)->Reject(context, + v8::Exception::Error(string(isolate_,"Shader compilation request cancelled"))).FromMaybe(false); + } + } + bool complete(completion_record record) { + check_scope(); + if(record.owner!=owner_)return false; + auto found=std::find_if(pending_.begin(),pending_.end(),[&](const auto& item){return item->operation==record.operation;}); + if(found==pending_.end())return true; + auto item=std::move(*found);pending_.erase(found); + auto context=item->context.Get(isolate_);v8::Context::Scope scope(context); + auto resolver=item->resolver.Get(isolate_); + if(record.status!=completion_status::success) { + (void)resolver->Reject(context,v8::Exception::Error(string(isolate_,"Shader compilation information unavailable"))).FromMaybe(false); + return true; + } + auto messages=v8::Array::New(isolate_,static_cast(item->result->info.messages.size())); + uint32_t index=0; + for(const auto& message:item->result->info.messages) { + if(!message.has_utf16) { + (void)resolver->Reject(context,v8::Exception::Error(string(isolate_,"UTF-16 shader diagnostics unavailable"))).FromMaybe(false); + return true; + } + auto object=v8::Object::New(isolate_); + auto put=[&](const char* name,v8::Local value){ + return object->DefineOwnProperty(context,string(isolate_,name),value, + static_cast(v8::ReadOnly|v8::DontDelete)).FromMaybe(false); + }; + auto number=[&](const char* name,uint64_t value){return put(name,v8::Number::New(isolate_,static_cast(value)));}; + auto text=v8::String::NewFromUtf8(isolate_,message.message.data(),v8::NewStringType::kNormal,static_cast(message.message.size())).ToLocalChecked(); + const char* type=message.type==wgpu::CompilationMessageType::Error?"error":message.type==wgpu::CompilationMessageType::Warning?"warning":"info"; + if(!put("message",text)||!put("type",string(isolate_,type))||!number("lineNum",message.line_num) + ||!number("linePos",message.utf16_line_pos)||!number("offset",message.utf16_offset)||!number("length",message.utf16_length) + ||!messages->Set(context,index++,object).FromMaybe(false))return true; + } + if(!messages->SetIntegrityLevel(context,v8::IntegrityLevel::kFrozen).FromMaybe(false))return true; + auto info=v8::Object::New(isolate_); + if(!info->DefineOwnProperty(context,string(isolate_,"messages"),messages, + static_cast(v8::ReadOnly|v8::DontDelete)).FromMaybe(false))return true; + (void)resolver->Resolve(context,info).FromMaybe(false); + return true; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_supported_features.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_supported_features.h new file mode 100644 index 000000000..27c9d929b --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_supported_features.h @@ -0,0 +1,106 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace webscene::graphics { +// Immutable WebIDL setlike contents. The backing Set stays in traced internal +// fields, not in native globals; retained feature objects outlive their factory. +template class v8_webgpu_feature_set { + alignas(void*) static inline char brand_{}; + v8::Isolate* isolate_; + const std::thread::id thread_=std::this_thread::get_id(); + v8::Global realm_; + v8::Global instance_; + v8::Global prototype_; + v8::Global values_,entries_; + static void fail(v8::Isolate* isolate,const char* message) { + isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate,message).ToLocalChecked())); + } + static v8::Local backing(const v8::FunctionCallbackInfo& info) { + auto object=info.This(); + if (object->InternalFieldCount()!=4 || !object->GetInternalField(0)->IsValue() + || !object->GetInternalField(0).As()->IsExternal() + || object->GetInternalField(0).As()->Value(v8::kExternalPointerTypeTagDefault)!=&brand_) { + fail(info.GetIsolate(),"Illegal GPUSupportedFeatures receiver"); return {}; + } + return object->GetInternalField(1).As(); + } + static void size(const v8::FunctionCallbackInfo& info) { + auto set=backing(info); if (!set.IsEmpty()) info.GetReturnValue().Set(static_cast(set->Size())); + } + static void has(const v8::FunctionCallbackInfo& info) { + auto set=backing(info); if (set.IsEmpty()) return; + if (!info.Length()) { fail(info.GetIsolate(),"has requires a feature name"); return; } + auto context=info.GetIsolate()->GetCurrentContext(); + v8::Local name; + if (!info[0]->ToString(context).ToLocal(&name)) return; + auto result=set->Has(context,name); + if (result.IsJust()) info.GetReturnValue().Set(result.FromJust()); + } + template static void iterator(const v8::FunctionCallbackInfo& info) { + auto set=backing(info); if (set.IsEmpty()) return; + auto function=info.This()->GetInternalField(Field).As(); + v8::Local result; + if (function->Call(info.GetIsolate()->GetCurrentContext(),set,0,nullptr).ToLocal(&result)) info.GetReturnValue().Set(result); + } + static void for_each(const v8::FunctionCallbackInfo& info) { + auto set=backing(info); if (set.IsEmpty()) return; + if (!info[0]->IsFunction()) { fail(info.GetIsolate(),"forEach requires a callback"); return; } + auto context=info.GetIsolate()->GetCurrentContext(); auto values=set->AsArray(); + for (uint32_t i=0;iLength();++i) { + v8::Local value; + if (!values->Get(context,i).ToLocal(&value)) return; + v8::Local args[]{value,value,info.This()}; + if (info[0].As()->Call(context,info[1],3,args).IsEmpty()) return; + } + } +public: + // Initialize in the trusted realm bootstrap, before user scripts can modify + // built-in Set iterator methods. Later global/prototype changes are ignored. + v8_webgpu_feature_set(v8::Isolate* isolate,v8::Local context):isolate_(isolate) { + realm_.Reset(isolate,context); + auto set=v8::Set::New(isolate); + auto native_prototype=set->GetPrototype().As(); + values_.Reset(isolate,native_prototype->Get(context,v8::String::NewFromUtf8Literal(isolate,"values")).ToLocalChecked().As()); + entries_.Reset(isolate,native_prototype->Get(context,v8::String::NewFromUtf8Literal(isolate,"entries")).ToLocalChecked().As()); + auto instance=v8::ObjectTemplate::New(isolate); instance->SetInternalFieldCount(4); instance_.Reset(isolate,instance); + auto prototype_template=v8::ObjectTemplate::New(isolate); + prototype_template->SetAccessorProperty(v8::String::NewFromUtf8Literal(isolate,"size"),v8::FunctionTemplate::New(isolate,size)); + auto has_method=v8::FunctionTemplate::New(isolate,has); has_method->SetLength(1); prototype_template->Set(isolate,"has",has_method); + auto for_each_method=v8::FunctionTemplate::New(isolate,for_each); for_each_method->SetLength(1); prototype_template->Set(isolate,"forEach",for_each_method); + auto prototype=prototype_template->NewInstance(context).ToLocalChecked(); + auto values_method=v8::Function::New(context,iterator<2>).ToLocalChecked(); + auto entries_method=v8::Function::New(context,iterator<3>).ToLocalChecked(); + prototype->DefineOwnProperty(context,v8::String::NewFromUtf8Literal(isolate,"values"),values_method).Check(); + prototype->DefineOwnProperty(context,v8::String::NewFromUtf8Literal(isolate,"keys"),values_method).Check(); + prototype->DefineOwnProperty(context,v8::Symbol::GetIterator(isolate),values_method).Check(); + prototype->DefineOwnProperty(context,v8::String::NewFromUtf8Literal(isolate,"entries"),entries_method).Check(); + prototype->DefineOwnProperty(context,v8::Symbol::GetToStringTag(isolate),v8::String::NewFromUtf8(isolate,Wgsl?"WGSLLanguageFeatures":"GPUSupportedFeatures").ToLocalChecked(),static_cast(v8::ReadOnly|v8::DontEnum)).Check(); + prototype_.Reset(isolate,prototype); + } + v8_webgpu_feature_set(const v8_webgpu_feature_set&)=delete; + v8_webgpu_feature_set& operator=(const v8_webgpu_feature_set&)=delete; + v8::MaybeLocal create(v8::Local context,std::span names) { + if (std::this_thread::get_id()!=thread_ || v8::Isolate::GetCurrent()!=isolate_ || realm_.Get(isolate_)!=context) + throw std::logic_error("Feature set belongs to another realm"); + auto set=v8::Set::New(isolate_); + for (const auto name:names) { + v8::Local value; + if (!v8::String::NewFromUtf8(isolate_,name.data(),v8::NewStringType::kNormal,static_cast(name.size())).ToLocal(&value)) return {}; + if (set->Add(context,value).IsEmpty()) return {}; + } + v8::Local object; + if (!instance_.Get(isolate_)->NewInstance(context).ToLocal(&object) + || !object->SetPrototype(context,prototype_.Get(isolate_)).FromMaybe(false)) return {}; + object->SetInternalField(0,v8::External::New(isolate_,&brand_,v8::kExternalPointerTypeTagDefault)); + object->SetInternalField(1,set); + object->SetInternalField(2,values_.Get(isolate_)); object->SetInternalField(3,entries_.Get(isolate_)); + return object; + } +}; +using v8_webgpu_supported_features=v8_webgpu_feature_set; +using v8_wgsl_language_features=v8_webgpu_feature_set; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_texture_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_texture_descriptor.h new file mode 100644 index 000000000..721f445d8 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_texture_descriptor.h @@ -0,0 +1,66 @@ +#pragma once +#include "v8_webgpu_vertex_state.h" +namespace webscene::graphics { +struct webgpu_texture_descriptor { + std::string label; + wgpu::TextureDimension dimension=wgpu::TextureDimension::e2D; + wgpu::TextureFormat format=wgpu::TextureFormat::Undefined; + uint32_t mip_levels=1,samples=1,usage=0; + wgpu::Extent3D size{0,1,1}; + bool valid_extent_shape=true; + wgpu::TextureViewDimension binding_dimension=wgpu::TextureViewDimension::Undefined; + std::vector view_formats; + template void with_native(Execute execute) const & { + if(!valid_extent_shape)throw std::invalid_argument("Invalid texture extent shape"); + wgpu::TextureDescriptor result{};result.label=wgpu::StringView(label.data(),label.size()); + result.dimension=dimension;result.format=format;result.mipLevelCount=mip_levels;result.sampleCount=samples;result.size=size; + // Exclude host-only usage bits. Invalid browser masks remain invalid + // native descriptors so Dawn reports validation instead of enabling them. + result.usage=(usage&~0x3fu)?wgpu::TextureUsage::None:static_cast(usage); + result.viewFormatCount=view_formats.size();result.viewFormats=view_formats.data(); + wgpu::TextureBindingViewDimension binding{};binding.textureBindingViewDimension=binding_dimension; + if(binding_dimension!=wgpu::TextureViewDimension::Undefined)result.nextInChain=&binding; + execute(result); + } +}; +inline bool read_webgpu_texture_descriptor(v8::Isolate* isolate,v8::Local context,v8::Local input,webgpu_texture_descriptor& output) { + webgpu_state_reader reader(isolate,context,input);webgpu_texture_descriptor converted;v8::Local value; + if(!reader.get("label",value))return false; + if(!value->IsUndefined()) { + v8::Local text;if(!value->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false;converted.label.assign(*bytes,bytes.length()); + } + if(!reader.enumeration("dimension",converted.dimension)||!reader.enumeration("format",converted.format,true) + ||!reader.uint32("mipLevelCount",converted.mip_levels)||!reader.uint32("sampleCount",converted.samples)||!reader.get("size",value))return false; + v8::Local iterator; + if(value->IsObject()) { + if(!value.As()->Get(context,v8::Symbol::GetIterator(isolate)).ToLocal(&iterator))return false; + } + if(!iterator.IsEmpty()&&!iterator->IsNullOrUndefined()) { + size_t count=0; + if(!read_webgpu_sequence(isolate,context,value,[&](auto coordinate) { + v8::Local number;if(!coordinate->ToNumber(context).ToLocal(&number))return false; + double truncated=std::trunc(number->Value()); + if(!std::isfinite(truncated)||truncated<0||truncated>4294967295.0)return reader.fail("Texture coordinate out of range"); + if(count==0)converted.size.width=static_cast(truncated); + if(count==1)converted.size.height=static_cast(truncated); + if(count==2)converted.size.depthOrArrayLayers=static_cast(truncated); + ++count;return true; + },iterator))return false; + converted.valid_extent_shape=count>=1&&count<=3; + } else { + webgpu_state_reader extent(isolate,context,value); + if(!extent.uint32("depthOrArrayLayers",converted.size.depthOrArrayLayers)||!extent.uint32("height",converted.size.height) + ||!extent.uint32("width",converted.size.width,true))return false; + } + if(!reader.enumeration("textureBindingViewDimension",converted.binding_dimension)||!reader.uint32("usage",converted.usage,true) + ||!reader.get("viewFormats",value))return false; + if(!value->IsUndefined()&&!read_webgpu_sequence(isolate,context,value,[&](auto format) { + v8::Local text;if(!format->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false; + for(const auto& [name,native]:webgpu_enum_names::values)if(name==std::string_view(*bytes,bytes.length())){converted.view_formats.push_back(native);return true;} + return reader.fail("Invalid texture view format"); + }))return false; + output=std::move(converted);return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_texture_view_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_texture_view_descriptor.h new file mode 100644 index 000000000..044301d28 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_texture_view_descriptor.h @@ -0,0 +1,63 @@ +#pragma once +#include "v8_webgpu_render_state.h" +#include +namespace webscene::graphics { +struct webgpu_texture_view_descriptor { + std::string label; + std::u16string swizzle=u"rgba"; + wgpu::TextureFormat format=wgpu::TextureFormat::Undefined; + wgpu::TextureViewDimension dimension=wgpu::TextureViewDimension::Undefined; + wgpu::TextureAspect aspect=wgpu::TextureAspect::All; + uint32_t base_mip=0,base_layer=0,usage=0; + std::optional mip_count,layer_count; + template void with_native(Execute execute) const & { + wgpu::TextureViewDescriptor result{};result.label=wgpu::StringView(label.data(),label.size()); + result.format=format;result.dimension=dimension;result.aspect=aspect;result.baseMipLevel=base_mip;result.baseArrayLayer=base_layer; + result.mipLevelCount=mip_count.value_or(wgpu::kMipLevelCountUndefined);result.arrayLayerCount=layer_count.value_or(wgpu::kArrayLayerCountUndefined); + result.usage=static_cast(usage&0x3fu); + // Explicit UINT_MAX counts must not become native "unspecified". + // Unknown browser usage bits must not enable native-only capabilities. + // Both are WebGPU validation failures, represented by an invalid native + // enum so Dawn returns an error view through its usual error machinery. + if((mip_count&&*mip_count==wgpu::kMipLevelCountUndefined)||(layer_count&&*layer_count==wgpu::kArrayLayerCountUndefined)||(usage&~0x3fu)) + result.dimension=static_cast(0xffffffffu); + wgpu::TextureComponentSwizzleDescriptor components{}; + if(swizzle!=u"rgba") { + const auto component=[](char16_t c) { + switch(c) {case u'r':return wgpu::ComponentSwizzle::R;case u'g':return wgpu::ComponentSwizzle::G; + case u'b':return wgpu::ComponentSwizzle::B;case u'a':return wgpu::ComponentSwizzle::A; + case u'0':return wgpu::ComponentSwizzle::Zero;case u'1':return wgpu::ComponentSwizzle::One; + default:return static_cast(0xffffffffu);} + }; + if(swizzle.size()==4)components.swizzle={component(swizzle[0]),component(swizzle[1]),component(swizzle[2]),component(swizzle[3])}; + else components.swizzle.r=static_cast(0xffffffffu); + result.nextInChain=&components; + } + execute(result); + } +}; +inline bool read_webgpu_texture_view_descriptor(v8::Isolate* isolate,v8::Local context,v8::Local input,webgpu_texture_view_descriptor& output) { + webgpu_state_reader reader(isolate,context,input);webgpu_texture_view_descriptor converted;v8::Local value; + const auto optional_count=[&](const char* name,std::optional& output) { + if(!reader.get(name,value))return false;if(value->IsUndefined())return true; + v8::Local number;if(!value->ToNumber(context).ToLocal(&number))return false; + const double n=std::trunc(number->Value());if(!std::isfinite(n)||n<0||n>4294967295.0)return reader.fail("Texture view count out of range"); + output=static_cast(n);return true; + }; + if(!reader.get("label",value))return false; + if(!value->IsUndefined()) { + v8::Local text;if(!value->ToString(context).ToLocal(&text))return false; + v8::String::Utf8Value bytes(isolate,text);if(!*bytes)return false;converted.label.assign(*bytes,bytes.length()); + } + if(!optional_count("arrayLayerCount",converted.layer_count)||!reader.enumeration("aspect",converted.aspect) + ||!reader.uint32("baseArrayLayer",converted.base_layer)||!reader.uint32("baseMipLevel",converted.base_mip) + ||!reader.enumeration("dimension",converted.dimension)||!reader.enumeration("format",converted.format) + ||!optional_count("mipLevelCount",converted.mip_count)||!reader.get("swizzle",value))return false; + if(!value->IsUndefined()) { + v8::Local text;if(!value->ToString(context).ToLocal(&text))return false; + v8::String::Value units(isolate,text);if(!*units)return false;converted.swizzle.assign(reinterpret_cast(*units),units.length()); + } + if(!reader.uint32("usage",converted.usage))return false; + output=std::move(converted);return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_texture_views.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_texture_views.h new file mode 100644 index 000000000..3cb749829 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_texture_views.h @@ -0,0 +1,15 @@ +#pragma once +#include "v8_webgpu_labeled_resources.h" +namespace webscene::graphics { +struct v8_webgpu_texture_views_traits { + using native_type=wgpu::TextureView; + static constexpr const char* name="GPUTextureView"; + template static void with(dawn_device& device,resource_handle handle,Execute execute) { + device.with_texture_view(handle,std::move(execute)); + } + static graphics_command release(resource_handle device,resource_handle handle) noexcept { + return graphics_service::deferred_texture_view_release(device,handle); + } +}; +using v8_webgpu_texture_views=v8_webgpu_labeled_resources; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_textures.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_textures.h new file mode 100644 index 000000000..944455721 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_textures.h @@ -0,0 +1,69 @@ +#pragma once +#include "v8_webgpu_texture_views.h" +#include "v8_webgpu_texture_descriptor.h" +#include "v8_webgpu_texture_view_descriptor.h" +namespace webscene::graphics { +struct v8_webgpu_textures_traits { + using native_type=wgpu::Texture;static constexpr const char* name="GPUTexture"; + template static void with(dawn_device& device,resource_handle handle,Execute execute){device.with_texture(handle,std::move(execute));} + static graphics_command release(resource_handle device,resource_handle handle)noexcept{return graphics_service::deferred_texture_release(device,handle);} +}; +class v8_webgpu_textures:public v8_webgpu_labeled_resources { + using base=v8_webgpu_labeled_resources; + v8::Global metadata_key_; + v8_webgpu_texture_views views_; + static void metadata(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info);if(!item)return;auto* registry=static_cast(item->registry); + auto context=info.GetIsolate()->GetCurrentContext();v8::Local data,value; + if(info.This()->GetPrivate(context,registry->metadata_key_.Get(info.GetIsolate())).ToLocal(&data) + &&data->IsArray()&&data.As()->Get(context,info.Data().As()->Value()).ToLocal(&value))info.GetReturnValue().Set(value); + } + static void destroy(const v8::FunctionCallbackInfo& info) { + auto* item=receiver(info);if(!item)return; + try{item->service->with_device(item->device,[&](auto& owned){owned.destroy_texture(item->resource);});} + catch(const std::exception&){fail(info.GetIsolate(),"GPUTexture ownership unavailable");} + } + static void create_view(const v8::FunctionCallbackInfo& info) { + if(!receiver(info))return;auto* isolate=info.GetIsolate();auto context=isolate->GetCurrentContext(); + try { + webgpu_texture_view_descriptor descriptor; + if(!read_webgpu_texture_view_descriptor(isolate,context,info[0],descriptor))return; + auto* item=receiver(info);if(!item)return; + auto* registry=static_cast(item->registry); + resource_handle view; + descriptor.with_native([&](const auto& native){item->service->with_device(item->device,[&](auto& owned){view=owned.create_texture_view(item->resource,native);});}); + v8::Local wrapper; + try { + if(!registry->views_.wrap(context,*item->service,item->device,view,info.This(),descriptor.label).ToLocal(&wrapper)) { + item->service->with_device(item->device,[&](auto& owned){owned.release_texture_view(view);}); + fail(isolate,"Texture view wrapper capacity exhausted");return; + } + }catch(...){item->service->with_device(item->device,[&](auto& owned){owned.release_texture_view(view);});throw;} + info.GetReturnValue().Set(wrapper); + }catch(const std::exception&){fail(isolate,"Texture view creation failed");} + } +public: + v8_webgpu_textures(v8::Isolate* isolate,v8::Local context,size_t capacity=1024,size_t view_capacity=4096) + :base(isolate,context,capacity),views_(isolate,context,view_capacity) { + metadata_key_.Reset(isolate,v8::Private::New(isolate));auto prototype=prototype_.Get(isolate); + auto view=v8::Function::New(context,create_view,{},0).ToLocalChecked();auto destroy_fn=v8::Function::New(context,destroy).ToLocalChecked(); + if(!prototype->Set(context,v8::String::NewFromUtf8Literal(isolate,"createView"),view).FromMaybe(false) + ||!prototype->Set(context,v8::String::NewFromUtf8Literal(isolate,"destroy"),destroy_fn).FromMaybe(false))throw std::runtime_error("Texture prototype initialization failed"); + const char* names[]={"width","height","depthOrArrayLayers","mipLevelCount","sampleCount","dimension","format","usage"}; + for(uint32_t i=0;i<8;++i) { + auto getter=v8::Function::New(context,metadata,v8::Integer::NewFromUnsigned(isolate,i)).ToLocalChecked(); + prototype->SetAccessorProperty(v8::String::NewFromUtf8(isolate,names[i]).ToLocalChecked(),getter,{}); + } + } + v8::MaybeLocal wrap_texture(v8::Local context,graphics_service& service,resource_handle device,resource_handle texture,v8::Local parent,const webgpu_texture_descriptor& descriptor) { + auto values=v8::Array::New(isolate_,8); + const uint32_t numbers[]={descriptor.size.width,descriptor.size.height,descriptor.size.depthOrArrayLayers,descriptor.mip_levels,descriptor.samples}; + for(uint32_t i=0;i<5;++i)if(!values->Set(context,i,v8::Integer::NewFromUnsigned(isolate_,numbers[i])).FromMaybe(false))return {}; + const auto set_enum=[&](uint32_t index,auto native){for(const auto& [name,value]:webgpu_enum_names::values)if(value==native)return values->Set(context,index,v8::String::NewFromUtf8(isolate_,name.data(),v8::NewStringType::kNormal,static_cast(name.size())).ToLocalChecked()).FromMaybe(false);return false;}; + if(!set_enum(5,descriptor.dimension)||!set_enum(6,descriptor.format)||!values->Set(context,7,v8::Integer::NewFromUnsigned(isolate_,descriptor.usage)).FromMaybe(false))return {}; + v8::Local wrapper;if(!base::wrap(context,service,device,texture,parent,descriptor.label).ToLocal(&wrapper))return {}; + if(!wrapper->SetPrivate(context,metadata_key_.Get(isolate_),values).FromMaybe(false))return {}; + return wrapper; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_vertex_state.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_vertex_state.h new file mode 100644 index 000000000..165301576 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/v8_webgpu_vertex_state.h @@ -0,0 +1,66 @@ +#pragma once +#include "v8_webgpu_render_state.h" +#include "v8_webgpu_programmable_stage.h" +namespace webscene::graphics { +// WebIDL sequence conversion caches next once and reads each result's done +// before value. The caller owns transactional output storage. +template bool read_webgpu_sequence(v8::Isolate* isolate,v8::Local context,v8::Local input,Convert convert,v8::Local iterator_method={}) { + webgpu_state_reader errors(isolate,context,input); + if(!input->IsObject())return errors.fail("WebGPU sequence must be an iterable object"); + v8::Local method,iterator,next; + if(!iterator_method.IsEmpty())method=iterator_method; + else if(!input.As()->Get(context,v8::Symbol::GetIterator(isolate)).ToLocal(&method))return false; + if(!method->IsFunction())return errors.fail("WebGPU sequence is not iterable"); + if(!method.As()->Call(context,input,0,nullptr).ToLocal(&iterator))return false; + if(!iterator->IsObject())return errors.fail("Iterator must return an object"); + if(!iterator.As()->Get(context,v8::String::NewFromUtf8Literal(isolate,"next")).ToLocal(&next))return false; + if(!next->IsFunction())return errors.fail("Iterator next must be callable"); + for(;;) { + v8::Local step,done,value; + if(!next.As()->Call(context,iterator,0,nullptr).ToLocal(&step))return false; + if(!step->IsObject())return errors.fail("Iterator result must be an object"); + if(!step.As()->Get(context,v8::String::NewFromUtf8Literal(isolate,"done")).ToLocal(&done))return false; + if(done->BooleanValue(isolate))return true; + if(!step.As()->Get(context,v8::String::NewFromUtf8Literal(isolate,"value")).ToLocal(&value))return false; + if(!convert(value))return false; + } +} +struct webgpu_vertex_buffer_layout { + uint64_t array_stride{}; + wgpu::VertexStepMode step_mode=wgpu::VertexStepMode::Vertex; + std::vector attributes; + wgpu::VertexBufferLayout native() const & { + wgpu::VertexBufferLayout result{};result.arrayStride=array_stride;result.stepMode=step_mode; + result.attributeCount=attributes.size();result.attributes=attributes.data();return result; + } + wgpu::VertexBufferLayout native() const &&=delete; +}; +inline bool read_webgpu_vertex_buffer_layout(v8::Isolate* isolate,v8::Local context,v8::Local input,webgpu_vertex_buffer_layout& output) { + webgpu_state_reader reader(isolate,context,input);webgpu_vertex_buffer_layout converted;v8::Local value; + if(!reader.uint64("arrayStride",converted.array_stride,true)||!reader.get("attributes",value))return false; + if(!read_webgpu_sequence(isolate,context,value,[&](auto item) { + webgpu_state_reader attribute(isolate,context,item);wgpu::VertexAttribute result{}; + if(!attribute.enumeration("format",result.format,true)||!attribute.uint64("offset",result.offset,true) + ||!attribute.uint32("shaderLocation",result.shaderLocation,true))return false; + converted.attributes.push_back(result);return true; + }))return false; + if(!reader.enumeration("stepMode",converted.step_mode))return false; + output=std::move(converted);return true; +} +struct webgpu_vertex_state { + webgpu_programmable_stage stage; + std::vector> buffers; +}; +inline bool read_webgpu_vertex_state(v8::Isolate* isolate,v8::Local context,v8::Local input,webgpu_vertex_state& output) { + webgpu_vertex_state converted; + if(!read_webgpu_programmable_stage(isolate,context,input,converted.stage))return false; + webgpu_state_reader reader(isolate,context,input);v8::Local value; + if(!reader.get("buffers",value))return false; + if(!value->IsUndefined() && !read_webgpu_sequence(isolate,context,value,[&](auto item) { + if(item->IsNullOrUndefined()){converted.buffers.push_back(std::nullopt);return true;} + webgpu_vertex_buffer_layout layout;if(!read_webgpu_vertex_buffer_layout(isolate,context,item,layout))return false; + converted.buffers.emplace_back(std::move(layout));return true; + }))return false; + output=std::move(converted);return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_adapter_info.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_adapter_info.h new file mode 100644 index 000000000..e53d9a7f4 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_adapter_info.h @@ -0,0 +1,52 @@ +#pragma once +#include +#include +#include +#include +namespace webscene::graphics { +struct webgpu_adapter_info { + std::string vendor,architecture,device,description; + uint32_t subgroup_min_size=4,subgroup_max_size=128; + bool is_fallback_adapter=false; +}; +inline std::string webgpu_info_string(wgpu::StringView value) { + if(!value.data)return {}; + return value.length==wgpu::kStrlen?std::string(value.data):std::string(value.data,value.length); +} +inline std::string webgpu_info_identifier(wgpu::StringView value) { + auto text=webgpu_info_string(value); + bool segment=false; + for(char c:text) { + if((c>='a'&&c<='z')||(c>='0'&&c<='9'))segment=true; + else if(c=='-'&&segment)segment=false; + else return {}; + } + return segment?text:std::string{}; +} +// Mirrors the pinned Dawn Vulkan fallback filter and gpu_info.json. Other +// shipping backends reject forceFallbackAdapter. Do not equate CPU with fallback. +inline bool webgpu_adapter_is_fallback(wgpu::BackendType backend,uint32_t vendor,uint32_t device) { + switch(backend) { + case wgpu::BackendType::Vulkan:return vendor==0x1ae0 && device==0xc0de; + case wgpu::BackendType::Metal:case wgpu::BackendType::D3D11:case wgpu::BackendType::D3D12: + case wgpu::BackendType::OpenGL:case wgpu::BackendType::OpenGLES:case wgpu::BackendType::Null:return false; + default:throw std::invalid_argument("Adapter backend has no qualified fallback classification"); + } +} +inline webgpu_adapter_info read_webgpu_adapter_info(const wgpu::Adapter& adapter) { + if(!adapter)throw std::invalid_argument("Adapter information requires a native adapter"); + wgpu::AdapterInfo native{}; + if(adapter.GetInfo(&native)!=wgpu::Status::Success)throw std::runtime_error("Native adapter information unavailable"); + webgpu_adapter_info result; + result.vendor=webgpu_info_identifier(native.vendor); + result.architecture=webgpu_info_identifier(native.architecture); + result.device=webgpu_info_identifier(native.device); + result.description=webgpu_info_string(native.description); + result.is_fallback_adapter=webgpu_adapter_is_fallback(native.backendType,native.vendorID,native.deviceID); + if(adapter.HasFeature(wgpu::FeatureName::Subgroups)) { + if(!native.subgroupMinSize || native.subgroupMaxSize +#include +#include + +namespace webscene::graphics { +// GPURequestAdapterOptions from @webref/idl 3.82.1. Browser dictionary fields +// remain separate from Dawn's backend/private adapter selection extensions. +struct webgpu_adapter_options { + std::u16string feature_level=u"core"; + std::optional power_preference; + bool force_fallback_adapter=false; + bool xr_compatible=false; +}; +// An absent descriptor means discovery must resolve null, not submit a +// different request. Backend choice belongs to the host, never the JS dictionary. +inline std::optional make_dawn_adapter_options( + const webgpu_adapter_options& requested,wgpu::BackendType host_backend=wgpu::BackendType::Undefined) { + if (requested.xr_compatible) return {}; // No WebXR device integration yet. + wgpu::RequestAdapterOptions native{}; + if (requested.feature_level==u"core") native.featureLevel=wgpu::FeatureLevel::Core; + else if (requested.feature_level==u"compatibility") native.featureLevel=wgpu::FeatureLevel::Compatibility; + else return {}; + native.powerPreference=requested.power_preference.value_or(wgpu::PowerPreference::Undefined); + native.forceFallbackAdapter=requested.force_fallback_adapter; + native.backendType=host_backend; + return native; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_buffer_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_buffer_descriptor.h new file mode 100644 index 000000000..98b8ecc0d --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_buffer_descriptor.h @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace webscene::graphics { +struct webgpu_buffer_descriptor { + std::string label; + uint64_t size{}; + uint32_t usage{}; + bool mapped_at_creation{}; +}; +// GPUBufferUsage from the pinned browser IDL. Dawn additionally supports private +// flags (currently TexelBuffer at 0x400); never pass browser bits by raw cast. +inline std::optional webgpu_buffer_usage(uint32_t bits) { + constexpr std::array flags{ + wgpu::BufferUsage::MapRead,wgpu::BufferUsage::MapWrite, + wgpu::BufferUsage::CopySrc,wgpu::BufferUsage::CopyDst, + wgpu::BufferUsage::Index,wgpu::BufferUsage::Vertex, + wgpu::BufferUsage::Uniform,wgpu::BufferUsage::Storage, + wgpu::BufferUsage::Indirect,wgpu::BufferUsage::QueryResolve}; + if (bits & ~0x3ffu) return std::nullopt; + auto result=wgpu::BufferUsage::None; + for (size_t i=0;i make_dawn_buffer_descriptor(const webgpu_buffer_descriptor& source) { + const auto usage=webgpu_buffer_usage(source.usage); + if (!usage) return std::nullopt; + wgpu::BufferDescriptor result{}; + result.label=wgpu::StringView(source.label.data(),source.label.size()); + result.size=source.size; + result.usage=*usage; + result.mappedAtCreation=source.mapped_at_creation; + return result; +} +// Reject temporaries: they would leave a dangling label in the borrowed result. +std::optional make_dawn_buffer_descriptor(webgpu_buffer_descriptor&&)=delete; +std::optional make_dawn_buffer_descriptor(const webgpu_buffer_descriptor&&)=delete; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_canvas_interop.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_canvas_interop.h new file mode 100644 index 000000000..800af76bd --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_canvas_interop.h @@ -0,0 +1,5 @@ +#pragma once +namespace webscene::graphics { +// Host-selected transport; never accepted through a JavaScript descriptor. +enum class webgpu_canvas_interop { none,iosurface,dxgi }; +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_canvas_texture_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_canvas_texture_descriptor.h new file mode 100644 index 000000000..13d916baa --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_canvas_texture_descriptor.h @@ -0,0 +1,25 @@ +#pragma once +#include "v8_webgpu_canvas_configuration.h" +#include "v8_webgpu_texture_descriptor.h" +namespace webscene::graphics { +// Content-side checks specific to canvas formats/usage. Required-format feature +// checks must precede these in configure; native texture validation follows. +inline void validate_webgpu_canvas_format_usage(const webgpu_canvas_configuration& configuration) { + switch(configuration.format) { + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::BGRA8Unorm: + case wgpu::TextureFormat::RGBA16Float:break; + default:throw std::invalid_argument("Unsupported WebGPU canvas format"); + } + if(configuration.usage&0x20u)throw std::invalid_argument("Canvas textures cannot use TRANSIENT_ATTACHMENT"); +} +// Snapshot the canvas bitmap size, not its CSS layout dimensions. In particular, +// zero dimensions remain zero for subsequent native validation, and a custom +// usage does not acquire RENDER_ATTACHMENT or presenter-only access implicitly. +inline webgpu_texture_descriptor webgpu_canvas_texture_descriptor(const webgpu_canvas_configuration& configuration,uint32_t width,uint32_t height) { + webgpu_texture_descriptor result; + result.size={width,height,1};result.format=configuration.format;result.usage=configuration.usage; + result.view_formats=configuration.view_formats; + return result; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_compilation_info.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_compilation_info.h new file mode 100644 index 000000000..a7948aa0a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_compilation_info.h @@ -0,0 +1,59 @@ +#pragma once +#include +#include +#include +#include + +namespace webscene::graphics { +// Owned diagnostic data copied before Dawn's callback-owned pointers expire. +// Base positions are Dawn byte offsets. The pinned backend also supplies +// UTF-16 positions through DawnCompilationMessageUtf16; preserve them explicitly +// so the V8 adapter never accidentally exposes byte offsets as WebGPU offsets. +struct webgpu_compilation_message { + std::string message; + wgpu::CompilationMessageType type; + uint64_t line_num, line_pos, offset, length; + bool has_utf16{}; + uint64_t utf16_line_pos{},utf16_offset{},utf16_length{}; +}; +struct webgpu_compilation_info { + std::vector messages; + static webgpu_compilation_info copy(const wgpu::CompilationInfo& source, + size_t maximum_messages=1024,size_t maximum_bytes=1024*1024) { + if(source.messageCount>maximum_messages) + throw std::length_error("Shader diagnostic count exceeds budget"); + if(source.messageCount&&!source.messages) + throw std::invalid_argument("Shader diagnostics lack message storage"); + webgpu_compilation_info result; + result.messages.reserve(source.messageCount); + size_t remaining=maximum_bytes; + for(size_t i=0;iremaining)throw std::length_error("Shader diagnostic text exceeds budget"); + if(length&&!input.message.data)throw std::invalid_argument("Shader diagnostic text is null"); + result.messages.push_back({length?std::string(input.message.data,length):std::string{}, + input.type,input.lineNum,input.linePos,input.offset,input.length}); + auto& copied=result.messages.back(); + size_t chain_length=0; + for(auto* chain=input.nextInChain;chain;chain=chain->nextInChain) { + if(++chain_length>16)throw std::length_error("Shader diagnostic extension chain exceeds budget"); + if(chain->sType!=wgpu::SType::DawnCompilationMessageUtf16)continue; + if(copied.has_utf16)throw std::invalid_argument("Duplicate UTF-16 diagnostic extension"); + const auto* utf16=static_cast(chain); + copied.has_utf16=true; + copied.utf16_line_pos=utf16->linePos; + copied.utf16_offset=utf16->offset; + copied.utf16_length=utf16->length; + } + remaining-=length; + } + return result; + } +}; +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_device_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_device_descriptor.h new file mode 100644 index 000000000..53135eb2e --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_device_descriptor.h @@ -0,0 +1,13 @@ +#pragma once +#include "webgpu_feature_names.h" +#include +#include +namespace webscene::graphics { +struct webgpu_device_descriptor { + std::string label,queue_label; + std::vector required_features; + // DOMString keys preserve UTF-16, including unpaired surrogates. Unknown + // names and undefined values must reach subsequent WebGPU validation intact. + std::vector>> required_limits; +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_feature_names.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_feature_names.h new file mode 100644 index 000000000..7d344d6ec --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_feature_names.h @@ -0,0 +1,56 @@ +// Generated by tools/webidl-v8-bindings/generate-webgpu-features.mjs. +// @webref/idl 3.82.1; webgpu.idl SHA256 009934b7965059a9ac5a855b3d809e6f18841ab1021605c84751b6e6aa7f978d +#pragma once +#include +#include +#include +#include +#include +#include + +namespace webscene::graphics { +struct webgpu_feature_name { std::string_view name; wgpu::FeatureName native; }; +inline constexpr std::array webgpu_feature_names{{ + {"core-features-and-limits",wgpu::FeatureName::CoreFeaturesAndLimits}, + {"depth-clip-control",wgpu::FeatureName::DepthClipControl}, + {"depth32float-stencil8",wgpu::FeatureName::Depth32FloatStencil8}, + {"texture-compression-bc",wgpu::FeatureName::TextureCompressionBC}, + {"texture-compression-bc-sliced-3d",wgpu::FeatureName::TextureCompressionBCSliced3D}, + {"texture-compression-etc2",wgpu::FeatureName::TextureCompressionETC2}, + {"texture-compression-astc",wgpu::FeatureName::TextureCompressionASTC}, + {"texture-compression-astc-sliced-3d",wgpu::FeatureName::TextureCompressionASTCSliced3D}, + {"timestamp-query",wgpu::FeatureName::TimestampQuery}, + {"indirect-first-instance",wgpu::FeatureName::IndirectFirstInstance}, + {"shader-f16",wgpu::FeatureName::ShaderF16}, + {"rg11b10ufloat-renderable",wgpu::FeatureName::RG11B10UfloatRenderable}, + {"bgra8unorm-storage",wgpu::FeatureName::BGRA8UnormStorage}, + {"float32-filterable",wgpu::FeatureName::Float32Filterable}, + {"float32-blendable",wgpu::FeatureName::Float32Blendable}, + {"clip-distances",wgpu::FeatureName::ClipDistances}, + {"dual-source-blending",wgpu::FeatureName::DualSourceBlending}, + {"subgroups",wgpu::FeatureName::Subgroups}, + {"texture-formats-tier1",wgpu::FeatureName::TextureFormatsTier1}, + {"texture-formats-tier2",wgpu::FeatureName::TextureFormatsTier2}, + {"primitive-index",wgpu::FeatureName::PrimitiveIndex}, + {"texture-component-swizzle",wgpu::FeatureName::TextureComponentSwizzle}, + {"subgroup-size-control",wgpu::FeatureName::SubgroupSizeControl}, +}}; +inline std::optional webgpu_feature_from_name(std::string_view name) { + for (const auto& feature:webgpu_feature_names) if (feature.name==name) return feature.native; + return std::nullopt; +} +inline std::optional webgpu_feature_to_name(wgpu::FeatureName native) { + for (const auto& feature:webgpu_feature_names) if (feature.native==native) return feature.name; + return std::nullopt; +} +// Both Adapter and Device implement HasFeature. Device features are the enabled +// subset, not the adapter's full capabilities. Never enumerate native extensions +// into the browser surface, even when the host enables them for shared images. +template std::vector webgpu_supported_feature_names(const Source& source) { + if (!source) throw std::invalid_argument("WebGPU feature source is null"); + std::vector result; + for (const auto& feature:webgpu_feature_names) + if (source.HasFeature(feature.native)) result.push_back(feature.name); + return result; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_limit_names.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_limit_names.h new file mode 100644 index 000000000..97acbfde3 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_limit_names.h @@ -0,0 +1,77 @@ +// Generated by tools/webidl-v8-bindings/generate-webgpu-limits.mjs. +// @webref/idl 3.82.1; webgpu.idl SHA256 009934b7965059a9ac5a855b3d809e6f18841ab1021605c84751b6e6aa7f978d +#pragma once +#include +#include +#include +#include +#include +#include +namespace webscene::graphics { +struct webgpu_limit_name { + std::u16string_view name; + std::variant member; + uint64_t read(const wgpu::Limits& limits,const wgpu::CompatibilityModeLimits& compatibility) const { + return std::visit([&](auto field) { + if constexpr (std::is_same_v) return uint64_t(compatibility.*field); + else return uint64_t(limits.*field); + },member); + } + // Reject values that cannot be represented, including Dawn's undefined + // sentinel. Never let a requested integer silently become an omitted limit. + bool write(wgpu::Limits& limits,wgpu::CompatibilityModeLimits& compatibility,uint64_t value) const { + return std::visit([&](auto field) { + if constexpr (std::is_same_v) { + if (value>=UINT32_MAX) return false; + compatibility.*field=static_cast(value); return true; + } else { + using T=std::remove_reference_t; + if (value>=std::numeric_limits::max()) return false; + limits.*field=static_cast(value); return true; + } + },member); + } +}; +inline const std::array webgpu_limit_names{{ + {u"maxTextureDimension1D",&wgpu::Limits::maxTextureDimension1D}, + {u"maxTextureDimension2D",&wgpu::Limits::maxTextureDimension2D}, + {u"maxTextureDimension3D",&wgpu::Limits::maxTextureDimension3D}, + {u"maxTextureArrayLayers",&wgpu::Limits::maxTextureArrayLayers}, + {u"maxBindGroups",&wgpu::Limits::maxBindGroups}, + {u"maxBindGroupsPlusVertexBuffers",&wgpu::Limits::maxBindGroupsPlusVertexBuffers}, + {u"maxImmediateSize",&wgpu::Limits::maxImmediateSize}, + {u"maxBindingsPerBindGroup",&wgpu::Limits::maxBindingsPerBindGroup}, + {u"maxDynamicUniformBuffersPerPipelineLayout",&wgpu::Limits::maxDynamicUniformBuffersPerPipelineLayout}, + {u"maxDynamicStorageBuffersPerPipelineLayout",&wgpu::Limits::maxDynamicStorageBuffersPerPipelineLayout}, + {u"maxSampledTexturesPerShaderStage",&wgpu::Limits::maxSampledTexturesPerShaderStage}, + {u"maxSamplersPerShaderStage",&wgpu::Limits::maxSamplersPerShaderStage}, + {u"maxStorageBuffersPerShaderStage",&wgpu::Limits::maxStorageBuffersPerShaderStage}, + {u"maxStorageBuffersInVertexStage",&wgpu::CompatibilityModeLimits::maxStorageBuffersInVertexStage}, + {u"maxStorageBuffersInFragmentStage",&wgpu::CompatibilityModeLimits::maxStorageBuffersInFragmentStage}, + {u"maxStorageTexturesPerShaderStage",&wgpu::Limits::maxStorageTexturesPerShaderStage}, + {u"maxStorageTexturesInVertexStage",&wgpu::CompatibilityModeLimits::maxStorageTexturesInVertexStage}, + {u"maxStorageTexturesInFragmentStage",&wgpu::CompatibilityModeLimits::maxStorageTexturesInFragmentStage}, + {u"maxUniformBuffersPerShaderStage",&wgpu::Limits::maxUniformBuffersPerShaderStage}, + {u"maxUniformBufferBindingSize",&wgpu::Limits::maxUniformBufferBindingSize}, + {u"maxStorageBufferBindingSize",&wgpu::Limits::maxStorageBufferBindingSize}, + {u"minUniformBufferOffsetAlignment",&wgpu::Limits::minUniformBufferOffsetAlignment}, + {u"minStorageBufferOffsetAlignment",&wgpu::Limits::minStorageBufferOffsetAlignment}, + {u"maxVertexBuffers",&wgpu::Limits::maxVertexBuffers}, + {u"maxBufferSize",&wgpu::Limits::maxBufferSize}, + {u"maxVertexAttributes",&wgpu::Limits::maxVertexAttributes}, + {u"maxVertexBufferArrayStride",&wgpu::Limits::maxVertexBufferArrayStride}, + {u"maxInterStageShaderVariables",&wgpu::Limits::maxInterStageShaderVariables}, + {u"maxColorAttachments",&wgpu::Limits::maxColorAttachments}, + {u"maxColorAttachmentBytesPerSample",&wgpu::Limits::maxColorAttachmentBytesPerSample}, + {u"maxComputeWorkgroupStorageSize",&wgpu::Limits::maxComputeWorkgroupStorageSize}, + {u"maxComputeInvocationsPerWorkgroup",&wgpu::Limits::maxComputeInvocationsPerWorkgroup}, + {u"maxComputeWorkgroupSizeX",&wgpu::Limits::maxComputeWorkgroupSizeX}, + {u"maxComputeWorkgroupSizeY",&wgpu::Limits::maxComputeWorkgroupSizeY}, + {u"maxComputeWorkgroupSizeZ",&wgpu::Limits::maxComputeWorkgroupSizeZ}, + {u"maxComputeWorkgroupsPerDimension",&wgpu::Limits::maxComputeWorkgroupsPerDimension}, +}}; +inline const webgpu_limit_name* webgpu_limit_from_name(std::u16string_view name) { + for (const auto& limit:webgpu_limit_names) if (limit.name==name) return &limit; + return nullptr; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_prepared_device_descriptor.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_prepared_device_descriptor.h new file mode 100644 index 000000000..44989cf6a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_prepared_device_descriptor.h @@ -0,0 +1,76 @@ +#pragma once +#include "webgpu_device_descriptor.h" +#include "webgpu_canvas_interop.h" +#include "webgpu_required_limits.h" +#include +#include +namespace webscene::graphics { +enum class webgpu_device_request_error { none,unsupported_feature,operation_error }; +// Owns all storage borrowed by the Dawn descriptor. Non-movable because the +// optional compatibility chain points into this allocation. Keep alive through +// RequestDevice; Dawn copies descriptor storage before that call returns. +class webgpu_prepared_device_descriptor { + webgpu_device_descriptor requested_; + wgpu::Limits limits_{}; + wgpu::CompatibilityModeLimits compatibility_{}; + webgpu_prepared_device_descriptor()=default; +public: + webgpu_prepared_device_descriptor(const webgpu_prepared_device_descriptor&)=delete; + webgpu_prepared_device_descriptor& operator=(const webgpu_prepared_device_descriptor&)=delete; + wgpu::DeviceDescriptor native() const & { + wgpu::DeviceDescriptor descriptor{}; + descriptor.label=wgpu::StringView(requested_.label.data(),requested_.label.size()); + descriptor.defaultQueue.label=wgpu::StringView(requested_.queue_label.data(),requested_.queue_label.size()); + descriptor.requiredFeatureCount=requested_.required_features.size(); + descriptor.requiredFeatures=requested_.required_features.data(); + descriptor.requiredLimits=&limits_; + return descriptor; + } + wgpu::DeviceDescriptor native() const &&=delete; + static std::unique_ptr prepare(const webgpu_device_descriptor& requested, + const wgpu::Adapter& adapter,bool consumed,webgpu_device_request_error& error,webgpu_canvas_interop interop=webgpu_canvas_interop::none) { + error=webgpu_device_request_error::operation_error; + if (!adapter) return {}; + // Feature failures have TypeError precedence over consumed/limit errors. + for (auto feature:requested.required_features) { + if (!webgpu_feature_to_name(feature) || !adapter.HasFeature(feature)) { + error=webgpu_device_request_error::unsupported_feature; return {}; + } + } + if (consumed) return {}; + bool need_compatibility=false; + for (const auto& [name,value]:requested.required_limits) { + const auto* limit=webgpu_limit_from_name(name); + if (value && limit && std::holds_alternative(limit->member)) need_compatibility=true; + } + wgpu::Limits supported{}; + wgpu::CompatibilityModeLimits supported_compatibility{}; + if (need_compatibility) supported.nextInChain=&supported_compatibility; + if (adapter.GetLimits(&supported)!=wgpu::Status::Success) return {}; + auto result=std::unique_ptr(new webgpu_prepared_device_descriptor()); + if (!prepare_webgpu_required_limits(requested.required_limits,supported,supported_compatibility,result->limits_,result->compatibility_)) return {}; + result->requested_=requested; + // WebGPU treats requiredFeatures as a set. Avoid passing duplicate + // native features while preserving the first occurrence's order. + auto& features=result->requested_.required_features; + for (size_t i=0;ilimits_.nextInChain=&result->compatibility_; + if(interop==webgpu_canvas_interop::dxgi) { + for(auto feature:{wgpu::FeatureName::SharedTextureMemoryDXGISharedHandle,wgpu::FeatureName::SharedFenceDXGISharedHandle}) { + if(!adapter.HasFeature(feature))return {}; + features.push_back(feature); + } + } + error=webgpu_device_request_error::none; return result; + } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_render_enums.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_render_enums.h new file mode 100644 index 000000000..55ca0f3fb --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_render_enums.h @@ -0,0 +1,333 @@ +// Generated by tools/webidl-v8-bindings/generate-webgpu-render-enums.mjs. +// @webref/idl 3.82.1; SHA256 009934b7965059a9ac5a855b3d809e6f18841ab1021605c84751b6e6aa7f978d +#pragma once +#include +#include +#include +#include +namespace webscene::graphics { +template struct webgpu_enum_names; +template<> struct webgpu_enum_names { + static inline constexpr std::array,5> values{{ + {"point-list",wgpu::PrimitiveTopology::PointList}, + {"line-list",wgpu::PrimitiveTopology::LineList}, + {"line-strip",wgpu::PrimitiveTopology::LineStrip}, + {"triangle-list",wgpu::PrimitiveTopology::TriangleList}, + {"triangle-strip",wgpu::PrimitiveTopology::TriangleStrip}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,2> values{{ + {"uint16",wgpu::IndexFormat::Uint16}, + {"uint32",wgpu::IndexFormat::Uint32}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,2> values{{ + {"ccw",wgpu::FrontFace::CCW}, + {"cw",wgpu::FrontFace::CW}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,3> values{{ + {"none",wgpu::CullMode::None}, + {"front",wgpu::CullMode::Front}, + {"back",wgpu::CullMode::Back}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,17> values{{ + {"zero",wgpu::BlendFactor::Zero}, + {"one",wgpu::BlendFactor::One}, + {"src",wgpu::BlendFactor::Src}, + {"one-minus-src",wgpu::BlendFactor::OneMinusSrc}, + {"src-alpha",wgpu::BlendFactor::SrcAlpha}, + {"one-minus-src-alpha",wgpu::BlendFactor::OneMinusSrcAlpha}, + {"dst",wgpu::BlendFactor::Dst}, + {"one-minus-dst",wgpu::BlendFactor::OneMinusDst}, + {"dst-alpha",wgpu::BlendFactor::DstAlpha}, + {"one-minus-dst-alpha",wgpu::BlendFactor::OneMinusDstAlpha}, + {"src-alpha-saturated",wgpu::BlendFactor::SrcAlphaSaturated}, + {"constant",wgpu::BlendFactor::Constant}, + {"one-minus-constant",wgpu::BlendFactor::OneMinusConstant}, + {"src1",wgpu::BlendFactor::Src1}, + {"one-minus-src1",wgpu::BlendFactor::OneMinusSrc1}, + {"src1-alpha",wgpu::BlendFactor::Src1Alpha}, + {"one-minus-src1-alpha",wgpu::BlendFactor::OneMinusSrc1Alpha}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,5> values{{ + {"add",wgpu::BlendOperation::Add}, + {"subtract",wgpu::BlendOperation::Subtract}, + {"reverse-subtract",wgpu::BlendOperation::ReverseSubtract}, + {"min",wgpu::BlendOperation::Min}, + {"max",wgpu::BlendOperation::Max}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,8> values{{ + {"keep",wgpu::StencilOperation::Keep}, + {"zero",wgpu::StencilOperation::Zero}, + {"replace",wgpu::StencilOperation::Replace}, + {"invert",wgpu::StencilOperation::Invert}, + {"increment-clamp",wgpu::StencilOperation::IncrementClamp}, + {"decrement-clamp",wgpu::StencilOperation::DecrementClamp}, + {"increment-wrap",wgpu::StencilOperation::IncrementWrap}, + {"decrement-wrap",wgpu::StencilOperation::DecrementWrap}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,8> values{{ + {"never",wgpu::CompareFunction::Never}, + {"less",wgpu::CompareFunction::Less}, + {"equal",wgpu::CompareFunction::Equal}, + {"less-equal",wgpu::CompareFunction::LessEqual}, + {"greater",wgpu::CompareFunction::Greater}, + {"not-equal",wgpu::CompareFunction::NotEqual}, + {"greater-equal",wgpu::CompareFunction::GreaterEqual}, + {"always",wgpu::CompareFunction::Always}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,101> values{{ + {"r8unorm",wgpu::TextureFormat::R8Unorm}, + {"r8snorm",wgpu::TextureFormat::R8Snorm}, + {"r8uint",wgpu::TextureFormat::R8Uint}, + {"r8sint",wgpu::TextureFormat::R8Sint}, + {"r16unorm",wgpu::TextureFormat::R16Unorm}, + {"r16snorm",wgpu::TextureFormat::R16Snorm}, + {"r16uint",wgpu::TextureFormat::R16Uint}, + {"r16sint",wgpu::TextureFormat::R16Sint}, + {"r16float",wgpu::TextureFormat::R16Float}, + {"rg8unorm",wgpu::TextureFormat::RG8Unorm}, + {"rg8snorm",wgpu::TextureFormat::RG8Snorm}, + {"rg8uint",wgpu::TextureFormat::RG8Uint}, + {"rg8sint",wgpu::TextureFormat::RG8Sint}, + {"r32uint",wgpu::TextureFormat::R32Uint}, + {"r32sint",wgpu::TextureFormat::R32Sint}, + {"r32float",wgpu::TextureFormat::R32Float}, + {"rg16unorm",wgpu::TextureFormat::RG16Unorm}, + {"rg16snorm",wgpu::TextureFormat::RG16Snorm}, + {"rg16uint",wgpu::TextureFormat::RG16Uint}, + {"rg16sint",wgpu::TextureFormat::RG16Sint}, + {"rg16float",wgpu::TextureFormat::RG16Float}, + {"rgba8unorm",wgpu::TextureFormat::RGBA8Unorm}, + {"rgba8unorm-srgb",wgpu::TextureFormat::RGBA8UnormSrgb}, + {"rgba8snorm",wgpu::TextureFormat::RGBA8Snorm}, + {"rgba8uint",wgpu::TextureFormat::RGBA8Uint}, + {"rgba8sint",wgpu::TextureFormat::RGBA8Sint}, + {"bgra8unorm",wgpu::TextureFormat::BGRA8Unorm}, + {"bgra8unorm-srgb",wgpu::TextureFormat::BGRA8UnormSrgb}, + {"rgb9e5ufloat",wgpu::TextureFormat::RGB9E5Ufloat}, + {"rgb10a2uint",wgpu::TextureFormat::RGB10A2Uint}, + {"rgb10a2unorm",wgpu::TextureFormat::RGB10A2Unorm}, + {"rg11b10ufloat",wgpu::TextureFormat::RG11B10Ufloat}, + {"rg32uint",wgpu::TextureFormat::RG32Uint}, + {"rg32sint",wgpu::TextureFormat::RG32Sint}, + {"rg32float",wgpu::TextureFormat::RG32Float}, + {"rgba16unorm",wgpu::TextureFormat::RGBA16Unorm}, + {"rgba16snorm",wgpu::TextureFormat::RGBA16Snorm}, + {"rgba16uint",wgpu::TextureFormat::RGBA16Uint}, + {"rgba16sint",wgpu::TextureFormat::RGBA16Sint}, + {"rgba16float",wgpu::TextureFormat::RGBA16Float}, + {"rgba32uint",wgpu::TextureFormat::RGBA32Uint}, + {"rgba32sint",wgpu::TextureFormat::RGBA32Sint}, + {"rgba32float",wgpu::TextureFormat::RGBA32Float}, + {"stencil8",wgpu::TextureFormat::Stencil8}, + {"depth16unorm",wgpu::TextureFormat::Depth16Unorm}, + {"depth24plus",wgpu::TextureFormat::Depth24Plus}, + {"depth24plus-stencil8",wgpu::TextureFormat::Depth24PlusStencil8}, + {"depth32float",wgpu::TextureFormat::Depth32Float}, + {"depth32float-stencil8",wgpu::TextureFormat::Depth32FloatStencil8}, + {"bc1-rgba-unorm",wgpu::TextureFormat::BC1RGBAUnorm}, + {"bc1-rgba-unorm-srgb",wgpu::TextureFormat::BC1RGBAUnormSrgb}, + {"bc2-rgba-unorm",wgpu::TextureFormat::BC2RGBAUnorm}, + {"bc2-rgba-unorm-srgb",wgpu::TextureFormat::BC2RGBAUnormSrgb}, + {"bc3-rgba-unorm",wgpu::TextureFormat::BC3RGBAUnorm}, + {"bc3-rgba-unorm-srgb",wgpu::TextureFormat::BC3RGBAUnormSrgb}, + {"bc4-r-unorm",wgpu::TextureFormat::BC4RUnorm}, + {"bc4-r-snorm",wgpu::TextureFormat::BC4RSnorm}, + {"bc5-rg-unorm",wgpu::TextureFormat::BC5RGUnorm}, + {"bc5-rg-snorm",wgpu::TextureFormat::BC5RGSnorm}, + {"bc6h-rgb-ufloat",wgpu::TextureFormat::BC6HRGBUfloat}, + {"bc6h-rgb-float",wgpu::TextureFormat::BC6HRGBFloat}, + {"bc7-rgba-unorm",wgpu::TextureFormat::BC7RGBAUnorm}, + {"bc7-rgba-unorm-srgb",wgpu::TextureFormat::BC7RGBAUnormSrgb}, + {"etc2-rgb8unorm",wgpu::TextureFormat::ETC2RGB8Unorm}, + {"etc2-rgb8unorm-srgb",wgpu::TextureFormat::ETC2RGB8UnormSrgb}, + {"etc2-rgb8a1unorm",wgpu::TextureFormat::ETC2RGB8A1Unorm}, + {"etc2-rgb8a1unorm-srgb",wgpu::TextureFormat::ETC2RGB8A1UnormSrgb}, + {"etc2-rgba8unorm",wgpu::TextureFormat::ETC2RGBA8Unorm}, + {"etc2-rgba8unorm-srgb",wgpu::TextureFormat::ETC2RGBA8UnormSrgb}, + {"eac-r11unorm",wgpu::TextureFormat::EACR11Unorm}, + {"eac-r11snorm",wgpu::TextureFormat::EACR11Snorm}, + {"eac-rg11unorm",wgpu::TextureFormat::EACRG11Unorm}, + {"eac-rg11snorm",wgpu::TextureFormat::EACRG11Snorm}, + {"astc-4x4-unorm",wgpu::TextureFormat::ASTC4x4Unorm}, + {"astc-4x4-unorm-srgb",wgpu::TextureFormat::ASTC4x4UnormSrgb}, + {"astc-5x4-unorm",wgpu::TextureFormat::ASTC5x4Unorm}, + {"astc-5x4-unorm-srgb",wgpu::TextureFormat::ASTC5x4UnormSrgb}, + {"astc-5x5-unorm",wgpu::TextureFormat::ASTC5x5Unorm}, + {"astc-5x5-unorm-srgb",wgpu::TextureFormat::ASTC5x5UnormSrgb}, + {"astc-6x5-unorm",wgpu::TextureFormat::ASTC6x5Unorm}, + {"astc-6x5-unorm-srgb",wgpu::TextureFormat::ASTC6x5UnormSrgb}, + {"astc-6x6-unorm",wgpu::TextureFormat::ASTC6x6Unorm}, + {"astc-6x6-unorm-srgb",wgpu::TextureFormat::ASTC6x6UnormSrgb}, + {"astc-8x5-unorm",wgpu::TextureFormat::ASTC8x5Unorm}, + {"astc-8x5-unorm-srgb",wgpu::TextureFormat::ASTC8x5UnormSrgb}, + {"astc-8x6-unorm",wgpu::TextureFormat::ASTC8x6Unorm}, + {"astc-8x6-unorm-srgb",wgpu::TextureFormat::ASTC8x6UnormSrgb}, + {"astc-8x8-unorm",wgpu::TextureFormat::ASTC8x8Unorm}, + {"astc-8x8-unorm-srgb",wgpu::TextureFormat::ASTC8x8UnormSrgb}, + {"astc-10x5-unorm",wgpu::TextureFormat::ASTC10x5Unorm}, + {"astc-10x5-unorm-srgb",wgpu::TextureFormat::ASTC10x5UnormSrgb}, + {"astc-10x6-unorm",wgpu::TextureFormat::ASTC10x6Unorm}, + {"astc-10x6-unorm-srgb",wgpu::TextureFormat::ASTC10x6UnormSrgb}, + {"astc-10x8-unorm",wgpu::TextureFormat::ASTC10x8Unorm}, + {"astc-10x8-unorm-srgb",wgpu::TextureFormat::ASTC10x8UnormSrgb}, + {"astc-10x10-unorm",wgpu::TextureFormat::ASTC10x10Unorm}, + {"astc-10x10-unorm-srgb",wgpu::TextureFormat::ASTC10x10UnormSrgb}, + {"astc-12x10-unorm",wgpu::TextureFormat::ASTC12x10Unorm}, + {"astc-12x10-unorm-srgb",wgpu::TextureFormat::ASTC12x10UnormSrgb}, + {"astc-12x12-unorm",wgpu::TextureFormat::ASTC12x12Unorm}, + {"astc-12x12-unorm-srgb",wgpu::TextureFormat::ASTC12x12UnormSrgb}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,41> values{{ + {"uint8",wgpu::VertexFormat::Uint8}, + {"uint8x2",wgpu::VertexFormat::Uint8x2}, + {"uint8x4",wgpu::VertexFormat::Uint8x4}, + {"sint8",wgpu::VertexFormat::Sint8}, + {"sint8x2",wgpu::VertexFormat::Sint8x2}, + {"sint8x4",wgpu::VertexFormat::Sint8x4}, + {"unorm8",wgpu::VertexFormat::Unorm8}, + {"unorm8x2",wgpu::VertexFormat::Unorm8x2}, + {"unorm8x4",wgpu::VertexFormat::Unorm8x4}, + {"snorm8",wgpu::VertexFormat::Snorm8}, + {"snorm8x2",wgpu::VertexFormat::Snorm8x2}, + {"snorm8x4",wgpu::VertexFormat::Snorm8x4}, + {"uint16",wgpu::VertexFormat::Uint16}, + {"uint16x2",wgpu::VertexFormat::Uint16x2}, + {"uint16x4",wgpu::VertexFormat::Uint16x4}, + {"sint16",wgpu::VertexFormat::Sint16}, + {"sint16x2",wgpu::VertexFormat::Sint16x2}, + {"sint16x4",wgpu::VertexFormat::Sint16x4}, + {"unorm16",wgpu::VertexFormat::Unorm16}, + {"unorm16x2",wgpu::VertexFormat::Unorm16x2}, + {"unorm16x4",wgpu::VertexFormat::Unorm16x4}, + {"snorm16",wgpu::VertexFormat::Snorm16}, + {"snorm16x2",wgpu::VertexFormat::Snorm16x2}, + {"snorm16x4",wgpu::VertexFormat::Snorm16x4}, + {"float16",wgpu::VertexFormat::Float16}, + {"float16x2",wgpu::VertexFormat::Float16x2}, + {"float16x4",wgpu::VertexFormat::Float16x4}, + {"float32",wgpu::VertexFormat::Float32}, + {"float32x2",wgpu::VertexFormat::Float32x2}, + {"float32x3",wgpu::VertexFormat::Float32x3}, + {"float32x4",wgpu::VertexFormat::Float32x4}, + {"uint32",wgpu::VertexFormat::Uint32}, + {"uint32x2",wgpu::VertexFormat::Uint32x2}, + {"uint32x3",wgpu::VertexFormat::Uint32x3}, + {"uint32x4",wgpu::VertexFormat::Uint32x4}, + {"sint32",wgpu::VertexFormat::Sint32}, + {"sint32x2",wgpu::VertexFormat::Sint32x2}, + {"sint32x3",wgpu::VertexFormat::Sint32x3}, + {"sint32x4",wgpu::VertexFormat::Sint32x4}, + {"unorm10-10-10-2",wgpu::VertexFormat::Unorm10_10_10_2}, + {"unorm8x4-bgra",wgpu::VertexFormat::Unorm8x4BGRA}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,2> values{{ + {"vertex",wgpu::VertexStepMode::Vertex}, + {"instance",wgpu::VertexStepMode::Instance}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,3> values{{ + {"1d",wgpu::TextureDimension::e1D}, + {"2d",wgpu::TextureDimension::e2D}, + {"3d",wgpu::TextureDimension::e3D}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,6> values{{ + {"1d",wgpu::TextureViewDimension::e1D}, + {"2d",wgpu::TextureViewDimension::e2D}, + {"2d-array",wgpu::TextureViewDimension::e2DArray}, + {"cube",wgpu::TextureViewDimension::Cube}, + {"cube-array",wgpu::TextureViewDimension::CubeArray}, + {"3d",wgpu::TextureViewDimension::e3D}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,3> values{{ + {"all",wgpu::TextureAspect::All}, + {"stencil-only",wgpu::TextureAspect::StencilOnly}, + {"depth-only",wgpu::TextureAspect::DepthOnly}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,2> values{{ + {"load",wgpu::LoadOp::Load}, + {"clear",wgpu::LoadOp::Clear}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,2> values{{ + {"store",wgpu::StoreOp::Store}, + {"discard",wgpu::StoreOp::Discard}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,3> values{{ + {"uniform",wgpu::BufferBindingType::Uniform}, + {"storage",wgpu::BufferBindingType::Storage}, + {"read-only-storage",wgpu::BufferBindingType::ReadOnlyStorage}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,3> values{{ + {"filtering",wgpu::SamplerBindingType::Filtering}, + {"non-filtering",wgpu::SamplerBindingType::NonFiltering}, + {"comparison",wgpu::SamplerBindingType::Comparison}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,5> values{{ + {"float",wgpu::TextureSampleType::Float}, + {"unfilterable-float",wgpu::TextureSampleType::UnfilterableFloat}, + {"depth",wgpu::TextureSampleType::Depth}, + {"sint",wgpu::TextureSampleType::Sint}, + {"uint",wgpu::TextureSampleType::Uint}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,3> values{{ + {"write-only",wgpu::StorageTextureAccess::WriteOnly}, + {"read-only",wgpu::StorageTextureAccess::ReadOnly}, + {"read-write",wgpu::StorageTextureAccess::ReadWrite}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,3> values{{ + {"clamp-to-edge",wgpu::AddressMode::ClampToEdge}, + {"repeat",wgpu::AddressMode::Repeat}, + {"mirror-repeat",wgpu::AddressMode::MirrorRepeat}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,2> values{{ + {"nearest",wgpu::FilterMode::Nearest}, + {"linear",wgpu::FilterMode::Linear}, + }}; +}; +template<> struct webgpu_enum_names { + static inline constexpr std::array,2> values{{ + {"nearest",wgpu::MipmapFilterMode::Nearest}, + {"linear",wgpu::MipmapFilterMode::Linear}, + }}; +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_required_limits.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_required_limits.h new file mode 100644 index 000000000..1e26c43e7 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_required_limits.h @@ -0,0 +1,33 @@ +#pragma once +#include "webgpu_limit_names.h" +#include +#include +#include +#include +namespace webscene::graphics { +using webgpu_required_limit=std::pair>; +// A false result maps to requestDevice's OperationError. Undefined entries are +// ignored even when unknown. Supported values must come from the same adapter. +// Output is unchained: the caller attaches compatibility output when passing it +// to Dawn, keeping the two structures alive for the duration of RequestDevice. +inline bool prepare_webgpu_required_limits(std::span requested, + const wgpu::Limits& supported,const wgpu::CompatibilityModeLimits& supported_compatibility, + wgpu::Limits& output,wgpu::CompatibilityModeLimits& output_compatibility) { + wgpu::Limits limits{}; + wgpu::CompatibilityModeLimits compatibility{}; + for (const auto& [name,value]:requested) { + if (!value) continue; + const auto* mapping=webgpu_limit_from_name(name); + if (!mapping) return false; + const auto available=mapping->read(supported,supported_compatibility); + const bool wide=std::holds_alternative(mapping->member); + if (available==(wide?UINT64_MAX:UINT32_MAX)) return false; + const bool alignment=name==u"minUniformBufferOffsetAlignment" || name==u"minStorageBufferOffsetAlignment"; + if (alignment) { + if (!*value || *value>=uint64_t{1}<<32 || (*value&(*value-1)) || *valueavailable) return false; + if (!mapping->write(limits,compatibility,*value)) return false; + } + output=limits; output_compatibility=compatibility; return true; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/wgsl_language_feature_names.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/wgsl_language_feature_names.h new file mode 100644 index 000000000..d850a5cd2 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/wgsl_language_feature_names.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include +#include +#include +namespace webscene::graphics { +// Explicit allowlist from https://www.w3.org/TR/WGSL/#language-extensions-sec +// reviewed 2026-09-07 against the pinned Dawn SDK. Never enumerate Chromium +// testing/experimental extensions into the browser-facing capability snapshot. +struct wgsl_language_feature_name { std::string_view name; wgpu::WGSLLanguageFeatureName native; }; +inline constexpr std::array wgsl_language_feature_names{{ + {"readonly_and_readwrite_storage_textures",wgpu::WGSLLanguageFeatureName::ReadonlyAndReadwriteStorageTextures}, + {"packed_4x8_integer_dot_product",wgpu::WGSLLanguageFeatureName::Packed4x8IntegerDotProduct}, + {"unrestricted_pointer_parameters",wgpu::WGSLLanguageFeatureName::UnrestrictedPointerParameters}, + {"pointer_composite_access",wgpu::WGSLLanguageFeatureName::PointerCompositeAccess}, + {"uniform_buffer_standard_layout",wgpu::WGSLLanguageFeatureName::UniformBufferStandardLayout}, + {"subgroup_id",wgpu::WGSLLanguageFeatureName::SubgroupId}, + {"subgroup_uniformity",wgpu::WGSLLanguageFeatureName::SubgroupUniformity}, + {"texture_and_sampler_let",wgpu::WGSLLanguageFeatureName::TextureAndSamplerLet}, + {"texture_formats_tier1",wgpu::WGSLLanguageFeatureName::TextureFormatsTier1}, + {"linear_indexing",wgpu::WGSLLanguageFeatureName::LinearIndexing}, + {"immediate_address_space",wgpu::WGSLLanguageFeatureName::ImmediateAddressSpace}, + {"fragment_depth",wgpu::WGSLLanguageFeatureName::FragmentDepth}, + {"buffer_view",wgpu::WGSLLanguageFeatureName::BufferView}, +}}; +inline std::vector supported_wgsl_language_feature_names(const wgpu::Instance& instance) { + if (!instance) throw std::invalid_argument("WGSL features require a native instance"); + std::vector result; + for (const auto& feature:wgsl_language_feature_names) + if (instance.HasWGSLLanguageFeature(feature.native)) result.push_back(feature.name); + return result; +} +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/windows_gpu_adapter.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/windows_gpu_adapter.h new file mode 100644 index 000000000..528857b78 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/windows_gpu_adapter.h @@ -0,0 +1,36 @@ +#pragma once +#if defined(_WIN32) +#include "dxgi_device_identity.h" +#include +#include + +namespace webscene::graphics { +// Select a real DXGI adapter, then use its LUID for both Dawn discovery and +// native allocation. The presenter independently checks its actual device. +// No process-global device survives the owning canvas/document. +inline Microsoft::WRL::ComPtr windows_gpu_adapter() { + Microsoft::WRL::ComPtr factory; + Microsoft::WRL::ComPtr adapter; + if (FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&factory))) + || FAILED(factory->EnumAdapters1(0,&adapter))) + throw std::runtime_error("DXGI hardware adapter unavailable"); + DXGI_ADAPTER_DESC1 description{}; + if (FAILED(adapter->GetDesc1(&description)) || (description.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) + throw std::runtime_error("DXGI software adapter is not a presentation backend"); + return adapter; +} +inline LUID windows_gpu_adapter_luid() { + DXGI_ADAPTER_DESC1 description{}; + if (FAILED(windows_gpu_adapter()->GetDesc1(&description))) + throw std::runtime_error("DXGI adapter identity unavailable"); + return description.AdapterLuid; +} +inline Microsoft::WRL::ComPtr windows_canvas_device() { + auto adapter=windows_gpu_adapter(); + Microsoft::WRL::ComPtr device; + if (FAILED(D3D12CreateDevice(adapter.Get(),D3D_FEATURE_LEVEL_11_0,IID_PPV_ARGS(&device)))) + throw std::runtime_error("D3D12 canvas device unavailable"); + return device; +} +} +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/windows_gpu_interop.cpp b/experiments/WebScene.NativeEngine.Probe/native/graphics/windows_gpu_interop.cpp new file mode 100644 index 000000000..d0472ff06 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/windows_gpu_interop.cpp @@ -0,0 +1,64 @@ +#include "../webscene_native_engine.h" +#if defined(_WIN32) && defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) +#include "d3d11_scene_consumer.h" +#include "windows_gpu_adapter.h" +#endif + +int32_t webscene_gpu_d3d11_supported_v3(void* device) { +#if defined(_WIN32) && defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + if(!device)return E_INVALIDARG; + try { + auto host=static_cast(device); + webscene::graphics::adapter_luid actual; + auto status=webscene::graphics::query_adapter_luid(host,actual);if(FAILED(status))return status; + auto selected=webscene::graphics::windows_gpu_adapter_luid(); + if(actual.low!=selected.LowPart||actual.high!=selected.HighPart)return DXGI_ERROR_UNSUPPORTED; + Microsoft::WRL::ComPtr device5; + status=host->QueryInterface(IID_PPV_ARGS(&device5));if(FAILED(status))return status; + Microsoft::WRL::ComPtr context;host->GetImmediateContext(&context); + Microsoft::WRL::ComPtr context4; + status=context.As(&context4);if(FAILED(status))return status; + Microsoft::WRL::ComPtr fence; + return device5->CreateFence(0,D3D11_FENCE_FLAG_NONE,IID_PPV_ARGS(&fence)); + }catch(...){return E_FAIL;} +#else + return static_cast(0x80004001U); +#endif +} + +int32_t webscene_gpu_d3d11_import_v3(webscene_gpu_image_consumer_v3* consumer, + void* device,void** owner,void** texture) { + if(owner)*owner=nullptr;if(texture)*texture=nullptr; +#if defined(_WIN32) && defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + if(!owner||!texture)return E_INVALIDARG; + try { + std::unique_ptr candidate; + const auto status=webscene::graphics::d3d11_scene_consumer::create(consumer, + static_cast(device),candidate); + if(FAILED(status))return status; + *texture=candidate->texture();*owner=candidate.release();return S_OK; + }catch(const std::bad_alloc&){return E_OUTOFMEMORY;} + catch(...){return E_INVALIDARG;} +#else + return static_cast(0x80004001U); // E_NOTIMPL; no graphics dependency. +#endif +} +int32_t webscene_gpu_d3d11_seal_v3(void* owner) { +#if defined(_WIN32) && defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + return owner?static_cast(owner)->seal():E_INVALIDARG; +#else + return static_cast(0x80004001U); +#endif +} +int32_t webscene_gpu_d3d11_poll_v3(void* owner) { +#if defined(_WIN32) && defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + return owner?static_cast(owner)->poll():E_INVALIDARG; +#else + return static_cast(0x80004001U); +#endif +} +void webscene_gpu_d3d11_destroy_v3(void* owner) { +#if defined(_WIN32) && defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + delete static_cast(owner); +#endif +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/graphics/work_queue.h b/experiments/WebScene.NativeEngine.Probe/native/graphics/work_queue.h new file mode 100644 index 000000000..9975a8162 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/graphics/work_queue.h @@ -0,0 +1,96 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace webscene::graphics { +enum class enqueue_result { accepted, full, closed, upload_too_large }; +struct queue_metrics { + size_t depth{}; + size_t high_water{}; + uint64_t accepted{}; + uint64_t upload_bytes{}; +}; + +// Fixed-capacity native command/record storage. Command must contain only native +// values or separately owned handles, never borrowed V8 pointers. One slot stays +// occupied throughout consumption, so reentrant producers cannot overwrite an +// upload while a native API is still reading it. The consumer copies/retains data +// further if its backend API outlives the consume scope. +template class work_queue { + static_assert(std::is_trivially_copyable_v); + struct slot { Command command{}; size_t bytes{}; uint64_t serial{}; }; + mutable std::mutex mutex_; + std::vector slots_; + std::vector uploads_; + size_t upload_limit_; + size_t head_{}; + bool consuming_{}; + bool closed_{}; + queue_metrics metrics_; +public: + work_queue(size_t capacity, size_t upload_limit) : upload_limit_(upload_limit) + { + if (!capacity || (upload_limit && capacity > std::numeric_limits::max() / upload_limit)) + throw std::invalid_argument("invalid graphics queue capacity"); + slots_.resize(capacity); + uploads_.resize(capacity * upload_limit); + } + // full is backpressure: the caller retains/retries the command. No accepted + // side effect is dropped or coalesced. Copy happens before this call returns. + enqueue_result try_push(Command command, std::span upload = {}) + { + std::lock_guard lock(mutex_); + if (closed_) return enqueue_result::closed; + if (upload.size() > upload_limit_) return enqueue_result::upload_too_large; + if (metrics_.depth == slots_.size()) return enqueue_result::full; + if (metrics_.accepted == std::numeric_limits::max() + || upload.size() > std::numeric_limits::max() - metrics_.upload_bytes) + throw std::overflow_error("graphics queue counters exhausted"); + const auto index = (head_ + metrics_.depth) % slots_.size(); + auto& item = slots_[index]; + item.command = command; + item.bytes = upload.size(); + if (!upload.empty()) std::copy(upload.begin(), upload.end(), uploads_.begin() + index * upload_limit_); + item.serial = ++metrics_.accepted; + metrics_.upload_bytes += upload.size(); + ++metrics_.depth; + metrics_.high_water = std::max(metrics_.high_water, metrics_.depth); + return enqueue_result::accepted; + } + template bool consume_one(Consumer consume) + { + size_t index; + { + std::lock_guard lock(mutex_); + if (consuming_) throw std::logic_error("graphics queue requires one non-reentrant consumer"); + if (!metrics_.depth) return false; + consuming_ = true; + index = head_; + } + auto release = [&] { + std::lock_guard lock(mutex_); + head_ = (head_ + 1) % slots_.size(); + --metrics_.depth; + consuming_ = false; + }; + const auto& item = slots_[index]; + const auto upload = item.bytes + ? std::span(uploads_.data() + index * upload_limit_, item.bytes) + : std::span{}; + try { consume(item.command, upload, item.serial); } + catch (...) { release(); throw; } + release(); + return true; + } + // Stops admission; accepted work remains drainable during shutdown. + void close() { std::lock_guard lock(mutex_); closed_ = true; } + queue_metrics metrics() const { std::lock_guard lock(mutex_); return metrics_; } +}; +} // namespace webscene::graphics diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/CMakeLists.txt b/experiments/WebScene.NativeEngine.Probe/native/media/CMakeLists.txt new file mode 100644 index 000000000..d589d942a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/CMakeLists.txt @@ -0,0 +1,41 @@ +# A small, pinned audio dependency, compiled into WebScene rather than shipped +# as another third-party dylib. FetchContent caches the verified source archive. +FetchContent_Declare(webscene_miniaudio + URL https://codeload.github.com/mackron/miniaudio/tar.gz/f40cf03f80cdb7e741d43e53b7e706e8c1394bcf + URL_HASH SHA256=412326cf55133404cbfb81ec8974b10149dc68732ce5bdeee8ba9cfc2695d646) +FetchContent_GetProperties(webscene_miniaudio) +if(NOT webscene_miniaudio_POPULATED) + FetchContent_Populate(webscene_miniaudio) +endif() +add_library(webscene_media STATIC audio_decode.cpp audio_graph.cpp media_session.cpp miniaudio_impl.c) +target_compile_features(webscene_media PUBLIC cxx_std_20) +target_include_directories(webscene_media PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") +target_include_directories(webscene_media SYSTEM PRIVATE "${webscene_miniaudio_SOURCE_DIR}") +set_target_properties(webscene_media PROPERTIES POSITION_INDEPENDENT_CODE ON C_VISIBILITY_PRESET hidden CXX_VISIBILITY_PRESET hidden) +# Decode is device-independent. Output opens only when playback/context resumes. +target_compile_definitions(webscene_media PRIVATE MA_NO_ENGINE MA_NO_RESOURCE_MANAGER MA_NO_GENERATION) +find_package(Threads REQUIRED) +target_link_libraries(webscene_media PUBLIC Threads::Threads ${CMAKE_DL_LIBS}) +if(APPLE) + enable_language(OBJCXX) + target_sources(webscene_media PRIVATE video_decode_macos.mm) + set_source_files_properties(video_decode_macos.mm PROPERTIES COMPILE_FLAGS "-fobjc-arc") + target_link_libraries(webscene_media PUBLIC "-framework AVFoundation" "-framework CoreMedia" "-framework CoreVideo" "-framework Foundation" "-framework IOSurface") +else() + target_sources(webscene_media PRIVATE video_decode_unsupported.cpp) +endif() +configure_file("${webscene_miniaudio_SOURCE_DIR}/LICENSE" "${CMAKE_BINARY_DIR}/webscene-miniaudio-LICENSE" COPYONLY) +if(BUILD_TESTING) + add_executable(webscene_audio_graph_tests ../../tests/audio_graph_tests.cpp) + target_link_libraries(webscene_audio_graph_tests PRIVATE webscene_media) + add_test(NAME webscene_audio_graph_tests COMMAND webscene_audio_graph_tests) + add_executable(webscene_media_decode_tests ../../tests/media_decode_tests.cpp) + target_link_libraries(webscene_media_decode_tests PRIVATE webscene_media) + if(APPLE) + add_test(NAME webscene_media_decode_tests COMMAND webscene_media_decode_tests "-" + "${CMAKE_CURRENT_SOURCE_DIR}/../../tests/fixtures/media/numbered-motion.mp4" + "${CMAKE_CURRENT_SOURCE_DIR}/../../tests/fixtures/media/flash-click.mp4") + else() + add_test(NAME webscene_media_decode_tests COMMAND webscene_media_decode_tests) + endif() +endif() diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/README.md b/experiments/WebScene.NativeEngine.Probe/native/media/README.md new file mode 100644 index 000000000..e516acb5c --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/README.md @@ -0,0 +1,169 @@ +# Native media and Web Audio (#54 / #55) + +WebScene now implements the Frameforge media-engine subset through native media +sessions and an actual audio graph. The unchanged `MediaEngine` runs in a macOS +Native AOT host. This is not full HTML media/Web Audio standards conformance and +is not completion of the Frameforge editor epic: WebGPU external textures and +external image copies remain #40, Canvas2D export remains #34, and recording/ +full editor qualification remain #58/#59. + +`WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA` defaults to ON. Runtime packaging scripts +set it explicitly. OFF remains available for a deliberately media-free build. +The browser interfaces then are absent, rather than successful placeholders. +Production Avalonia 11 remains supported; Frameforge can opt into Avalonia 12. + +## Architecture and ownership + +- The existing host loader admits bytes. `WebSceneTextResource.BinaryContent` + preserves binary data through Avalonia and Uno, archives, HTTP, files, data + resources and the native ABI. Fetch/Response/Blob retain byte fidelity and + enforce body consumption/revocation. Decoders never fetch application URLs. +- HTML audio/video wrappers inherit the correct media/HTMLElement prototypes. + A small native DOM media registry discovers markup/source changes at task + boundaries, including parser-created nodes. It does not depend on the + currently unsupported MutationObserver and does not traverse the full DOM. + The registry shares lazy auxiliary document storage with modal registration; + Linux sizeof(native_document) remains 368 bytes (384-byte budget). +- A persistent `media_session` worker owns the source and decoder. Load generations + cancel stale work. Rapid seek requests coalesce; completed current-generation + frames can still present while a newer request waits, avoiding starvation. + Metadata/readiness/seek events are driven by actual decode results. Play + promises reject on interruption/error. Media task events are generation scoped. +- Playback time is independent of presentation. Vsync requests the next video + position; audio processes fixed quanta on its device callback. Source control + changes anchor the audio cursor, which then advances continuously across + callback/quantum boundaries. Gain automation uses the audio context clock. +- macOS retains decoded CVPixelBuffers and adopts their IOSurfaces into the + **same versioned image lease and retained scene composition** used by WebGPU. + No child window, AVPlayerLayer or overlay exists. No steady-state video readback + or CPU pixel upload is used. Retaining the CVPixelBuffer prevents decoder pool + reuse until scene/GPU consumers finish. Three image slots bound presentation; + completion wakes retry the latest pending image after backpressure. Native + allocation metadata and image contents stay immutable for existing consumers. +- `audio_graph` uses miniaudio for native device output, with a 128-frame requested + period and three periods. Device negotiation can choose a different hardware + period. The callback uses fixed storage; it does not invoke JS, allocate, + lock a mutex, decode files or perform I/O. Stereo mixing, mono/quad/5.1 speaker + conversion, interpolation/resampling, gain/target automation and time-domain + metering are implemented for Frameforge. Explicit media source routing removes + the default element output to avoid doubled sound. +- Each stream destination writes real stereo PCM to a bounded native capture + ring. `audio_track` readers have independent cursors, clone/stop/enabled state, + absolute frame timestamps and explicit overrun counts. Context close ends the + producer. `resolve_audio_track` validates a private V8 brand and returns the + native lease for recording adapters; no application-supplied numeric field is + trusted. The encoder/MediaRecorder consumer belongs to #58. +- `decodeAudioData` detaches input, decodes off-thread, resamples to the context + rate and returns actual planar PCM. The original Frameforge waveform worker + transfers and reduces these arrays without application changes. + +## Dependency decision and platform scope + +| Option | Assessment / decision | +| --- | --- | +| miniaudio 0.11.23 | Adopted for portable WAV/MP3/FLAC decode and device I/O. Pinned commit `f40cf03f80cdb7e741d43e53b7e706e8c1394bcf`, verified archive SHA256, static compilation. MIT-0/public-domain license choice. | +| AVFoundation/AVAssetReader | Implemented macOS provider: persistent H.264 native video surfaces and container audio/AAC decoding; OS frameworks add no shipped codec dylib. | +| FFmpeg/libav | Credible portable video provider with hardware backends. Not bundled. Static distribution needs reviewed configuration/licenses and LGPL obligations. Evaluate for platform parity before adding a second codec stack. | +| GStreamer | Viable portable pipelines, with more plugin/deployment machinery. Not selected for this subset. | +| Native Windows/Linux providers | Common session/lease contracts isolate platform code. Windows can use Media Foundation/DXGI; Linux may use FFmpeg/VAAPI/dma-buf. Selection and hardware qualification tracked in #61/#62. | +| SDL audio/mixer | Does not replace general video demux/decode. No additional dependency adopted. | + +Miniaudio is built into the native engine, not shipped as another DLL/dylib. +FetchContent caches the pinned source archive. Native NuGet packaging includes +`licenses/Miniaudio-LICENSE.txt` and rejects a media-enabled package missing it. +Existing engine/Dawn/ICU/snapshot packaging is unchanged; this is not a claim +that the whole application is one executable. AVFoundation/CoreVideo/IOSurface +are OS frameworks, not statically redistributed libraries. + +Windows/Linux use the common miniaudio/audio graph code and portable CTests; +video support currently reports unsupported. Hardware qualification is tracked +explicitly in #61 and #62. Do not mark those platforms' video tests passed. + +Primary sources used for the decision: +- https://miniaud.io/index.html +- https://miniaud.io/docs/manual/ +- https://github.com/mackron/miniaudio/tree/f40cf03f80cdb7e741d43e53b7e706e8c1394bcf +- https://www.ffmpeg.org/legal.html +- https://ffmpeg.org/ffmpeg.html +- https://gstreamer.freedesktop.org/documentation/frequently-asked-questions/licensing.html +- https://developer.apple.com/documentation/avfoundation/avassetreader +- https://wiki.libsdl.org/SDL3/Libraries + +## Bounds and subset limitations + +32 media sessions; 256 MiB encoded source and decoded PCM limits per decode; +64 megapixel decode limit; 128 MiB retained image pool per video (oversize native +allocations fail explicitly). One pending seek/current decoded frame per session, +three presentation slots, eight queued standalone audio decodes. Audio contexts +are limited to eight, with 64 nodes, 1,024 pending graph commands, 32 outstanding +scheduled gain events per node and 512 MiB retained PCM per context. Capture +stores 16,384 stereo frames; lagging readers receive a discontinuity count. + +The current admitted source is fully buffered; `preload` is a hint, not streaming +or a network-byte guarantee. Streaming/adaptive media, DRM, media controls UI, +negative native playback, frequency-domain analyser APIs, arbitrary AudioWorklet +processing and comprehensive Web Audio nodes are outside this Frameforge subset. +Video rotation/color-space qualification beyond the original unrotated sRGB demo +clips is not claimed. Stored display transforms are available to #40 consumers. +Device/display physical latency and long-duration A/V drift still require the +full playback qualification in #59; the included flash/click test measures +pipeline timestamps through the real mixer/capture renderer. + +## Reproducible verification + +Build the usual native V8 engine with media enabled, then: + +```sh +cmake --build artifacts/checkpoint-native -j 6 +ctest --test-dir artifacts/checkpoint-native --output-on-failure +dotnet run --project tests/WebPlatformSubset/runner -c Release -- \ + --manifest tests/WebPlatformSubset/webscene-media-runtime-profile.json \ + --selection required --native-library "$PWD/artifacts/checkpoint-native/libwebscene_native_engine.dylib" \ + --output artifacts/wpt-media-runtime +dotnet run --project tests/WebPlatformSubset/runner -c Release -- \ + --manifest tests/WebPlatformSubset/webscene-macos-video-runtime-profile.json \ + --selection required --native-library "$PWD/artifacts/checkpoint-native/libwebscene_native_engine.dylib" \ + --output artifacts/wpt-media-video +``` + +Both manifests are labeled project-owned contracts, not imported WPT conformance. +The video manifest is macOS-only. Native tests cover actual PCM, invalid data, +limits, cancellation/future settlement, source replacement, end/backward/rapid +seek, frame ownership through decoder/pool teardown, backpressure/completion, +gain/rate/seek/quantum continuity, capture timestamps/stereo/clone/stop/overrun, +and a generated H.264/AAC flash/click fixture. Its maximum measured pipeline +A/V difference was 0.0417 ms, with a 10 ms limit; physical output latency excluded. + +Obtain the original public app without relying on an agent-local artifact: + +```sh +git clone https://github.com/wieslawsoltes/Frameforge artifacts/Frameforge +git -C artifacts/Frameforge checkout f414cd44896b659a5c6da2e4fc596a2ded055f2a +dotnet publish experiments/WebScene.Frameforge -c Release -r osx-arm64 \ + -p:PublishAot=true -p:JsonSerializerIsReflectionEnabledByDefault=false \ + -p:WebSceneAvalonia12Sample=true -o artifacts/frameforge-media-aot/publish +FRAMEFORGE_ASSETS="$PWD/artifacts/Frameforge" \ +WEBSCENE_TEST_NATIVE_LIBRARY="$PWD/artifacts/checkpoint-native/libwebscene_native_engine.dylib" \ + artifacts/frameforge-media-aot/publish/Frameforge --media-verify +``` + +`--media-verify` imports the original MediaEngine/core/worker modules. It prepares +nine cut positions, checks playback/pause/nonzero RMS, exercises volume/mute and +native track cloning, then compares the original worker's waveform against an +independent PCM reduction. The observed maximum waveform error was 1.49e-8. +The diagnostic page also displays an actual retained video under rounded clipping. +It is deliberately separate from full-editor `--verify`, which still requires +#40 and the remaining #53 issues. No original application source is patched. + +The sample's `verify-media.py` also verifies the actual server's MIME, HEAD, +closed/open/suffix byte ranges and 416 responses against original file bytes. +A local native NuGet inventory check confirmed the exact pinned miniaudio license, +engine/Dawn/ICU/snapshot payloads and no extra miniaudio shared library. + +Managed regression coverage includes raw binary file/resource envelope and archive +round trips. The Avalonia suite passes on net8.0/net10.0 (305 passed, 15 existing +skips per framework); Uno builds. The AOT media test passes with real decode and +audio device output. Existing unrelated interop library IL warnings may still be +emitted during compilation; media bindings add no reflection activation path. + +An Ubuntu 22.04 linux/amd64 container also passed the native document footprint, audio graph/capture and PCM decode/session tests. This validates portable native code, not Linux hardware video or audio-device qualification. diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/audio_capture.h b/experiments/WebScene.NativeEngine.Probe/native/media/audio_capture.h new file mode 100644 index 000000000..249947c14 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/audio_capture.h @@ -0,0 +1,100 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +namespace webscene::media { +// Single realtime producer, independent recorder readers. Bounded stereo PCM +// ring with absolute timestamps. A lagging reader reports an overrun explicitly; +// it never blocks the audio callback or silently presents a discontinuity. +class audio_capture { + static constexpr uint64_t capacity = 16384; + struct frame { + std::atomic stamp{UINT64_MAX}; + std::atomic left{}, right{}; + }; + std::array data_{}; + std::atomic end_{}; + std::atomic ended_{}; + uint32_t rate_; + + public: + struct result { + uint64_t first_frame{}, frames{}, dropped{}; + bool ended{}; + }; + explicit audio_capture(uint32_t rate) : rate_(rate) {} + uint32_t sample_rate() const noexcept { return rate_; } + uint64_t end_frame() const noexcept { return end_.load(std::memory_order_acquire); } + bool ended() const noexcept { return ended_.load(std::memory_order_acquire); } + void end() noexcept { ended_.store(true, std::memory_order_release); } + void write(std::span stereo) noexcept { + auto end = end_.load(std::memory_order_relaxed); + for (size_t i = 0; i < stereo.size() / 2; ++i) { + auto &f = data_[(end + i) % capacity]; + f.stamp.store(UINT64_MAX, std::memory_order_seq_cst); + f.left.store(stereo[i * 2], std::memory_order_seq_cst); + f.right.store(stereo[i * 2 + 1], std::memory_order_seq_cst); + f.stamp.store(end + i, std::memory_order_seq_cst); + } + end_.store(end + stereo.size() / 2, std::memory_order_release); + } + result read(uint64_t &cursor, std::span stereo) const noexcept { + result out; + auto end = end_frame(), begin = end > capacity ? end - capacity : 0; + if (cursor < begin) { + out.dropped = begin - cursor; + cursor = begin; + } + out.first_frame = cursor; + out.ended = ended(); + auto count = std::min(stereo.size() / 2, end - cursor); + for (; out.frames < count; ++out.frames) { + auto index = cursor + out.frames; + const auto &f = data_[index % capacity]; + auto before = f.stamp.load(std::memory_order_seq_cst); + auto left = f.left.load(std::memory_order_seq_cst), + right = f.right.load(std::memory_order_seq_cst); + if (before != index || f.stamp.load(std::memory_order_seq_cst) != index) + break; + stereo[out.frames * 2] = left; + stereo[out.frames * 2 + 1] = right; + } + cursor += out.frames; + return out; + } +}; +// Stopping/cloning affects one consumer only; monitor and sibling tracks keep +// their graph connection. Recorder keeps this lease after JS wrapper collection. +class audio_track { + std::shared_ptr source_; + uint64_t cursor_; + std::atomic stopped_{}; + + public: + std::atomic enabled{true}; + explicit audio_track(std::shared_ptr source) + : source_(std::move(source)), cursor_(source_->end_frame()) {} + std::shared_ptr clone() const { + auto result = std::make_shared(source_); + if (ended()) + result->stop(); + result->enabled = enabled.load(); + return result; + } + void stop() noexcept { stopped_ = true; } + bool ended() const noexcept { return stopped_.load() || source_->ended(); } + uint32_t sample_rate() const noexcept { return source_->sample_rate(); } + audio_capture::result read(std::span stereo) noexcept { + if (stopped_) + return {cursor_, 0, 0, true}; + auto result = source_->read(cursor_, stereo); + if (!enabled) + std::fill_n(stereo.begin(), result.frames * 2, 0.f); + return result; + } +}; +} // namespace webscene::media diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/audio_decode.cpp b/experiments/WebScene.NativeEngine.Probe/native/media/audio_decode.cpp new file mode 100644 index 000000000..0eb9ffa96 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/audio_decode.cpp @@ -0,0 +1,38 @@ +#include "media_decode.h" +#include "miniaudio.h" +#include +#include + +namespace webscene::media { +audio_buffer decode_audio(std::span bytes, decode_limits limits, std::stop_token stop, uint32_t target_sample_rate) { + if (bytes.empty() || bytes.size() > limits.encoded_bytes) + throw std::invalid_argument("Encoded audio size is outside the admitted limit"); + if (stop.stop_requested()) throw std::runtime_error("Audio decode cancelled"); + if(target_sample_rate && (target_sample_rate<8000 || target_sample_rate>192000))throw std::invalid_argument("Invalid target audio sample rate"); + auto config = ma_decoder_config_init(ma_format_f32, 0, target_sample_rate); + ma_decoder decoder{}; + if (ma_decoder_init_memory(bytes.data(), bytes.size(), &config, &decoder) != MA_SUCCESS) + return decode_native_audio(bytes, limits, stop, target_sample_rate); + struct cleanup { ma_decoder* value; ~cleanup() { ma_decoder_uninit(value); } } guard{&decoder}; + audio_buffer result{decoder.outputChannels, decoder.outputSampleRate, {}}; + if (!result.channels || result.channels > 32 || !result.sample_rate) + throw std::runtime_error("Invalid audio channel count or sample rate"); + const uint64_t maximum_samples = limits.decoded_audio_bytes / sizeof(float); + // Decode in bounded chunks; never trust an encoded file's declared length. + std::vector chunk(4096 * result.channels); + for (;;) { + if (stop.stop_requested()) throw std::runtime_error("Audio decode cancelled"); + ma_uint64 frames = 0; + const auto status = ma_decoder_read_pcm_frames(&decoder, chunk.data(), 4096, &frames); + if (status != MA_SUCCESS && status != MA_AT_END) + throw std::runtime_error("Audio decoder failed"); + const uint64_t count = frames * result.channels; + if (count > maximum_samples || result.samples.size() > maximum_samples - count) + throw std::length_error("Decoded audio exceeds memory limit"); + result.samples.insert(result.samples.end(), chunk.data(), chunk.data() + count); + if (status == MA_AT_END || frames == 0) break; + } + if (result.samples.empty()) throw std::runtime_error("Audio contains no decoded frames"); + return result; +} +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/audio_graph.cpp b/experiments/WebScene.NativeEngine.Probe/native/media/audio_graph.cpp new file mode 100644 index 000000000..f595d6b87 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/audio_graph.cpp @@ -0,0 +1,417 @@ +#include "audio_graph.h" +#include "miniaudio.h" +#include +#include +#include +#include +#include +#include +namespace webscene::media { +namespace { +double now() { + return std::chrono::duration(std::chrono::steady_clock::now().time_since_epoch()).count(); +} +constexpr uint32_t nodes = 64, quantum = 128, history = 32768, capacity = 1024; +struct command { + enum class operation { create, connect, disconnect, gain, source } op; + uint32_t a{}, b{}; + double value{}, start{}, tau{}; + const audio_buffer *pcm{}; + playback_control *control{}; +}; +} // namespace +void playback_control::set(double time, double speed, bool play, double gain, bool mute) { + sequence.fetch_add(1, std::memory_order_acq_rel); + position = time; + epoch = now(); + rate = speed; + volume = gain; + muted = mute; + playing = play; + sequence.fetch_add(1, std::memory_order_release); +} +double playback_control::time() const noexcept { + return time_at(now()); +} +double playback_control::time_at(double steady_seconds) const noexcept { + const double p = position.load(); + if (!playing.load()) return p; + const auto revision = sequence.load(std::memory_order_acquire); + const auto sample = output_sequence.load(std::memory_order_acquire); + const auto sample_revision = output_revision.load(std::memory_order_relaxed); + const auto sample_position = output_position.load(std::memory_order_relaxed); + const auto sample_epoch = output_epoch.load(std::memory_order_relaxed); + if (!(sample & 1) && sample_revision == revision && + sample == output_sequence.load(std::memory_order_acquire) && + std::abs(steady_seconds - sample_epoch) < .25) { + return sample_position + (steady_seconds - sample_epoch) * rate.load(); + } + return p + (steady_seconds - epoch.load()) * rate.load(); +} +void playback_control::observe_output(uint64_t revision, double media_seconds, double host_seconds) noexcept { + output_sequence.fetch_add(1, std::memory_order_acq_rel); + output_position.store(media_seconds, std::memory_order_relaxed); + output_epoch.store(host_seconds, std::memory_order_relaxed); + output_revision.store(revision, std::memory_order_relaxed); + output_sequence.fetch_add(1, std::memory_order_release); +} +struct audio_graph::implementation { + struct node { + kind type{kind::gain}; + bool edges[nodes]{}; + float gain{1}, target{1}; + double tau{}, pole{}; + std::atomic scheduled{}; + struct automation { + float value; + double start, tau; + }; + std::array events{}; + uint32_t event_count{}; + const audio_buffer *pcm{}; + playback_control *control{}; + std::shared_ptr capture; + uint64_t control_version{UINT64_MAX}; + double cursor{}; + std::array data{}; + std::array, history> samples{}; + std::atomic written{}; + }; + std::array graph{}; + std::array owner_types{}; + bool owner_edges[nodes][nodes]{}; + uint32_t count{1}, rate; + std::atomic rt_count{1}; + std::array queue{}; + std::atomic head{}, tail{}; + std::atomic frames{}; + std::atomic running{}; + bool use_device, initialized{}, closed{}; + ma_device device{}; + std::vector> keep_pcm; + std::vector> keep_controls; + uint64_t pcm_bytes{}; + implementation(bool output, uint32_t sample_rate) : rate(sample_rate), use_device(output) { + graph[0].type = kind::destination; + owner_types[0] = kind::destination; + } + void push(command c) { + if (closed) + throw std::runtime_error("Audio context closed"); + auto h = head.load(std::memory_order_relaxed); + if (h - tail.load(std::memory_order_acquire) >= capacity) + throw std::length_error("Audio command queue full"); + queue[h % capacity] = c; + head.store(h + 1, std::memory_order_release); + } + void check(uint32_t id) const { + if (id >= count) + throw std::invalid_argument("Invalid audio node"); + } + void commands() noexcept { + auto t = tail.load(std::memory_order_relaxed); + auto h = head.load(std::memory_order_acquire); + while (t != h) { + const auto c = queue[t++ % capacity]; + auto &n = graph[c.a]; + switch (c.op) { + case command::operation::create: + n.type = static_cast(c.b); + rt_count.store(std::max(rt_count.load(), c.a + 1)); + break; + case command::operation::connect: + graph[c.b].edges[c.a] = true; + break; + case command::operation::disconnect: + for (auto &d : graph) + d.edges[c.a] = false; + break; + case command::operation::gain: { + auto index = n.event_count++; + n.events[index] = {static_cast(c.value), c.start, c.tau}; + while (index && n.events[index].start < n.events[index - 1].start) { + std::swap(n.events[index], n.events[index - 1]); + --index; + } + break; + } + case command::operation::source: + n.pcm = c.pcm; + n.control = c.control; + break; + } + } + tail.store(t, std::memory_order_release); + } + void process(uint32_t id, uint32_t size, std::array &done, double wall, + double clock) noexcept { + if (done[id]) + return; + done[id] = true; + auto &n = graph[id]; + std::fill_n(n.data.data(), size * 2, 0.f); + if (n.type == kind::source && n.pcm && n.control) { + auto &c = *n.control; + auto version = c.sequence.load(std::memory_order_acquire); + double position = c.position.load(), epoch = c.epoch.load(), speed = c.rate.load(), + volume = c.volume.load(); + bool play = c.playing.load(), mute = c.muted.load(); + if (!(version & 1) && version == c.sequence.load(std::memory_order_acquire) && play && !mute && + speed > 0) { + const auto &p = *n.pcm; + if (n.control_version != version) { + n.cursor = (position + (wall - epoch) * speed) * p.sample_rate; + n.control_version = version; + } + double start = n.cursor; + // Anchor video to the device-rendered sample cursor instead of + // an independently advancing wall clock. This is callback-time + // feedback; backend DAC latency is not yet measured here. + if (use_device) + c.observe_output(version, start / p.sample_rate, wall); + for (uint32_t i = 0; i < size; ++i) { + double source = start + double(i) * speed * p.sample_rate / rate; + if (source < 0 || source >= p.frames()) + continue; + auto a = static_cast(source), b = std::min(a + 1, p.frames() - 1); + float f = static_cast(source - a); + auto sample = [&](uint32_t channel) { + float x = p.samples[a * p.channels + channel], + y = p.samples[b * p.channels + channel]; + return x + (y - x) * f; + }; + float left = sample(0), right = sample(std::min(1U, p.channels - 1)); + // Web Audio speaker downmix: quad L/R/surrounds and 5.1 + // L/R/C/LFE/SL/SR. LFE is not folded into stereo. + if (p.channels == 4) { + left = (left + sample(2)) * .5f; + right = (right + sample(3)) * .5f; + } else if (p.channels == 6) { + left += .7071067811865475f * (sample(2) + sample(4)); + right += .7071067811865475f * (sample(2) + sample(5)); + } + n.data[i * 2] = left * volume; + n.data[i * 2 + 1] = right * volume; + } + n.cursor += double(size) * speed * p.sample_rate / rate; + } + } + for (uint32_t input = 0; input < rt_count.load(); ++input) + if (n.edges[input]) { + process(input, size, done, wall, clock); + for (uint32_t i = 0; i < size * 2; ++i) + n.data[i] += graph[input].data[i]; + } + if (n.type == kind::gain) + for (uint32_t i = 0; i < size; ++i) { + double t = clock + double(i) / rate; + while (n.event_count && n.events[0].start <= t) { + auto e = n.events[0]; + for (uint32_t j = 1; j < n.event_count; ++j) + n.events[j - 1] = n.events[j]; + --n.event_count; + n.scheduled.fetch_sub(1, std::memory_order_release); + n.target = e.value; + n.tau = e.tau; + n.pole = n.tau ? std::exp(-1. / (n.tau * rate)) : 0; + if (!n.tau) + n.gain = n.target; + } + n.data[i * 2] *= n.gain; + n.data[i * 2 + 1] *= n.gain; + if (n.tau) + n.gain = n.target + (n.gain - n.target) * n.pole; + } + if (n.capture) + n.capture->write(std::span(n.data.data(), size * 2)); + if (n.type == kind::analyser || n.type == kind::stream) { + auto written = n.written.load(std::memory_order_relaxed); + for (uint32_t i = 0; i < size; ++i) + n.samples[(written + i) % history].store((n.data[i * 2] + n.data[i * 2 + 1]) * .5f, + std::memory_order_relaxed); + n.written.store(written + size, std::memory_order_release); + } + } +}; +audio_graph::audio_graph(bool device, uint32_t rate) : impl_(std::make_unique(device, rate)) { + if (rate < 8000 || rate > 192000) + throw std::invalid_argument("Audio sample rate out of range"); +} +audio_graph::~audio_graph() { close(); } +uint32_t audio_graph::create(kind type) { + auto &p = *impl_; + if (p.count >= nodes) + throw std::length_error("Audio node limit reached"); + auto id = p.count; + if (type == kind::stream) + p.graph[id].capture = std::make_shared(p.rate); + p.push({command::operation::create, id, static_cast(type)}); + p.owner_types[id] = type; + ++p.count; + return id; +} +void audio_graph::connect(uint32_t source, uint32_t dest) { + auto &p = *impl_; + p.check(source); + p.check(dest); + if (p.owner_types[source] == kind::destination || p.owner_types[source] == kind::stream || p.owner_types[dest] == kind::source) + throw std::invalid_argument("Audio node has no such input/output port"); + if (source == dest) + throw std::invalid_argument("Unsupported zero-delay audio cycle"); + std::array visited{}; + auto reaches = [&](auto &&self, uint32_t x) -> bool { + if (x == source) + return true; + if (visited[x]) + return false; + visited[x] = true; + for (uint32_t y = 0; y < p.count; ++y) + if (p.owner_edges[x][y] && self(self, y)) + return true; + return false; + }; + if (reaches(reaches, dest)) + throw std::invalid_argument("Unsupported zero-delay audio cycle"); + p.push({command::operation::connect, source, dest}); + p.owner_edges[source][dest] = true; +} +void audio_graph::disconnect(uint32_t source) { + auto &p = *impl_; + p.check(source); + p.push({command::operation::disconnect, source}); + std::fill_n(p.owner_edges[source], nodes, false); +} +void audio_graph::set_gain(uint32_t id, float value, double start, double tau) { + auto &p = *impl_; + p.check(id); + if (p.owner_types[id] != kind::gain || !std::isfinite(value) || !std::isfinite(start) || start < 0 || + !std::isfinite(tau) || tau < 0) + throw std::invalid_argument("Invalid gain automation"); + if (p.graph[id].scheduled.fetch_add(1, std::memory_order_acq_rel) >= 32) { + p.graph[id].scheduled.fetch_sub(1); + throw std::length_error("Audio automation limit"); + } + try { + p.push({command::operation::gain, id, 0, value, start, tau}); + } catch (...) { + p.graph[id].scheduled.fetch_sub(1); + throw; + } +} +void audio_graph::set_source(uint32_t id, std::shared_ptr pcm, + std::shared_ptr control) { + auto &p = *impl_; + p.check(id); + if (p.owner_types[id] != kind::source || !pcm || !pcm->channels || !pcm->sample_rate || !control) + throw std::invalid_argument("Invalid audio source"); + auto size = pcm->samples.size() * sizeof(float); + if (size > 512ULL * 1024 * 1024 - p.pcm_bytes) + throw std::length_error("Audio context PCM limit"); + p.keep_pcm.push_back(pcm); + p.keep_controls.push_back(control); + try { + command c{command::operation::source, id}; + c.pcm = pcm.get(); + c.control = control.get(); + p.push(c); + p.pcm_bytes += size; + } catch (...) { + p.keep_pcm.pop_back(); + p.keep_controls.pop_back(); + throw; + } +} +void audio_graph::resume() { + auto &p = *impl_; + if (p.closed) + throw std::runtime_error("Audio context closed"); + if (p.running.load()) + return; + for (auto &n : p.graph) + n.control_version = UINT64_MAX; + if (p.use_device && !p.initialized) { + auto config = ma_device_config_init(ma_device_type_playback); + config.playback.format = ma_format_f32; + config.playback.channels = 2; + config.sampleRate = p.rate; + config.periodSizeInFrames = 128; + config.periods = 3; + config.pUserData = this; + config.dataCallback = [](ma_device *d, void *out, const void *, ma_uint32 frames) { + static_cast(d->pUserData)->render(static_cast(out), frames); + }; + if (ma_device_init(nullptr, &config, &p.device) != MA_SUCCESS) + throw std::runtime_error("Audio output device unavailable"); + p.initialized = true; + } + p.running = true; + if (p.initialized && ma_device_start(&p.device) != MA_SUCCESS) { + p.running = false; + throw std::runtime_error("Cannot start audio output"); + } +} +void audio_graph::suspend() { + auto &p = *impl_; + p.running = false; + if (p.initialized) + ma_device_stop(&p.device); +} +void audio_graph::close() { + if (!impl_ || impl_->closed) + return; + auto &p = *impl_; + p.running = false; + if (p.initialized) { + ma_device_uninit(&p.device); + p.initialized = false; + } + p.closed = true; + for (auto &n : p.graph) + if (n.capture) + n.capture->end(); + p.keep_pcm.clear(); + p.keep_controls.clear(); +} +double audio_graph::time() const noexcept { return double(impl_->frames.load()) / impl_->rate; } +uint32_t audio_graph::sample_rate() const noexcept { return impl_->rate; } +void audio_graph::analyser(uint32_t id, std::span out) const { + auto &p = *impl_; + p.check(id); + auto &n = p.graph[id]; + auto end = n.written.load(std::memory_order_acquire); + size_t count = std::min({out.size(), history, static_cast(end)}); + std::fill(out.begin(), out.end(), 0.f); + for (size_t i = 0; i < count; ++i) + out[i] = n.samples[(end - count + i) % history].load(std::memory_order_relaxed); +} +std::shared_ptr audio_graph::capture(uint32_t id) { + auto &p = *impl_; + p.check(id); + if (p.owner_types[id] != kind::stream || p.closed) + throw std::invalid_argument("Invalid capture destination"); + return std::make_shared(p.graph[id].capture); +} +void audio_graph::render(float *out, uint32_t frames) noexcept { render_at(out, frames, now()); } +void audio_graph::render_at(float *out, uint32_t frames, double steady_time) noexcept { + auto &p = *impl_; + std::fill_n(out, frames * 2, 0.f); + if (!p.running.load()) + return; + while (frames) { + p.commands(); + auto count = std::min(frames, quantum); + std::array done{}; + auto clock = time(); + double wall = steady_time; + p.process(0, count, done, wall, clock); + for (uint32_t i = 1; i < p.rt_count.load(); ++i) + if (p.graph[i].type == kind::stream || p.graph[i].type == kind::analyser) + p.process(i, count, done, wall, clock); + std::copy_n(p.graph[0].data.data(), count * 2, out); + p.frames.fetch_add(count); + out += count * 2; + frames -= count; + steady_time += double(count) / p.rate; + } +} +} // namespace webscene::media diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/audio_graph.h b/experiments/WebScene.NativeEngine.Probe/native/media/audio_graph.h new file mode 100644 index 000000000..5fc5f8789 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/audio_graph.h @@ -0,0 +1,46 @@ +#pragma once +#include "audio_capture.h" +#include "media_decode.h" +#include +#include +#include +namespace webscene::media { +struct playback_control { + std::atomic sequence{}; + std::atomic position{}, epoch{}, rate{1}, volume{1}; + std::atomic playing{}, muted{}; + std::atomic output_sequence{}, output_revision{UINT64_MAX}; + std::atomic output_position{}, output_epoch{}; + void observe_output(uint64_t revision, double media_seconds, double host_seconds) noexcept; + void set(double time, double speed, bool play, double gain, bool mute); + double time() const noexcept; + double time_at(double steady_seconds) const noexcept; +}; +class audio_graph { + public: + enum class kind { destination, gain, analyser, source, stream }; + explicit audio_graph(bool device_output = true, uint32_t sample_rate = 48000); + ~audio_graph(); + audio_graph(const audio_graph &) = delete; + uint32_t create(kind); + void connect(uint32_t source, uint32_t destination); + void disconnect(uint32_t source); + void set_gain(uint32_t, float value, double start, double time_constant); + void set_source(uint32_t, std::shared_ptr, std::shared_ptr); + void resume(); + void suspend(); + void close(); + double time() const noexcept; + uint32_t sample_rate() const noexcept; + void analyser(uint32_t, std::span) const; + std::shared_ptr capture(uint32_t); + // Same quantum renderer used by the device callback and numeric tests. + // No allocation, locks, JS callbacks or disk I/O on this path. + void render(float *stereo, uint32_t frames) noexcept; + void render_at(float *stereo, uint32_t frames, double steady_time) noexcept; + + private: + struct implementation; + std::unique_ptr impl_; +}; +} // namespace webscene::media diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/audio_platform.js.inc b/experiments/WebScene.NativeEngine.Probe/native/media/audio_platform.js.inc new file mode 100644 index 000000000..ea13ad5a4 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/audio_platform.js.inc @@ -0,0 +1,77 @@ +R"AUDIOJS( +(()=>{ +'use strict';const native=globalThis.__websceneAudio;delete globalThis.__websceneAudio; +const contexts=new WeakMap(),nodes=new WeakMap(),params=new WeakMap(),buffers=new WeakMap(); +const nodeToken={}; +const invalid=message=>new DOMException(message,'InvalidStateError'); +function ctx(context){const c=contexts.get(context);if(!c)throw new TypeError('Illegal AudioContext receiver');if(c.state==='closed')throw invalid('AudioContext is closed');return c;} +class AudioBuffer { + constructor(options){const channels=Number(options.numberOfChannels??1),length=Number(options.length),rate=Number(options.sampleRate);if(!Number.isInteger(channels)||channels<1||channels>32||!Number.isInteger(length)||length<1||!Number.isFinite(rate)||rate<8000||rate>192000||channels*length*4>268435456)throw new DOMException('Invalid AudioBuffer size','NotSupportedError');buffers.set(this,{channels:Array.from({length:channels},()=>new Float32Array(length)),rate,length});} + get sampleRate(){return buffers.get(this).rate;}get length(){return buffers.get(this).length;}get numberOfChannels(){return buffers.get(this).channels.length;}get duration(){return this.length/this.sampleRate;} + getChannelData(channel){channel=Number(channel);if(!Number.isInteger(channel)||!buffers.get(this).channels[channel])throw new DOMException('Channel out of bounds','IndexSizeError');return buffers.get(this).channels[channel];} + copyFromChannel(destination,channel,start=0){destination.set(this.getChannelData(channel).subarray(start,start+destination.length));} + copyToChannel(source,channel,start=0){const target=this.getChannelData(channel);target.set(source.subarray(0,Math.max(0,target.length-start)),start);} +} +class AudioParam { + constructor(context,node,value=1,token){if(token!==nodeToken)throw new TypeError('Illegal constructor');params.set(this,{context,node,value});} + get value(){return params.get(this).value;} + set value(value){const p=params.get(this);value=Number(value);if(!Number.isFinite(value))throw new TypeError('Invalid AudioParam value');native('gain',ctx(p.context).id,p.node,value,p.context.currentTime,0);p.value=value;} + setTargetAtTime(value,start,timeConstant){const p=params.get(this);value=Number(value);start=Number(start);timeConstant=Number(timeConstant);if(!Number.isFinite(value)||!Number.isFinite(start)||start<0||!Number.isFinite(timeConstant)||timeConstant<0)throw new RangeError('Invalid audio automation');native('gain',ctx(p.context).id,p.node,value,start,timeConstant);return this;} + setValueAtTime(value,start){return this.setTargetAtTime(value,start,0);} +} +class AudioNode { + constructor(context,kind,id,token){if(token!==nodeToken)throw new TypeError('Illegal constructor');const c=ctx(context);nodes.set(this,{context,id:id??native('node',c.id,kind)});this.context=context;this.channelCount=2;this.channelCountMode='max';this.channelInterpretation='speakers';} + connect(destination,output=0,input=0){const a=nodes.get(this),b=nodes.get(destination);if(!a||!b||a.context!==b.context)throw new DOMException('Audio nodes must share a context','InvalidAccessError');if(output!==0||input!==0)throw new DOMException('Invalid audio port','IndexSizeError');native('connect',ctx(a.context).id,a.id,b.id);return destination;} + disconnect(){const a=nodes.get(this);native('disconnect',ctx(a.context).id,a.id);} + get numberOfInputs(){return 1;}get numberOfOutputs(){return 1;} +} +class GainNode extends AudioNode {constructor(context){super(context,1,undefined,nodeToken);this.gain=new AudioParam(context,nodes.get(this).id,1,nodeToken);}} +class AnalyserNode extends AudioNode { + constructor(context){super(context,2,undefined,nodeToken);this._fft=2048;this._smoothing=.8;} + get fftSize(){return this._fft;}set fftSize(v){if(!Number.isInteger(v)||v<32||v>32768||(v&(v-1)))throw new DOMException('Invalid FFT size','IndexSizeError');this._fft=v;} + get frequencyBinCount(){return this._fft/2;} + get smoothingTimeConstant(){return this._smoothing;}set smoothingTimeConstant(v){if(!Number.isFinite(v)||v<0||v>1)throw new DOMException('Invalid smoothing','IndexSizeError');this._smoothing=v;} + getFloatTimeDomainData(array){if(!(array instanceof Float32Array))throw new TypeError('Float32Array required');const n=nodes.get(this);native('samples',ctx(n.context).id,n.id,array.subarray(0,this.fftSize));} +} +class MediaElementAudioSourceNode extends AudioNode {constructor(context,options){if(!(options?.mediaElement instanceof HTMLMediaElement))throw new TypeError('Media element required');try{native('validateSource',ctx(context).id,options.mediaElement);}catch(error){throw invalid(error.message);}super(context,3,undefined,nodeToken);this.mediaElement=options.mediaElement;native('source',ctx(context).id,nodes.get(this).id,this.mediaElement);}get numberOfInputs(){return 0;}} +const tracks=new WeakMap(),streams=new WeakMap(); +const trackToken={}; +class MediaStreamTrack extends EventTarget { + constructor(token,id){super();if(token!==trackToken)throw new TypeError('Illegal constructor');tracks.set(this,{id,enabled:true});native('trackBind',id,this);} + get id(){return 'webscene-audio-'+tracks.get(this).id;}get kind(){return 'audio';}get label(){return 'WebScene audio mix';} + get readyState(){return native('trackEnded',tracks.get(this).id)?'ended':'live';}get muted(){return false;} + get enabled(){return tracks.get(this).enabled;}set enabled(value){const t=tracks.get(this);t.enabled=!!value;native('trackEnabled',t.id,t.enabled);} + clone(){const result=new MediaStreamTrack(trackToken,native('trackClone',tracks.get(this).id));result.enabled=this.enabled;return result;} + stop(){native('trackStop',tracks.get(this).id);} + getSettings(){return {channelCount:2};} +} +let streamId=0; +class MediaStream extends EventTarget { + constructor(input=[]){super();const items=input instanceof MediaStream?input.getTracks():Array.from(input);if(items.some(t=>!tracks.has(t)))throw new TypeError('MediaStreamTrack required');streams.set(this,{id:'webscene-stream-'+(++streamId),tracks:[...new Set(items)]});} + get id(){return streams.get(this).id;}get active(){return this.getTracks().some(t=>t.readyState==='live');} + getTracks(){return streams.get(this).tracks.slice();}getAudioTracks(){return this.getTracks();}getVideoTracks(){return [];} + getTrackById(id){return this.getTracks().find(t=>t.id===id)||null;} + addTrack(track){if(!tracks.has(track))throw new TypeError('MediaStreamTrack required');const s=streams.get(this);if(!s.tracks.includes(track))s.tracks.push(track);} + removeTrack(track){const s=streams.get(this),i=s.tracks.indexOf(track);if(i>=0)s.tracks.splice(i,1);} + clone(){return new MediaStream(this.getTracks().map(t=>t.clone()));} +} +class MediaStreamAudioDestinationNode extends AudioNode { + constructor(context){super(context,4,undefined,nodeToken);this.stream=new MediaStream([new MediaStreamTrack(trackToken,native('capture',ctx(context).id,nodes.get(this).id))]);} + get numberOfOutputs(){return 0;} +} +class AudioContext extends EventTarget { + constructor(options={}){super();const rate=Number(options.sampleRate??48000);contexts.set(this,{id:native('create',rate),state:'suspended',rate});this.destination=new AudioNode(this,0,0,nodeToken);} + get sampleRate(){return contexts.get(this).rate;}get currentTime(){const c=contexts.get(this);return c.state==='closed'?(c.closedTime||0):native('time',c.id);}get state(){return contexts.get(this).state;} + async resume(){const c=ctx(this);native('resume',c.id);if(c.state!=='running'){c.state='running';this.dispatchEvent(new Event('statechange'));}} + async suspend(){const c=ctx(this);native('suspend',c.id);if(c.state!=='suspended'){c.state='suspended';this.dispatchEvent(new Event('statechange'));}} + async close(){const c=ctx(this);c.closedTime=this.currentTime;native('close',c.id);c.state='closed';this.dispatchEvent(new Event('statechange'));} + createGain(){return new GainNode(this);}createAnalyser(){return new AnalyserNode(this);} + createMediaElementSource(element){return new MediaElementAudioSourceNode(this,{mediaElement:element});} + createMediaStreamDestination(){return new MediaStreamAudioDestinationNode(this);} + createBuffer(channels,length,rate){return new AudioBuffer({numberOfChannels:channels,length,sampleRate:rate});} + decodeAudioData(data,success,error){ctx(this);let pending;try{pending=native('decode',data,this.sampleRate);}catch(e){pending=Promise.reject(e);}const promise=pending.catch(error=>{if(error instanceof TypeError)throw error;throw new DOMException(error.message,'EncodingError');}).then(result=>{const buffer=Object.create(AudioBuffer.prototype);buffers.set(buffer,{channels:result.channels,rate:result.sampleRate,length:result.channels[0].length});return buffer;});if(typeof success==='function'||typeof error==='function')promise.then(success,error);return promise;} +} +for(const ctor of [AudioContext,AudioBuffer,AudioNode,AudioParam,GainNode,AnalyserNode,MediaElementAudioSourceNode,MediaStreamAudioDestinationNode,MediaStream,MediaStreamTrack])Object.defineProperty(ctor.prototype,Symbol.toStringTag,{value:ctor.name,configurable:true}); +Object.assign(globalThis,{AudioContext,AudioBuffer,AudioNode,AudioParam,GainNode,AnalyserNode,MediaElementAudioSourceNode,MediaStreamAudioDestinationNode,MediaStream,MediaStreamTrack}); +})(); +)AUDIOJS" diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/decode_service.h b/experiments/WebScene.NativeEngine.Probe/native/media/decode_service.h new file mode 100644 index 000000000..583e798af --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/decode_service.h @@ -0,0 +1,71 @@ +#pragma once +#include "media_decode.h" +#include +#include +#include +#include +#include +#include +#include + +namespace webscene::media { +// Bounded owner-independent decode worker. Callers deliver ready results into +// their existing runtime completion mailbox, never call JS from this thread. +class decode_service { + std::mutex mutex_; + std::condition_variable wake_; + std::deque> pending_; + bool closing_{}; + std::function notify_; + std::jthread worker_; + template std::future submit(Work work) { + auto promise = std::make_shared>(); + auto future = promise->get_future(); + std::lock_guard lock(mutex_); + if (closing_ || pending_.size() >= 8) throw std::runtime_error("Media decode queue unavailable"); + pending_.push_back([this, promise, work = std::move(work)](std::stop_token stop) mutable { + try { + if (stop.stop_requested()) throw std::runtime_error("Media service closed"); + promise->set_value(work(stop)); + } catch (...) { promise->set_exception(std::current_exception()); } + if(notify_)notify_(); + }); + wake_.notify_one(); + return future; + } +public: + explicit decode_service(std::function notify = {}) : notify_(std::move(notify)), worker_([this](std::stop_token stop) { + for (;;) { + std::function work; + { + std::unique_lock lock(mutex_); + wake_.wait(lock, [this] { return closing_ || !pending_.empty(); }); + if (pending_.empty() && closing_) return; + work = std::move(pending_.front()); pending_.pop_front(); + } + work(stop); + } + }) {} + ~decode_service() { close(); } + decode_service(const decode_service&) = delete; + decode_service& operator=(const decode_service&) = delete; + // Owner-thread lifecycle operation; closing resolves every queued future. + void close() { + { std::lock_guard lock(mutex_); closing_ = true; } + worker_.request_stop(); wake_.notify_all(); + if (worker_.joinable()) worker_.join(); + } + std::future audio(std::shared_ptr source, decode_limits limits = {}, uint32_t target_sample_rate = 0) { + if (!source) throw std::invalid_argument("Missing media source"); + return submit([source = std::move(source), limits, target_sample_rate](std::stop_token stop) { + return decode_audio(source->bytes, limits, stop, target_sample_rate); + }); + } + std::future video(std::shared_ptr source, double seconds, decode_limits limits = {}) { + if (!source) throw std::invalid_argument("Missing media source"); + return submit([source = std::move(source), seconds, limits](std::stop_token stop) { + return decode_video_frame(*source, seconds, limits, stop); + }); + } +}; +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/media_decode.h b/experiments/WebScene.NativeEngine.Probe/native/media/media_decode.h new file mode 100644 index 000000000..96b726b9d --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/media_decode.h @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace webscene::media { +// Only bytes already admitted by the host resource loader enter a decoder. +// Decoder backends must never fetch an arbitrary application URL themselves. +struct encoded_source { + std::vector bytes; + std::string extension; +}; +struct audio_buffer { + uint32_t channels{}, sample_rate{}; + std::vector samples; // interleaved; immutable once published + uint64_t frames() const { return channels ? samples.size() / channels : 0; } +}; +struct decode_limits { + uint64_t encoded_bytes = 256ULL * 1024 * 1024; + uint64_t decoded_audio_bytes = 256ULL * 1024 * 1024; + uint64_t video_pixels = 64ULL * 1024 * 1024; +}; +// A native frame lease, independent of JS wrappers and decoder/session lifetime. +// On macOS native_surface is a retained CVPixelBuffer. No CPU pixels are copied. +enum class surface_kind { cv_pixel_buffer, d3d11_texture, dma_buf, host_pixels }; +struct video_frame { + surface_kind kind{surface_kind::cv_pixel_buffer}; + std::array display_transform{1, 0, 0, 1, 0, 0}; + std::shared_ptr native_surface; + uint32_t width{}, height{}, pixel_format{}; + double timestamp{}, duration{}, sample_duration{}; +}; +class video_decoder { +public: + virtual ~video_decoder() = default; + virtual video_frame read(double seconds, std::stop_token = {}) = 0; + virtual double duration() const noexcept = 0; + virtual audio_buffer audio(std::stop_token = {}) = 0; +}; +std::unique_ptr open_video(const encoded_source&, decode_limits = {}, std::stop_token = {}); +audio_buffer decode_native_audio(std::span, decode_limits, std::stop_token, uint32_t target_sample_rate); +audio_buffer decode_audio(std::span, decode_limits = {}, std::stop_token = {}, uint32_t target_sample_rate = 0); +video_frame decode_video_frame(const encoded_source&, double seconds, decode_limits = {}, std::stop_token = {}); +bool native_video_decode_available() noexcept; +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/media_platform.js.inc b/experiments/WebScene.NativeEngine.Probe/native/media/media_platform.js.inc new file mode 100644 index 000000000..d8f17957c --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/media_platform.js.inc @@ -0,0 +1,108 @@ +R"MEDIAJS( +(()=>{ +'use strict'; +const native=globalThis.__websceneMedia;delete globalThis.__websceneMedia; +const states=new WeakMap(),errors=new WeakMap(); +function MediaError(){throw new TypeError('Illegal constructor');} +for(const name of ['code','message'])Object.defineProperty(MediaError.prototype,name,{get(){const value=errors.get(this);if(!value)throw new TypeError('Illegal MediaError receiver');return value[name];}}); +for(const [name,value]of Object.entries({MEDIA_ERR_ABORTED:1,MEDIA_ERR_NETWORK:2,MEDIA_ERR_DECODE:3,MEDIA_ERR_SRC_NOT_SUPPORTED:4})){Object.defineProperty(MediaError,name,{value});Object.defineProperty(MediaError.prototype,name,{value});} +function mediaError(code,message){const error=Object.create(MediaError.prototype);errors.set(error,{code,message});return error;} + +const fail=(message,name='InvalidStateError')=>new DOMException(message,name); +function state(el){ + if(!el||!['VIDEO','AUDIO'].includes(el.tagName))throw new TypeError('Illegal media receiver'); + let s=states.get(el);if(!s){native('brand',el);s={generation:0,readyState:0,networkState:0,duration:NaN,time:0,paused:true,ended:false,seeking:false,rate:1,defaultRate:1,volume:1,muted:el.hasAttribute('muted'),error:null,width:0,height:0,pending:[],played:[],playStart:0,currentSrc:'',frame:0,lastTick:0};states.set(el,s);} + return s; +} +function emit(el,name){const generation=state(el).generation;setTimeout(()=>{if(state(el).generation===generation)el.dispatchEvent(new Event(name));},0);} +function recordPlayed(s,time){ + if(s.paused||time<=s.playStart)return; + const ranges=[...s.played,[s.playStart,time]].sort((a,b)=>a[0]-b[0]);s.played=[]; + for(const range of ranges){const last=s.played[s.played.length-1];if(last&&range[0]<=last[1])last[1]=Math.max(last[1],range[1]);else s.played.push(range);} +} +function rejectPlay(s,error){for(const p of s.pending.splice(0))p.reject(error);} +function sync(el,s){native('state',el,s.time,s.rate,!s.paused,s.volume,s.muted);} +function finishPlay(el,s){ + if(s.paused||s.readyState<2||s.playTask)return; + s.playTask=true;const generation=s.generation; + setTimeout(()=>{s.playTask=false;if(s.generation!==generation||s.paused||s.readyState<2)return;s.playStart=s.time;try{sync(el,s);}catch(error){s.paused=true;native('state',el,s.time,s.rate,false,s.volume,s.muted);s.error=mediaError(3,String(error.message||error));rejectPlay(s,fail(s.error.message,'NotSupportedError'));emit(el,'error');return;}el.dispatchEvent(new Event('playing'));for(const p of s.pending.splice(0))p.resolve();tick(el,s);},0); +} +function tick(el,s){ + if(s.frame||s.paused||s.readyState<2)return; + s.lastTick=performance.now(); + s.frame=requestAnimationFrame(now=>{ + s.frame=0;if(s.paused)return; + s.time=Math.min(s.duration,native('time',el)??s.time); + if(now-(s.lastEvent||0)>250){s.lastEvent=now;emit(el,'timeupdate');} + recordPlayed(s,s.time); + if(s.time>=s.duration){if(el.loop){s.time=0;s.playStart=0;sync(el,s);native('seek',el,0);}else{s.paused=true;s.ended=true;sync(el,s);emit(el,'timeupdate');emit(el,'pause');emit(el,'ended');return;}} + tick(el,s); + }); +} +function signature(el){return [el.getAttribute('src'),...Array.from(el.querySelectorAll('source'),s=>s.getAttribute('src')+';'+s.getAttribute('type'))].join('|');} +function load(el,select=true){ + const s=state(el),old=s.networkState;s.sourceSignature=signature(el);const generation=++s.generation; + s.playTask=false;s.abort?.abort();s.abort=new AbortController();native('release',el); + if(s.frame)cancelAnimationFrame(s.frame);s.frame=0; + rejectPlay(s,fail('Media load interrupted playback','AbortError')); + Object.assign(s,{readyState:0,networkState:0,duration:NaN,time:0,paused:true,ended:false,seeking:false,error:null,width:0,height:0,currentSrc:'',played:[],playStart:0}); + if(old){emit(el,'abort');emit(el,'emptied');} + if(!select){s.sourceSignature=undefined;return;} + s.rate=s.defaultRate; + const src=el.getAttribute('src')||Array.from(el.querySelectorAll('source')).find(source=>!source.getAttribute('type')||el.canPlayType(source.getAttribute('type')))?.getAttribute('src');if(!src)return; + const url=new URL(src,document.baseURI).href;s.currentSrc=url;s.networkState=2; + emit(el,'loadstart'); + fetch(url,{signal:s.abort.signal}).then(r=>{if(!r.ok)throw Error('Media resource failed: '+r.status);return r.arrayBuffer();}).then(bytes=>{ + if(s.generation!==generation)return; + const ext=new URL(url).pathname.match(/\.[^.\/]+$/)?.[0]?.toLowerCase()|| (el.tagName==='VIDEO'?'.mp4':'.wav'); + native('load',el,bytes,ext,function(info){ + if(s.generation!==generation)return; + if(info.error){s.error=mediaError(3,info.error);s.networkState=3;rejectPlay(s,fail(info.error,'NotSupportedError'));emit(el,'error');return;} + const first=s.readyState===0;Object.assign(s,{duration:info.duration,width:info.width,height:info.height,networkState:1}); + if(first){s.readyState=1;emit(el,'loadedmetadata');s.readyState=2;emit(el,'loadeddata');s.readyState=4;emit(el,'progress');emit(el,'canplay');emit(el,'canplaythrough');} + if(s.seeking&&!info.seeking){s.seeking=false;emit(el,'timeupdate');emit(el,'seeked');} + if(first){finishPlay(el,s);if(el.autoplay)el.play().catch(()=>{});} + }); + }).catch(error=>{if(s.generation!==generation)return;s.error=mediaError(4,String(error.message||error));s.networkState=3;rejectPlay(s,fail(s.error.message,'NotSupportedError'));emit(el,'error');}); +} +function HTMLMediaElement(){throw new TypeError('Illegal constructor');} +Object.setPrototypeOf(HTMLMediaElement.prototype,HTMLElement.prototype);Object.setPrototypeOf(HTMLMediaElement,HTMLElement); +function HTMLVideoElement(){throw new TypeError('Illegal constructor');} +function HTMLAudioElement(){throw new TypeError('Illegal constructor');} +Object.setPrototypeOf(HTMLVideoElement,HTMLMediaElement);Object.setPrototypeOf(HTMLAudioElement,HTMLMediaElement);Object.setPrototypeOf(HTMLVideoElement.prototype,HTMLMediaElement.prototype);Object.setPrototypeOf(HTMLAudioElement.prototype,HTMLMediaElement.prototype); +const proto=HTMLMediaElement.prototype; +proto.setAttribute=function(name,value){HTMLElement.prototype.setAttribute.call(this,name,value);if(String(name).toLowerCase()==='src')load(this);}; +proto.removeAttribute=function(name){HTMLElement.prototype.removeAttribute.call(this,name);if(String(name).toLowerCase()==='src')load(this);}; +const prop=(name,get,set)=>Object.defineProperty(proto,name,{get,set,enumerable:true,configurable:true}); +for(const name of ['readyState','networkState','duration','paused','ended','seeking','error','currentSrc'])prop(name,function(){return state(this)[name];}); +prop('src',function(){const v=this.getAttribute('src');return v?new URL(v,document.baseURI).href:'';},function(v){this.setAttribute('src',String(v));}); +prop('currentTime',function(){const s=state(this);const time=s.paused?s.time:(native('time',this)??s.time);return Number.isFinite(s.duration)?Math.min(s.duration,time):time;},function(v){const s=state(this);v=Number(v);if(!Number.isFinite(v))throw new TypeError('Invalid currentTime');recordPlayed(s,this.currentTime);s.time=Math.max(0,Number.isFinite(s.duration)?Math.min(v,s.duration):v);s.playStart=s.time;s.ended=false;sync(this,s);if(s.readyState){s.seeking=true;emit(this,'seeking');native('seek',this,s.time);}}); +prop('playbackRate',function(){return state(this).rate;},function(v){v=Number(v);if(!Number.isFinite(v)||v<0||v>16)throw fail('Unsupported playback rate','NotSupportedError');const s=state(this);s.time=this.currentTime;s.rate=v;sync(this,s);emit(this,'ratechange');}); +prop('defaultPlaybackRate',function(){return state(this).defaultRate;},function(v){v=Number(v);if(!Number.isFinite(v)||v<0||v>16)throw fail('Unsupported playback rate','NotSupportedError');state(this).defaultRate=v;emit(this,'ratechange');}); +prop('volume',function(){return state(this).volume;},function(v){v=Number(v);if(!Number.isFinite(v)||v<0||v>1)throw fail('Volume out of range','IndexSizeError');const s=state(this);s.time=this.currentTime;s.volume=v;sync(this,s);emit(this,'volumechange');}); +prop('muted',function(){return state(this).muted;},function(v){const s=state(this);s.time=this.currentTime;s.muted=!!v;sync(this,s);emit(this,'volumechange');}); +for(const name of ['autoplay','loop','defaultMuted','playsInline']){const attr={defaultMuted:'muted',playsInline:'playsinline'}[name]||name;prop(name,function(){return this.hasAttribute(attr);},function(v){if(v)this.setAttribute(attr,'');else this.removeAttribute(attr);});} +for(const name of ['preload','crossOrigin']){const attr=name.toLowerCase();prop(name,function(){return this.getAttribute(attr)||'';},function(v){this.setAttribute(attr,String(v));});} +for(const name of ['videoWidth','videoHeight'])Object.defineProperty(HTMLVideoElement.prototype,name,{get(){return state(this)[name==='videoWidth'?'width':'height'];},configurable:true}); +proto.load=function(){load(this);};proto.pause=function(){const s=state(this);if(!s.paused){s.time=this.currentTime;recordPlayed(s,s.time);s.paused=true;sync(this,s);emit(this,'timeupdate');emit(this,'pause');}rejectPlay(s,fail('Playback paused','AbortError'));}; +proto.play=function(){const s=state(this);if(!s.currentSrc)load(this);if(!s.currentSrc)return Promise.reject(fail('No media source','NotSupportedError'));if(s.error)return Promise.reject(fail(s.error.message,'NotSupportedError'));if(s.ended){s.time=0;s.ended=false;native('seek',this,0);}const changed=s.paused;s.paused=false;const result=new Promise((resolve,reject)=>s.pending.push({resolve,reject}));if(changed)emit(this,'play');if(s.readyState>=2)finishPlay(this,s);return result;}; +proto.canPlayType=function(type){type=String(type).toLowerCase();if(this.tagName==='VIDEO')return !native('videoSupported',this)?'':/^(video\/(mp4|quicktime))([; ]|$)/.test(type)?'maybe':'';return /^(audio\/(wav|wave|x-wav|mpeg|flac|x-flac))([; ]|$)/.test(type)?'maybe':'';}; +for(const [name,value]of Object.entries({HAVE_NOTHING:0,HAVE_METADATA:1,HAVE_CURRENT_DATA:2,HAVE_FUTURE_DATA:3,HAVE_ENOUGH_DATA:4,NETWORK_EMPTY:0,NETWORK_IDLE:1,NETWORK_LOADING:2,NETWORK_NO_SOURCE:3})){Object.defineProperty(proto,name,{value,enumerable:true});Object.defineProperty(HTMLMediaElement,name,{value,enumerable:true});} +const timeRanges=new WeakMap(); +function TimeRanges(){throw new TypeError('Illegal constructor');} +Object.defineProperty(TimeRanges.prototype,'length',{get(){return timeRanges.get(this).length;}}); +for(const [method,index]of [['start',0],['end',1]])TimeRanges.prototype[method]=function(i){i=Number(i)>>>0;const ranges=timeRanges.get(this);if(i>=ranges.length)throw fail('TimeRanges index','IndexSizeError');return ranges[i][index];}; +for(const name of ['buffered','seekable','played'])prop(name,function(){const s=state(this);if(name==='played')recordPlayed(s,this.currentTime);const ranges=name==='played'?s.played.map(r=>r.slice()):s.readyState&&s.duration>0?[[0,s.duration]]:[];const result=Object.create(TimeRanges.prototype);timeRanges.set(result,ranges);return result;}); +Object.assign(globalThis,{MediaError,TimeRanges,HTMLMediaElement,HTMLVideoElement,HTMLAudioElement,Audio:function(src){const el=document.createElement('audio');el.preload='auto';if(src!==undefined)el.src=src;return el;}}); +for(const ctor of [HTMLMediaElement,HTMLVideoElement,HTMLAudioElement,MediaError,TimeRanges])Object.defineProperty(ctor.prototype,Symbol.toStringTag,{value:ctor.name,configurable:true}); +globalThis.Audio.prototype=HTMLAudioElement.prototype; +function initialize(el,connected){ + const s=state(el);if(s.connected&&!connected)load(el,false);s.connected=connected; + if(!connected||s.initializeQueued||s.sourceSignature===signature(el))return; + s.initializeQueued=true;setTimeout(()=>{s.initializeQueued=false;if(s.sourceSignature!==signature(el)&&(el.hasAttribute('src')||el.querySelector('source')||s.currentSrc))load(el);},0); +} +initialize.videoPrototype=HTMLVideoElement.prototype;initialize.audioPrototype=HTMLAudioElement.prototype; +globalThis.__websceneInitializeMedia=initialize; + +})(); +)MEDIAJS" diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/media_session.cpp b/experiments/WebScene.NativeEngine.Probe/native/media/media_session.cpp new file mode 100644 index 000000000..d2b97fed4 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/media_session.cpp @@ -0,0 +1,183 @@ +#include "media_session.h" +#include +#include +namespace webscene::media { +media_session::media_session(std::function notify) + : notify_(std::move(notify)), worker_([this](std::stop_token stop) { run(stop); }) {} +media_session::~media_session() { close(); } +void media_session::close() { + std::stop_source active; + { + std::lock_guard lock(mutex_); + closing_ = true; + active = active_stop_; + } + active.request_stop(); + worker_.request_stop(); + wake_.notify_all(); + if (worker_.joinable()) + worker_.join(); +} +void media_session::load(std::shared_ptr source, bool video) { + if (!source || source->bytes.empty() || source->bytes.size() > decode_limits{}.encoded_bytes) + throw std::invalid_argument("Invalid admitted media source"); + std::stop_source old; + { + std::lock_guard lock(mutex_); + if (closing_) + throw std::runtime_error("Media session closed"); + old = active_stop_; + active_stop_ = std::stop_source{}; + source_ = std::move(source); + video_ = video; + ++generation_; + ++request_; + requested_time_ = 0; + queued_.clear(); + exhausted_ = false; + published_ = {}; + published_.generation = generation_; + } + old.request_stop(); + wake_.notify_one(); +} +void media_session::seek(double time) { + if (!std::isfinite(time) || time < 0) + throw std::invalid_argument("Invalid media seek"); + { + std::lock_guard lock(mutex_); + if (closing_) + throw std::runtime_error("Media session closed"); + requested_time_ = time; + queued_.clear(); + exhausted_ = false; + ++request_; + published_.seeking = true; + } + wake_.notify_one(); +} +media_session::snapshot media_session::read() { + std::lock_guard lock(mutex_); + auto result = published_; + result.buffered_frames = queued_.size(); + return result; +} +media_session::snapshot media_session::present(double media_time) { + std::lock_guard lock(mutex_); + // Timestamp coverage, rather than completion order. A frame remains valid + // until the next frame's PTS. Never replace it with an early future frame. + if (!published_.seeking) { + const auto consumed = select_video_frame(queued_, media_time, published_.video); + if (consumed) { + ++published_.version; + ++published_.selected; + published_.dropped += consumed - 1; + } else { + ++published_.repeated; + } + // A discontinuity or sustained overload must not leave decoding many + // seconds behind. Catch up with a single coalesced decoder request. + if (published_.ready && !exhausted_ && queued_.empty() && + media_time > published_.video.timestamp + .5 && video_) { + requested_time_ = media_time; + published_.seeking = true; + ++request_; + } + } + wake_.notify_one(); + return published_; +} +void media_session::run(std::stop_token stop) { + uint64_t loaded = 0, processed = 0; + std::unique_ptr decoder; + std::shared_ptr pcm; + while (!stop.stop_requested()) { + std::shared_ptr source; + uint64_t generation, request; + double time; + bool video, prefetch; + std::stop_token cancel; + { + std::unique_lock lock(mutex_); + wake_.wait(lock, [&] { + return closing_ || request_ != processed || + (video_ && published_.ready && !exhausted_ && queued_.size() < queue_capacity && + (queued_.empty() || (queued_.size() + 1) * uint64_t(published_.video.width) * + published_.video.height * 4 <= 128ULL * 1024 * 1024)); + }); + if (closing_) + return; + source = source_; + generation = generation_; + request = request_; + prefetch = request == processed; + time = requested_time_; + if (prefetch) { + const auto &last = queued_.empty() ? published_.video : queued_.back(); + time = last.timestamp + 1e-5; + } + video = video_; + cancel = active_stop_.get_token(); + } + processed = request; + snapshot next; + next.generation = generation; + try { + if (!source) + throw std::runtime_error("No media source"); + if (loaded != generation) { + decoder.reset(); + pcm.reset(); + if (video) { + decoder = open_video(*source, {}, cancel); + auto audio = decoder->audio(cancel); + if (audio.frames()) + pcm = std::make_shared(std::move(audio)); + } else + pcm = std::make_shared(decode_audio(source->bytes, {}, cancel)); + loaded = generation; + } + next.audio = pcm; + if (video) { + next.video = decoder->read(time, cancel); + next.duration = decoder->duration(); + } else { + next.audio = pcm; + next.duration = double(pcm->frames()) / pcm->sample_rate; + } + next.ready = true; + } catch (const std::exception &e) { + next.error = e.what(); + } + bool deliver = false; + { + std::lock_guard lock(mutex_); + if (!closing_ && generation == generation_ && request == request_) { + if (prefetch) { + const auto &last = queued_.empty() ? published_.video : queued_.back(); + if (next.error.empty()) { + if (!next.video.native_surface || next.video.timestamp <= last.timestamp + 1e-7) + exhausted_ = true; + else + queued_.push_back(std::move(next.video)); + // Refill silently; only presentation opportunities publish. + continue; + } + exhausted_ = true; + // Decoder failures still reach the element's error event. + next.video = published_.video; + } + next.seeking = request != request_; + next.version = published_.version + 1; + next.selected = published_.selected; + next.dropped = published_.dropped; + next.repeated = published_.repeated; + published_ = std::move(next); + deliver = true; + } + } + if (deliver && notify_) + notify_(); + } +} +} // namespace webscene::media diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/media_session.h b/experiments/WebScene.NativeEngine.Probe/native/media/media_session.h new file mode 100644 index 000000000..8b819fb76 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/media_session.h @@ -0,0 +1,60 @@ +#pragma once +#include "media_decode.h" +#include +#include +#include +#include +#include +#include + +namespace webscene::media { +// Consume only frames whose presentation interval has begun. Returns consumed +// count (more than one means obsolete decoded frames were dropped). +inline size_t select_video_frame(std::deque& queue, double time, video_frame& current) { + size_t consumed = 0; + while (!queue.empty() && queue.front().timestamp <= time + 1e-7) { + current = std::move(queue.front()); + queue.pop_front(); + ++consumed; + } + return consumed; +} +// Persistent source/decoder with bounded decode-ahead. The host presentation +// opportunity selects a complete frame; decoder completion never advances it. +class media_session { + public: + struct snapshot { + uint64_t generation{}, version{}, selected{}, dropped{}, repeated{}; + double duration{}; + size_t buffered_frames{}; + bool ready{}, seeking{}; + std::string error; + std::shared_ptr audio; + video_frame video; + }; + + private: + std::mutex mutex_; + std::condition_variable wake_; + std::shared_ptr source_; + snapshot published_; + uint64_t generation_{}, request_{}; + double requested_time_{}; + bool video_{}, closing_{}, exhausted_{}; + std::deque queued_; + static constexpr size_t queue_capacity = 4; + std::stop_source active_stop_; + std::function notify_; + std::jthread worker_; + void run(std::stop_token); + + public: + explicit media_session(std::function notify = {}); + ~media_session(); + void load(std::shared_ptr, bool video); + void seek(double); + snapshot read(); + snapshot present(double media_time); + void close(); +}; +} // namespace webscene::media diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/miniaudio_impl.c b/experiments/WebScene.NativeEngine.Probe/native/media/miniaudio_impl.c new file mode 100644 index 000000000..609e6aa25 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/miniaudio_impl.c @@ -0,0 +1,3 @@ +// Statically compiled once; MIT-0 license is shipped alongside native artifacts. +#define MINIAUDIO_IMPLEMENTATION +#include "miniaudio.h" diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/video_decode_macos.mm b/experiments/WebScene.NativeEngine.Probe/native/media/video_decode_macos.mm new file mode 100644 index 000000000..ca0229ed9 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/video_decode_macos.mm @@ -0,0 +1,277 @@ +#include "media_decode.h" +#import +#import +#include +#include +#include +#include +#include +#include +#include + +namespace webscene::media { +namespace { +// Private spool file because AVAssetReader consumes file assets. Only admitted +// bytes are written. It is removed on every success/error path after reader use. +struct source_file { + std::string path; + source_file(const encoded_source &source, std::stop_token stop) { + const auto ext = source.extension; + if (ext != ".mp4" && ext != ".mov" && ext != ".m4v") + throw std::invalid_argument("Unsupported video container extension"); + std::string pattern = + std::string([NSTemporaryDirectory() fileSystemRepresentation]) + "webscene-media-XXXXXX" + ext; + std::vector name(pattern.begin(), pattern.end()); + name.push_back(0); + int fd = mkstemps(name.data(), static_cast(ext.size())); + if (fd < 0) + throw std::runtime_error("Cannot create private media spool"); + path = name.data(); + size_t offset = 0; + while (offset < source.bytes.size()) { + if (stop.stop_requested()) { + close(fd); + unlink(path.c_str()); + throw std::runtime_error("Video decode cancelled"); + } + auto count = write(fd, source.bytes.data() + offset, + std::min(source.bytes.size() - offset, 1024 * 1024)); + if (count < 0 && errno == EINTR) + continue; + if (count <= 0) { + close(fd); + unlink(path.c_str()); + throw std::runtime_error("Cannot write media spool"); + } + offset += static_cast(count); + } + if (close(fd) != 0) { + unlink(path.c_str()); + throw std::runtime_error("Cannot close media spool"); + } + } + ~source_file() { unlink(path.c_str()); } +}; +std::runtime_error failure(NSError *error, const char *fallback) { + return std::runtime_error(error ? [[error localizedDescription] UTF8String] : fallback); +} +audio_buffer read_audio(AVAsset *asset, decode_limits limits, std::stop_token stop, uint32_t rate) { + auto *track = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject]; + if (!track) + return {}; + if (stop.stop_requested()) + throw std::runtime_error("Audio decode cancelled"); + NSError *error = nil; + auto *reader = [[AVAssetReader alloc] initWithAsset:asset error:&error]; + if (!reader) + throw failure(error, "Cannot create audio reader"); + auto *settings = [@{ + AVFormatIDKey : @(kAudioFormatLinearPCM), + AVLinearPCMBitDepthKey : @32, + AVLinearPCMIsFloatKey : @YES, + AVLinearPCMIsNonInterleaved : @NO + } mutableCopy]; + if (rate) + settings[AVSampleRateKey] = @(rate); + auto *output = [[AVAssetReaderTrackOutput alloc] initWithTrack:track outputSettings:settings]; + output.alwaysCopiesSampleData = NO; + if (![reader canAddOutput:output]) + throw std::runtime_error("Audio output format unavailable"); + [reader addOutput:output]; + std::stop_callback cancel(stop, [reader] { [reader cancelReading]; }); + if (![reader startReading]) + throw failure(reader.error, "Cannot start audio reader"); + audio_buffer result; + for (;;) { + if (stop.stop_requested()) + throw std::runtime_error("Audio decode cancelled"); + auto sample = [output copyNextSampleBuffer]; + if (!sample) { + if (reader.status != AVAssetReaderStatusCompleted) + throw failure(reader.error, "Audio decode failed"); + break; + } + struct release { + CMSampleBufferRef sample; + ~release() { CFRelease(sample); } + } owner{sample}; + auto format = + CMAudioFormatDescriptionGetStreamBasicDescription(CMSampleBufferGetFormatDescription(sample)); + if (!format || format->mFormatID != kAudioFormatLinearPCM || format->mBitsPerChannel != 32 || + !(format->mFormatFlags & kAudioFormatFlagIsFloat) || + (format->mFormatFlags & kAudioFormatFlagIsNonInterleaved) || !format->mChannelsPerFrame || + format->mChannelsPerFrame > 32) + throw std::runtime_error("Invalid PCM output format"); + if (result.channels && + (result.channels != format->mChannelsPerFrame || result.sample_rate != format->mSampleRate)) + throw std::runtime_error("Midstream audio format change unsupported"); + result.channels = format->mChannelsPerFrame; + result.sample_rate = static_cast(format->mSampleRate); + auto block = CMSampleBufferGetDataBuffer(sample); + if (!block) + throw std::runtime_error("Missing PCM buffer"); + auto bytes = CMBlockBufferGetDataLength(block); + if (bytes % sizeof(float) || bytes > limits.decoded_audio_bytes || + result.samples.size() > (limits.decoded_audio_bytes - bytes) / sizeof(float)) + throw std::length_error("Decoded audio exceeds memory limit"); + auto offset = result.samples.size(); + result.samples.resize(offset + bytes / sizeof(float)); + if (CMBlockBufferCopyDataBytes(block, 0, bytes, result.samples.data() + offset) != + kCMBlockBufferNoErr) + throw std::runtime_error("Cannot read PCM output"); + } + return result; +} + +} +class apple_video_decoder final : public video_decoder { + std::unique_ptr file_; + AVURLAsset *asset_; + AVAssetTrack *track_; + AVAssetReader *reader_; + AVAssetReaderTrackOutput *output_; + decode_limits limits_; + video_frame last_; + double requested_{-1}, duration_{}; + void restart(double seconds) { + [reader_ cancelReading]; + NSError *error = nil; + reader_ = [[AVAssetReader alloc] initWithAsset:asset_ error:&error]; + if (!reader_) + throw failure(error, "Cannot create video reader"); + output_ = [[AVAssetReaderTrackOutput alloc] + initWithTrack:track_ + outputSettings:@{ + (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA), + (id)kCVPixelBufferIOSurfacePropertiesKey : @{}, + (id)kCVPixelBufferMetalCompatibilityKey : @YES + }]; + output_.alwaysCopiesSampleData = NO; + if (![reader_ canAddOutput:output_]) + throw std::runtime_error("Video output format is unavailable"); + [reader_ addOutput:output_]; + reader_.timeRange = CMTimeRangeMake(CMTimeMakeWithSeconds(seconds, 600000), kCMTimePositiveInfinity); + if (![reader_ startReading]) + throw failure(reader_.error, "Cannot start video reader"); + last_ = {}; + } + + public: + apple_video_decoder(const encoded_source &source, decode_limits limits, std::stop_token stop) + : limits_(limits) { + if (source.bytes.empty() || source.bytes.size() > limits.encoded_bytes) + throw std::invalid_argument("Encoded video size is outside the admitted limit"); + if (stop.stop_requested()) + throw std::runtime_error("Video decode cancelled"); + file_ = std::make_unique(source, stop); + asset_ = [AVURLAsset + URLAssetWithURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:file_->path.c_str()]] + options:@{ + AVURLAssetReferenceRestrictionsKey : @(AVAssetReferenceRestrictionForbidAll) + }]; + track_ = [[asset_ tracksWithMediaType:AVMediaTypeVideo] firstObject]; + if (!track_) + throw std::runtime_error("Video has no decodable video track"); + const auto size = track_.naturalSize; + if (size.width <= 0 || size.height <= 0 || size.width * size.height > limits.video_pixels) + throw std::length_error("Video dimensions exceed decode limit"); + duration_ = CMTimeGetSeconds(asset_.duration); + if (!std::isfinite(duration_) || duration_ <= 0) + throw std::runtime_error("Invalid video duration"); + } + ~apple_video_decoder() override { [reader_ cancelReading]; } + double duration() const noexcept override { return duration_; } + audio_buffer audio(std::stop_token stop) override { + @autoreleasepool { + return read_audio(asset_, limits_, stop, 0); + } + } + video_frame read(double seconds, std::stop_token stop) override { + if (!std::isfinite(seconds) || seconds < 0) + throw std::invalid_argument("Invalid video seek time"); + if (stop.stop_requested()) + throw std::runtime_error("Video decode cancelled"); + @autoreleasepool { + // Reuse one asset/spool and reader during sequential playback. Only + // discontinuities reopen a reader. Keep at most the current lease. + seconds = std::min( + seconds, std::max(0.0, duration_ - 1.0 / std::max(1.0, double(track_.nominalFrameRate)))); + if (!reader_ || seconds < requested_ || seconds - requested_ > .5) { + if (std::getenv("WEBSCENE_MEDIA_TRACE")) + std::fprintf(stderr, "media_restart requested=%.6f previous=%.6f\n", seconds, requested_); + restart(seconds); + } + requested_ = seconds; + AVAssetReader *active_reader = reader_; + std::stop_callback cancel(stop, [active_reader] { [active_reader cancelReading]; }); + while (!last_.native_surface || last_.timestamp + 1e-7 < seconds) { + CMSampleBufferRef sample = [output_ copyNextSampleBuffer]; + if (!sample) { + if (stop.stop_requested()) + throw std::runtime_error("Video decode cancelled"); + if (reader_.status == AVAssetReaderStatusCompleted && last_.native_surface) + break; + throw failure(reader_.error, "No video frame at requested time"); + } + struct release_sample { + CMSampleBufferRef value; + ~release_sample() { CFRelease(value); } + } release{sample}; + CVPixelBufferRef pixel = CMSampleBufferGetImageBuffer(sample); + if (!pixel) + throw std::runtime_error("Decoded sample has no pixel buffer"); + const auto width = CVPixelBufferGetWidth(pixel), height = CVPixelBufferGetHeight(pixel); + if (!width || !height || width > limits_.video_pixels / height) + throw std::length_error("Decoded video exceeds pixel limit"); + CVPixelBufferRetain(pixel); + video_frame frame; + frame.native_surface = std::shared_ptr( + pixel, [](void *value) { CVPixelBufferRelease(static_cast(value)); }); + const auto transform = track_.preferredTransform; + frame.display_transform = {transform.a, transform.b, transform.c, + transform.d, transform.tx, transform.ty}; + frame.width = static_cast(width); + frame.height = static_cast(height); + frame.pixel_format = CVPixelBufferGetPixelFormatType(pixel); + frame.timestamp = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sample)); + frame.duration = duration_; + frame.sample_duration = CMTimeGetSeconds(CMSampleBufferGetDuration(sample)); + if (!std::isfinite(frame.sample_duration) || frame.sample_duration <= 0) + frame.sample_duration = 1.0 / std::max(1.0, double(track_.nominalFrameRate)); + last_ = std::move(frame); + } + if (stop.stop_requested()) + throw std::runtime_error("Video decode cancelled"); + return last_; + } + } +}; +bool native_video_decode_available() noexcept { return true; } +std::unique_ptr open_video(const encoded_source &source, decode_limits limits, + std::stop_token stop) { + @autoreleasepool { + return std::make_unique(source, limits, stop); + } +} +video_frame decode_video_frame(const encoded_source &source, double seconds, decode_limits limits, + std::stop_token stop) { + return open_video(source, limits, stop)->read(seconds, stop); +} +audio_buffer decode_native_audio(std::span bytes, decode_limits limits, std::stop_token stop, + uint32_t rate) { + @autoreleasepool { + encoded_source source{{bytes.begin(), bytes.end()}, ".mp4"}; + source_file file(source, stop); + auto *asset = [AVURLAsset + URLAssetWithURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:file.path.c_str()]] + options:@{ + AVURLAssetReferenceRestrictionsKey : @(AVAssetReferenceRestrictionForbidAll) + }]; + auto result = read_audio(asset, limits, stop, rate); + if (result.samples.empty()) + throw std::runtime_error("Unsupported or invalid audio data"); + return result; + } +} + +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/media/video_decode_unsupported.cpp b/experiments/WebScene.NativeEngine.Probe/native/media/video_decode_unsupported.cpp new file mode 100644 index 000000000..2d258ad46 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/media/video_decode_unsupported.cpp @@ -0,0 +1,15 @@ +#include "media_decode.h" +#include +namespace webscene::media { +bool native_video_decode_available() noexcept { return false; } +std::unique_ptr open_video(const encoded_source&, decode_limits, std::stop_token) { + throw std::runtime_error("Native video decoding is not implemented on this platform"); +} +video_frame decode_video_frame(const encoded_source&, double, decode_limits, std::stop_token) { + throw std::runtime_error("Native video decoding is not implemented on this platform"); +} +} + +namespace webscene::media { +audio_buffer decode_native_audio(std::span,decode_limits,std::stop_token,uint32_t) { throw std::runtime_error("Unsupported or invalid audio data"); } +} diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_frame_trace.h b/experiments/WebScene.NativeEngine.Probe/native/webscene_frame_trace.h new file mode 100644 index 000000000..40ce5f594 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_frame_trace.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include +#include + +// Worker-owned diagnostic ring. Never prints or allocates while recording; +// timestamps share steady_clock's epoch with the native scheduling metrics. +struct webscene_frame_trace final { + struct sample { const char* stage; long long timestamp; unsigned long long sequence; }; + std::vector samples; + size_t count = 0; + ~webscene_frame_trace() { dump(); } + struct scope { + webscene_frame_trace& trace; + const char* end; + unsigned long long sequence; + scope(webscene_frame_trace& owner, const char* start, const char* finish, unsigned long long id) + : trace(owner), end(finish), sequence(id) { trace.mark(start, id); } + ~scope() { trace.mark(end, sequence); } + }; + webscene_frame_trace() { + const auto* setting = std::getenv("WEBSCENE_TRACE_FRAME_PIPELINE"); + if (setting && setting[0] == '1') samples.resize(65536); + } + void mark(const char* stage, unsigned long long sequence) { + if (samples.empty()) return; + samples[count++ % samples.size()] = {stage, + std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count(), sequence}; + } + void dump() { + const auto begin = count > samples.size() ? count - samples.size() : 0; + for (auto i = begin; i < count; ++i) { + const auto& value = samples[i % samples.size()]; + std::fprintf(stderr, "Frame pipeline: {\"stage\":\"%s\",\"ns\":%lld,\"sequence\":%llu}\n", + value.stage, value.timestamp, value.sequence); + } + count = 0; + } +}; diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.cpp b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.cpp index f56255e67..262359a08 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.cpp +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.cpp @@ -207,7 +207,7 @@ paint_z_index_update update_paint_z_index( // element or an authored ::after overlay to a different CSS paint phase. // Basing this bit on the current command list made the cached layout state // alternate between backdrop and overlay across ordinary chart redraws. - auto contains_retained_canvas = node.tag == "canvas" + auto contains_retained_canvas = (node.tag == "canvas" || node.tag == "video") && node.visible && node.style.display != display_mode::none; for (auto* child : document.composed_children(node)) { @@ -278,7 +278,7 @@ size_t count_retained_canvases( // Count visible canvas elements rather than non-empty display lists. The // latter can be transiently empty between reset and redraw and must not // change the stable backdrop/canvas/overlay partition. - auto count = node.tag == "canvas" + auto count = (node.tag == "canvas" || node.tag == "video") && node.visible && node.style.display != display_mode::none ? size_t{1U} @@ -301,7 +301,7 @@ void update_retained_canvas_paint_phase( // the final canvas can safely use the global overlay by document order. node.paints_after_retained_canvas = retained_canvas_seen && retained_canvases_remaining == 0U; - if (node.tag == "canvas" + if ((node.tag == "canvas" || node.tag == "video") && node.visible && node.style.display != display_mode::none) { retained_canvas_seen = true; diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.h b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.h index 6b3328d06..80fd7cb80 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.h +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom.h @@ -1,6 +1,8 @@ #pragma once #include "webscene_native_engine.h" +#include "graphics/canvas_backing.h" +#include "graphics/image_lease_abi.h" #include #include @@ -9,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -824,13 +827,46 @@ struct text_layout_fragment final { std::string text; }; +// One immutable dependency binding captured with the CPU scene. Resolving it +// never consults the live DOM or substitutes a newer canvas output. +struct gpu_canvas_scene_binding final { + uint32_t node_id{}; + webscene::graphics::image_metadata metadata; + std::shared_ptr pending; + std::shared_ptr completed; + uint64_t presentation_generation{}; + std::shared_ptr resolve() const { + return pending ? pending->resolve() : completed; + } +}; + struct canvas_node_data final { + webscene::graphics::canvas_backing backing; + std::shared_ptr gpu_image; + std::shared_ptr gpu_snapshot; + std::shared_ptr gpu_presentation_image; + void publish_gpu_image(std::shared_ptr image) { + if (!image) throw std::invalid_argument("missing GPU canvas image"); + const auto m=image->value.describe(); + if (backing.mode()==webscene::graphics::canvas_context_mode::none + || m.canvas!=backing.identity() || m.allocation_generation!=backing.allocation_generation() + || !backing.accepts_completed_content(m.content_serial) || m.width!=backing.width() || m.height!=backing.height()) + throw std::invalid_argument("GPU image does not match canvas backing"); + if (gpu_image && gpu_image->value.describe().content_serial>m.content_serial) + throw std::invalid_argument("GPU image publication cannot regress"); + gpu_image=std::move(image); + } std::vector rects; std::vector lines; uint64_t generation{1}; std::vector commands; std::vector strings; std::unordered_map string_indices; + // Worker-owned derived state. Canvas commands append within a generation; + // resets advance the generation before clearing the command list. + mutable uint64_t dependency_generation{0}; + mutable size_t dependency_command_count{0}; + mutable std::unordered_set canvas_dependencies; #if defined(WEBSCENE_NATIVE_ENGINE_CERTIFICATION) uint64_t fill_rect_calls{0}; uint64_t probable_volume_fill_rect_calls{0}; @@ -992,6 +1028,11 @@ struct dom_node final { float column_gap{0}; }; + struct dialog_data final { + std::string return_value; + uint32_t previously_focused_id{}; + }; + struct form_control_data final { std::string value; size_t selection_start{0}; @@ -1110,13 +1151,13 @@ struct dom_node final { uint32_t id{0}; dom_node_kind kind{dom_node_kind::element}; + // XML documents preserve qualified/tag and attribute name case. HTML nodes + // continue to apply the ASCII case-insensitive name rules at the binding. + bool xml_mode{false}; std::string tag; std::string id_attribute; std::string class_name; std::string text_content; - // XML documents preserve qualified/tag and attribute name case. HTML nodes - // continue to apply the ASCII case-insensitive name rules at the binding. - bool xml_mode{false}; attribute_collection attributes; std::string_view namespace_uri() const noexcept { @@ -1264,6 +1305,9 @@ struct dom_node final { } std::unique_ptr form_control_state; + // Dialog state is independent of authored attributes and survives wrapper GC. + std::unique_ptr dialog_state; + const replaced_image_data& replaced_image() const noexcept { static const replaced_image_data empty; @@ -1535,13 +1579,26 @@ class native_document final { dom_node* find_by_native_id(uint32_t id) noexcept; dom_node* find_by_id(const std::string& id) noexcept; std::vector query_selector_all(dom_node& root, const std::string& selector); + bool register_modal_dialog(dom_node& scope, dom_node& dialog); + void unregister_modal_dialog(const dom_node& dialog); + void unregister_modal_subtree(const dom_node& root); + const dom_node* active_modal_dialog(const dom_node& scope) const noexcept; + bool is_modal_dialog(const dom_node& node) const noexcept; + bool is_in_modal_layer(const dom_node& node) const noexcept; + bool is_inert(const dom_node& node) const noexcept; dom_node* hit_test(dom_node& root, float x, float y); void clear(); void layout(float viewport_width, float viewport_height); void build_scene( std::vector& commands, std::vector& strings, - std::vector& string_bytes) const; + std::vector& string_bytes, bool ordered_canvas = false, bool capture_gpu_outputs = false) const; + // Engine-thread publication: validates document ownership and backing version, + // then requests a scene without forcing style/layout work. + void publish_gpu_canvas_image(dom_node& node,std::shared_ptr image); + bool validate_gpu_canvas_binding(const gpu_canvas_scene_binding& binding) const; + void build_gpu_canvas_bindings(std::vector& bindings) const; + void build_gpu_canvas_images(std::vector>& images) const; void build_canvas_layouts(std::vector& layouts) const; void build_canvas_display_lists( std::vector& layers, @@ -1567,6 +1624,7 @@ class native_document final { std::array intrinsic_view_box_parse_counts() const noexcept; #endif size_t node_count() const noexcept; + std::span media_elements() const noexcept { return auxiliary_nodes_ ? std::span(auxiliary_nodes_->media) : std::span{}; } allocation_metrics read_allocation_metrics() const noexcept; size_t count_tag(const std::string& tag) const noexcept; size_t sum_attribute_bytes(const std::string& tag, const std::string& attribute) const noexcept; @@ -1575,6 +1633,7 @@ class native_document final { layout_rect busiest_canvas_layout() const noexcept; uint64_t scene_generation() const noexcept; void mark_scene_changed() noexcept; + bool has_canvas_references(uint32_t node_id) const; bool dirty() const noexcept; void mark_dirty() noexcept; void mark_out_of_flow_geometry_dirty(dom_node& node) noexcept; @@ -1903,7 +1962,10 @@ class native_document final { bool defer_fixed_descendants, bool defer_positive_descendants = false, const dom_node* paint_target = nullptr, - const node_style::pseudo_element* paint_pseudo_target = nullptr) const; + const node_style::pseudo_element* paint_pseudo_target = nullptr, + bool ordered_canvas = false, + bool paint_modal_root = false, + bool capture_gpu_outputs = false) const; static bool matches_selector(const dom_node& node, const std::string& selector); static void collect_matches( dom_node& node, @@ -1936,6 +1998,15 @@ class native_document final { // trimmed when possible so short-lived text-node churn does not retain an // ever-growing pointer table. std::vector native_id_index_; + struct modal_dialog_entry final { uint32_t scope_id; uint32_t dialog_id; }; + // Most documents never open a modal. Keep the container allocation lazy + // and its implementation-specific vector footprint out of every document. + struct auxiliary_nodes { std::vector dialogs; std::vector media; }; + std::unique_ptr auxiliary_nodes_; + std::span modal_dialogs() const noexcept { + return auxiliary_nodes_ ? std::span(auxiliary_nodes_->dialogs) + : std::span{}; + } #if !defined(WEBSCENE_NATIVE_ENGINE_INTRINSIC_SIZE_HASH_CACHE_CONTROL) // Mirror the native-ID index so intrinsic lookup remains direct without // making every DOM node pay a cross-library object-footprint tax. The diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_layout.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_layout.inc index eb714823f..3e25f6b92 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_layout.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_layout.inc @@ -1098,6 +1098,15 @@ void native_document::layout_children(dom_node& parent) horizontal_axis ? node.style.width : node.style.height, available); }; + const auto positioned_auto_start_margin = [](float available, float start, float end, + float size, float start_margin, float end_margin, bool start_auto, bool end_auto, + bool horizontal_axis) { + if (!start_auto) return start_margin; + const auto free = available - start - end - size - (end_auto ? 0.0F : end_margin); + // CSS absolute positioning permits negative auto margins vertically. + // In the horizontal LTR case, excess width overflows at the right edge. + return end_auto ? (horizontal_axis && free < 0 ? 0.0F : free * 0.5F) : free; + }; const auto layout_positioned_grid_child = [&](dom_node& child) { auto* containing_node = body_; if (child.style.position == position_mode::absolute) { @@ -1152,11 +1161,11 @@ void native_document::layout_children(dom_node& parent) ? resolve_length(child, child.style.right, containing.width, 0) : 0.0F; const auto bottom = has_bottom ? resolve_length(child, child.style.bottom, containing.height, 0) : 0.0F; - const auto margin_left = resolve_length( + auto margin_left = resolve_length( child, child.style.margin_left, containing.width, 0); const auto margin_right = resolve_length( child, child.style.margin_right, containing.width, 0); - const auto margin_top = resolve_length( + auto margin_top = resolve_length( child, child.style.margin_top, containing.height, 0); const auto margin_bottom = resolve_length( child, child.style.margin_bottom, containing.height, 0); @@ -1203,6 +1212,12 @@ void native_document::layout_children(dom_node& parent) }; assigned.width = constrain_positioned_size(true, assigned.width); assigned.height = constrain_positioned_size(false, assigned.height); + if (has_left && has_right) margin_left = positioned_auto_start_margin( + containing.width, left, right, assigned.width, margin_left, margin_right, + child.style.margin_left_auto, child.style.margin_right_auto, true); + if (has_top && has_bottom) margin_top = positioned_auto_start_margin( + containing.height, top, bottom, assigned.height, margin_top, margin_bottom, + child.style.margin_top_auto, child.style.margin_bottom_auto, false); assigned.x = has_left ? containing.x + left + margin_left : has_right @@ -1290,6 +1305,12 @@ void native_document::layout_children(dom_node& parent) // BR is inline-level but forces a line break. The flattened text-run // path has no sentinel for forced breaks, so retain it as an item. if (node.tag == "br") return false; + // Replaced inline elements contribute their intrinsic box, even + // without CSS width/height. Flattening them into text runs loses + // the box entirely (canvas fallback children are not its contents). + if (node.tag == "canvas" || node.tag == "video" || node.tag == "img" || node.tag == "svg" + || node.tag == "input" || node.tag == "select" || node.tag == "textarea") + return false; // Inline-block and inline-table descendants are atomic inline // boxes. Flattening their text into the surrounding line discards // their own padding, background, and border geometry. @@ -1726,8 +1747,32 @@ void native_document::layout_children(dom_node& parent) const auto implicit_column_grid = parent_grid.auto_flow_column && !parent_grid.auto_columns.empty(); if (is_grid_container(parent.style.display) - && (parent_grid.two_columns || implicit_column_grid)) { + && (parent_grid.two_columns || implicit_column_grid + || !parent_grid.template_columns.empty() || !parent_grid.template_rows.empty())) { using grid_track = node_style::grid_data::track; + const auto distribute_fractions = [](std::vector& sizes, + const std::vector& tracks, float available, float gaps) { + auto remaining = available - gaps; + std::vector flexible; + for (size_t index=0;index0)flexible.push_back(index); + else remaining-=sizes[index]; + } + while(!flexible.empty()) { + float weight=0; + for(const auto index:flexible)weight+=tracks[index].fraction; + const auto fraction=std::max(0.0F,remaining)/std::max(1.0F,weight); + bool froze=false; + for(auto it=flexible.begin();it!=flexible.end();) { + if(fraction*tracks[*it].fraction( - implicit_column_grid ? 1U : 2U, + parent_grid.two_columns && effective_columns.empty() ? 2U : 1U, effective_columns.size()); const auto parse_line = [](std::string_view value) -> std::optional { const auto first = value.find_first_not_of(" \t\r\n"); @@ -1988,15 +2033,11 @@ void native_document::layout_children(dom_node& parent) if (!inherited_subgrid_columns) { const auto committed_width = std::accumulate( column_widths.begin(), column_widths.end(), total_gap); - const auto fractional_space = std::max(0.0F, content.width - committed_width); if (fractional_weight > 0) { - for (size_t column = 0; column < effective_columns.size(); ++column) { - const auto& track = effective_columns[column]; - if (track.fraction <= 0) continue; - column_widths[column] = std::max( - column_widths[column], - fractional_space * track.fraction / fractional_weight); - } + // Flexible tracks own their share of the available space, + // including their minimum. Freeze undersized shares at that + // minimum and recompute the fraction for the remaining tracks. + distribute_fractions(column_widths,effective_columns,content.width,total_gap); } else if (committed_width < content.width && !column_widths.empty()) { // `justify-content: normal` stretches auto grid tracks, not // fixed tracks. Donating all free space to the final column @@ -2024,6 +2065,7 @@ void native_document::layout_children(dom_node& parent) const auto row_count = std::max( occupied.size(), parent_grid.template_rows.size()); std::vector row_heights(row_count, 0.0F); + std::vector row_minimums(row_count, 0.0F); const auto contains_wrapping_flex = [&](const auto& self, const dom_node& node) -> bool { if (is_flex_container(node.style.display) && node.style.flex_wrap) { @@ -2073,6 +2115,26 @@ void native_document::layout_children(dom_node& parent) auto authored = definite_height ? outer_authored_size(*item.node, false, content.height) : intrinsic_size(*item.node, false, content.height); + const auto record_contribution = [&](float contribution) { + auto minimum = contribution; + if (!definite_height) { + const auto& style = item.node->style; + const auto scrollable = style.overflow_y == overflow_mode::hidden + || style.overflow_y == overflow_mode::automatic + || style.overflow_y == overflow_mode::scroll; + if (is_specified(style.min_height) || scrollable) { + const auto padding = resolve_vertical_padding(*item.node, style.padding_top, content.height, 0) + + resolve_vertical_padding(*item.node, style.padding_bottom, content.height, 0) + + resolve_length(*item.node, style.border_top_width, content.height, 0) + + resolve_length(*item.node, style.border_bottom_width, content.height, 0); + const auto authored_minimum = is_specified(style.min_height) + ? resolve_length(*item.node, style.min_height, content.height, 0) : 0.0F; + minimum = std::max(padding, authored_minimum + (style.border_box ? 0.0F : padding)); + } + } + row_minimums[item.row] = std::max(row_minimums[item.row], minimum + margins); + row_heights[item.row] = std::max(row_heights[item.row], contribution + margins); + }; if (!definite_height) { // Block-axis intrinsic sizing alone only sees an unwrapped // line-height. Grid row sizing, however, happens after the @@ -2108,8 +2170,7 @@ void native_document::layout_children(dom_node& parent) const auto has_wrapping_flex = !wraps_at_used_width && contains_wrapping_flex(contains_wrapping_flex, *item.node); if (!wraps_at_used_width && !has_wrapping_flex) { - row_heights[item.row] = std::max( - row_heights[item.row], authored + margins); + record_contribution(authored); continue; } if (item.node->style.grid().subgrid_columns) { @@ -2159,23 +2220,24 @@ void native_document::layout_children(dom_node& parent) item.node->scroll_content_height, flow_bounds.second - flow_bounds.first))); } - row_heights[item.row] = std::max(row_heights[item.row], authored + margins); + record_contribution(authored); + } + const auto single_auto_row = row_count == 1U + && (parent_grid.template_rows.empty() + || parent_grid.template_rows[0].kind == grid_track::sizing::automatic); + if (single_auto_row && parent.used_height_is_definite) { + // An auto track's max-content growth limit is not its minimum. + // Scrollable items and explicit min-height can allow the row to + // shrink, including after a larger previously arranged viewport. + row_heights[0] = std::max(row_minimums[0], std::min(row_heights[0], content.height)); } const auto total_row_gap = row_count > 0 ? row_gap * static_cast(row_count - 1U) : 0.0F; const auto committed_height = std::accumulate( row_heights.begin(), row_heights.end(), total_row_gap); - const auto fractional_row_space = std::max(0.0F, content.height - committed_height); if (fractional_row_weight > 0) { - for (size_t row = 0; row < parent_grid.template_rows.size(); ++row) { - const auto& track = parent_grid.template_rows[row]; - if (track.fraction <= 0) continue; - row_heights[row] = std::max( - row_heights[row], - fractional_row_space * track.fraction / fractional_row_weight); - } - } else if (row_count == 1U - && parent_grid.template_rows.empty() + distribute_fractions(row_heights,parent_grid.template_rows,content.height,total_row_gap); + } else if (single_auto_row && committed_height < content.height) { // The common one-row implicit grid case stretches its auto row to // the definite container height. Keep this compatibility path @@ -2248,7 +2310,7 @@ void native_document::layout_children(dom_node& parent) column_offsets[item.column] + margin_left, row_offsets[item.row] + margin_top, width, - height}); + height}, true); // The resolved grid area supplies the stretched block size. }; arrange(item.node); } @@ -2566,7 +2628,7 @@ void native_document::layout_children(dom_node& parent) const auto contains_replaced_content = [&](const auto& self, const dom_node& candidate) -> bool { if (candidate.tag == "svg" || candidate.tag == "img" - || candidate.tag == "canvas" || candidate.tag == "input" + || candidate.tag == "canvas" || candidate.tag == "video" || candidate.tag == "input" || candidate.tag == "select" || candidate.tag == "textarea") { return true; } @@ -2903,7 +2965,7 @@ void native_document::layout_children(dom_node& parent) const auto bottom = has_bottom ? resolve_length(*child, child->style.bottom, containing.height, 0) : 0; - const auto margin_left = resolve_length( + auto margin_left = resolve_length( *child, child->style.margin_left, containing.width, @@ -2913,7 +2975,7 @@ void native_document::layout_children(dom_node& parent) child->style.margin_right, containing.width, 0); - const auto margin_top = resolve_length( + auto margin_top = resolve_length( *child, child->style.margin_top, containing.height, @@ -2950,6 +3012,12 @@ void native_document::layout_children(dom_node& parent) false, assigned.height, containing.height); + if (has_left && has_right) margin_left = positioned_auto_start_margin( + containing.width, left, right, assigned.width, margin_left, margin_right, + child->style.margin_left_auto, child->style.margin_right_auto, true); + if (has_top && has_bottom) margin_top = positioned_auto_start_margin( + containing.height, top, bottom, assigned.height, margin_top, margin_bottom, + child->style.margin_top_auto, child->style.margin_bottom_auto, false); const auto flex_static_main_offset = [&] { // An auto-inset out-of-flow child keeps the position its // margin box would have occupied in normal flow. `cursor` is @@ -3657,7 +3725,7 @@ float native_document::min_content_inline_size( return constrain(padding + widest_segment); } if (node.tag == "input" || node.tag == "select" || node.tag == "textarea" - || node.tag == "img" || node.tag == "svg" || node.tag == "canvas") { + || node.tag == "img" || node.tag == "svg" || node.tag == "canvas" || node.tag == "video") { return intrinsic_size(node, true, available); } @@ -4063,11 +4131,18 @@ float native_document::compute_intrinsic_size( // applied to the wrapper and used to size its portaled listbox. return constrain(columns * measure_text_width("0", node) + padding); } - if (node.tag == "svg" || node.tag == "img" || node.tag == "canvas") { + if (node.tag == "svg" || node.tag == "img" || node.tag == "canvas" || node.tag == "video") { #if defined(WEBSCENE_NATIVE_ENGINE_INTRINSIC_SIZE_BRANCH_BENCHMARK) ++intrinsic_size_branch_counts_for_benchmark[5]; #endif const auto attribute = node.attributes.find(horizontal ? "width" : "height"); + if (node.tag == "video" && attribute == node.attributes.end()) { + const auto& backing=node.canvas().backing; + return constrain(static_cast(horizontal?backing.width():backing.height())+padding); + } + if (node.tag == "canvas" && attribute == node.attributes.end()) { + return constrain((horizontal ? 300.0F : 150.0F) + padding); + } if (attribute != node.attributes.end()) { const auto intrinsic = parse_number(attribute->second, 0); if (intrinsic > 0) return constrain(intrinsic + padding); @@ -4419,6 +4494,28 @@ void native_document::layout_child( layout_rect assigned, bool assigned_height_is_definite) { + // A video's decoded size is intrinsic, not its used CSS height. Resolve + // auto height from the final content width before transforms and parent flow + // placement; otherwise width:100% stretches a native-resolution-tall image. + if (child.tag == "video" && !is_specified(child.style.height) + && !child.attributes.contains("height") && !assigned_height_is_definite) { + const auto& backing = child.canvas().backing; + if (backing.width() > 0 && backing.height() > 0) { + const auto horizontal_edges = + resolve_length(child, child.style.padding_left, assigned.width, 0) + + resolve_length(child, child.style.padding_right, assigned.width, 0) + + resolve_length(child, child.style.border_left_width, assigned.width, 0) + + resolve_length(child, child.style.border_right_width, assigned.width, 0); + const auto vertical_edges = + resolve_vertical_padding(child, child.style.padding_top, assigned.height, 0) + + resolve_vertical_padding(child, child.style.padding_bottom, assigned.height, 0) + + resolve_length(child, child.style.border_top_width, assigned.height, 0) + + resolve_length(child, child.style.border_bottom_width, assigned.height, 0); + assigned.height = std::max(0.0F, assigned.width - horizontal_edges) + * static_cast(backing.height()) / static_cast(backing.width()) + + vertical_edges; + } + } // A CSS transform changes the painted and hit-test box, not the space the // element occupies in its parent's flow. Percentage translations resolve // against the transformed element's own border box. @@ -4548,10 +4645,29 @@ void native_document::layout_child( child.layout.height, 0); const auto previous_height = child.layout.height; - child.layout.height = std::max( - child.layout.height, - content_bottom - child.layout.y + padding_bottom + border_bottom); - if (child.layout.height > previous_height + 0.01F) { + const auto final_content_height = content_bottom - child.layout.y + + padding_bottom + border_bottom; + // Ordinary auto-height blocks follow their laid-out children, including + // replaced content whose used height shrinks after resolving its width. + // Keeping the provisional intrinsic height leaves a native-size blank + // area below responsive video and pushes following controls offscreen. + const auto recompute_auto_block = child.style.display == display_mode::block + && !has_visible_text(child.text_content) + && !pseudo_generates_box(child.style.before_pseudo()) + && !pseudo_generates_box(child.style.after_pseudo()); + const auto minimum_height = is_specified(child.style.min_height) + ? resolve_length(child, child.style.min_height, containing_block.height, 0) + : 0.0F; + child.layout.height = recompute_auto_block + ? std::max(minimum_height, final_content_height) + : std::max(child.layout.height, final_content_height); + if (recompute_auto_block && child.layout.height < previous_height - 0.01F) { + // Recompute overflow against the settled viewport. The provisional + // content height also participates in scroll extents; retaining it + // creates an empty scroll range after responsive content shrinks. + layout_children(child); + } + if (std::abs(child.layout.height - previous_height) > 0.01F) { // layout_children() established scroll geometry from the // provisional auto height. Wrapped content can then grow this box // without changing its public client/scroll metrics, leaving a diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_metrics.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_metrics.inc index 6f78e7982..77dd13b1a 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_metrics.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_metrics.inc @@ -157,7 +157,8 @@ native_document::allocation_metrics native_document::read_allocation_metrics() c return values.bucket_count() * sizeof(void*) + values.size() * (sizeof(value_type) + 2U * sizeof(void*)); }; - result.canvas_storage_bytes += hash_bytes(canvas.string_indices); + result.canvas_storage_bytes += hash_bytes(canvas.string_indices) + + hash_bytes(canvas.canvas_dependencies); #if defined(WEBSCENE_NATIVE_ENGINE_CERTIFICATION) result.canvas_storage_bytes += hash_bytes(canvas.probable_volume_by_generation) diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc index 358f50b9b..2f9da7945 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_scene.inc @@ -1,13 +1,13 @@ void native_document::build_scene( std::vector& commands, std::vector& strings, - std::vector& string_bytes) const + std::vector& string_bytes, bool ordered_canvas, bool capture_gpu_outputs) const { commands.clear(); commands.reserve(nodes_.size()); strings.clear(); string_bytes.clear(); - append_scene(*body_, commands, strings, string_bytes, false, true); + append_scene(*body_, commands, strings, string_bytes, false, true, false, nullptr, nullptr, ordered_canvas, false, capture_gpu_outputs); std::vector fixed; for (auto* child : composed_children(*body_)) { collect_outermost_fixed_positioned_nodes(*this, *child, fixed); @@ -19,7 +19,7 @@ void native_document::build_scene( return left->paint_z_index < right->paint_z_index; }); for (const auto* fixed_node : fixed) { - if (!display_tree_allows_render(*this, *fixed_node)) continue; + if (is_in_modal_layer(*fixed_node) || !display_tree_allows_render(*this, *fixed_node)) continue; auto inherited_visibility_hidden = false; std::vector ancestors; for (auto* ancestor = composed_parent(*fixed_node); @@ -38,7 +38,109 @@ void native_document::build_scene( strings, string_bytes, inherited_visibility_hidden, - false); + false, false, nullptr, nullptr, ordered_canvas, false, capture_gpu_outputs); + } + // Modal roots leave their DOM stacking context and are emitted exactly + // once, in registration order, after ordinary/fixed document content. + for (const auto& entry : modal_dialogs()) { + if (entry.dialog_id > native_id_index_.size()) continue; + const auto* modal = native_id_index_[entry.dialog_id - 1U]; + if (modal == nullptr || !display_tree_allows_render(*this, *modal)) continue; + bool hidden = false; + std::vector ancestors; + for (auto* ancestor = composed_parent(*modal); ancestor != nullptr; + ancestor = composed_parent(*ancestor)) ancestors.push_back(ancestor); + for (auto ancestor = ancestors.rbegin(); ancestor != ancestors.rend(); ++ancestor) { + if ((*ancestor)->style.visibility_specified) hidden = (*ancestor)->style.visibility_hidden; + } + append_scene(*modal, commands, strings, string_bytes, hidden, + false, false, nullptr, nullptr, ordered_canvas, true, capture_gpu_outputs); + } +} + +void native_document::publish_gpu_canvas_image(dom_node& node, + std::shared_ptr image) +{ + if ((node.tag!="canvas" && node.tag!="video") || find_by_native_id(node.id)!=&node) + throw std::invalid_argument("GPU canvas belongs to another document or is not a canvas"); + auto& canvas=node.mutable_canvas(); + const bool changed=canvas.gpu_image!=image; + canvas.publish_gpu_image(std::move(image)); + if (changed) { + // Submission already invalidated the document and captured this output. + // Completion resolves that dependency; it does not create new content. + // Keep ordinary completed-image publication invalidating as before. + if(canvas.gpu_snapshot && canvas.gpu_snapshot->state()!=webscene_gpu_image_snapshot::status::failed) { + const auto captured=canvas.gpu_snapshot->describe(); + const auto completed=canvas.gpu_image->value.describe(); + if(captured.canvas==completed.canvas && captured.allocation==completed.allocation + && captured.allocation_generation==completed.allocation_generation + && captured.content_serial==completed.content_serial)return; + } + mark_scene_changed(); + } +} + +bool native_document::validate_gpu_canvas_binding(const gpu_canvas_scene_binding& binding) const +{ + const auto* node=binding.node_id==0 || binding.node_id>native_id_index_.size() + ? nullptr : native_id_index_[binding.node_id-1]; + if(!node || (node->tag!="canvas" && node->tag!="video") || !is_connected(*node))return false; + const auto& backing=node->canvas().backing; + if(binding.presentation_generation) { + return binding.completed && !binding.pending + && node->canvas().gpu_presentation_image==binding.completed + && backing.identity()==binding.metadata.canvas + && backing.allocation_generation()==binding.presentation_generation; + } + return backing.identity()==binding.metadata.canvas + && backing.allocation_generation()==binding.metadata.allocation_generation + && backing.accepts_completed_content(binding.metadata.content_serial) + && backing.width()==binding.metadata.width && backing.height()==binding.metadata.height; +} + +void native_document::build_gpu_canvas_bindings( + std::vector& bindings) const +{ + bindings.clear(); + for(const auto& node:nodes_) { + if((node->tag!="canvas" && node->tag!="video") || !is_connected(*node) || !node->visible + || !display_tree_allows_render(*this,*node))continue; + const auto& canvas=node->canvas(); + if(!canvas.gpu_snapshot&&!canvas.gpu_image) { + if(canvas.gpu_presentation_image) { + const auto metadata=canvas.gpu_presentation_image->value.describe(); + if(metadata.canvas==canvas.backing.identity()) + bindings.push_back({node->id,metadata,nullptr,canvas.gpu_presentation_image, + canvas.backing.allocation_generation()}); + } + continue; + } + const auto metadata=canvas.gpu_snapshot ? canvas.gpu_snapshot->describe() + : canvas.gpu_image->value.describe(); + if(metadata.canvas!=canvas.backing.identity() + || metadata.allocation_generation!=canvas.backing.allocation_generation() + || !canvas.backing.accepts_completed_content(metadata.content_serial))continue; + bindings.push_back({node->id,metadata,canvas.gpu_snapshot, + canvas.gpu_snapshot ? nullptr : canvas.gpu_image}); + } +} + +void native_document::build_gpu_canvas_images( + std::vector>& images) const +{ + images.clear(); + for (const auto& node : nodes_) { + if ((node->tag!="canvas" && node->tag!="video") || !is_connected(*node) || !node->visible + || !display_tree_allows_render(*this,*node)) continue; + const auto& canvas=node->canvas(); + if (!canvas.gpu_image) continue; + const auto m=canvas.gpu_image->value.describe(); + // A bitmap reset invalidates the presented content. Retained scenes + // still own the previous frame until their consumers finish. + if (m.allocation_generation!=canvas.backing.allocation_generation() + || !canvas.backing.accepts_completed_content(m.content_serial)) continue; + images.push_back(canvas.gpu_image); } } @@ -100,9 +202,20 @@ void native_document::build_canvas_display_lists( added_dependency = false; for (const auto& node : nodes_) { if (!retained_canvas_ids.contains(node->id)) continue; - for (const auto& command : node->canvas().commands) { - if (command.kind == 27U - && retained_canvas_ids.insert(command.resource_id).second) { + const auto& canvas = node->canvas(); + if (canvas.dependency_generation != canvas.generation + || canvas.dependency_command_count > canvas.commands.size()) { + canvas.canvas_dependencies.clear(); + canvas.dependency_command_count = 0; + canvas.dependency_generation = canvas.generation; + } + for (auto index = canvas.dependency_command_count; index < canvas.commands.size(); ++index) { + const auto& command = canvas.commands[index]; + if (command.kind == 27U) canvas.canvas_dependencies.insert(command.resource_id); + } + canvas.dependency_command_count = canvas.commands.size(); + for (const auto dependency : canvas.canvas_dependencies) { + if (retained_canvas_ids.insert(dependency).second) { added_dependency = true; } } @@ -171,6 +284,16 @@ void native_document::retain_canvas_for_export(dom_node& node) noexcept mark_scene_changed(); } +bool native_document::has_canvas_references(uint32_t node_id) const +{ + for (const auto& node : nodes_) { + if (node->tag != "canvas") continue; + for (const auto& command : node->canvas().commands) + if (command.kind == 27U && command.resource_id == node_id) return true; + } + return false; +} + bool native_document::release_canvas_export(uint32_t node_id) noexcept { if (node_id == 0U || retained_export_canvas_id_ != node_id) return false; @@ -188,8 +311,12 @@ void native_document::append_scene( bool defer_fixed_descendants, bool defer_positive_descendants, const dom_node* paint_target, - const node_style::pseudo_element* paint_pseudo_target) const + const node_style::pseudo_element* paint_pseudo_target, + bool ordered_canvas, + bool paint_modal_root, + bool capture_gpu_outputs) const { + if (!paint_modal_root && is_modal_dialog(node)) return; const auto elevated_target = paint_target == &node; const auto pseudo_only = elevated_target && paint_pseudo_target != nullptr; if (paint_target == &node) paint_target = nullptr; @@ -308,7 +435,7 @@ void native_document::append_scene( // backgrounds must be emitted after retained chart canvases, just // like their text and SVG foreground commands. } - return node.paints_after_retained_canvas || fixed_layer || outermost_z_index > 0; + return is_in_modal_layer(node) || node.paints_after_retained_canvas || fixed_layer || outermost_z_index > 0; }(); struct resolved_radii final { float top_left; @@ -1193,7 +1320,26 @@ void native_document::append_scene( append_pseudo(node.style.after_pseudo()); } const auto& canvas = node.canvas(); - if (paint_self + const auto gpu_metadata=capture_gpu_outputs && canvas.gpu_snapshot + ? std::optional(canvas.gpu_snapshot->describe()) + : canvas.gpu_image ? std::optional(canvas.gpu_image->value.describe()) : std::nullopt; + const bool presentation_gpu=capture_gpu_outputs && !gpu_metadata && canvas.gpu_presentation_image + && canvas.gpu_presentation_image->value.describe().canvas==canvas.backing.identity(); + const bool paints_gpu=presentation_gpu || (gpu_metadata + && gpu_metadata->allocation_generation==canvas.backing.allocation_generation() + && canvas.backing.accepts_completed_content(gpu_metadata->content_serial)); + if (paint_self && (node.tag=="canvas" || node.tag=="video") && paints_gpu) { + // Native DOM output carries the node ID. Publication resolves it to the + // scene-local image index before exposing the command to consumers. + commands.push_back(webscene_scene_command{ + WEBSCENE_SCENE_COMMAND_GPU_IMAGE,0U,node.layout.x,node.layout.y, + node.layout.width,node.layout.height,0U,node.id,0,0,0,0,0}); + } + if (ordered_canvas && paint_self && !paints_gpu && node.tag=="canvas" && !canvas.commands.empty()) { + commands.push_back(webscene_scene_command{ + WEBSCENE_SCENE_COMMAND_CANVAS_LAYER,0U,0,0,0,0,0U,node.id,0,0,0,0,0}); + } + if (paint_self && !paints_gpu && node.tag == "canvas" && canvas.commands.empty() && (!canvas.rects.empty() || !canvas.lines.empty())) { @@ -1641,7 +1787,7 @@ void native_document::append_scene( defer_fixed_descendants, true, paint_target, - paint_pseudo_target); + paint_pseudo_target, ordered_canvas, false, capture_gpu_outputs); } else if (entry.pseudo != nullptr) { if (!pseudo_only && entry.z_index > 0) continue; append_pseudo(*entry.pseudo); @@ -1680,7 +1826,7 @@ void native_document::append_scene( // Walk only this branch. Ancestors retain overflow clips and // visibility inheritance but do not duplicate backgrounds or SVGs. append_scene(*branch, commands, strings, string_bytes, - visibility_hidden, defer_fixed_descendants, true, target, entry.pseudo); + visibility_hidden, defer_fixed_descendants, true, target, entry.pseudo, ordered_canvas, false, capture_gpu_outputs); } } if (clip_contents) { diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_tree.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_tree.inc index d2a8172ab..f5379fbe7 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_tree.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_dom_tree.inc @@ -464,6 +464,10 @@ dom_node& native_document::create_node(dom_node_kind kind, std::string name) #endif nodes_.push_back(std::move(node)); native_id_index_[result->id - 1U] = result; + if(kind==dom_node_kind::element&&(result->tag=="audio"||result->tag=="video")){ + if(!auxiliary_nodes_)auxiliary_nodes_=std::make_unique(); + auxiliary_nodes_->media.push_back(result); + } mark_dirty(); return *result; } @@ -486,6 +490,7 @@ bool native_document::parser_remove_from_parent(dom_node& child) noexcept auto& siblings = child.parent->children; const auto known = std::find(siblings.begin(), siblings.end(), &child); if (known == siblings.end()) return false; + unregister_modal_subtree(child); siblings.erase(known); child.parent = nullptr; return true; @@ -540,6 +545,7 @@ dom_node& native_document::parser_template_contents(dom_node& element) void native_document::remove_all_children(dom_node& parent) { for (auto* child : parent.children) { + unregister_modal_subtree(*child); child->parent = nullptr; child->visible = false; } @@ -589,6 +595,12 @@ size_t native_document::erase_detached_subtrees( } if (shadow_dom_storage_->node_data.empty()) shadow_dom_storage_.reset(); } + if (auxiliary_nodes_) std::erase_if(auxiliary_nodes_->dialogs, [&](const auto& entry) { + return entry.scope_id > native_id_index_.size() || entry.dialog_id > native_id_index_.size() + || native_id_index_[entry.scope_id - 1U] == nullptr + || native_id_index_[entry.dialog_id - 1U] == nullptr; + }); + if(auxiliary_nodes_)std::erase_if(auxiliary_nodes_->media,[&](auto* node){return removed.contains(node);}); std::erase_if(nodes_, [&](const auto& node) { return removed.contains(node.get()); }); while (!native_id_index_.empty() && native_id_index_.back() == nullptr) { native_id_index_.pop_back(); @@ -625,8 +637,106 @@ std::vector native_document::query_selector_all( return result; } +bool native_document::register_modal_dialog(dom_node& scope, dom_node& dialog) +{ + if (&scope == &dialog || find_by_native_id(scope.id) != &scope || find_by_native_id(dialog.id) != &dialog + || dialog.tag != "dialog" || dialog.namespace_uri() != dom_node::html_namespace_uri) return false; + auto* scope_ancestor = &scope; + while (scope_ancestor != nullptr && scope_ancestor != body_) scope_ancestor = composed_parent(*scope_ancestor); + if (scope_ancestor == nullptr) return false; + auto* ancestor = &dialog; + while (ancestor != nullptr && ancestor != &scope) ancestor = composed_parent(*ancestor); + if (ancestor == nullptr) return false; + for (const auto& entry : modal_dialogs()) { + if (entry.dialog_id == dialog.id) return entry.scope_id == scope.id; + } + if (!auxiliary_nodes_) auxiliary_nodes_ = std::make_unique(); + auxiliary_nodes_->dialogs.push_back({scope.id, dialog.id}); + mark_dirty(); + return true; +} + +void native_document::unregister_modal_dialog(const dom_node& dialog) +{ + if (dialog.id == 0 || dialog.id > native_id_index_.size() + || native_id_index_[dialog.id - 1U] != &dialog) return; + if (auxiliary_nodes_ && std::erase_if(auxiliary_nodes_->dialogs, [&](const auto& entry) { + return entry.dialog_id == dialog.id; + }) != 0) mark_dirty(); +} + +void native_document::unregister_modal_subtree(const dom_node& root) +{ + if (auxiliary_nodes_ && std::erase_if(auxiliary_nodes_->dialogs, [&](const auto& entry) { + const auto within = [&](uint32_t id) { + const auto* candidate = id <= native_id_index_.size() ? native_id_index_[id - 1U] : nullptr; + while (candidate != nullptr && candidate != &root) candidate = composed_parent(*candidate); + return candidate != nullptr; + }; + return within(entry.dialog_id) || within(entry.scope_id); + }) != 0) mark_dirty(); +} + +const dom_node* native_document::active_modal_dialog(const dom_node& scope) const noexcept +{ + if (modal_dialogs().empty()) return nullptr; + if (scope.id == 0 || scope.id > native_id_index_.size() + || native_id_index_[scope.id - 1U] != &scope) return nullptr; + for (auto entry = modal_dialogs().rbegin(); entry != modal_dialogs().rend(); ++entry) { + if (entry->scope_id != scope.id || entry->dialog_id > native_id_index_.size()) continue; + const auto* dialog = native_id_index_[entry->dialog_id - 1U]; + if (dialog == nullptr) continue; + for (auto* ancestor = dialog; ancestor != nullptr; ancestor = composed_parent(*ancestor)) { + if (ancestor == &scope) return dialog; + } + } + return nullptr; +} + +bool native_document::is_modal_dialog(const dom_node& node) const noexcept +{ + if (modal_dialogs().empty() || node.id == 0 || node.id > native_id_index_.size() + || native_id_index_[node.id - 1U] != &node) return false; + return std::any_of(modal_dialogs().begin(), modal_dialogs().end(), + [&](const auto& entry) { return entry.dialog_id == node.id; }); +} + +bool native_document::is_in_modal_layer(const dom_node& node) const noexcept +{ + if (modal_dialogs().empty()) return false; + for (auto* ancestor = &node; ancestor != nullptr; ancestor = composed_parent(*ancestor)) { + if (is_modal_dialog(*ancestor)) return true; + } + return false; +} + +bool native_document::is_inert(const dom_node& node) const noexcept +{ + // The empty stack keeps ordinary documents on the attribute-only path. + // Scope checks are independent: a nested browsing scope cannot escape a + // modal that blocks its embedding subtree. + bool attribute_inert = false; + bool escaped_ancestors = false; + for (auto* ancestor = &node; ancestor != nullptr; ancestor = composed_parent(*ancestor)) { + if (!escaped_ancestors && ancestor->attributes.contains("inert")) attribute_inert = true; + if (modal_dialogs().empty()) continue; + for (const auto& entry : modal_dialogs()) { + if (entry.dialog_id == ancestor->id) escaped_ancestors = true; + } + if (const auto* modal = active_modal_dialog(*ancestor)) { + auto* candidate = &node; + while (candidate != nullptr && candidate != modal) candidate = composed_parent(*candidate); + if (candidate == nullptr) return true; + } + } + return attribute_inert; +} + dom_node* native_document::hit_test(dom_node& root, float x, float y) { + if (const auto* modal = active_modal_dialog(root)) { + return hit_test(*const_cast(modal), x, y); + } const auto nearest_element = [this](dom_node* hit) { while (hit != nullptr && hit->tag == "#text") hit = composed_parent(*hit); return hit; @@ -708,7 +818,12 @@ dom_node* native_document::hit_test_node( bool inherited_pointer_events_none, bool ignore_own_clip) noexcept { - if (!node.visible || node.style.display == display_mode::none) return nullptr; + // A scope is itself inert while blocked. Enter its active modal before + // pruning the scope, otherwise no control in that dialog could be hit. + if (const auto* modal = active_modal_dialog(node)) { + return hit_test(*const_cast(modal), x, y); + } + if (!node.visible || node.style.display == display_mode::none || is_inert(node)) return nullptr; const auto visibility_hidden = node.style.visibility_specified ? node.style.visibility_hidden : inherited_visibility_hidden; @@ -802,6 +917,7 @@ dom_node* native_document::hit_test_node( void native_document::clear() { + auxiliary_nodes_.reset(); retained_export_canvas_id_ = 0U; shadow_dom_storage_.reset(); nodes_.clear(); diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.cpp b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.cpp index 899a6abe2..2917bdd0b 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.cpp +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.cpp @@ -2,6 +2,13 @@ #include "webscene_native_dom.h" #include "webscene_v8_runtime.h" #include "webscene_runtime_diagnostics.h" +#include "webscene_frame_trace.h" +#include "graphics/engine_wake.h" +#include "graphics/webgpu_canvas_interop.h" +#include "graphics/image_lease_abi.h" +#if defined(__APPLE__) && defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) +#include "graphics/iosurface_canvas_images.h" +#endif #include #include @@ -60,9 +67,16 @@ static_assert(sizeof(webscene_runtime_work_metrics) == 168); static_assert(sizeof(webscene_document_script) == 40); static_assert(sizeof(webscene_navigation_options) == 16); +struct queued_input_event : webscene_input_event { + uint64_t observed_compositor_timestamp = 0; + queued_input_event() = default; + queued_input_event(const webscene_input_event& value, uint64_t observed = 0) + : webscene_input_event(value), observed_compositor_timestamp(observed) {} +}; + class input_ring final { public: - bool try_push(const webscene_input_event& value) + bool try_push(const queued_input_event& value) { // Pointer/keyboard input is submitted by the host UI thread while // display frames are submitted by the compositor thread. Serialize @@ -83,7 +97,7 @@ class input_ring final { return true; } - bool try_pop(webscene_input_event& value) + bool try_pop(queued_input_event& value) { const auto read = read_.load(std::memory_order_relaxed); if (read == write_.load(std::memory_order_acquire)) { @@ -107,7 +121,7 @@ class input_ring final { return (value + 1U) % input_capacity; } - std::array values_{}; + std::array values_{}; std::mutex producer_mutex_; alignas(64) std::atomic write_{0}; alignas(64) std::atomic read_{0}; @@ -122,6 +136,7 @@ struct canvas_layer_version final { float y{0}; float width{0}; float height{0}; + uint64_t command_hash{0}; bool visually_equals(const canvas_layer_version& other) const noexcept { @@ -137,6 +152,10 @@ struct canvas_layer_version final { struct scene final { webscene_scene_header header{}; + uint64_t required_capabilities{}; + uint64_t captured_generation{}; + std::vector gpu_bindings; + std::vector> gpu_images; std::vector commands; std::vector canvas_layers; std::vector canvas_commands; @@ -148,9 +167,74 @@ struct scene final { uint64_t published_timestamp_nanoseconds{0}; }; +// Scene payloads are immutable while borrowed. Once the last reference goes +// away, reuse their allocations instead of faulting in a large new command +// buffer on every frame. The pool owns no live scene or GPU image references. +struct scene_storage_pool final : std::enable_shared_from_this { + std::mutex mutex; + std::array,3> free; + size_t retained_bytes=0; + void trim() { + std::lock_guard lock(mutex); + for(auto& slot:free)slot.reset(); + retained_bytes=0; + } + static size_t capacity_bytes(const scene& value) noexcept { + return value.commands.capacity()*sizeof(webscene_scene_command) + +value.canvas_commands.capacity()*sizeof(webscene_canvas_command) + +value.canvas_layers.capacity()*sizeof(webscene_canvas_layer) + +value.canvas_strings.capacity()*sizeof(webscene_scene_string) + +value.canvas_string_bytes.capacity() + +value.damage_rects.capacity()*sizeof(webscene_damage_rect); + } + std::shared_ptr acquire() { + std::unique_ptr value; + { std::lock_guard lock(mutex); + for(auto& slot:free)if(slot) { + retained_bytes-=capacity_bytes(*slot);value=std::move(slot);break; + } + } + if(!value)value=std::make_unique(); + return std::shared_ptr(value.release(),[pool=shared_from_this()](scene* returned) { + std::unique_ptr owner(returned); + returned->header={};returned->required_capabilities=0;returned->captured_generation=0; + returned->gpu_bindings.clear();returned->gpu_images.clear(); + returned->commands.clear();returned->canvas_commands.clear();returned->canvas_layers.clear(); + returned->canvas_strings.clear();returned->canvas_string_bytes.clear();returned->damage_rects.clear(); + returned->full_layer_versions.clear();returned->dom_hash=0;returned->published_timestamp_nanoseconds=0; + const auto bytes=capacity_bytes(*returned); + std::lock_guard lock(pool->mutex); + constexpr size_t budget=64U*1024U*1024U; + if(bytes>budget-pool->retained_bytes)return; + for(auto& slot:pool->free)if(!slot) { + pool->retained_bytes+=bytes;slot=std::move(owner);break; + } + }); + } +}; + +constexpr uint64_t scene_command_capabilities(uint32_t kind) noexcept +{ + switch (kind) { + case WEBSCENE_SCENE_COMMAND_GPU_IMAGE: return WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES; + case WEBSCENE_SCENE_COMMAND_CANVAS_LAYER: return WEBSCENE_SCENE_CAPABILITY_ORDERED_CANVAS; + default: return 0; + } +} + +uint64_t scene_capabilities(const scene& value) +{ + auto capabilities=value.required_capabilities | (value.gpu_images.empty() ? 0 : WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES); + for(const auto& image:value.gpu_images) + if(image && image->requires_producer_wait) capabilities|=WEBSCENE_SCENE_CAPABILITY_PRODUCER_GPU_WAITS; + return capabilities; +} + uint64_t retained_scene_bytes(const scene& value) { return sizeof(scene) + + value.gpu_images.capacity() * sizeof(decltype(value.gpu_images)::value_type) + + value.gpu_bindings.capacity() * sizeof(decltype(value.gpu_bindings)::value_type) + value.commands.capacity() * sizeof(webscene_scene_command) + value.canvas_layers.capacity() * sizeof(webscene_canvas_layer) + value.canvas_commands.capacity() * sizeof(webscene_canvas_command) @@ -162,6 +246,11 @@ uint64_t retained_scene_bytes(const scene& value) } struct acknowledgement_state final { + void reset_for_checkpoint() { + std::lock_guard lock(mutex); + revision=0; dom_hash=0; viewport_width=0; viewport_height=0; + layer_versions.clear(); value.reset(); presentation_images.clear(); pending_scenes.clear(); + } std::mutex mutex; uint64_t revision{0}; uint64_t dom_hash{0}; @@ -169,6 +258,9 @@ struct acknowledgement_state final { float viewport_height{0}; std::unordered_map layer_versions; std::shared_ptr value; + // Latest compositor-accepted image set, including image-only diffs. This is + // separate from the DOM comparison snapshot and from live drawing storage. + std::vector> presentation_images; std::deque> pending_scenes; std::atomic acknowledged_scenes{0}; std::atomic total_acknowledgement_nanoseconds{0}; @@ -181,6 +273,12 @@ struct script_request final { std::string document_name; }; +struct canvas_checkpoint_request { + uint32_t node_id, command_count; + uint64_t generation; + std::string payload; +}; + struct url_request final { std::string url; std::vector document_start_scripts; @@ -258,6 +356,7 @@ struct inspector_pump_work final { } // namespace struct webscene_engine final { + enum class publication_result { deferred, published, discarded }; #include "webscene_native_engine_lifecycle.inc" #include "webscene_native_engine_interop_api.inc" #include "webscene_native_engine_diagnostics.inc" @@ -272,12 +371,17 @@ struct webscene_engine final { return diagnostics_.copy_failure(destination, capacity); } private: +#if defined(WEBSCENE_GRAPHICS_SCENE_TESTS) + friend void test_native_gpu_scene_leases(); + explicit webscene_engine(std::nullptr_t) : command_count_(0) {} +#endif #include "webscene_native_engine_interop_work.inc" #include "webscene_native_engine_worker.inc" #include "webscene_native_engine_input.inc" #include "webscene_native_engine_metric_updates.inc" #include "webscene_native_engine_scene.inc" #include "webscene_native_engine_errors.inc" + webscene_frame_trace frame_trace_; uint32_t command_count_; std::string compilation_cache_directory_; webscene_resource_load_callback resource_load_callback_{nullptr}; @@ -288,6 +392,8 @@ struct webscene_engine final { void* resource_load_v3_user_data_{nullptr}; webscene_stylesheet_consumed_callback stylesheet_consumed_callback_{nullptr}; void* stylesheet_consumed_user_data_{nullptr}; + webscene_webgpu_policy_callback webgpu_policy_callback_{nullptr}; + void* webgpu_policy_user_data_{nullptr}; webscene_scene_published_callback scene_published_callback_{nullptr}; void* scene_published_user_data_{nullptr}; webscene_host_request_available_callback @@ -359,10 +465,18 @@ struct webscene_engine final { std::atomic next_interop_operation_id_{1U}; std::shared_ptr latest_{}; std::atomic ordered_scene_consumer_{false}; - std::condition_variable wake_; - std::mutex wake_mutex_; - bool wake_pending_{false}; + std::atomic producer_gpu_wait_consumer_{false}; +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + std::shared_ptr graphics_wake_{ + std::make_shared()}; + webscene::graphics::engine_wake& worker_wake_{*graphics_wake_}; +#else + webscene::graphics::engine_wake worker_wake_; +#endif uint64_t next_revision_{1}; + std::shared_ptr staged_scene_; + uint64_t published_document_generation_{}; + uint64_t last_input_sequence_{0}; double viewport_width_{1000}; double viewport_height_{616}; @@ -503,6 +617,19 @@ struct webscene_engine final { std::atomic coalesced_pointer_move_inputs_{0}; std::atomic coalesced_wheel_inputs_{0}; std::atomic applied_pointer_move_inputs_{0}; + std::atomic pending_pointer_move_inputs_{0}; + std::shared_ptr scene_storage_=std::make_shared(); + std::vector canvas_layers_scratch_; + std::vector canvas_commands_scratch_; + std::vector canvas_strings_scratch_; + std::vector canvas_string_bytes_scratch_; + void trim_scene_storage() { + scene_storage_->trim(); + decltype(canvas_layers_scratch_){}.swap(canvas_layers_scratch_); + decltype(canvas_commands_scratch_){}.swap(canvas_commands_scratch_); + decltype(canvas_strings_scratch_){}.swap(canvas_strings_scratch_); + decltype(canvas_string_bytes_scratch_){}.swap(canvas_string_bytes_scratch_); + } std::atomic applied_wheel_inputs_{0}; std::atomic applied_animation_frames_{0}; std::atomic coalesced_animation_frames_{0}; @@ -565,6 +692,8 @@ struct webscene_engine final { #endif std::atomic current_cursor_{WEBSCENE_CURSOR_DEFAULT}; std::atomic checkpoint_requested_{false}; + std::mutex canvas_checkpoint_mutex_; + std::optional canvas_checkpoint_; std::atomic pending_canvas_export_release_id_{0U}; mutable std::mutex iframe_html_mutex_; std::string iframe_html_; @@ -634,6 +763,10 @@ struct webscene_scene_lease final { != value->header.revision) { return false; } + // Allocate before changing acknowledged state so failure cannot publish + // a partial transition. Rejected/stale acknowledgements never get here. + auto presentation_images = value->gpu_images; + acknowledgement->presentation_images.swap(presentation_images); acknowledgement->revision = value->header.revision; acknowledgement->dom_hash = value->dom_hash; acknowledgement->viewport_width = value->header.viewport_width; @@ -760,7 +893,8 @@ webscene_engine* webscene_engine_create_with_options(const webscene_engine_optio const auto has_resource_callback_v3 = options->struct_size >= offsetof(webscene_engine_options, stylesheet_consumed_callback); const auto has_stylesheet_consumed_callback = - options->struct_size >= sizeof(webscene_engine_options); + options->struct_size >= offsetof(webscene_engine_options, webgpu_policy_callback); + const auto has_webgpu_policy = options->struct_size >= sizeof(webscene_engine_options); return new webscene_engine( options->simulated_chart_command_count, std::move(cache_directory), @@ -793,7 +927,9 @@ webscene_engine* webscene_engine_create_with_options(const webscene_engine_optio ? options->animation_frame_requested_user_data : nullptr, has_stylesheet_consumed_callback ? options->stylesheet_consumed_callback : nullptr, - has_stylesheet_consumed_callback ? options->stylesheet_consumed_user_data : nullptr); + has_stylesheet_consumed_callback ? options->stylesheet_consumed_user_data : nullptr, + has_webgpu_policy ? options->webgpu_policy_callback : nullptr, + has_webgpu_policy ? options->webgpu_policy_user_data : nullptr); } catch (...) { return nullptr; } @@ -1197,6 +1333,14 @@ uint8_t webscene_engine_request_scene_checkpoint(webscene_engine* engine) return engine != nullptr && engine->request_scene_checkpoint() ? 1U : 0U; } +uint8_t webscene_engine_submit_canvas_checkpoint_v3(webscene_engine* engine, + uint32_t node_id, uint64_t generation, uint32_t command_count, + const char* payload, size_t length) +{ + try { return engine && engine->submit_canvas_checkpoint(node_id,generation,command_count,payload,length); } + catch (...) { return 0; } +} + uint8_t webscene_engine_release_canvas_export( webscene_engine* engine, uint32_t node_id) @@ -1224,6 +1368,168 @@ uint8_t webscene_engine_set_preferred_color_scheme( : 0U; } +namespace { +struct scene_lease_v3 { + webscene_scene_lease cpu; + webscene_scene_view_v3 view; + scene_lease_v3(std::shared_ptr value,std::shared_ptr acknowledgement) + : cpu(std::move(value),std::move(acknowledgement)), + view{sizeof(webscene_scene_view_v3),WEBSCENE_SCENE_VIEW_VERSION_3,scene_capabilities(*cpu.value),&cpu.view,this} {} +}; +webscene_scene_acquire_status acquire_scene_value_v3(std::shared_ptr value, + std::shared_ptr acknowledgement,uint64_t capabilities, + const webscene_scene_view_v3** result) +{ + *result=nullptr; + if (!value) return WEBSCENE_SCENE_ACQUIRE_EMPTY; + if (scene_capabilities(*value) & ~capabilities) + return WEBSCENE_SCENE_ACQUIRE_UNSUPPORTED_CAPABILITIES; + auto* lease=new scene_lease_v3(std::move(value),std::move(acknowledgement)); + *result=&lease->view; + return WEBSCENE_SCENE_ACQUIRE_SUCCESS; +} +webscene_scene_acquire_status acquire_scene_v3(webscene_engine* engine, + const webscene_scene_acquire_options_v3* options,const webscene_scene_view_v3** result,bool ordered) +{ + if (!result) return WEBSCENE_SCENE_ACQUIRE_INVALID_ARGUMENT; + *result=nullptr; + if (!engine || !options || options->struct_sizescene_version!=WEBSCENE_SCENE_VIEW_VERSION_3) return WEBSCENE_SCENE_ACQUIRE_UNSUPPORTED_VERSION; + try { + engine->set_producer_gpu_wait_consumer( + (options->consumer_capabilities & WEBSCENE_SCENE_CAPABILITY_PRODUCER_GPU_WAITS)!=0); + auto value=ordered ? engine->acquire_next() : engine->acquire_latest(); + return acquire_scene_value_v3(std::move(value),engine->acknowledgement_state_handle(), + options->consumer_capabilities,result); + } catch (const std::bad_alloc&) { return WEBSCENE_SCENE_ACQUIRE_OUT_OF_MEMORY; } + catch (...) { return WEBSCENE_SCENE_ACQUIRE_INTERNAL_ERROR; } +} +} +webscene_scene_acquire_status webscene_engine_acquire_latest_scene_v3(webscene_engine* engine, + const webscene_scene_acquire_options_v3* options,const webscene_scene_view_v3** result) +{ return acquire_scene_v3(engine,options,result,false); } +webscene_scene_acquire_status webscene_engine_acquire_next_scene_v3(webscene_engine* engine, + const webscene_scene_acquire_options_v3* options,const webscene_scene_view_v3** result) +{ return acquire_scene_v3(engine,options,result,true); } +uint8_t webscene_scene_acknowledge_v3(const webscene_scene_view_v3* view) +{ + if (!view || view->struct_size < sizeof(webscene_scene_view_v3) || + view->scene_version!=WEBSCENE_SCENE_VIEW_VERSION_3 || !view->lease_token) return 0; + return static_cast(const_cast(view->lease_token))->cpu.acknowledge() ? 1 : 0; +} +void webscene_scene_release_v3(const webscene_scene_view_v3* view) +{ + if (!view || view->struct_size < sizeof(webscene_scene_view_v3) || + view->scene_version!=WEBSCENE_SCENE_VIEW_VERSION_3 || !view->lease_token) return; + delete static_cast(view->lease_token); +} + +uint32_t webscene_scene_gpu_image_count_v3(const webscene_scene_view_v3* view) +{ + if (!view || view->struct_size < sizeof(*view) || view->scene_version!=3 || !view->lease_token) return 0; + return static_cast(view->lease_token)->cpu.value->gpu_images.size(); +} +webscene_scene_acquire_status webscene_gpu_image_retain_v3( + const webscene_gpu_image_lease_v3* image,webscene_gpu_image_lease_v3** result) +{ + if (!result) return WEBSCENE_SCENE_ACQUIRE_INVALID_ARGUMENT; + *result=nullptr; + if (!image) return WEBSCENE_SCENE_ACQUIRE_INVALID_ARGUMENT; + try { + auto retained=image->value.retain(); + if (!retained) return WEBSCENE_SCENE_ACQUIRE_BACKPRESSURE; + *result=new webscene_gpu_image_lease_v3(std::move(*retained),image->dependencies,image->requires_producer_wait); + return WEBSCENE_SCENE_ACQUIRE_SUCCESS; + } catch (const std::bad_alloc&) { return WEBSCENE_SCENE_ACQUIRE_OUT_OF_MEMORY; } + catch (...) { return WEBSCENE_SCENE_ACQUIRE_INTERNAL_ERROR; } +} +webscene_scene_acquire_status webscene_scene_retain_gpu_image_v3( + const webscene_scene_view_v3* view,uint32_t index,webscene_gpu_image_lease_v3** result) +{ + if (!result) return WEBSCENE_SCENE_ACQUIRE_INVALID_ARGUMENT; + *result=nullptr; + if (index>=webscene_scene_gpu_image_count_v3(view)) return WEBSCENE_SCENE_ACQUIRE_INVALID_ARGUMENT; + return webscene_gpu_image_retain_v3( + static_cast(view->lease_token)->cpu.value->gpu_images[index].get(),result); +} +uint8_t webscene_gpu_image_describe_v3(const webscene_gpu_image_lease_v3* image,webscene_gpu_image_info_v3* result) +{ + if (!image || !result || result->struct_sizeversion!=3) return 0; + try { + const auto m=image->value.describe(); + *result={sizeof(*result),3,m.canvas,m.allocation,m.allocation_generation,m.content_serial, + m.producer_timeline,m.producer_value,m.width,m.height,static_cast(m.format), + static_cast(m.alpha),static_cast(m.color_space),static_cast(m.orientation)}; + return 1; + } catch (...) { return 0; } +} +void webscene_gpu_image_release_v3(webscene_gpu_image_lease_v3* image) { delete image; } +webscene_scene_acquire_status webscene_gpu_image_begin_consumer_v3( + const webscene_gpu_image_lease_v3* image,webscene_gpu_image_consumer_v3** result) +{ + if (!result) return WEBSCENE_SCENE_ACQUIRE_INVALID_ARGUMENT; + *result=nullptr; + if (!image) return WEBSCENE_SCENE_ACQUIRE_INVALID_ARGUMENT; + try { + // Allocate the wrapper before registering GPU use: OOM must not abandon + // a consumer ticket whose destructor correctly requires completion. + void* storage=::operator new(sizeof(webscene_gpu_image_consumer_v3)); + try { + auto consumer=image->value.begin_consumer(); + if (!consumer) { ::operator delete(storage); return WEBSCENE_SCENE_ACQUIRE_BACKPRESSURE; } + *result=new (storage) webscene_gpu_image_consumer_v3(std::move(*consumer),image->dependencies); + } catch (...) { ::operator delete(storage); throw; } + return WEBSCENE_SCENE_ACQUIRE_SUCCESS; + } catch (const std::bad_alloc&) { return WEBSCENE_SCENE_ACQUIRE_OUT_OF_MEMORY; } + catch (...) { return WEBSCENE_SCENE_ACQUIRE_INTERNAL_ERROR; } +} +uint8_t webscene_gpu_image_get_iosurface_v3( + const webscene_gpu_image_consumer_v3* consumer,webscene_gpu_iosurface_view_v3* result) +{ + if (!result || result->struct_sizeversion!=3) return 0; + result->borrowed_iosurface=nullptr; result->allocation_bytes=0; + if (!consumer) return 0; +#if defined(__APPLE__) && defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + try { + const auto& image=webscene::graphics::iosurface_canvas_images::resolve(consumer->value); + result->borrowed_iosurface=image.borrowed_handle(); + result->allocation_bytes=image.allocation_bytes(); + return 1; + } catch (...) { return 0; } +#else + return 0; +#endif +} +uint8_t webscene_gpu_image_dependency_count_v3( + const webscene_gpu_image_consumer_v3* consumer,uint32_t* count) +{ + if(!count) return 0; + *count=0; + if(!consumer) return 0; + const auto size=consumer->dependencies ? consumer->dependencies->count() : 0; + if(size>UINT32_MAX) return 0; + *count=static_cast(size); return 1; +} +uint8_t webscene_gpu_image_get_metal_event_v3( + const webscene_gpu_image_consumer_v3* consumer,uint32_t index,webscene_gpu_metal_event_view_v3* result) +{ + if(!result || result->struct_sizeversion!=3) return 0; + result->borrowed_shared_event=nullptr;result->signaled_value=0; + if(!consumer || !consumer->dependencies) return 0; + try { + void* event=nullptr;uint64_t value=0; + if(index>=consumer->dependencies->count() || + !consumer->dependencies->metal_event(index,event,value) || !event) return 0; + result->borrowed_shared_event=event;result->signaled_value=value;return 1; + } catch(...) { return 0; } +} + +void webscene_gpu_image_complete_consumer_v3(webscene_gpu_image_consumer_v3* consumer) +{ + if (!consumer) return; + consumer->value.complete(); delete consumer; +} + const webscene_scene_view* webscene_engine_acquire_latest_scene(webscene_engine* engine) { if (engine == nullptr) { @@ -1231,7 +1537,7 @@ const webscene_scene_view* webscene_engine_acquire_latest_scene(webscene_engine* } auto scene_value = engine->acquire_latest(); - if (!scene_value) { + if (!scene_value || scene_capabilities(*scene_value)) { return nullptr; } @@ -1252,7 +1558,7 @@ const webscene_scene_view* webscene_engine_acquire_next_scene(webscene_engine* e } auto scene_value = engine->acquire_next(); - if (!scene_value) { + if (!scene_value || scene_capabilities(*scene_value)) { return nullptr; } @@ -1421,3 +1727,7 @@ uint8_t webscene_engine_get_memory_metrics( } } // extern "C" + +#if defined(WEBSCENE_GRAPHICS_SCENE_TESTS) +#include "../tests/graphics_scene_lease_tests.inc" +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.exports b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.exports index 982aeb4ca..ccca42fa0 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.exports +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.exports @@ -64,3 +64,26 @@ _webscene_scene_acknowledge _webscene_scene_get_commands _webscene_scene_get_header _webscene_scene_release + +_webscene_engine_acquire_latest_scene_v3 +_webscene_engine_acquire_next_scene_v3 +_webscene_scene_acknowledge_v3 +_webscene_scene_release_v3 +_webscene_scene_gpu_image_count_v3 +_webscene_scene_retain_gpu_image_v3 +_webscene_gpu_image_retain_v3 +_webscene_gpu_image_describe_v3 +_webscene_gpu_image_release_v3 +_webscene_gpu_image_begin_consumer_v3 +_webscene_gpu_image_complete_consumer_v3 + +_webscene_gpu_image_get_iosurface_v3 + +_webscene_gpu_image_dependency_count_v3 +_webscene_gpu_image_get_metal_event_v3 +_webscene_gpu_d3d11_supported_v3 +_webscene_gpu_d3d11_import_v3 +_webscene_gpu_d3d11_seal_v3 +_webscene_gpu_d3d11_poll_v3 +_webscene_gpu_d3d11_destroy_v3 +_webscene_engine_submit_canvas_checkpoint_v3 diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.h b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.h index ce12261f8..64b25327f 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.h +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine.h @@ -39,6 +39,7 @@ WEBSCENE_API size_t webscene_engine_copy_runtime_failure( webscene_engine* engine, char* destination, size_t destination_capacity); typedef struct webscene_scene_view webscene_scene_view; typedef struct webscene_interop_result_view_v3 webscene_interop_result_view_v3; +typedef struct webscene_interop_callback_view_v3 webscene_interop_callback_view_v3; /* Legacy direct-message callback retained for ABI compatibility. */ typedef void (*webscene_inspector_message_callback)( @@ -477,6 +478,127 @@ struct webscene_scene_view { uint32_t reserved; }; +/* Separately versioned scene acquisition. No GPU capability is advertised yet. */ +#define WEBSCENE_SCENE_VIEW_VERSION_3 3U +#define WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES (UINT64_C(1) << 0) +#define WEBSCENE_SCENE_CAPABILITY_ORDERED_CANVAS (UINT64_C(1) << 1) +/* Consumer must enqueue every native producer dependency before GPU reads. */ +#define WEBSCENE_SCENE_CAPABILITY_PRODUCER_GPU_WAITS (UINT64_C(1) << 2) +#define WEBSCENE_SCENE_CAPABILITY_CANVAS_CHECKPOINTS (UINT64_C(1) << 3) +/* UTF-8 versioned raster/state checkpoint resource, interpreted by capable hosts. */ +#define WEBSCENE_CANVAS_COMMAND_RASTER_CHECKPOINT 58U +WEBSCENE_API uint8_t webscene_engine_submit_canvas_checkpoint_v3( + webscene_engine* engine, uint32_t node_id, uint64_t generation, + uint32_t command_count, const char* payload, size_t payload_length); +/* Draw in the existing command stream at x/y/width/height. rgba carries the + * scene GPU image index (not a color); node_id retains the canvas node ID. Existing transform/clip/isolation operations apply. */ +#define WEBSCENE_SCENE_COMMAND_GPU_IMAGE 256U +/* Requires SCENE_CAPABILITY_ORDERED_CANVAS in the scene capability mask. + * Ordered v3 placement of a retained Canvas2D layer. node_id selects the layer; + * its current layout and bitmap dimensions provide placement/scaling. */ +#define WEBSCENE_SCENE_COMMAND_CANVAS_LAYER 257U +/* Optional hint on a replacement canvas layer. Against header.base_revision, + * the previous layer's command and string arrays are unchanged prefixes of + * this complete replacement payload. Consumers may ignore this hint. */ +#define WEBSCENE_CANVAS_LAYER_UNCHANGED_PREFIX 4U +typedef struct webscene_scene_acquire_options_v3 { + uint32_t struct_size; + uint32_t scene_version; + uint64_t consumer_capabilities; +} webscene_scene_acquire_options_v3; +typedef enum webscene_scene_acquire_status { + WEBSCENE_SCENE_ACQUIRE_SUCCESS = 0, + WEBSCENE_SCENE_ACQUIRE_EMPTY = 1, + WEBSCENE_SCENE_ACQUIRE_INVALID_ARGUMENT = 2, + WEBSCENE_SCENE_ACQUIRE_UNSUPPORTED_VERSION = 3, + WEBSCENE_SCENE_ACQUIRE_UNSUPPORTED_CAPABILITIES = 4, + WEBSCENE_SCENE_ACQUIRE_OUT_OF_MEMORY = 5, + WEBSCENE_SCENE_ACQUIRE_INTERNAL_ERROR = 6, + WEBSCENE_SCENE_ACQUIRE_BACKPRESSURE = 7 +} webscene_scene_acquire_status; +typedef struct webscene_scene_view_v3 { + uint32_t struct_size; + uint32_t scene_version; + uint64_t required_capabilities; + /* Borrowed for this v3 lease's lifetime. Do not release separately. */ + const webscene_scene_view* cpu_view; + const void* lease_token; +} webscene_scene_view_v3; +WEBSCENE_API webscene_scene_acquire_status webscene_engine_acquire_latest_scene_v3( + webscene_engine* engine,const webscene_scene_acquire_options_v3* options,const webscene_scene_view_v3** result); +WEBSCENE_API webscene_scene_acquire_status webscene_engine_acquire_next_scene_v3( + webscene_engine* engine,const webscene_scene_acquire_options_v3* options,const webscene_scene_view_v3** result); +WEBSCENE_API uint8_t webscene_scene_acknowledge_v3(const webscene_scene_view_v3* scene); +WEBSCENE_API void webscene_scene_release_v3(const webscene_scene_view_v3* scene); + +/* Opaque native image leases. A retained lease can outlive its scene/engine. + * Release ends CPU retention only. Complete a consumer only after its GPU fence. + * Calls on the same handle must be externally serialized; released handles are invalid. + */ +typedef struct webscene_gpu_image_lease_v3 webscene_gpu_image_lease_v3; +typedef struct webscene_gpu_image_consumer_v3 webscene_gpu_image_consumer_v3; +typedef struct webscene_gpu_image_info_v3 { + uint32_t struct_size, version; + uint64_t canvas, allocation, allocation_generation, content_serial; + uint64_t producer_timeline, producer_value; + uint32_t width, height; + /* format: 1 RGBA8 unorm, 2 BGRA8 unorm, 3 RGBA16 float, + * 4 RGBA8 sRGB, 5 BGRA8 sRGB. + * alpha: 1 opaque, 2 premultiplied, 3 straight. + * color_space: 1 sRGB, 2 Display P3. orientation: 1 top-left, 2 bottom-left. + * Timeline/allocation IDs require native provider resolution, never casts. + */ + uint32_t format, alpha, color_space, orientation; +} webscene_gpu_image_info_v3; +WEBSCENE_API uint32_t webscene_scene_gpu_image_count_v3(const webscene_scene_view_v3* scene); +WEBSCENE_API webscene_scene_acquire_status webscene_scene_retain_gpu_image_v3( + const webscene_scene_view_v3* scene,uint32_t index,webscene_gpu_image_lease_v3** result); +WEBSCENE_API webscene_scene_acquire_status webscene_gpu_image_retain_v3( + const webscene_gpu_image_lease_v3* image,webscene_gpu_image_lease_v3** result); +WEBSCENE_API uint8_t webscene_gpu_image_describe_v3( + const webscene_gpu_image_lease_v3* image,webscene_gpu_image_info_v3* result); +WEBSCENE_API void webscene_gpu_image_release_v3(webscene_gpu_image_lease_v3* image); +WEBSCENE_API webscene_scene_acquire_status webscene_gpu_image_begin_consumer_v3( + const webscene_gpu_image_lease_v3* image,webscene_gpu_image_consumer_v3** result); +WEBSCENE_API void webscene_gpu_image_complete_consumer_v3(webscene_gpu_image_consumer_v3* consumer); +/* macOS native presenter hook, not portable image metadata or a JavaScript API. + * The pointer is borrowed until consumer completion. This lookup does not wait + * for the producer, begin native access, or authorize early consumer completion. + * Returns zero for unsupported providers/platforms/builds or invalid arguments. + */ +typedef struct webscene_gpu_iosurface_view_v3 { + uint32_t struct_size, version; + void* borrowed_iosurface; + uint64_t allocation_bytes; +} webscene_gpu_iosurface_view_v3; +WEBSCENE_API uint8_t webscene_gpu_image_get_iosurface_v3( + const webscene_gpu_image_consumer_v3* consumer,webscene_gpu_iosurface_view_v3* result); + +/* Windows host-queue bridge. Keep consumer alive until seal on the drawing + * thread and poll returning S_OK (zero). Texture is borrowed from import_owner. + * Destroy only before any draw, or after completed retirement. */ +WEBSCENE_API int32_t webscene_gpu_d3d11_import_v3(webscene_gpu_image_consumer_v3* consumer, + void* borrowed_device,void** import_owner,void** borrowed_texture); +WEBSCENE_API int32_t webscene_gpu_d3d11_supported_v3(void* borrowed_device); +WEBSCENE_API int32_t webscene_gpu_d3d11_seal_v3(void* import_owner); +WEBSCENE_API int32_t webscene_gpu_d3d11_poll_v3(void* import_owner); +WEBSCENE_API void webscene_gpu_d3d11_destroy_v3(void* import_owner); + +/* Optional native producer synchronization. Borrowed event ownership follows the + * consumer; callers must encode every dependency before reading an early image. + * A successful zero count means no attached dependencies, not GPU completion. + * These hooks never wait or authorize consumer completion. */ +typedef struct webscene_gpu_metal_event_view_v3 { + uint32_t struct_size, version; + void* borrowed_shared_event; + uint64_t signaled_value; +} webscene_gpu_metal_event_view_v3; +WEBSCENE_API uint8_t webscene_gpu_image_dependency_count_v3( + const webscene_gpu_image_consumer_v3* consumer,uint32_t* count); +WEBSCENE_API uint8_t webscene_gpu_image_get_metal_event_v3( + const webscene_gpu_image_consumer_v3* consumer,uint32_t index,webscene_gpu_metal_event_view_v3* result); + + typedef enum webscene_resource_kind { WEBSCENE_RESOURCE_DOCUMENT = 0, WEBSCENE_RESOURCE_SCRIPT = 1, @@ -672,6 +794,15 @@ typedef void (*webscene_stylesheet_consumed_callback)( void* user_data, const char* address, size_t address_length, const char* css, size_t css_length); +/* Optional host admission for the main document, evaluated on the runtime + * worker before scripts with the final resolved URL (initially about:blank). + * IOSURFACE certifies both a secure context and a GPU-capable scene consumer. + * Return DISABLED for untrusted/non-secure documents. No ABI reentry or throws. + * Current implementation supports IOSURFACE only on graphics-enabled macOS. */ +enum { WEBSCENE_WEBGPU_DISABLED = 0, WEBSCENE_WEBGPU_IOSURFACE = 1, WEBSCENE_WEBGPU_DXGI = 2 }; +typedef uint32_t (*webscene_webgpu_policy_callback)(void* user_data, + const char* document_url, size_t document_url_length); + typedef struct webscene_engine_options { uint32_t struct_size; uint32_t simulated_chart_command_count; @@ -695,6 +826,8 @@ typedef struct webscene_engine_options { void* resource_load_v3_user_data; webscene_stylesheet_consumed_callback stylesheet_consumed_callback; void* stylesheet_consumed_user_data; + webscene_webgpu_policy_callback webgpu_policy_callback; + void* webgpu_policy_user_data; } webscene_engine_options; enum { diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_diagnostics.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_diagnostics.inc index b39f4a0a9..856b3ceb7 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_diagnostics.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_diagnostics.inc @@ -158,6 +158,9 @@ return required; } + void set_producer_gpu_wait_consumer(bool enabled) { + producer_gpu_wait_consumer_.store(enabled,std::memory_order_release); + } std::shared_ptr acquire_latest() { auto result = std::atomic_load_explicit(&latest_, std::memory_order_acquire); @@ -180,16 +183,7 @@ bool request_scene_checkpoint() { - { - std::lock_guard lock(acknowledgement_->mutex); - acknowledgement_->revision = 0; - acknowledgement_->dom_hash = 0; - acknowledgement_->viewport_width = 0; - acknowledgement_->viewport_height = 0; - acknowledgement_->layer_versions.clear(); - acknowledgement_->value.reset(); - acknowledgement_->pending_scenes.clear(); - } + acknowledgement_->reset_for_checkpoint(); std::atomic_store_explicit( &latest_, std::shared_ptr{}, @@ -199,6 +193,59 @@ return true; } + bool submit_canvas_checkpoint(uint32_t node_id, uint64_t generation, + uint32_t command_count, const char* payload, size_t length) { + if (!node_id || !command_count || !payload || !length || length > 32U*1024U*1024U) return false; + { std::lock_guard lock(canvas_checkpoint_mutex_); + if (canvas_checkpoint_) return false; + canvas_checkpoint_ = canvas_checkpoint_request{node_id,command_count,generation,std::string(payload,length)}; + } + signal_worker(); return true; + } + + bool apply_canvas_checkpoint() { + std::optional request; + { std::lock_guard lock(canvas_checkpoint_mutex_); request.swap(canvas_checkpoint_); } + if (!request) return false; + auto* node = document_.find_by_native_id(request->node_id); + if (!node || node->tag != "canvas") return false; + auto& canvas = node->mutable_canvas(); + if (canvas.generation != request->generation || request->command_count > canvas.commands.size()) return false; + // A source canvas command captures generation/index metadata. Do not + // rebase a dependent canvas until that protocol supports checkpoints. + if (!canvas.canvas_dependencies.empty() || document_.has_canvas_references(node->id)) return false; + std::vector commands; + commands.reserve(1 + canvas.commands.size() - request->command_count); + webscene_canvas_command checkpoint{}; + checkpoint.kind = WEBSCENE_CANVAS_COMMAND_RASTER_CHECKPOINT; + commands.push_back(checkpoint); + commands.insert(commands.end(),canvas.commands.begin()+request->command_count,canvas.commands.end()); + std::vector strings; + strings.push_back(std::move(request->payload)); + std::unordered_map indices; + indices.emplace(strings.front(),0); + for(size_t index=1;index=canvas.strings.size()) return false; + { const auto& value=canvas.strings[command.resource_id]; + auto [found,inserted]=indices.emplace(value,static_cast(strings.size())); + if(inserted)strings.push_back(value); + command.resource_id=found->second; + } + break; + case 27: case WEBSCENE_CANVAS_COMMAND_RASTER_CHECKPOINT: return false; + } + } + canvas.commands.swap(commands);canvas.strings.swap(strings);canvas.string_indices.swap(indices); + ++canvas.generation; + document_.mark_scene_changed(); + return true; + } + bool release_canvas_export(uint32_t node_id) { if (node_id == 0U) return false; diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_input.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_input.inc index 8dd0bb255..0180a91d7 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_input.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_input.inc @@ -1,5 +1,6 @@ void apply(const webscene_input_event& event) { + frame_trace_.mark(event.kind == WEBSCENE_INPUT_FRAME ? "frame-start" : "input-start", event.sequence); last_input_sequence_ = std::max(last_input_sequence_, event.sequence); const auto measures_input_dispatch = event.kind != WEBSCENE_INPUT_RESIZE @@ -7,6 +8,17 @@ const auto input_dispatch_started = measures_input_dispatch ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; + // The host can advance its clock after the worker's loop-entry sample + // but before this input is dequeued. Start input-triggered transitions + // on that clock, rather than immediately expiring them on the next loop. + // Observing the timeline does not release RAF or publish another frame. + if (measures_input_dispatch) { + const auto host_timestamp = observed_host_timestamp_microseconds_.load( + std::memory_order_acquire); + if (host_timestamp != 0U) { + document_.signal_animation_frame(static_cast(host_timestamp) / 1000.0); + } + } switch (event.kind) { case WEBSCENE_INPUT_POINTER_MOVE: case WEBSCENE_INPUT_POINTER_DOWN: @@ -15,7 +27,15 @@ pointer_x_ = event.x; pointer_y_ = event.y; #if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8) + // A paced pointer's cursor is resolved against the publication + // layout after RAF. Forcing layout here duplicates that work. +#if defined(_WIN32) + if (runtime_ != nullptr && !runtime_->dispatch_input(event, + event.kind == WEBSCENE_INPUT_POINTER_MOVE + && observed_compositor_timestamp_microseconds_.load(std::memory_order_acquire) != 0)) { +#else if (runtime_ != nullptr && !runtime_->dispatch_input(event)) { +#endif script_errors_.fetch_add(1, std::memory_order_relaxed); record_input_dispatch_failure(event, runtime_->last_error()); } @@ -121,6 +141,7 @@ default: break; } + frame_trace_.mark(event.kind == WEBSCENE_INPUT_FRAME ? "frame-end" : "input-end", event.sequence); if (measures_input_dispatch) { const auto elapsed = static_cast( std::chrono::duration_cast( diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_interop_work.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_interop_work.inc index cf8c90270..368d52e91 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_interop_work.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_interop_work.inc @@ -183,15 +183,7 @@ void signal_worker() noexcept { - { - // This is an auto-reset event, not an unlatched condition-variable - // edge. Mutating the predicate under the wait mutex closes the - // producer's check-to-sleep race while still coalescing any number - // of signals into one immediate worker wake. - std::lock_guard lock(wake_mutex_); - wake_pending_ = true; - } - wake_.notify_one(); + worker_wake_.signal(); } bool take_latest_resize( diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_lifecycle.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_lifecycle.inc index c3e40a2e3..d19ecac86 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_lifecycle.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_lifecycle.inc @@ -21,7 +21,9 @@ animation_frame_requested_callback = nullptr, void* animation_frame_requested_user_data = nullptr, webscene_stylesheet_consumed_callback stylesheet_consumed_callback = nullptr, - void* stylesheet_consumed_user_data = nullptr) + void* stylesheet_consumed_user_data = nullptr, + webscene_webgpu_policy_callback webgpu_policy_callback = nullptr, + void* webgpu_policy_user_data = nullptr) : command_count_(command_count == 0U ? 0U : (command_count < minimum_command_count @@ -36,6 +38,8 @@ , resource_load_v3_user_data_(resource_load_v3_user_data) , stylesheet_consumed_callback_(stylesheet_consumed_callback) , stylesheet_consumed_user_data_(stylesheet_consumed_user_data) + , webgpu_policy_callback_(webgpu_policy_callback) + , webgpu_policy_user_data_(webgpu_policy_user_data) , scene_published_callback_(scene_published_callback) , scene_published_user_data_(scene_published_user_data) , host_request_available_callback_(host_request_available_callback) @@ -53,7 +57,7 @@ { worker_.request_stop(); signal_worker(); - worker_.join(); + if (worker_.joinable()) worker_.join(); #if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8_INSPECTOR) delete inspector_state_.exchange(nullptr, std::memory_order_acq_rel); #endif @@ -111,7 +115,11 @@ return true; } - if (!inputs_.try_push(event)) { + const auto pointer_move = event.kind == WEBSCENE_INPUT_POINTER_MOVE; + if (pointer_move) pending_pointer_move_inputs_.fetch_add(1, std::memory_order_release); + if (!inputs_.try_push(queued_input_event(event, + observed_compositor_timestamp_microseconds_.load(std::memory_order_acquire)))) { + if (pointer_move) pending_pointer_move_inputs_.fetch_sub(1, std::memory_order_release); dropped_inputs_.fetch_add(1, std::memory_order_relaxed); return false; } @@ -236,11 +244,16 @@ uint8_t animation_frame_demand() const noexcept { + // Read the handoff flag first: observing false acquires the RAF demand + // published before the worker released its in-flight pointer demand. + const auto pointer_pending = frame_paced_pointer_pending_.load(std::memory_order_acquire); + const auto queued_pointer = pending_pointer_move_inputs_.load(std::memory_order_acquire) != 0 + && observed_compositor_timestamp_microseconds_.load(std::memory_order_acquire) != 0; const auto host_demand = host_animation_frame_requested_.load( std::memory_order_acquire); return static_cast( host_demand - | (frame_paced_pointer_pending_.load(std::memory_order_acquire) + | (pointer_pending || queued_pointer ? uint8_t{4U} : uint8_t{0U})); } diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_scene.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_scene.inc index 081014c44..09670b9ab 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_scene.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_scene.inc @@ -9,13 +9,10 @@ } #endif - bool publish_scene() + publication_result publish_scene() { scene_publication_attempts_.fetch_add(1, std::memory_order_relaxed); const auto publication_started = std::chrono::steady_clock::now(); - auto next = std::make_shared(); - next->commands.reserve(command_count_ + 4U); - uint64_t hash = 1469598103934665603ULL; const auto width = static_cast(viewport_width_); const auto height = static_cast(viewport_height_); @@ -26,6 +23,7 @@ // otherwise backpressure can strand an observer-dependent layout // at its temporary measurement size. if (document_.dirty()) { + webscene_frame_trace::scope trace(frame_trace_, "publish-layout-start", "publish-layout-end", last_input_sequence_); const auto layout_started = std::chrono::steady_clock::now(); document_.layout(width, height); last_layout_nanoseconds_.store( @@ -38,8 +36,25 @@ && !runtime_->deliver_resize_observers()) { set_last_error(runtime_->last_error()); } + if (runtime_ != nullptr) { + runtime_->refresh_pointer_cursor_after_layout(); + current_cursor_.store(runtime_->current_cursor_kind(), std::memory_order_release); + } #endif } + // A staged scene owns an immutable CPU capture and exact GPU dependencies. + // Only a new capture must wait for the current rendering opportunity; + // unrelated newer GPU work must not hold an already completed capture. + // Commit still validates resets, viewport, completion and mailbox capacity. + if(staged_scene_)return commit_captured_scene(staged_scene_,publication_started); +#if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8) + if(runtime_) { + std::vector> images; + { std::lock_guard lock(acknowledgement_->mutex); images=acknowledgement_->presentation_images; } + runtime_->update_gpu_presentation_images(images); + if(runtime_->has_open_gpu_output())return publication_result::deferred; + } +#endif uint64_t base_revision = 0; uint64_t acknowledged_dom_hash = 0; float acknowledged_width = 0; @@ -62,7 +77,7 @@ blocked_scene_publications_.fetch_add( 1, std::memory_order_relaxed); - return false; + return publication_result::deferred; } } if (!acknowledgement_->pending_scenes.empty()) { @@ -84,6 +99,10 @@ acknowledged_scene = acknowledgement_->value; } } + // Waiting on a captured GPU output or a full consumer mailbox requires + // no new scene storage. Allocate only after admission succeeds. + auto next = scene_storage_->acquire(); + next->commands.reserve(command_count_ + 4U); uint32_t scene_flags = base_revision == 0 ? scene_flag_checkpoint : 0U; const auto collect_runtime_work_metrics = runtime_work_metrics_enabled_.load(std::memory_order_relaxed); @@ -91,15 +110,30 @@ scene_builds_.fetch_add(1, std::memory_order_relaxed); } if (native_scene_active_) { + frame_trace_.mark("build-dom-start", last_input_sequence_); const auto scene_build_started = std::chrono::steady_clock::now(); + document_.build_gpu_canvas_bindings(next->gpu_bindings); + next->gpu_images.resize(next->gpu_bindings.size()); document_.build_scene( next->commands, next->canvas_strings, - next->canvas_string_bytes); + next->canvas_string_bytes, + !next->gpu_bindings.empty(), true); + std::unordered_map gpu_indices; + for (uint32_t index=0;indexgpu_bindings.size();++index) + gpu_indices.emplace(next->gpu_bindings[index].metadata.canvas,index); + for (auto& command:next->commands) { + if (command.kind!=WEBSCENE_SCENE_COMMAND_GPU_IMAGE) continue; + const auto* node=document_.find_by_native_id(command.node_id); + if (!node) throw std::logic_error("GPU paint node missing"); + command.rgba=gpu_indices.at(node->canvas().backing.identity()); + } last_scene_build_nanoseconds_.store( static_cast(std::chrono::duration_cast( std::chrono::steady_clock::now() - scene_build_started).count()), std::memory_order_relaxed); + frame_trace_.mark("build-dom-end", last_input_sequence_); + frame_trace_.mark("metadata-start", last_input_sequence_); dom_nodes_.store(document_.node_count(), std::memory_order_relaxed); layout_passes_.store(document_.layout_passes(), std::memory_order_relaxed); iframe_nodes_.store(document_.count_tag("iframe"), std::memory_order_relaxed); @@ -163,18 +197,28 @@ std::lock_guard lock(canvas_layout_mutex_); document_.build_canvas_layouts(canvas_layouts_); } - std::vector all_layers; - std::vector all_canvas_commands; - std::vector all_canvas_strings; - std::vector all_canvas_string_bytes; + auto& all_layers=canvas_layers_scratch_; + frame_trace_.mark("metadata-end", last_input_sequence_); + frame_trace_.mark("build-canvas-start", last_input_sequence_); + auto& all_canvas_commands=canvas_commands_scratch_; + auto& all_canvas_strings=canvas_strings_scratch_; + auto& all_canvas_string_bytes=canvas_string_bytes_scratch_; document_.build_canvas_display_lists( all_layers, all_canvas_commands, all_canvas_strings, all_canvas_string_bytes); + for (const auto& layer : all_layers) { + if (layer.command_count && all_canvas_commands[layer.command_offset].kind == WEBSCENE_CANVAS_COMMAND_RASTER_CHECKPOINT) + next->required_capabilities |= WEBSCENE_SCENE_CAPABILITY_CANVAS_CHECKPOINTS; + } + frame_trace_.mark("build-canvas-end", last_input_sequence_); uint64_t dom_hash = 1469598103934665603ULL; for (const auto& command : next->commands) { + // Compute from the full paint stream before unchanged DOM + // commands are removed from an incremental scene below. + next->required_capabilities |= scene_command_capabilities(command.kind); dom_hash = mix_hash(dom_hash, static_cast(command.kind) << 32U | command.flags); dom_hash = mix_hash(dom_hash, static_cast(command.node_id) << 32U | command.rgba); dom_hash = mix_hash( @@ -203,11 +247,20 @@ for (const auto byte : next->canvas_string_bytes) { dom_hash = mix_hash(dom_hash, static_cast(byte)); } + for (const auto& binding:next->gpu_bindings) { + const auto m=binding.metadata; + dom_hash=mix_hash(dom_hash,m.canvas); + dom_hash=mix_hash(dom_hash,m.allocation); + dom_hash=mix_hash(dom_hash,m.allocation_generation); + dom_hash=mix_hash(dom_hash,m.content_serial); + } next->dom_hash = dom_hash; const auto viewport_changed = width != acknowledged_width || height != acknowledged_height; if (base_revision == 0 || viewport_changed || dom_hash != acknowledged_dom_hash) { scene_flags |= scene_flag_dom_replacement; - const auto localized = base_revision != 0 + const auto localized = next->gpu_images.empty() + && (!acknowledged_scene || acknowledged_scene->gpu_images.empty()) + && base_revision != 0 && !viewport_changed && acknowledged_scene != nullptr && append_localized_dom_damage( @@ -229,12 +282,15 @@ if (damage_width <= 0 || damage_height <= 0) return; next->damage_rects.push_back(webscene_damage_rect{x, y, damage_width, damage_height}); }; + size_t published_canvas_command_count = 0; for (auto layer : all_layers) { - const auto content_hash = canvas_layer_content_hash( + const auto old = acknowledged_layers.find(layer.node_id); + const auto [content_hash,command_hash,unchanged_prefix] = canvas_layer_content_hash( layer, all_canvas_commands, all_canvas_strings, - all_canvas_string_bytes); + all_canvas_string_bytes, + old==acknowledged_layers.end() ? nullptr : &old->second); const canvas_layer_version version{ layer.generation, content_hash, @@ -243,9 +299,9 @@ layer.x, layer.y, layer.width, - layer.height}; + layer.height, + command_hash}; next->full_layer_versions[layer.node_id] = version; - const auto old = acknowledged_layers.find(layer.node_id); if (base_revision != 0 && old != acknowledged_layers.end() && old->second.visually_equals(version)) { @@ -259,13 +315,19 @@ const auto source_command_offset = layer.command_offset; const auto source_string_offset = layer.string_offset; - layer.flags = canvas_layer_flag_replace; - layer.command_offset = static_cast(next->canvas_commands.size()); + layer.flags = canvas_layer_flag_replace + | (base_revision!=0 && unchanged_prefix ? WEBSCENE_CANVAS_LAYER_UNCHANGED_PREFIX : 0U); + layer.command_offset = static_cast(published_canvas_command_count); layer.string_offset = static_cast(next->canvas_strings.size()); - next->canvas_commands.insert( - next->canvas_commands.end(), - all_canvas_commands.begin() + source_command_offset, - all_canvas_commands.begin() + source_command_offset + layer.command_count); + // Compact changed layers in their existing allocation, then + // transfer ownership to the immutable publication. Unchanged + // layers can leave gaps, so overlapping ranges require memmove. + if (layer.command_count != 0 && published_canvas_command_count != source_command_offset) { + std::memmove(all_canvas_commands.data() + published_canvas_command_count, + all_canvas_commands.data() + source_command_offset, + static_cast(layer.command_count) * sizeof(webscene_canvas_command)); + } + published_canvas_command_count += layer.command_count; for (uint32_t index = 0; index < layer.string_count; ++index) { const auto& source_string = all_canvas_strings[source_string_offset + index]; const auto byte_offset = static_cast(next->canvas_string_bytes.size()); @@ -279,6 +341,8 @@ } next->canvas_layers.push_back(layer); } + all_canvas_commands.resize(published_canvas_command_count); + next->canvas_commands.swap(all_canvas_commands); for (const auto& [node_id, old] : acknowledged_layers) { if (next->full_layer_versions.contains(node_id)) continue; append_damage(old.x, old.y, old.width, old.height); @@ -365,16 +429,78 @@ mix_hash( mix_hash(hash, std::bit_cast(width)), std::bit_cast(height))}; + next->captured_generation=document_.scene_generation(); + return commit_captured_scene(std::move(next),publication_started); + } + + publication_result commit_captured_scene(std::shared_ptr next, + std::chrono::steady_clock::time_point publication_started) + { + // Staging retains one exact CPU scene and its matching GPU dependencies. + // A later live DOM mutation cannot alter this capture. + if(next->header.viewport_width!=static_cast(viewport_width_) + || next->header.viewport_height!=static_cast(viewport_height_)) { + staged_scene_.reset();return publication_result::deferred; + } + // Inspect every dependency before waiting on any one of them. A later + // failed/reset canvas invalidates the capture even if an earlier GPU + // producer is still pending; producer ownership still guards its writes. + for(const auto& binding:next->gpu_bindings) { + if(!document_.validate_gpu_canvas_binding(binding)) { + staged_scene_.reset();return publication_result::deferred; + } + if(binding.pending && binding.pending->state()==webscene_gpu_image_snapshot::status::failed) { + staged_scene_.reset(); + set_last_error("GPU scene producer failed; retaining the last complete scene"); + return publication_result::discarded; + } + } + for(size_t index=0;indexgpu_bindings.size();++index) { + const auto& binding=next->gpu_bindings[index]; + if(!next->gpu_images[index]) { + next->gpu_images[index]=binding.pending && producer_gpu_wait_consumer_.load(std::memory_order_acquire) + ? binding.pending->resolve_with_gpu_waits() : binding.resolve(); + } + if(!next->gpu_images[index]) { + staged_scene_=std::move(next);return publication_result::deferred; + } + const auto actual=next->gpu_images[index]->value.describe(); + const auto& expected=binding.metadata; + if(actual.canvas!=expected.canvas || actual.allocation!=expected.allocation + || actual.allocation_generation!=expected.allocation_generation + || actual.content_serial!=expected.content_serial) { + staged_scene_.reset(); + set_last_error("Captured GPU output resolved to a different image version"); + return publication_result::discarded; + } + } + std::unique_lock acknowledgement_lock(acknowledgement_->mutex); + { + const auto limit=ordered_scene_consumer_.load(std::memory_order_acquire) ? 2U : 1U; + if(acknowledgement_->pending_scenes.size()>=limit) { + staged_scene_=std::move(next);return publication_result::deferred; + } + const auto predecessor=acknowledgement_->pending_scenes.empty() + ? acknowledgement_->revision : acknowledgement_->pending_scenes.back()->header.revision; + if(predecessor!=next->header.base_revision) { + staged_scene_.reset();return publication_result::deferred; + } + } + staged_scene_.reset(); + published_document_generation_=next->captured_generation; + next->gpu_bindings.clear(); + const auto revision=next->header.revision; + const auto width=next->header.viewport_width; + const auto height=next->header.viewport_height; + const auto sequence=next->header.consumed_input_sequence; const auto latest_scene_bytes = retained_scene_bytes(*next); latest_scene_bytes_.store(latest_scene_bytes, std::memory_order_relaxed); next->published_timestamp_nanoseconds = static_cast( std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()).count()); auto published_scene = std::shared_ptr(std::move(next)); - { - std::lock_guard lock(acknowledgement_->mutex); - acknowledgement_->pending_scenes.push_back(published_scene); - } + acknowledgement_->pending_scenes.push_back(published_scene); + acknowledgement_lock.unlock(); std::atomic_store_explicit( &latest_, std::move(published_scene), @@ -391,9 +517,9 @@ scene_published_callback_( scene_published_user_data_, revision, - last_input_sequence_, + sequence, width, height); } - return true; + return publication_result::published; } diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_scene_utils.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_scene_utils.inc index 30c02e7b9..11a93e6ef 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_scene_utils.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_scene_utils.inc @@ -4,16 +4,31 @@ uint64_t mix_hash(uint64_t hash, uint64_t value) return hash; } -uint64_t canvas_layer_content_hash( +struct canvas_content_hash_result { uint64_t content, commands; bool unchanged_prefix; }; + +canvas_content_hash_result canvas_layer_content_hash( const webscene_canvas_layer& layer, const std::vector& commands, const std::vector& strings, - const std::vector& string_bytes) + const std::vector& string_bytes, + const canvas_layer_version* previous) { auto hash = 1469598103934665603ULL; + size_t prefix_count=0; + bool unchanged_prefix=false; + if(previous && previous->generation==layer.generation + && previous->command_count<=layer.command_count && previous->string_count<=layer.string_count) { + // Native canvas writers only append finalized commands and intern new + // strings. Bitmap reset and full-overwrite compaction both advance the + // generation before clearing either array. Use the actual base canvas + // version, independently of whether that scene replaced its DOM data. + hash=previous->command_hash; + prefix_count=previous->command_count; + unchanged_prefix=true; + } const auto command_end = static_cast(layer.command_offset) + layer.command_count; - for (auto index = static_cast(layer.command_offset); + for (auto index = static_cast(layer.command_offset)+prefix_count; index < command_end; ++index) { const auto& command = commands[index]; @@ -29,6 +44,7 @@ uint64_t canvas_layer_content_hash( } } + const auto command_hash=hash; const auto string_end = static_cast(layer.string_offset) + layer.string_count; for (auto index = static_cast(layer.string_offset); @@ -46,7 +62,7 @@ uint64_t canvas_layer_content_hash( static_cast(string_bytes[byte_index])); } } - return hash; + return {hash,command_hash,unchanged_prefix}; } void store_maximum(std::atomic& target, uint64_t value) diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_worker.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_worker.inc index 4aee868cf..07249dc4e 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_worker.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_engine_worker.inc @@ -199,7 +199,7 @@ return enqueue_callback_v3(std::move(callback)); }, [this] { - wake_.notify_all(); + signal_worker(); }, &diagnostics_); runtime_->set_work_metrics_enabled( runtime_work_metrics_enabled_.load(std::memory_order_acquire)); @@ -209,6 +209,16 @@ address.data(), address.size(), css.data(), css.size()); }); } +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + if(webgpu_policy_callback_)runtime_->set_webgpu_policy(graphics_wake_,[this](const std::string& url) { + const auto decision=webgpu_policy_callback_(webgpu_policy_user_data_,url.data(),url.size()); +#if defined(_WIN32) + return decision==WEBSCENE_WEBGPU_DXGI ? webscene::graphics::webgpu_canvas_interop::dxgi : webscene::graphics::webgpu_canvas_interop::none; +#else + return decision==WEBSCENE_WEBGPU_IOSURFACE ? webscene::graphics::webgpu_canvas_interop::iosurface : webscene::graphics::webgpu_canvas_interop::none; +#endif + }); +#endif if (!runtime_->initialize()) { set_last_error(runtime_->last_error()); webscene_native::runtime_diagnostic failure; @@ -227,7 +237,7 @@ auto next_scene_publication = std::chrono::steady_clock::now() + std::chrono::milliseconds(16); bool scene_pending = false; - std::optional deferred_input; + std::optional deferred_input; struct pending_resize_frame_publication final { uint64_t sequence; std::chrono::steady_clock::time_point enqueued_at; @@ -239,6 +249,7 @@ std::optional frame_paced_pointer_move; uint64_t frame_paced_pointer_move_count = 0; uint64_t frame_paced_pointer_observed_after_frame = 0; + uint64_t last_pointer_dispatch_frame = 0; while (!token.stop_requested()) { // Avalonia observes every compositor boundary even while no V8 RAF // or CSS animation is active. Import that clock without treating @@ -253,7 +264,7 @@ static_cast( observed_host_timestamp_microseconds) / 1000.0); } - webscene_input_event event{}; + queued_input_event event{}; bool changed = checkpoint_requested_.exchange( false, std::memory_order_acq_rel); @@ -271,6 +282,7 @@ component_ready_.load(std::memory_order_relaxed); bool resize_applied = false; bool host_frame_applied = false; + changed = apply_canvas_checkpoint() || changed; #if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8) const auto refresh_preferred_color_scheme = [&] { if (!preferred_color_scheme_changed_.exchange( @@ -306,6 +318,7 @@ if (low_memory_requested_.exchange(false, std::memory_order_acq_rel) && runtime_ != nullptr) { runtime_->notify_low_memory(); + trim_scene_storage(); low_memory_notifications_.fetch_add(1, std::memory_order_relaxed); update_compilation_metrics(); } @@ -314,6 +327,7 @@ >= *hidden_low_memory_deadline && runtime_ != nullptr) { runtime_->notify_low_memory(); + trim_scene_storage(); low_memory_notifications_.fetch_add(1, std::memory_order_relaxed); hidden_low_memory_notifications_.fetch_add( 1, @@ -412,10 +426,16 @@ frame_paced_pointer_move.reset(); frame_paced_pointer_move_count = 0; frame_paced_pointer_observed_after_frame = 0; + last_pointer_dispatch_frame = observed_compositor_timestamp_microseconds_.load( + std::memory_order_acquire); + apply(pointer_move); + // Transfer frame demand from the in-flight pointer handler to + // any RAF it requested before clearing the pointer demand bit. + update_host_animation_frame_demand(); + pending_pointer_move_inputs_.fetch_sub(pointer_move_count, std::memory_order_release); frame_paced_pointer_pending_.store( false, std::memory_order_release); - apply(pointer_move); applied_pointer_move_inputs_.fetch_add( 1, std::memory_order_relaxed); @@ -455,7 +475,7 @@ std::memory_order_acquire); if (frame_paced_pointer_move.has_value() && compositor_timestamp_microseconds - > frame_paced_pointer_observed_after_frame) { + > std::max(frame_paced_pointer_observed_after_frame, last_pointer_dispatch_frame)) { apply_frame_paced_pointer_move(); ++applied_input_groups; continue; @@ -475,20 +495,26 @@ event.kind == WEBSCENE_INPUT_POINTER_MOVE && event.flags == frame_paced_pointer_move->flags && compositor_timestamp_microseconds - <= frame_paced_pointer_observed_after_frame; + <= std::max(frame_paced_pointer_observed_after_frame, last_pointer_dispatch_frame); if (compatible_pointer_move) { frame_paced_pointer_move = event; ++frame_paced_pointer_move_count; continue; } - // A button/key/wheel event is an ordering barrier. Deliver - // the latest preceding movement before that discrete input, - // even when both arrived within one display interval. - deferred_input = event; - apply_frame_paced_pointer_move(); - ++applied_input_groups; - continue; + // A frame queued at the already observed boundary does not + // release a newer pointer sample retained for the next one. + // Button/key/wheel events still preserve input ordering. + const auto already_observed_frame = event.kind == WEBSCENE_INPUT_FRAME + && std::isfinite(event.x) && event.x >= 0 + && event.x * 1000.0 < static_cast(std::max( + frame_paced_pointer_observed_after_frame, last_pointer_dispatch_frame)) + 1.0; + if (!already_observed_frame) { + deferred_input = event; + apply_frame_paced_pointer_move(); + ++applied_input_groups; + continue; + } } const auto frame_paced_pointer = @@ -497,8 +523,12 @@ if (frame_paced_pointer) { frame_paced_pointer_move = event; frame_paced_pointer_move_count = 1; + // Age the sample from host admission, not worker dequeue. + // A busy handler must not move an already queued sample into + // a later display interval. The dispatch boundary above still + // limits continuous pointer work to once per observed frame. frame_paced_pointer_observed_after_frame = - compositor_timestamp_microseconds; + event.observed_compositor_timestamp; retain_frame_paced_pointer_move(); continue; } @@ -535,6 +565,11 @@ return true; } if (candidate.kind == WEBSCENE_INPUT_POINTER_MOVE) { + // A frame/wheel prefix must not bypass the paced + // pointer lane. Retain this move for the next + // observed display boundary instead of dispatching + // a second expensive pointer handler in this RAF. + if (compositor_timestamp_microseconds != 0U) return false; if (pointer_move.has_value() && pointer_move->flags != candidate.flags) { return false; @@ -580,7 +615,7 @@ static_cast(accumulate(event)); uint64_t consumed_count = 1; - webscene_input_event next{}; + queued_input_event next{}; while (consumed_count < 256U && inputs_.try_pop(next)) { if (!accumulate(next)) { deferred_input = next; @@ -627,6 +662,8 @@ const auto aggregate_count = item.count; apply(aggregate); if (aggregate.kind == WEBSCENE_INPUT_POINTER_MOVE) { + update_host_animation_frame_demand(); + pending_pointer_move_inputs_.fetch_sub(aggregate_count, std::memory_order_release); applied_pointer_move_inputs_.fetch_add( 1, std::memory_order_relaxed); @@ -673,6 +710,8 @@ apply(event); consumed_inputs_.fetch_add(1, std::memory_order_relaxed); if (event.kind == WEBSCENE_INPUT_POINTER_MOVE) { + update_host_animation_frame_demand(); + pending_pointer_move_inputs_.fetch_sub(1, std::memory_order_release); applied_pointer_move_inputs_.fetch_add(1, std::memory_order_relaxed); } else if (event.kind == WEBSCENE_INPUT_WHEEL) { applied_wheel_inputs_.fetch_add(1, std::memory_order_relaxed); @@ -706,7 +745,8 @@ } #if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8) - if (runtime_ != nullptr && resize_applied && host_frame_applied) { + if (runtime_ != nullptr && host_frame_applied) { + frame_trace_.mark("raf-start", last_input_sequence_); // `signal_animation_frame` releases the callbacks belonging to // this rendering opportunity; execute that complete RAF batch // before publishing. Do not run unrelated resources/timers here, @@ -729,18 +769,21 @@ std::chrono::duration_cast( std::chrono::steady_clock::now() - animation_frame_batch_started).count()); - resize_frame_animation_callbacks_.fetch_add( - animation_frame_callbacks, - std::memory_order_relaxed); - total_resize_frame_animation_batch_nanoseconds_.fetch_add( - animation_frame_batch_nanoseconds, - std::memory_order_relaxed); - last_resize_frame_animation_batch_nanoseconds_.store( - animation_frame_batch_nanoseconds, - std::memory_order_relaxed); - store_maximum( - maximum_resize_frame_animation_batch_nanoseconds_, - animation_frame_batch_nanoseconds); + if (resize_applied) { + resize_frame_animation_callbacks_.fetch_add( + animation_frame_callbacks, + std::memory_order_relaxed); + total_resize_frame_animation_batch_nanoseconds_.fetch_add( + animation_frame_batch_nanoseconds, + std::memory_order_relaxed); + last_resize_frame_animation_batch_nanoseconds_.store( + animation_frame_batch_nanoseconds, + std::memory_order_relaxed); + store_maximum( + maximum_resize_frame_animation_batch_nanoseconds_, + animation_frame_batch_nanoseconds); + } + frame_trace_.mark("raf-end", last_input_sequence_); frame_scripts_executed_.store( runtime_->frame_scripts_executed(), std::memory_order_relaxed); @@ -752,14 +795,14 @@ } if (runtime_ != nullptr && runtime_->has_pending_tasks() - && !(resize_applied && host_frame_applied)) { + && !host_frame_applied) { // Resource discovery during component startup can produce many // immediately runnable stylesheet/script tasks. Processing one // and then sleeping for 2 ms added hundreds of milliseconds // that browsers do not impose. Drain a bounded batch while no // input/resize work is waiting; the time/count caps preserve a // render opportunity and keep interaction latency bounded. - // A paired live-resize RAF is already the current rendering + // An admitted host RAF is already the current rendering // opportunity. Arbitrary queued macrotasks run after that scene // is published instead of delaying the visual boundary. constexpr uint32_t maximum_task_batch = 32U; @@ -1084,17 +1127,26 @@ != starting_component_ready; scene_pending = scene_pending || changed; const auto now = std::chrono::steady_clock::now(); - // A resize and its host RAF are one browser rendering opportunity. - // Do not defer that completed boundary behind a producer-only phase: - // macOS live resize may not deliver another compositor callback until - // the following display slot. - const auto resize_frame_boundary = - resize_applied && host_frame_applied; + // Every host RAF is a display-driven rendering opportunity, including + // pointer-driven sidebar resize and pan. A separate producer timer + // must not postpone a completed batch until after its display slot. + // GPU completion and mailbox admission still gate publication. + const auto host_frame_boundary = host_frame_applied; if (scene_pending && host_visible_.load(std::memory_order_acquire) - && (resize_frame_boundary || now >= next_scene_publication)) { - if (publish_scene()) { - scene_pending = false; + && (host_frame_boundary || now >= next_scene_publication)) { + frame_trace_.mark("publish-start", last_input_sequence_); + const auto publication=publish_scene(); + frame_trace_.mark(publication == publication_result::deferred + ? "publish-deferred" : publication == publication_result::published + ? "publish-end" : "publish-discarded", last_input_sequence_); + if (publication!=publication_result::deferred) { + scene_pending = publication==publication_result::published + && document_.scene_generation()!=published_document_generation_; + if(publication==publication_result::discarded) { + next_scene_publication=now+std::chrono::milliseconds(16); + continue; + } if (pending_paired_resize_publication.has_value() && last_input_sequence_ >= pending_paired_resize_publication->sequence) { @@ -1130,7 +1182,7 @@ } else { next_scene_publication += scene_interval; } - if (resize_frame_boundary) { + if (host_frame_boundary) { next_scene_publication = now + scene_interval; } continue; @@ -1147,7 +1199,7 @@ } #endif - // Producers notify wake_ for immediate dispatch. Retain one display + // Producers signal worker_wake_ for immediate dispatch. Retain one display // interval as a safety watchdog for V8/platform task sources that // do not yet expose a notification edge. auto idle_wait = std::chrono::milliseconds(16); @@ -1156,23 +1208,20 @@ idle_wait = runtime_->recommended_idle_wait(idle_wait); } #endif - std::unique_lock lock(wake_mutex_); const auto collect_runtime_work_metrics = runtime_work_metrics_enabled_.load(std::memory_order_relaxed); if (collect_runtime_work_metrics) { worker_waits_.fetch_add(1, std::memory_order_relaxed); } - const auto signalled = wake_.wait_for( - lock, + const auto signalled = worker_wake_.wait_for( idle_wait, [this, &token, &deferred_input] { - // Every script-queue producer latches wake_pending_ after it + // Every script-queue producer latches the wake event after it // enqueues work. Do not inspect script_work_ while holding the // wake mutex: producers can signal while holding script_mutex_, // and taking the locks in the opposite order deadlocks shared // isolate startup. - return wake_pending_ - || token.stop_requested() + return token.stop_requested() || deferred_input.has_value() || !inputs_.empty() || resize_pending_.load(std::memory_order_acquire) @@ -1186,12 +1235,15 @@ } else if (collect_runtime_work_metrics) { worker_timeout_wakes_.fetch_add(1, std::memory_order_relaxed); } - wake_pending_ = false; } #if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8) #if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8_INSPECTOR) inspector_runtime_.store(nullptr, std::memory_order_release); +#endif +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + if (runtime_) runtime_->shutdown_graphics(); #endif runtime_.reset(); #endif + frame_trace_.dump(); } diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.cpp b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.cpp index e68b33e1e..ddb579904 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.cpp +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.cpp @@ -1,4 +1,21 @@ +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) +#include "media/media_session.h" +#include "media/audio_graph.h" +#include "media/decode_service.h" +#if defined(__APPLE__) +#include "graphics/iosurface_canvas_images.h" +#endif +#endif +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) +#include "graphics/platform_webgpu_canvas.h" +#endif #include "webscene_v8_runtime.h" +#include "webscene_frame_trace.h" +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) +#include "graphics/graphics_service.h" +#include "graphics/v8_webgpu_realm.h" +#include "graphics/v8_webgpu_constants.h" +#endif #include "webscene_runtime_diagnostics.h" #include "webscene_embed_fallback.h" @@ -209,6 +226,34 @@ void prewarm_v8_process() } struct v8_dom_runtime::implementation final { + webscene_frame_trace frame_trace; +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + bool graphics_transitioning{}; + bool graphics_delivering{}; + bool graphics_shutdown{}; + const std::thread::id graphics_thread = std::this_thread::get_id(); + std::unique_ptr graphics; + std::unique_ptr webgpu; + v8::Global webgpu_navigator; + v8::Global webgpu_dom_exception; + std::function webgpu_document_policy; + webscene::graphics::webgpu_canvas_interop webgpu_interop=webscene::graphics::webgpu_canvas_interop::none; + std::shared_ptr webgpu_wake; +#if (defined(__APPLE__) || defined(_WIN32)) + struct gpu_canvas_entry { + dom_node* node; + std::shared_ptr provider; + std::unique_ptr context; + bool bitmap_reset_awaiting_frame=false; + bool presentation_resize_pending=false; + uint64_t presentation_generation_floor=0; + }; + std::unordered_map gpu_canvases; + bool gpu_rendering_opportunity=false; + uint64_t gpu_canvas_timeline=webscene::graphics::new_owner_token(); +#endif + std::function graphics_deliver; +#endif #include "webscene_v8_runtime_state_types.inc" #if defined(WEBSCENE_NATIVE_ENGINE_WITH_V8_INSPECTOR) #include "webscene_v8_runtime_inspector.inc" @@ -218,7 +263,7 @@ struct v8_dom_runtime::implementation final { { prune_persistent_compilation_cache(); initialize_v8_process(); - if (std::getenv("WEBSCENE_V8_SHARED_ISOLATE") != nullptr) { + if (!force_dedicated_isolate && std::getenv("WEBSCENE_V8_SHARED_ISOLATE") != nullptr) { try { shared_isolate = acquire_shared_isolate(); } catch (const std::exception& exception) { @@ -227,9 +272,11 @@ struct v8_dom_runtime::implementation final { } isolate = shared_isolate == nullptr ? nullptr : shared_isolate->isolate; } else { - allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + allocator.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator()); v8::Isolate::CreateParams params; - params.array_buffer_allocator = allocator; + // Transferred backing stores can outlive the originating worker isolate. + // V8 retains this shared allocator until the final backing store is released. + params.array_buffer_allocator_shared = allocator; if (const auto maximum_heap_mib = unsigned_environment_value("WEBSCENE_V8_MAX_HEAP_MIB"); maximum_heap_mib.has_value() && *maximum_heap_mib > 0) { @@ -243,7 +290,7 @@ struct v8_dom_runtime::implementation final { configure_startup_snapshot(params); } catch (const std::exception& exception) { last_error = exception.what(); - delete allocator; + allocator.reset(); allocator = nullptr; return false; } @@ -260,6 +307,8 @@ struct v8_dom_runtime::implementation final { } isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); isolate->SetPromiseRejectCallback(promise_rejected); + isolate->SetHostInitializeImportMetaObjectCallback(initialize_import_meta); + isolate->SetHostImportModuleDynamicallyCallback(import_module_dynamically); #if defined(WEBSCENE_NATIVE_ENGINE_CERTIFICATION) if (profile_bindings || profile_resize_cpu) { cpu_profiler = v8::CpuProfiler::New(isolate); @@ -1226,6 +1275,13 @@ struct v8_dom_runtime::implementation final { body, content_type}; const auto local_context = info.GetIsolate()->GetCurrentContext(); + if(specifier.starts_with("blob:")) { + auto resolver=v8::Promise::Resolver::New(local_context).ToLocalChecked(); + auto found=self->object_url_binary.find(specifier); + if(found==self->object_url_binary.end()||found->second.origin!=resource_origin(base))resolver->Reject(local_context,v8::Exception::TypeError(js_string(info.GetIsolate(),"Blob URL is unavailable for this origin"))).Check(); + else {auto value=v8::Object::New(info.GetIsolate());auto bytes=v8::ArrayBuffer::New(info.GetIsolate(),found->second.bytes.size());if(!found->second.bytes.empty())std::memcpy(bytes->GetBackingStore()->Data(),found->second.bytes.data(),found->second.bytes.size());value->CreateDataProperty(local_context,js_string(info.GetIsolate(),"body"),bytes).Check();value->CreateDataProperty(local_context,js_string(info.GetIsolate(),"url"),js_dom_string(info.GetIsolate(),specifier)).Check();resolver->Resolve(local_context,value).Check();} + info.GetReturnValue().Set(resolver->GetPromise());return; + } if (self->pending_fetches.size() >= maximum_pending_fetches) { info.GetIsolate()->ThrowException(v8::Exception::Error( js_string(info.GetIsolate(), "Too many pending fetch requests"))); @@ -3151,7 +3207,7 @@ struct v8_dom_runtime::implementation final { install_console(local_context, global); install_host_bridge(local_context); - constexpr std::string_view crypto_source = R"JS( + constexpr std::string_view crypto_source_parts[] = {R"JS( class WebSceneBlob { constructor(parts = [], options = {}) { __webSceneRecordWebApi( @@ -3161,7 +3217,9 @@ struct v8_dom_runtime::implementation final { let size = 0; for (const part of parts) { let bytes; - if (part instanceof ArrayBuffer) { + if (part instanceof WebSceneBlob) { + bytes=part._bytes; + } else if (part instanceof ArrayBuffer) { bytes = new Uint8Array(part); } else if (ArrayBuffer.isView(part)) { bytes = new Uint8Array(part.buffer, part.byteOffset, part.byteLength); @@ -3182,6 +3240,9 @@ struct v8_dom_runtime::implementation final { this._text = Array.from(parts, String).join(''); } toString() { return this._text; } + text() { return Promise.resolve(new TextDecoder().decode(this._bytes)); } + arrayBuffer() { return Promise.resolve(this._bytes.slice().buffer); } + slice(start=0,end=this.size,type='') {return new WebSceneBlob([this._bytes.slice(start,end)],{type});} } class WebSceneURLSearchParams { constructor(init = null) { @@ -3291,13 +3352,45 @@ struct v8_dom_runtime::implementation final { } } class WebSceneFormData { - constructor(form = undefined) { + constructor(form = undefined, submitter = null) { __webSceneRecordWebApi( 'FormData.constructor', 'partially-supported', 'ordered string and Blob fields with multipart fetch serialization'); this._entries = []; - if (form !== undefined && form !== null) { - throw new TypeError('Constructing FormData from a form is not yet supported'); + if (form !== undefined) { + if (!(form instanceof HTMLFormElement)) throw new TypeError('FormData requires an HTMLFormElement'); + const isSubmit = control => control && ((control.tagName === 'BUTTON' && (!control.type || control.type === 'submit')) || + )JS", R"JS( + (control.tagName === 'INPUT' && ['submit', 'image'].includes(control.type))); + if (submitter !== null) { + if (!(submitter instanceof HTMLElement) || !isSubmit(submitter)) throw new TypeError('FormData submitter must be a submit button'); + if (submitter.form !== form) throw new DOMException('Submitter belongs to another form', 'NotFoundError'); + } + const root = form.getRootNode(); + for (const control of root.querySelectorAll('input,select,textarea,button')) { + if (control.form !== form || control.matches(':disabled') || control.closest('datalist')) continue; + const tag = control.tagName; + const type = String(control.type || (tag === 'BUTTON' ? 'submit' : 'text')).toLowerCase(); + if ((tag === 'BUTTON' || ['submit', 'image', 'reset', 'button'].includes(type)) && control !== submitter) continue; + const name = control.getAttribute('name') || ''; + if (type === 'image') { + this.append(name ? name + '.x' : 'x', '0'); + this.append(name ? name + '.y' : 'y', '0'); + continue; + } + if (!name || (['checkbox', 'radio'].includes(type) && !control.checked)) continue; + if (tag === 'SELECT') { + for (const option of control.options) { + if (option.selected && !option.matches(':disabled')) this.append(name, option.value); + } + } else if (type === 'file') { + throw new TypeError('File controls in FormData are not yet supported'); + } else { + const value = type === 'hidden' && name === '_charset_' ? 'UTF-8' : + ['checkbox', 'radio'].includes(type) && !control.hasAttribute('value') ? 'on' : control.value; + this.append(name, value); + } + } } } append(name, value, filename = undefined) { @@ -3388,7 +3481,7 @@ struct v8_dom_runtime::implementation final { } toJSON() { return this.toString(); } static createObjectURL(blob) { return __webSceneCreateObjectUrl(blob); } - static revokeObjectURL() {} + static revokeObjectURL(url) { __webSceneRevokeObjectUrl(String(url)); } } class WebSceneDOMException extends Error { constructor(message = '', name = 'Error') { @@ -3405,6 +3498,7 @@ struct v8_dom_runtime::implementation final { InUseAttributeError: 10, InvalidStateError: 11, SyntaxError: 12, + )JS", R"JS( InvalidModificationError: 13, NamespaceError: 14, InvalidAccessError: 15, @@ -3442,18 +3536,34 @@ struct v8_dom_runtime::implementation final { } }, configurable: true } }); - )JS"; + )JS"}; + std::string crypto_source; + for (const auto part : crypto_source_parts) crypto_source.append(part); auto crypto_script = v8::Script::Compile( local_context, js_string(isolate, std::string(crypto_source).c_str())).ToLocalChecked(); crypto_script->Run(local_context).ToLocalChecked(); + local_context->Global()->Set(local_context, js_string(isolate, "structuredClone"), + v8::Function::New(local_context, structured_clone, {}, 1).ToLocalChecked()).Check(); + auto worker_constructor=v8::Function::New(local_context, worker_construct, {}, 1).ToLocalChecked(); + v8::Local event_target,worker_prototype,event_prototype; + if(local_context->Global()->Get(local_context,js_string(isolate,"EventTarget")).ToLocal(&event_target) + &&event_target->IsFunction() + &&worker_constructor->Get(local_context,js_string(isolate,"prototype")).ToLocal(&worker_prototype) + &&event_target.As()->Get(local_context,js_string(isolate,"prototype")).ToLocal(&event_prototype)) + worker_prototype.As()->SetPrototype(local_context,event_prototype).FromMaybe(false); + local_context->Global()->Set(local_context, js_string(isolate, "Worker"),worker_constructor).Check(); install_clipboard_api(local_context); install_websocket_globals(local_context); install_editor_web_platform_globals(local_context); install_tree_walker_platform(local_context); install_custom_elements_platform(local_context); + local_context->Global()->Set(local_context,js_string(isolate,"__webSceneRevokeObjectUrl"),v8::Function::New(local_context,revoke_object_url).ToLocalChecked()).Check(); install_fetch_globals(local_context); install_intersection_observer_polyfill(local_context); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) + install_media_globals(local_context); +#endif } void install_fetch_globals(v8::Local local_context) @@ -3512,7 +3622,7 @@ struct v8_dom_runtime::implementation final { class WebSceneResponse { constructor(body = '', options = {}) { - this._body = String(body ?? ''); + this._body = body instanceof ArrayBuffer ? new Uint8Array(body.slice(0)) : ArrayBuffer.isView(body) ? new Uint8Array(body.buffer.slice(body.byteOffset,body.byteOffset+body.byteLength)) : body instanceof Blob ? body._bytes.slice() : new TextEncoder().encode(String(body ?? '')); this.bodyUsed = false; this.status = Number(options.status ?? 200); this.statusText = String(options.statusText ?? 'OK'); @@ -3527,11 +3637,19 @@ struct v8_dom_runtime::implementation final { return Promise.reject(new TypeError('Response body already used')); } this.bodyUsed = true; - return Promise.resolve(this._body); + return Promise.resolve(new TextDecoder().decode(this._body)); } json() { return this.text().then(value => JSON.parse(value)); } + arrayBuffer() { + if(this.bodyUsed)return Promise.reject(new TypeError('Response body already used')); + this.bodyUsed=true;return Promise.resolve(this._body.slice().buffer); + } + blob() { + if(this.bodyUsed)return Promise.reject(new TypeError('Response body already used')); + this.bodyUsed=true;return Promise.resolve(new Blob([this._body],{type:this.headers.get('content-type')||''})); + } clone() { if (this.bodyUsed) throw new TypeError('Response body already used'); return new WebSceneResponse(this._body, { @@ -3561,7 +3679,7 @@ struct v8_dom_runtime::implementation final { } } - function webSceneFetch(input, options = {}) { + function webSceneFetchInternal(input, options = {}) { const request = new WebSceneRequest(input, options); if ((request.method === 'GET' || request.method === 'HEAD') && request.body !== null) { @@ -3627,6 +3745,17 @@ struct v8_dom_runtime::implementation final { } } + function webSceneFetch(input, options = {}) { + const signal=options.signal ?? input?.signal; + if(!signal)return webSceneFetchInternal(input,options); + if(signal.aborted)return Promise.reject(signal.reason ?? new DOMException('Fetch aborted','AbortError')); + return new Promise((resolve,reject)=>{ + const abort=()=>reject(signal.reason ?? new DOMException('Fetch aborted','AbortError')); + signal.addEventListener('abort',abort,{once:true}); + webSceneFetchInternal(input,options).then(resolve,reject).finally(()=>signal.removeEventListener('abort',abort)); + }); + } + class WebSceneXMLHttpRequest { constructor() { this.readyState = 0; @@ -4095,6 +4224,10 @@ struct v8_dom_runtime::implementation final { info.GetReturnValue().Set(v8::True(info.GetIsolate())); } +#include "webscene_v8_runtime_clone.inc" +#include "webscene_v8_runtime_modules.inc" +#include "webscene_v8_runtime_workers.inc" +#include "webscene_v8_runtime_media.inc" #include "webscene_v8_runtime_navigation.inc" // Keep these fragments in one translation unit: their order and direct // visibility preserve the runtime's existing release code generation. @@ -4221,7 +4354,11 @@ v8_dom_runtime::~v8_dom_runtime() = default; bool v8_dom_runtime::initialize() { - return impl_->initialize(); + if(!impl_->initialize())return false; +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + if(impl_->webgpu_document_policy)impl_->webgpu_document_policy("about:blank"); +#endif + return true; } bool v8_dom_runtime::execute(const std::string& source, const std::string& document_name) @@ -4448,12 +4585,22 @@ uint64_t v8_dom_runtime::last_resize_observers_nanoseconds() const noexcept #endif } -bool v8_dom_runtime::dispatch_input(const webscene_input_event& event) +bool v8_dom_runtime::dispatch_input(const webscene_input_event& event, bool defer_cursor_update) { - return impl_->dispatch_input(event) + return impl_->dispatch_input(event, defer_cursor_update) && impl_->promote_pending_promise_error(); } +void v8_dom_runtime::refresh_pointer_cursor_after_layout() +{ + if (!impl_->pointer_cursor_update_pending) return; + impl_->pointer_cursor_update_pending = false; + auto* target = impl_->document.hit_test(impl_->document.body(), + static_cast(impl_->last_pointer_x), static_cast(impl_->last_pointer_y)); + impl_->current_cursor_kind_value = target == nullptr + ? WEBSCENE_CURSOR_DEFAULT : impl_->cursor_kind_for(*target); +} + bool v8_dom_runtime::dispatch_transition_events() { return impl_->dispatch_transition_events() @@ -4489,6 +4636,20 @@ void v8_dom_runtime::signal_animation_frame(double timestamp_ms) ? timestamp_ms : std::chrono::duration(now.time_since_epoch()).count(); impl_->last_animation_frame_timestamp_ms = timestamp; +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) + impl_->signal_media_presentation(timestamp); +#endif +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + // Admit a new RAF batch only when configured canvases can obtain storage. + // Existing current textures still need their rendering opportunity to end. + // Pending callbacks retain their sentinel until a later host frame; no wait + // or GPU work is performed here. + for(auto& [key,canvas]:impl_->gpu_canvases) + if(canvas.context->is_configured()&&!canvas.context->has_current_texture()&&!canvas.provider->can_acquire()) + return; + for(auto& [key,canvas]:impl_->gpu_canvases)canvas.bitmap_reset_awaiting_frame=false; + if(impl_->webgpu)impl_->gpu_rendering_opportunity=true; +#endif if (impl_->is_text_control(impl_->active_element) && impl_->active_element->mutable_form_control().input_focused) { const auto elapsed = std::max(0.0, timestamp - impl_->caret_blink_epoch_ms); @@ -4520,12 +4681,21 @@ bool v8_dom_runtime::pump_animation_frame_task() v8::HandleScope handle_scope(impl_->isolate); auto local_context = impl_->context.Get(impl_->isolate); v8::Context::Scope context_scope(local_context); - return impl_->drain_animation_frame_task() - && impl_->promote_pending_promise_error(); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) + impl_->drain_media(); +#endif + const bool result=impl_->drain_animation_frame_task()&&impl_->promote_pending_promise_error(); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + impl_->finish_gpu_rendering_opportunity(result); +#endif + return result; } bool v8_dom_runtime::has_pending_animation_frame_task() const noexcept { +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + if(impl_->gpu_rendering_opportunity)return true; +#endif return impl_->has_due_animation_frame_task(); } @@ -4534,6 +4704,15 @@ uint8_t v8_dom_runtime::host_animation_frame_demand() const noexcept auto demand = impl_->has_waiting_animation_frame_task() ? uint8_t{1U} : uint8_t{0U}; +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + for(const auto& [key,canvas]:impl_->gpu_canvases)if(canvas.context->has_current_texture()){demand|=1U;break;} +#endif +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) + // Media requires continuous host opportunities even while its JS RAF is + // already admitted/running. Otherwise that brief gap suppresses a refresh. + for (const auto& [key, binding] : impl_->media_bindings) + if (binding->control->playing.load()) { demand |= 1U; break; } +#endif if (impl_->is_text_control(impl_->active_element) && impl_->active_element->mutable_form_control().input_focused) { demand |= 4U; @@ -4541,6 +4720,144 @@ uint8_t v8_dom_runtime::host_animation_frame_demand() const noexcept return demand; } +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) +void v8_dom_runtime::shutdown_graphics() +{ + if (std::this_thread::get_id()!=impl_->graphics_thread) + throw std::logic_error("Graphics shutdown requires the runtime owner thread"); + if (impl_->graphics_transitioning || impl_->graphics_delivering) + throw std::logic_error("Graphics shutdown during completion delivery"); + if (impl_->graphics_shutdown) return; + impl_->graphics_shutdown=true; + if (!impl_->graphics) return; + if (!impl_->isolate || impl_->context.IsEmpty()) { + impl_->graphics.reset(); + impl_->graphics_deliver={}; + return; + } + auto isolate_locker=impl_->lock_shared_isolate(); + v8::Isolate::Scope isolate_scope(impl_->isolate); + v8::HandleScope handle_scope(impl_->isolate); + auto context=impl_->context.Get(impl_->isolate); + v8::Context::Scope context_scope(context); + impl_->retire_document_graphics(); +} + +void v8_dom_runtime::set_webgpu_policy(std::shared_ptr wake, + std::function policy) +{ + if(std::this_thread::get_id()!=impl_->graphics_thread||impl_->isolate) + throw std::logic_error("WebGPU policy must be installed on the owner thread before initialization"); + if(!wake||!policy)throw std::invalid_argument("WebGPU policy requires a wake and host decision callback"); + impl_->webgpu_document_policy=[this,wake=std::move(wake),policy=std::move(policy)](const std::string& url) { + const auto interop=policy(url); + #if defined(__APPLE__) || defined(_WIN32) + if(interop==webscene::graphics::platform_canvas_interop)install_webgpu(wake,true,interop); +#endif + }; +} + +bool v8_dom_runtime::install_webgpu(std::shared_ptr wake, + bool secure_context,webscene::graphics::webgpu_canvas_interop interop) +{ + if (!secure_context) return false; + if(std::this_thread::get_id()!=impl_->graphics_thread)throw std::logic_error("WebGPU installation requires the runtime owner thread"); + if (!impl_->isolate || impl_->context.IsEmpty()) throw std::logic_error("WebGPU requires an initialized runtime"); + auto isolate_locker=impl_->lock_shared_isolate(); + v8::Isolate::Scope isolate_scope(impl_->isolate); + v8::HandleScope handle_scope(impl_->isolate); + auto context=impl_->context.Get(impl_->isolate); + v8::Context::Scope context_scope(context); + auto& service=initialize_graphics(wake,[self=impl_.get()](auto record) { + if(self->webgpu)self->webgpu->complete(record); + }); + try { + auto global=context->Global();v8::Local navigator,exception; + if(!global->Get(context,js_string(impl_->isolate,"navigator")).ToLocal(&navigator)||!navigator->IsObject()|| + !global->Get(context,js_string(impl_->isolate,"DOMException")).ToLocal(&exception)||!exception->IsFunction()) + throw std::logic_error("WebGPU requires installed Navigator and DOMException"); + impl_->webgpu=std::make_unique(impl_->isolate,context,service, + exception.As(),interop, +#if defined(_WIN32) + wgpu::BackendType::D3D12, +#else + wgpu::BackendType::Undefined, +#endif + wgpu::TextureFormat::BGRA8Unorm +#if defined(WEBSCENE_NATIVE_ENGINE_GENERATED_DOM_BINDINGS) + ,impl_->event_target_template.Get(impl_->isolate),[self=impl_.get()](v8::Local object) { + if(self->next_standalone_event_target_id==UINT32_MAX)throw std::length_error("EventTarget identity exhausted"); + auto key=v8::Private::ForApi(self->isolate,js_string(self->isolate,"WebScene.EventTarget.identity")); + return object->SetPrivate(self->context.Get(self->isolate),key, + v8::Integer::NewFromUnsigned(self->isolate,self->next_standalone_event_target_id++)).FromMaybe(false); + } +#endif + ); + auto getter=v8::Function::New(context,[](const v8::FunctionCallbackInfo& info) { + info.GetReturnValue().Set(info.Data()); + },impl_->webgpu->object()).ToLocalChecked(); + if(!webscene::graphics::install_webgpu_flag_namespaces(impl_->isolate,context)) + throw std::runtime_error("WebGPU flag namespace installation failed"); + navigator.As()->SetAccessorProperty(js_string(impl_->isolate,"gpu"),getter); + impl_->webgpu_navigator.Reset(impl_->isolate,navigator.As()); + impl_->webgpu_dom_exception.Reset(impl_->isolate,exception.As()); + impl_->webgpu_interop=interop;impl_->webgpu_wake=std::move(wake); + return true; + } catch(...) { + impl_->webgpu.reset();impl_->graphics.reset();impl_->graphics_deliver={};throw; + } +} + +webscene::graphics::graphics_service& v8_dom_runtime::initialize_graphics( + std::shared_ptr wake, + std::function deliver) +{ + if (std::this_thread::get_id() != impl_->graphics_thread) + throw std::logic_error("Graphics initialization requires the runtime owner thread"); + if (impl_->graphics_shutdown) throw std::logic_error("Graphics runtime is shut down"); + if (impl_->graphics_transitioning) throw std::logic_error("Graphics document transition is in progress"); + if (impl_->graphics) throw std::logic_error("Graphics dispatcher already initialized"); + if (!deliver || !wake) throw std::invalid_argument("Graphics requires completion delivery and a safe wake signal"); + auto service = std::make_unique(std::move(wake)); + impl_->graphics_deliver = std::move(deliver); + impl_->graphics = std::move(service); + return *impl_->graphics; +} +#endif + +void v8_dom_runtime::update_gpu_presentation_images( + const std::vector>& images) +{ +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + for(auto& [key,entry]:impl_->gpu_canvases) { + auto& canvas=entry.node->mutable_canvas(); + std::shared_ptr retained; + if(entry.presentation_resize_pending && entry.context->is_configured() + && !canvas.gpu_image && !canvas.gpu_snapshot) { + for(const auto& image:images) + if(image->value.describe().canvas==canvas.backing.identity() + && image->value.describe().allocation_generation>=entry.presentation_generation_floor){retained=image;break;} + } + if(canvas.gpu_presentation_image!=retained) { + canvas.gpu_presentation_image=std::move(retained); + impl_->document.mark_scene_changed(); + } + } +#endif +} + +bool v8_dom_runtime::has_open_gpu_output() const +{ +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + if(impl_->gpu_rendering_opportunity)return true; + for(const auto& [key,canvas]:impl_->gpu_canvases) + if(canvas.context->has_current_texture() + || (canvas.bitmap_reset_awaiting_frame && !canvas.node->canvas().gpu_presentation_image && canvas.context->is_configured() + && impl_->has_waiting_animation_frame_task()))return true; +#endif + return false; +} + bool v8_dom_runtime::pump_task() { auto isolate_locker = impl_->lock_shared_isolate(); @@ -4548,16 +4865,34 @@ bool v8_dom_runtime::pump_task() v8::HandleScope handle_scope(impl_->isolate); auto local_context = impl_->context.Get(impl_->isolate); v8::Context::Scope context_scope(local_context); - return impl_->drain_tasks() - && impl_->promote_pending_promise_error(); + const bool result = impl_->drain_tasks() && impl_->promote_pending_promise_error(); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + impl_->finish_gpu_rendering_opportunity(result); +#endif + return result; } bool v8_dom_runtime::has_pending_tasks() const noexcept { +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) + if(impl_->media_work_ready.load(std::memory_order_acquire))return true; +#if defined(__APPLE__) + if(impl_->media_images_ready&&impl_->media_images_ready->ready.load(std::memory_order_acquire))return true; +#endif +#endif +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + if (impl_->graphics && impl_->graphics->has_ready_work()) return true; +#if (defined(__APPLE__) || defined(_WIN32)) + if(impl_->gpu_rendering_opportunity)return true; + for(const auto& [key,canvas]:impl_->gpu_canvases)if(canvas.provider->has_completed_retirements())return true; +#endif +#endif return impl_->has_pending_detached_dom_collection() || impl_->websocket_transport.has_pending_events() || !impl_->pending_window_messages.empty() + || impl_->has_worker_messages() || impl_->has_ready_fetch_task() + || !impl_->pending_dialog_close_events.empty() || !impl_->pending_programmatic_scroll_events.empty() || !impl_->pending_frame_hydrations.empty() || !impl_->connected_resources.empty() @@ -4570,6 +4905,9 @@ std::chrono::milliseconds v8_dom_runtime::recommended_idle_wait( { const auto now = std::chrono::steady_clock::now(); auto wait = maximum; +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + if (impl_->graphics) wait = impl_->graphics->recommended_idle_wait(wait); +#endif for (const auto& timer : impl_->timers) { // An unreleased requestAnimationFrame is woken by the host frame input, // not by wall-clock polling. diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.h b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.h index 81775e126..73ba5a1b4 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.h +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime.h @@ -10,6 +10,17 @@ #include #include +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) +namespace webscene::graphics { +class graphics_service; +struct completion_wake; +struct completion_record; +enum class webgpu_canvas_interop; +} +#endif + +struct webscene_gpu_image_lease_v3; + namespace webscene_native { class native_document; @@ -261,9 +272,13 @@ class v8_dom_runtime final { bool has_pending_inspector_tasks() const noexcept; bool dispatch_resize(); bool deliver_resize_observers(); + bool has_open_gpu_output() const; + void update_gpu_presentation_images(const std::vector>& images); bool refresh_media_environment(); bool set_visible(bool visible); - bool dispatch_input(const webscene_input_event& event); + bool dispatch_input(const webscene_input_event& event, bool defer_cursor_update = false); + // Worker-only: call after publication layout and ResizeObserver delivery. + void refresh_pointer_cursor_after_layout(); bool dispatch_transition_events(); uint32_t current_cursor_kind() const noexcept; void notify_low_memory(); @@ -271,6 +286,23 @@ class v8_dom_runtime final { bool pump_animation_frame_task(); bool has_pending_animation_frame_task() const noexcept; uint8_t host_animation_frame_demand() const noexcept; +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + // Native binding initialization, on the owning runtime thread. Install the + // completion dispatcher before issuing backend operations. No JS API is + // exposed merely by creating this service. + // Normal engine disposal: terminate records before releasing the context. + void shutdown_graphics(); + // Host-only opt-in, before application scripts. The host must establish the + // document's secure-context status and negotiate the presenter policy. + // Denied exposure does not initialize graphics. Reinstall after navigation. + void set_webgpu_policy(std::shared_ptr wake, + std::function policy); + bool install_webgpu(std::shared_ptr wake, + bool secure_context,webscene::graphics::webgpu_canvas_interop interop); + webscene::graphics::graphics_service& initialize_graphics( + std::shared_ptr wake, + std::function deliver); +#endif bool pump_task(); bool has_pending_tasks() const noexcept; std::chrono::milliseconds recommended_idle_wait( diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_audio.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_audio.inc new file mode 100644 index 000000000..51b355cc0 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_audio.inc @@ -0,0 +1,317 @@ +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) +struct audio_context_binding { + std::unique_ptr graph; + std::unordered_map sources; + std::unordered_map attached; +}; +std::unordered_map audio_contexts; +std::unordered_set media_audio_source_elements; +v8::Global audio_track_key; +uint32_t next_audio_context{1}, next_audio_track{1}; +std::unordered_map> audio_tracks; +// Recording adapters retain these native PCM consumers, independently of +// JS/context lifetime. Each track has its own cursor and stop state. + +std::unique_ptr media_default_audio; +std::unordered_map media_default_sources; +void start_default_media_audio(uint64_t key, media_binding &b) { + if (!b.frame.audio || !b.control->playing.load()) + return; + if (media_audio_source_elements.contains(key)) + return; + if (!media_default_audio) + media_default_audio = std::make_unique(); + if (!media_default_sources.contains(key)) { + auto id = media_default_audio->create(webscene::media::audio_graph::kind::source); + media_default_audio->set_source(id, b.frame.audio, b.control); + media_default_audio->connect(id, 0); + media_default_sources.emplace(key, id); + } + media_default_audio->resume(); +} +void release_default_media_audio(uint64_t key) { + auto found = media_default_sources.find(key); + if (found != media_default_sources.end()) { + media_default_audio->disconnect(found->second); + media_default_sources.erase(found); + if (media_default_sources.empty()) + media_default_audio.reset(); + } +} + +struct audio_decode_binding { + std::future result; + v8::Global realm; + v8::Global promise; +}; +std::vector audio_decodes; +std::unique_ptr audio_decoder; +void clear_audio_bindings() { + audio_track_key.Reset(); + audio_tracks.clear(); + media_audio_source_elements.clear(); + media_default_audio.reset(); + media_default_sources.clear(); + audio_decoder.reset(); + audio_decodes.clear(); + audio_contexts.clear(); +} +void refresh_audio_source(uint64_t key, media_binding &b) { + if (!b.frame.audio) + return; + for (auto &[id, c] : audio_contexts) { + auto found = c.sources.find(key); + if (found != c.sources.end() && c.attached[key] != b.frame.audio.get()) { + c.graph->set_source(found->second, b.frame.audio, b.control); + c.attached[key] = b.frame.audio.get(); + } + } +} +void drain_audio_decodes() { + for (size_t i = 0; i < audio_decodes.size();) { + if (audio_decodes[i].result.wait_for(std::chrono::seconds(0)) != std::future_status::ready) { + ++i; + continue; + } + auto job = std::move(audio_decodes[i]); + audio_decodes.erase(audio_decodes.begin() + i); + auto realm = job.realm.Get(isolate); + v8::Context::Scope scope(realm); + auto resolver = job.promise.Get(isolate); + try { + auto pcm = job.result.get(); + auto value = v8::Object::New(isolate); + value + ->CreateDataProperty(realm, js_string(isolate, "sampleRate"), + v8::Integer::NewFromUnsigned(isolate, pcm.sample_rate)) + .Check(); + auto channels = v8::Array::New(isolate, pcm.channels); + for (uint32_t ch = 0; ch < pcm.channels; ++ch) { + auto buffer = v8::ArrayBuffer::New(isolate, pcm.frames() * sizeof(float)); + auto data = static_cast(buffer->GetBackingStore()->Data()); + for (uint64_t f = 0; f < pcm.frames(); ++f) + data[f] = pcm.samples[f * pcm.channels + ch]; + channels->Set(realm, ch, v8::Float32Array::New(buffer, 0, pcm.frames())).Check(); + } + value->CreateDataProperty(realm, js_string(isolate, "channels"), channels).Check(); + resolver->Resolve(realm, value).Check(); + } catch (const std::exception &e) { + resolver->Reject(realm, v8::Exception::Error(js_string(isolate, e.what()))).Check(); + } + } +} +std::shared_ptr resolve_audio_track(v8::Local value) { + if (!value->IsObject() || audio_track_key.IsEmpty()) + throw std::invalid_argument("Native audio track required"); + auto realm = isolate->GetCurrentContext(); + v8::Local id; + if (!value.As()->GetPrivate(realm, audio_track_key.Get(isolate)).ToLocal(&id) || + !id->IsUint32()) + throw std::invalid_argument("Native audio track required"); + auto found = audio_tracks.find(id.As()->Value()); + if (found == audio_tracks.end()) + throw std::invalid_argument("Audio track has been released"); + return found->second; +} +static void audio_native_call(const v8::FunctionCallbackInfo &info) { + auto *self = current(info.GetIsolate()); + if (!self) + return; + auto realm = info.GetIsolate()->GetCurrentContext(); + try { + if (info.Length() < 1) + throw std::invalid_argument("Audio operation required"); + auto op = to_utf8(info.GetIsolate(), info[0]); + if (op == "trackBind" || op == "trackClone" || op == "trackStop" || op == "trackEnded" || + op == "trackEnabled") { + auto id = info[1]->Uint32Value(realm).FromMaybe(0); + auto found = self->audio_tracks.find(id); + if (found == self->audio_tracks.end()) + throw std::invalid_argument("Audio track unavailable"); + auto track = found->second; + if (op == "trackBind") { + if (info.Length() < 3 || !info[2]->IsObject()) + throw std::invalid_argument("Audio track object required"); + if (self->audio_track_key.IsEmpty()) + self->audio_track_key.Reset(info.GetIsolate(), v8::Private::New(info.GetIsolate())); + info[2] + .As() + ->SetPrivate(realm, self->audio_track_key.Get(info.GetIsolate()), + v8::Integer::NewFromUnsigned(info.GetIsolate(), id)) + .Check(); + return; + } + if (op == "trackStop") { + track->stop(); + return; + } + if (op == "trackEnded") { + info.GetReturnValue().Set(track->ended()); + return; + } + if (op == "trackEnabled") { + track->enabled = info[2]->BooleanValue(info.GetIsolate()); + return; + } + if (self->audio_tracks.size() >= 256) + throw std::length_error("Audio track limit"); + auto clone = self->next_audio_track++; + self->audio_tracks.emplace(clone, track->clone()); + info.GetReturnValue().Set(clone); + return; + } + if (op == "create") { + if (self->audio_contexts.size() >= 8) + throw std::length_error("Audio context limit"); + auto id = self->next_audio_context++; + auto rate = info.Length() > 1 ? info[1]->Uint32Value(realm).FromMaybe(48000) : 48000; + self->audio_contexts.emplace( + id, + audio_context_binding{std::make_unique(true, rate), {}, {}}); + info.GetReturnValue().Set(id); + return; + } + if (op == "decode") { + if (info.Length() < 2 || !info[1]->IsArrayBuffer()) + throw std::invalid_argument("decodeAudioData requires ArrayBuffer"); + if (self->audio_decodes.size() >= 8) + throw std::length_error("Audio decode queue full"); + if (!self->audio_decoder) + self->audio_decoder = std::make_unique([self] { + self->media_work_ready.store(true, std::memory_order_release); + if (self->runtime_work_available) + self->runtime_work_available(); + }); + auto buffer = info[1].As(); + if (buffer->WasDetached()) + throw std::invalid_argument("Detached audio data"); + auto bytes = buffer->GetBackingStore(); + if (bytes->ByteLength() > webscene::media::decode_limits{}.encoded_bytes) + throw std::length_error("Audio input too large"); + auto source = std::make_shared(); + auto p = static_cast(bytes->Data()); + if (bytes->ByteLength()) + source->bytes.assign(p, p + bytes->ByteLength()); + auto resolver = v8::Promise::Resolver::New(realm).ToLocalChecked(); + auto future = self->audio_decoder->audio( + source, {}, info.Length() > 2 ? info[2]->Uint32Value(realm).FromMaybe(0) : 0); + buffer->Detach(v8::Local()).Check(); + self->audio_decodes.push_back({std::move(future), + v8::Global(info.GetIsolate(), realm), + v8::Global(info.GetIsolate(), resolver)}); + info.GetReturnValue().Set(resolver->GetPromise()); + return; + } + if (info.Length() < 2) + throw std::invalid_argument("Audio context required"); + auto id = info[1]->Uint32Value(realm).FromMaybe(0); + auto found = self->audio_contexts.find(id); + if (found == self->audio_contexts.end()) + throw std::invalid_argument("Audio context unavailable"); + auto &c = found->second; + auto number = [&](int i, double fallback = 0.) { + return info.Length() > i ? info[i]->NumberValue(realm).FromMaybe(fallback) : fallback; + }; + if (op == "close") { + self->audio_contexts.erase(found); + return; + } + if (op == "resume") { + c.graph->resume(); + return; + } + if (op == "suspend") { + c.graph->suspend(); + return; + } + if (op == "time") { + info.GetReturnValue().Set(c.graph->time()); + return; + } + if (op == "node") { + auto kind = static_cast(static_cast(number(2))); + if (number(2) < 1 || number(2) > 4) + throw std::invalid_argument("Invalid audio node kind"); + info.GetReturnValue().Set(c.graph->create(kind)); + return; + } + if (op == "connect") { + c.graph->connect(number(2), number(3)); + return; + } + if (op == "disconnect") { + c.graph->disconnect(number(2)); + return; + } + if (op == "gain") { + c.graph->set_gain(number(2), number(3), number(4), number(5)); + return; + } + if (op == "capture") { + if (self->audio_tracks.size() >= 256) + throw std::length_error("Audio track limit"); + auto track = c.graph->capture(number(2)); + auto track_id = self->next_audio_track++; + self->audio_tracks.emplace(track_id, std::move(track)); + info.GetReturnValue().Set(track_id); + return; + } + if (op == "validateSource") { + if (info.Length() < 3 || !info[2]->IsObject()) + throw std::invalid_argument("Media element required"); + auto *element = unwrap_node(info[2].As()); + if (!element || (element->tag != "audio" && element->tag != "video")) + throw std::invalid_argument("Media element required"); + if (self->media_audio_source_elements.contains(self->wrapper_key(*element))) + throw std::invalid_argument("Media element already has an audio source node"); + return; + } + if (op == "source") { + if (info.Length() < 4 || !info[3]->IsObject()) + throw std::invalid_argument("Media source required"); + auto *element = unwrap_node(info[3].As()); + if (!element) + throw std::invalid_argument("Media source required"); + auto key = self->wrapper_key(*element); + if (self->media_audio_source_elements.contains(key)) + throw std::invalid_argument("Media element already has an audio source node"); + self->media_audio_source_elements.insert(key); + self->release_default_media_audio(key); + c.sources.emplace(key, number(2)); + auto media = self->media_bindings.find(key); + if (media != self->media_bindings.end() && media->second->frame.audio) { + c.graph->set_source(number(2), media->second->frame.audio, media->second->control); + c.attached[key] = media->second->frame.audio.get(); + } + return; + } + if (op == "samples") { + if (info.Length() < 4 || !info[3]->IsFloat32Array()) + throw std::invalid_argument("Analyser requires Float32Array"); + auto array = info[3].As(); + auto backing = array->Buffer()->GetBackingStore(); + if (array->Buffer()->WasDetached()) + throw std::invalid_argument("Detached analyser array"); + c.graph->analyser(number(2), std::span(reinterpret_cast( + static_cast(backing->Data()) + + array->ByteOffset()), + array->Length())); + return; + } + throw std::invalid_argument("Unknown audio operation"); + } catch (const std::exception &e) { + info.GetIsolate()->ThrowException(v8::Exception::TypeError(js_string(info.GetIsolate(), e.what()))); + } +} +void install_audio_globals(v8::Local realm) { + realm->Global() + ->Set(realm, js_string(isolate, "__websceneAudio"), + v8::Function::New(realm, audio_native_call).ToLocalChecked()) + .Check(); + constexpr std::string_view source = +#include "media/audio_platform.js.inc" + ; + auto script = v8::Script::Compile(realm, js_dom_string(isolate, std::string(source))).ToLocalChecked(); + script->Run(realm).ToLocalChecked(); +} +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_browser_apis.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_browser_apis.inc index dbe46e995..1a0a907f7 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_browser_apis.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_browser_apis.inc @@ -1472,6 +1472,9 @@ : generated_standalone_event_target_id(info.GetIsolate(), receiver); #endif auto* target = receiver.IsEmpty() || receiver_is_document +#if defined(WEBSCENE_NATIVE_ENGINE_GENERATED_DOM_BINDINGS) + || standalone_target_id.has_value() +#endif ? nullptr : unwrap_node(receiver); auto target_value = target != nullptr @@ -1925,11 +1928,19 @@ info.GetIsolate(), self->enqueue_host_request(local_context, request))); } + static void revoke_object_url(const v8::FunctionCallbackInfo& info) { + auto* self=current(info.GetIsolate());if(!self||!info.Length())return; + auto url=to_utf8(info.GetIsolate(),info[0]);auto found=self->object_url_binary.find(url); + if(found!=self->object_url_binary.end()&&found->second.origin==resource_origin(self->current_base_address())){ + self->object_url_binary.erase(found);self->object_urls.erase(url);self->object_url_download_payloads.erase(url);self->object_url_canvas_node_ids.erase(url); + } + } static void create_object_url(const v8::FunctionCallbackInfo& info) { auto* self = current(info.GetIsolate()); auto payload = info.Length() > 0 ? to_utf8(info.GetIsolate(), info[0]) : std::string{}; std::string download_payload; + std::string binary_payload; uint32_t canvas_node_id = 0; if (info.Length() > 0 && info[0]->IsObject()) { auto local_context = info.GetIsolate()->GetCurrentContext(); @@ -1957,6 +1968,7 @@ js_string(info.GetIsolate(), "type")).ToLocal(&type_value) ? to_utf8(info.GetIsolate(), type_value) : std::string{}; + if(view->ByteLength())binary_payload.assign(reinterpret_cast(bytes),view->ByteLength()); const auto is_textual = type.starts_with("text/") || type == "application/xhtml+xml" || type == "application/xml" @@ -1973,6 +1985,7 @@ } const auto url = "blob:webscene-native/" + std::to_string(self->next_object_url_id++); self->object_urls[url] = std::move(payload); + self->object_url_binary[url]={std::move(binary_payload),resource_origin(self->current_base_address())}; if (!download_payload.empty()) { self->object_url_download_payloads[url] = std::move(download_payload); } diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_cache_and_frames.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_cache_and_frames.inc index 0051520b7..bf92eeaf2 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_cache_and_frames.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_cache_and_frames.inc @@ -1605,13 +1605,44 @@ )JS"; constexpr std::string_view message_channel_source_suffix = R"JS( class WebSceneFormData { - constructor(form = undefined) { + constructor(form = undefined, submitter = null) { __webSceneRecordWebApi( 'FormData.constructor', 'partially-supported', 'ordered string and Blob fields with multipart fetch serialization'); this._entries = []; - if (form !== undefined && form !== null) { - throw new TypeError('Constructing FormData from a form is not yet supported'); + if (form !== undefined) { + if (!(form instanceof HTMLFormElement)) throw new TypeError('FormData requires an HTMLFormElement'); + const isSubmit = control => control && ((control.tagName === 'BUTTON' && (!control.type || control.type === 'submit')) || + (control.tagName === 'INPUT' && ['submit', 'image'].includes(control.type))); + if (submitter !== null) { + if (!(submitter instanceof HTMLElement) || !isSubmit(submitter)) throw new TypeError('FormData submitter must be a submit button'); + if (submitter.form !== form) throw new DOMException('Submitter belongs to another form', 'NotFoundError'); + } + const root = form.getRootNode(); + for (const control of root.querySelectorAll('input,select,textarea,button')) { + if (control.form !== form || control.matches(':disabled') || control.closest('datalist')) continue; + const tag = control.tagName; + const type = String(control.type || (tag === 'BUTTON' ? 'submit' : 'text')).toLowerCase(); + if ((tag === 'BUTTON' || ['submit', 'image', 'reset', 'button'].includes(type)) && control !== submitter) continue; + const name = control.getAttribute('name') || ''; + if (type === 'image') { + this.append(name ? name + '.x' : 'x', '0'); + this.append(name ? name + '.y' : 'y', '0'); + continue; + } + if (!name || (['checkbox', 'radio'].includes(type) && !control.checked)) continue; + if (tag === 'SELECT') { + for (const option of control.options) { + if (option.selected && !option.matches(':disabled')) this.append(name, option.value); + } + } else if (type === 'file') { + throw new TypeError('File controls in FormData are not yet supported'); + } else { + const value = type === 'hidden' && name === '_charset_' ? 'UTF-8' : + ['checkbox', 'radio'].includes(type) && !control.hasAttribute('value') ? 'on' : control.value; + this.append(name, value); + } + } } } append(name, value, filename = undefined) { @@ -2035,7 +2066,7 @@ } std::string source = script.code; std::shared_ptr immutable_source; - auto name = "webscene-frame-inline-" + std::to_string(script.index) + ".js"; + auto name = hydration.base_address + "#inline-script-" + std::to_string(script.index); if (!script.source.empty()) { if (!load_text_resource( script.source, @@ -2054,19 +2085,15 @@ auto document_value = local_frame_context->Global()->Get( local_frame_context, js_string(isolate, "document")).ToLocalChecked().As(); - set_current_script(local_frame_context, document_value, name); + if(script.module)document_value->Set(local_frame_context,js_string(isolate,"currentScript"),v8::Null(isolate)).Check(); + else set_current_script(local_frame_context, document_value, name); } std::string error; const auto script_source = immutable_source != nullptr ? immutable_source->view() : std::string_view(source); - if (!execute_in_context( - local_frame_context, - script_source, - name, - error, - true, - std::move(immutable_source))) { + if (!(script.module ? execute_module(local_frame_context,script_source,name,error) + : execute_in_context(local_frame_context,script_source,name,error,true,std::move(immutable_source)))) { frame_last_error_value = "Iframe script #" + std::to_string(script.index) + " failed: " + error; ++frame_script_error_count; diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_canvas.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_canvas.inc index d48e26d0d..b03d720c6 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_canvas.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_canvas.inc @@ -258,6 +258,7 @@ command.data.values[index++] = argument; } node.mutable_canvas().commands.push_back(command); + node.mutable_canvas().backing.publish_content(); } static void canvas_append_resource_command( @@ -277,6 +278,7 @@ command.data.values[index++] = argument; } node.mutable_canvas().commands.push_back(command); + node.mutable_canvas().backing.publish_content(); } static void canvas_append_line_dash_command( @@ -292,6 +294,7 @@ command.data.values[index + 1U] = segments[index]; } node.mutable_canvas().commands.push_back(command); + node.mutable_canvas().backing.publish_content(); } static uint32_t canvas_intern_string(dom_node& node, const std::string& value) @@ -2645,6 +2648,45 @@ js_string(isolate, "source-over")).Check(); } +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + void finish_gpu_rendering_opportunity(bool present) { + if(!gpu_rendering_opportunity||has_due_animation_frame_task())return; + gpu_rendering_opportunity=false; + for(auto& [key,canvas]:gpu_canvases) { + const auto submitted=canvas.context->has_current_texture(); + canvas.context->end_frame(present); + if(submitted) { + // A reset inside this RAF batch may occur after frame admission. + // Its replacement output now owns the publication dependency. + canvas.bitmap_reset_awaiting_frame=false; + canvas.presentation_resize_pending=false; + canvas.node->mutable_canvas().gpu_presentation_image.reset(); + auto& output=canvas.node->mutable_canvas().gpu_snapshot; + output.reset(); + if(present) { + auto ticket=canvas.provider->capture_latest_submission(); + if(ticket)output=std::make_shared(std::move(ticket)); + else { + const auto& backing=canvas.node->canvas().backing; + output=std::make_shared(webscene::graphics::image_metadata{ + backing.identity(),0,backing.allocation_generation(),backing.content_serial(), + 0,0,backing.width(),backing.height()}); + } + document.mark_scene_changed(); + } + } + } + publish_ready_gpu_canvases(); + } + void publish_ready_gpu_canvases() { + for(auto& [key,canvas]:gpu_canvases)while(auto image=canvas.provider->take_ready()) { + const auto metadata=image->describe();const auto& backing=canvas.node->canvas().backing; + if(metadata.canvas!=backing.identity()||metadata.allocation_generation!=backing.allocation_generation()||!backing.accepts_completed_content(metadata.content_serial))continue; + if(canvas.node->canvas().gpu_image&&canvas.node->canvas().gpu_image->value.describe().content_serial>metadata.content_serial)continue; + document.publish_gpu_canvas_image(*canvas.node,std::make_shared(std::move(*image))); + } + } +#endif static void canvas_get_context(const v8::FunctionCallbackInfo& info) { auto* self = current(info.GetIsolate()); @@ -2657,6 +2699,45 @@ const auto requested = info.Length() > 0 ? to_utf8(info.GetIsolate(), info[0]) : std::string{}; +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + if(requested=="webgpu"&&self->webgpu&&self->webgpu_interop==webscene::graphics::platform_canvas_interop&&!self->in_frame_context()) { + using namespace webscene::graphics; + auto& backing=node->mutable_canvas().backing; + if(backing.mode()!=canvas_context_mode::none&&backing.mode()!=canvas_context_mode::webgpu) { + info.GetReturnValue().SetNull();return; + } + const auto key=self->wrapper_key(*node); + const auto known=self->gpu_canvases.find(key); + if(known!=self->gpu_canvases.end()){info.GetReturnValue().Set(known->second.context->object());return;} + try { + auto context=info.GetIsolate()->GetCurrentContext(); + auto exception=self->webgpu_dom_exception.Get(info.GetIsolate()); + auto width=canvas_bitmap_dimension(*node,"width",300),height=canvas_bitmap_dimension(*node,"height",150); + if(backing.width()!=width||backing.height()!=height)backing.reset_bitmap(width,height); + auto provider=std::make_shared(64ULL*1024*1024,self->webgpu_wake); + auto host=make_platform_webgpu_canvas_host(provider,[self,node] { + auto& backing=node->mutable_canvas().backing;backing.publish_content(); + image_metadata metadata;metadata.canvas=backing.identity();metadata.allocation_generation=backing.allocation_generation(); + metadata.content_serial=backing.content_serial();metadata.producer_timeline=self->gpu_canvas_timeline;metadata.producer_value=metadata.content_serial; + return metadata; + }); + host.invalidate=[self,node] { + auto& canvas=node->mutable_canvas();canvas.backing.reset_bitmap(canvas.backing.width(),canvas.backing.height());canvas.gpu_image.reset();canvas.gpu_snapshot.reset();canvas.gpu_presentation_image.reset(); + const auto known=self->gpu_canvases.find(self->wrapper_key(*node)); + if(known!=self->gpu_canvases.end()) { + known->second.presentation_resize_pending=false; + known->second.presentation_generation_floor=canvas.backing.allocation_generation(); + } + self->document.mark_scene_changed(); + }; + auto controller=std::make_unique(info.GetIsolate(),context,info.This(),exception,width,height,std::move(host)); + auto object=controller->object(); + self->gpu_canvases.emplace(key,gpu_canvas_entry{node,std::move(provider),std::move(controller)}); + backing.claim_context(canvas_context_mode::webgpu); + info.GetReturnValue().Set(object);return; + }catch(const std::exception&){info.GetIsolate()->ThrowException(v8::Exception::Error(js_string(info.GetIsolate(),"WebGPU canvas initialization failed")));return;} + } +#endif if (requested != "2d") { self->record_feature( "canvas", @@ -2667,6 +2748,10 @@ info.GetReturnValue().Set(v8::Null(info.GetIsolate())); return; } + if (!node->mutable_canvas().backing.claim_context(webscene::graphics::canvas_context_mode::two_d)) { + info.GetReturnValue().Set(v8::Null(info.GetIsolate())); + return; + } self->record_feature( "canvas", "HTMLCanvasElement.getContext:2d", @@ -2688,6 +2773,10 @@ } auto local_context = info.GetIsolate()->GetCurrentContext(); auto result = v8::Object::New(info.GetIsolate()); + auto& backing=node->mutable_canvas().backing; + const auto width=canvas_bitmap_dimension(*node,"width",300); + const auto height=canvas_bitmap_dimension(*node,"height",150); + if (backing.width()!=width || backing.height()!=height) backing.reset_bitmap(width,height); auto state = std::make_unique(); state->node = node; auto* state_pointer = state.get(); diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_clone.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_clone.inc new file mode 100644 index 000000000..0188ae2cf --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_clone.inc @@ -0,0 +1,80 @@ + struct clone_packet { + std::vector bytes; + std::vector> transfers; + }; + struct clone_delegate : v8::ValueSerializer::Delegate { + const v8::FunctionCallbackInfo& info; + explicit clone_delegate(const v8::FunctionCallbackInfo& value) : info(value) {} + void ThrowDataCloneError(v8::Local message) override { + const auto text = to_utf8(info.GetIsolate(), message); + throw_dom_exception(info, text.c_str(), "DataCloneError"); + } + }; + static bool serialize_clone_impl(const v8::FunctionCallbackInfo& info, + v8::Local value, v8::Local transfer, clone_packet& packet) { + auto* isolate = info.GetIsolate(); auto realm = isolate->GetCurrentContext(); + clone_delegate delegate(info); + v8::ValueSerializer serializer(isolate, &delegate); + std::vector> buffers; + if (!transfer->IsUndefined()) { + if (!transfer->IsArray()) { + isolate->ThrowException(v8::Exception::TypeError(js_string(isolate, "transfer must be an array"))); return false; + } + auto list = transfer.As(); + for (uint32_t i = 0; i < list->Length(); ++i) { + v8::Local candidate; + if (!list->Get(realm, i).ToLocal(&candidate)) return false; + if (!candidate->IsArrayBuffer()) { + throw_dom_exception(info, "Unsupported transferable", "DataCloneError"); return false; + } + auto buffer = candidate.As(); + if (!buffer->IsDetachable() || buffer->WasDetached() + || std::find(buffers.begin(), buffers.end(), buffer) != buffers.end()) { + throw_dom_exception(info, "Invalid or duplicate transferable", "DataCloneError"); return false; + } + buffers.push_back(buffer); + serializer.TransferArrayBuffer(i, buffer); + } + } + serializer.WriteHeader(); + if (!serializer.WriteValue(realm, value).FromMaybe(false)) return false; + auto [data, size] = serializer.Release(); + std::unique_ptr memory(data, std::free); + packet.bytes.assign(data, data + size); + for (auto buffer : buffers) packet.transfers.push_back(buffer->GetBackingStore()); + // Serialization must succeed before ownership changes on the sender. + for (auto buffer : buffers) if (!buffer->Detach({}).FromMaybe(false)) return false; + return true; + } + static bool serialize_clone(const v8::FunctionCallbackInfo& info, + v8::Local value, v8::Local transfer, clone_packet& packet) { + try {return serialize_clone_impl(info,value,transfer,packet);} + catch(const std::exception& e){ + info.GetIsolate()->ThrowException(v8::Exception::RangeError(js_string(info.GetIsolate(),e.what()))); + return false; + } + } + static v8::MaybeLocal deserialize_clone(v8::Isolate* isolate, + v8::Local realm, const clone_packet& packet) { + v8::ValueDeserializer deserializer(isolate, packet.bytes.data(), packet.bytes.size()); + for (uint32_t i = 0; i < packet.transfers.size(); ++i) + deserializer.TransferArrayBuffer(i, v8::ArrayBuffer::New(isolate, packet.transfers[i])); + if (!deserializer.ReadHeader(realm).FromMaybe(false)) return {}; + return deserializer.ReadValue(realm); + } + static void structured_clone(const v8::FunctionCallbackInfo& info) { + if (info.Length() == 0) { + info.GetIsolate()->ThrowException(v8::Exception::TypeError(js_string(info.GetIsolate(), "structuredClone requires a value"))); return; + } + auto realm = info.GetIsolate()->GetCurrentContext(); + if(info.Length()>1&&!info[1]->IsNullOrUndefined()&&!info[1]->IsObject()){ + info.GetIsolate()->ThrowException(v8::Exception::TypeError(js_string(info.GetIsolate(),"StructuredSerializeOptions must be a dictionary")));return; + } + v8::Local transfer = v8::Undefined(info.GetIsolate()); + if (info.Length() > 1 && info[1]->IsObject() + && !info[1].As()->Get(realm, js_string(info.GetIsolate(), "transfer")).ToLocal(&transfer)) return; + clone_packet packet; + if (!serialize_clone(info, info[0], transfer, packet)) return; + v8::Local result; + if (deserialize_clone(info.GetIsolate(), realm, packet).ToLocal(&result)) info.GetReturnValue().Set(result); + } diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_css_cascade.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_css_cascade.inc index 719ee011e..0a8a4ef5e 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_css_cascade.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_css_cascade.inc @@ -2081,20 +2081,9 @@ // initial currentColor value. Keep that dependency deferred. color = 0; current_color = true; - auto remaining = value; - auto color_start = remaining.find("rgba("); - if (color_start == std::string::npos) color_start = remaining.find("rgb("); - if (color_start != std::string::npos) { - const auto color_end = remaining.find(')', color_start); - if (color_end != std::string::npos) { - color = native_document::parse_color( - remaining.substr(color_start, color_end - color_start + 1U)); - current_color = false; - remaining.erase(color_start, color_end - color_start + 1U); - } - } - std::istringstream stream(remaining); - for (std::string token; stream >> token;) { + // Functional colors are single CSS components. Splitting their + // arguments at spaces turns color-mix percentages into border widths. + for (const auto& token : split_transition_tokens(value)) { if (token == "none") { width = {}; color = 0; @@ -4016,6 +4005,7 @@ dom_node& node, std::string_view reason = {}) { + webscene_frame_trace::scope cascade_trace(frame_trace, "recascade-start", "recascade-end", 0); if (!is_connected(node)) return; auto* previous_cascade_root = const_cast(active_css_cascade_root); auto* node_cascade_root = css_cascade_root_for_node(node); diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_css_parsing.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_css_parsing.inc index 2b77912d9..5b345b1ba 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_css_parsing.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_css_parsing.inc @@ -548,7 +548,7 @@ bool attribute_requires_recascade(std::string_view name) const { if (name == "id" || name == "class" || name == "style" - || name == "type" || name == "hidden") return true; + || name == "type" || name == "hidden" || name == "open") return true; return css_attribute_dependencies.contains(std::string(name)); } diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_document.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_document.inc index 017a36030..85000112c 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_document.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_document.inc @@ -710,6 +710,7 @@ return; } node->attributes[name] = value; + if ((name == "width" || name == "height")) reset_canvas_backing_store(*self, *node); if (name == "id") node->id_attribute = value; else if (name == "class") node->class_name = value; else if (name == "value" && node->tag == "input") { @@ -778,6 +779,7 @@ : std::optional(previous->second); self->record_html_attribute_feature(name, "setAttributeNS"); node->attributes[name] = value; + if ((!namespace_uri || namespace_uri->empty()) && (name == "width" || name == "height")) reset_canvas_backing_store(*self, *node); if (name == "id") node->id_attribute = value; else if (name == "class") node->class_name = value; if (self->attribute_requires_recascade(name)) { @@ -807,6 +809,7 @@ const auto old_value = std::optional(node->attributes.at(name)); self->detach_attr(*node, name, *old_value); node->attributes.erase(name); + if ((name == "width" || name == "height")) reset_canvas_backing_store(*self, *node); if (name == "id") node->id_attribute.clear(); else if (name == "class") node->class_name.clear(); else if (name == "src" && node->tag == "img") { @@ -879,6 +882,7 @@ const auto old_value = std::optional(match->second); self->detach_attr(*node, name, *old_value); node->attributes.erase(name); + if ((!namespace_uri || namespace_uri->empty()) && (name == "width" || name == "height")) reset_canvas_backing_store(*self, *node); if (name == "id") node->id_attribute.clear(); else if (name == "class") node->class_name.clear(); else if (name == "src" && node->tag == "img") node->clear_replaced_image(); @@ -917,6 +921,7 @@ } if (force_present) { node->attributes[name] = {}; + if (name == "width" || name == "height") reset_canvas_backing_store(*self, *node); if (name == "id") node->id_attribute.clear(); else if (name == "class") node->class_name.clear(); self->notify_custom_element_attribute( @@ -924,6 +929,7 @@ } else { const auto old_value = std::optional(node->attributes.at(name)); node->attributes.erase(name); + if (name == "width" || name == "height") reset_canvas_backing_store(*self, *node); if (name == "id") node->id_attribute.clear(); else if (name == "class") node->class_name.clear(); self->notify_custom_element_attribute( @@ -989,8 +995,9 @@ result.push_back(frame_script{ extract_html_attribute(attributes, "src"), html.substr(open_end + 1U, close - open_end - 1U), - attributes.find("defer") != std::string::npos, - index++}); + attributes.find("defer") != std::string::npos || extract_html_attribute(attributes, "type") == "module", + index++, + extract_html_attribute(attributes, "type") == "module"}); cursor = close + 9U; } return result; @@ -1021,8 +1028,34 @@ } } + static std::vector parsed_inline_stylesheets(const dom_node& root) + { + std::vector result; + const auto visit = [&](const auto& self, const dom_node& node) -> void { + // Tree construction handles raw text, comments and template contents. + // Never scan script strings for markup belonging to another document. + if (node.tag == "style") { + auto text = node.text_content; + for (const auto* child : node.children) + if (child != nullptr && child->tag == "#text") text += child->text_content; + result.push_back(std::move(text)); + return; + } + if (node.tag == "template") return; + for (const auto* child : node.children) if (child != nullptr) self(self, *child); + }; + visit(visit, root); + return result; + } + static std::vector parse_inline_stylesheets(const std::string& html) { +#if defined(WEBSCENE_NATIVE_ENGINE_HTML5EVER) + native_document parsed; + const auto status = parse_html_document(parsed, parsed.body(), html); + if (!status) return {}; + return parsed_inline_stylesheets(parsed.body()); +#else std::vector result; size_t cursor = 0U; while (true) { @@ -1036,6 +1069,7 @@ cursor = close + 8U; } return result; +#endif } static std::string extract_element_contents(const std::string& html, const std::string& tag) @@ -1181,6 +1215,40 @@ local_context, js_string(isolate, "toString"), v8::Function::New(local_context, location_to_string).ToLocalChecked()).Check(); + std::string secure_host = host; + if (const auto at = secure_host.rfind('@'); at != std::string::npos) secure_host.erase(0, at + 1); + if (secure_host.starts_with("[")) { + const auto close = secure_host.find(']'); + secure_host = secure_host.substr(0, close == std::string::npos ? secure_host.size() : close + 1); + } else if (const auto colon = secure_host.find(':'); colon != std::string::npos) secure_host.resize(colon); + std::transform(secure_host.begin(), secure_host.end(), secure_host.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + bool loopback = secure_host == "localhost" || secure_host.ends_with(".localhost") + || secure_host == "[::1]"; + if (secure_host.starts_with("127.")) { + size_t start = 0, components = 0; + bool valid = true; + while (start < secure_host.size()) { + auto end = secure_host.find('.', start); + if (end == std::string::npos) end = secure_host.size(); + unsigned value = 0; + const auto parsed = std::from_chars(secure_host.data() + start, secure_host.data() + end, value); + if (parsed.ec != std::errc{} || parsed.ptr != secure_host.data() + end || value > 255) valid = false; + ++components; start = end + 1; + } + loopback = valid && components == 4; + } + const auto protocol = scheme_end == std::string::npos ? std::string{} : href.substr(0, scheme_end); + bool secure = protocol == "https" || protocol == "wss" || protocol == "file" + || ((protocol == "http" || protocol == "ws") && loopback); + // A trustworthy child cannot escape an insecure top-level ancestor. + if (!context.IsEmpty() && local_context != context.Get(isolate)) { + v8::Local parent_secure; + secure = secure && context.Get(isolate)->Global()->Get(context.Get(isolate), + js_string(isolate, "isSecureContext")).ToLocal(&parent_secure) && parent_secure->IsTrue(); + } + global->DefineOwnProperty(local_context, js_string(isolate, "isSecureContext"), + v8::Boolean::New(isolate, secure), v8::ReadOnly).Check(); global->Set(local_context, js_string(isolate, "location"), location).Check(); v8::Local document_value; if (global->Get( diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_dom_core.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_dom_core.inc index aa6b72367..f3e0367ea 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_dom_core.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_dom_core.inc @@ -26,6 +26,7 @@ void ensure_layout(const dom_node* reusable_client_geometry_subject = nullptr) { + webscene_frame_trace::scope trace(frame_trace, "layout-start", "layout-end", current_input_sequence); flush_pending_style_recascades(); if (!document.dirty()) return; if (reusable_client_geometry_subject != nullptr @@ -776,6 +777,11 @@ [&](const auto& task) { return task.target_context.Get(isolate) == local_context; }); + std::erase_if( + pending_dialog_close_events, + [&](const auto& task) { + return task.context.Get(isolate) == local_context; + }); std::erase_if( pending_programmatic_scroll_events, [&](const auto& task) { @@ -1459,6 +1465,8 @@ ? (node.namespace_uri() == dom_node::html_namespace_uri ? (node.tag == "slot" ? html_slot_element_template.Get(isolate) + : node.tag == "dialog" + ? html_dialog_element_template.Get(isolate) : node.tag == "form" ? html_form_element_template.Get(isolate) : node.tag == "select" @@ -1470,7 +1478,8 @@ : node.tag == "td" || node.tag == "th" ? html_table_cell_element_template.Get(isolate) : html_element_template.Get(isolate)) - : element_template.Get(isolate)) + : node.namespace_uri() == "http://www.w3.org/2000/svg" + ? svg_element_template.Get(isolate) : element_template.Get(isolate)) : node.kind == dom_node_kind::document_fragment ? (document.is_shadow_root(node) ? shadow_root_template.Get(isolate) @@ -1521,6 +1530,9 @@ js_string(isolate, "form"), get_form_owner).Check(); } +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) + attach_media_prototype(local_context,node,object); +#endif auto& wrapper = node_wrappers[key]; wrapper.Reset(isolate, object); if (!is_connected(node)) wrapper.SetWeak(); @@ -1634,6 +1646,7 @@ if (detached_document_roots.contains(ancestor->id)) return; } if (node.tag == "script") { + bool module_script = false; const auto authored_type = node.attributes.find("type"); if (authored_type != node.attributes.end()) { auto type = lower_html_name(authored_type->second); @@ -1642,6 +1655,7 @@ type = first == std::string::npos ? std::string{} : type.substr(first, last - first + 1U); + module_script = type == "module"; const auto executable = type.empty() || type == "module" || type == "text/javascript" || type == "application/javascript" @@ -1685,12 +1699,11 @@ wrapper).Check(); } std::string error; - const auto executed = execute_in_context( - resource_context, - source, - name, - error, - false); + if (module_script && !document_value.IsEmpty() && document_value->IsObject()) + document_value.As()->Set(resource_context,js_string(isolate,"currentScript"),v8::Null(isolate)).Check(); + const auto executed = module_script + ? execute_module(resource_context, source, name, error) + : execute_in_context(resource_context, source, name, error, false); if (!document_value.IsEmpty() && document_value->IsObject()) { document_value.As()->Set( resource_context, @@ -3283,6 +3296,8 @@ ? std::optional(state->owner->attributes.at(state->name)) : std::nullopt; state->owner->attributes[state->name] = text; + if (state->namespace_uri.empty() && (state->name == "width" || state->name == "height")) + reset_canvas_backing_store(*self, *state->owner); if (state->name == "id") state->owner->id_attribute = text; else if (state->name == "class") state->owner->class_name = text; if (state->name == "class") { @@ -3353,6 +3368,8 @@ ? std::optional(owner->attributes.at(state->name)) : std::nullopt; owner->attributes[state->name] = value; + if (state->namespace_uri.empty() && (state->name == "width" || state->name == "height")) + reset_canvas_backing_store(*self, *owner); if (state->name == "id") owner->id_attribute = value; else if (state->name == "class") owner->class_name = value; if (state->name == "class") { @@ -3390,6 +3407,8 @@ } const auto old_value = owner->attributes.at(state->name); owner->attributes.erase(state->name); + if (state->namespace_uri.empty() && (state->name == "width" || state->name == "height")) + reset_canvas_backing_store(*self, *owner); if (state->name == "id") owner->id_attribute.clear(); else if (state->name == "class") owner->class_name.clear(); state->value = old_value; diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_dom_properties.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_dom_properties.inc index b48dcef2c..c13e5ccb1 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_dom_properties.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_dom_properties.inc @@ -1471,11 +1471,12 @@ } } - static void advance_canvas_generation(dom_node& node) + static void advance_canvas_generation(dom_node& node, bool publish_backing = true) { mark_canvas_scene_changed(); auto& canvas = node.mutable_canvas(); ++canvas.generation; + if (publish_backing) canvas.backing.publish_content(); #if defined(WEBSCENE_NATIVE_ENGINE_CERTIFICATION) constexpr uint64_t retained_diagnostic_generations = 64U; if (canvas.generation <= retained_diagnostic_generations) return; @@ -1496,13 +1497,34 @@ element_dimension(*node, "width", node->tag == "canvas" ? 300 : node->layout.width))); } + static uint32_t canvas_bitmap_dimension(dom_node& node,const char* name,uint32_t fallback) + { + const auto value=element_dimension(node,name,fallback); + if (!std::isfinite(value)) return fallback; + return static_cast(std::clamp(value,0.0,static_cast(UINT32_MAX))); + } + static void reset_canvas_backing_store(implementation& self, dom_node& node) { if (node.tag != "canvas") return; auto& canvas = node.mutable_canvas(); canvas.rects.clear(); canvas.lines.clear(); - advance_canvas_generation(node); + canvas.backing.reset_bitmap(canvas_bitmap_dimension(node,"width",300),canvas_bitmap_dimension(node,"height",150)); + canvas.gpu_image.reset(); + canvas.gpu_snapshot.reset(); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + const auto gpu=self.gpu_canvases.find(self.wrapper_key(node)); + if(gpu!=self.gpu_canvases.end()) { + gpu->second.context->resize(canvas.backing.width(),canvas.backing.height()); + // ResizeObserver may reset the bitmap and queue a redraw for the + // next host RAF. Keep the preceding coherent scene until that + // opportunity, rather than publishing an intermediate blank canvas. + gpu->second.bitmap_reset_awaiting_frame=true; + gpu->second.presentation_resize_pending=true; + } +#endif + advance_canvas_generation(node,false); canvas.commands.clear(); canvas.strings.clear(); canvas.string_indices.clear(); @@ -1769,6 +1791,160 @@ set_boolean_attribute(value, info, "readonly"); } + static void open_dialog(const v8::FunctionCallbackInfo& info, bool modal) + { + auto* self = current(info.GetIsolate()); + auto* node = unwrap_node(info.This()); + if (node->attributes.contains("open")) { + if (self->document.is_modal_dialog(*node) != modal) + throw_dom_exception(info, "Dialog is already open in a different mode", "InvalidStateError"); + return; + } + if (modal && !self->is_connected(*node)) { + throw_dom_exception(info, "Modal dialog must be connected", "InvalidStateError"); + return; + } + webscene_input_event input{}; + bool prevented = false; + if (!self->dispatch_input_event_type(input, "beforetoggle", *node, &prevented) || prevented) return; + // A beforetoggle handler can open, detach or move this dialog. + if (node->attributes.contains("open") || (modal && !self->is_connected(*node))) return; + if (modal) { + auto* root = self->css_cascade_root_for_node(*node); + if (!root || !self->document.register_modal_dialog(*root, *node)) { + throw_dom_exception(info, "Modal dialog has no active document", "InvalidStateError"); + return; + } + } + if (!node->dialog_state) node->dialog_state = std::make_unique(); + node->dialog_state->previously_focused_id = self->active_element ? self->active_element->id : 0; + node->attributes["open"] = ""; + self->recascade_connected_subtree(*node); + self->ensure_layout(); // Opening changes the hidden subtree before focus selection. + // Dialog focus starts with autofocus, then a focusable descendant, + // otherwise the dialog itself. Do not change its authored tabindex. + dom_node* first = nullptr; + dom_node* autofocus = nullptr; + const auto visit = [&](const auto& recurse, dom_node& candidate) -> void { + if (candidate.attributes.contains("autofocus") + && (&candidate == node || self->is_programmatically_focusable(&candidate))) + if (!autofocus) autofocus = &candidate; + if (&candidate != node && !first && self->is_programmatically_focusable(&candidate)) first = &candidate; + for (auto* child : self->document.composed_children(candidate)) if (child) recurse(recurse, *child); + }; + visit(visit, *node); + if (self->is_connected(*node)) self->set_active_element(autofocus ? autofocus : first ? first : node, input); + } + + static void dialog_show(const v8::FunctionCallbackInfo& info) { open_dialog(info, false); } + static void dialog_show_modal(const v8::FunctionCallbackInfo& info) { open_dialog(info, true); } + + void close_dialog(dom_node& node, const std::optional& result) + { + if (!node.attributes.contains("open")) return; + const auto old_open = node.attributes.at("open"); + detach_attr(node, "open", old_open); + node.attributes.erase("open"); + document.unregister_modal_dialog(node); + if (result) { + if (!node.dialog_state) node.dialog_state = std::make_unique(); + node.dialog_state->return_value = *result; + } + recascade_connected_subtree(node); + if (node.dialog_state && node.dialog_state->previously_focused_id) { + auto* previous = document.find_by_native_id(node.dialog_state->previously_focused_id); + node.dialog_state->previously_focused_id = 0; + if (previous && is_connected(*previous) && is_programmatically_focusable(previous)) { + webscene_input_event input{}; + set_active_element(previous, input); + } + } + pending_dialog_close_events.push_back(pending_dialog_close_event{ + node.id, v8::Global(isolate, context_for_node(node)), + v8::Global(isolate, wrap_node(node))}); + } + + static bool dialog_close_argument( + const v8::FunctionCallbackInfo& info, std::optional& result) + { + if (info.Length() == 0 || info[0]->IsUndefined()) return true; + v8::Local converted; + if (!info[0]->ToString(info.GetIsolate()->GetCurrentContext()).ToLocal(&converted)) return false; + result = to_wtf8(info.GetIsolate(), converted); + return true; + } + + static void dialog_close(const v8::FunctionCallbackInfo& info) + { + auto* self = current(info.GetIsolate()); + auto* node = unwrap_node(info.This()); + std::optional result; + if (!dialog_close_argument(info, result)) return; + self->close_dialog(*node, result); + } + + static void dialog_request_close(const v8::FunctionCallbackInfo& info) + { + auto* self = current(info.GetIsolate()); + auto* node = unwrap_node(info.This()); + std::optional result; + if (!dialog_close_argument(info, result) || !node->attributes.contains("open")) return; + webscene_input_event input{}; + bool prevented = false; + if (!self->dispatch_input_event_type(input, "cancel", *node, &prevented) || prevented) return; + self->close_dialog(*node, result); + } + + static void get_dialog_open( + v8::Local, const v8::PropertyCallbackInfo& info) + { + auto* node = unwrap_node(info.Holder()); + if (node == nullptr) return; + info.GetReturnValue().Set(node->attributes.contains("open")); + } + + static void set_dialog_open( + v8::Local, v8::Local value, + const v8::PropertyCallbackInfo& info) + { + set_boolean_attribute(value, info, "open"); + } + + static void get_dialog_return_value( + v8::Local, const v8::PropertyCallbackInfo& info) + { + auto* node = unwrap_node(info.Holder()); + if (node == nullptr) return; + info.GetReturnValue().Set(js_dom_string(info.GetIsolate(), + node->dialog_state == nullptr ? std::string{} : node->dialog_state->return_value)); + } + + static void set_dialog_return_value( + v8::Local, v8::Local value, + const v8::PropertyCallbackInfo& info) + { + auto* node = unwrap_node(info.Holder()); + if (node == nullptr) return; + v8::Local converted; + if (!value->ToString(info.GetIsolate()->GetCurrentContext()).ToLocal(&converted)) return; + auto result = to_wtf8(info.GetIsolate(), converted); + if (node->dialog_state == nullptr) node->dialog_state = std::make_unique(); + node->dialog_state->return_value = std::move(result); + } + + static void get_inert( + v8::Local, const v8::PropertyCallbackInfo& info) + { + get_boolean_attribute(info, "inert"); + } + + static void set_inert( + v8::Local, v8::Local value, + const v8::PropertyCallbackInfo& info) + { + set_boolean_attribute(value, info, "inert"); + } + static void get_hidden( v8::Local, const v8::PropertyCallbackInfo& info) diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_lifecycle.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_lifecycle.inc index 97b2c8b9a..590054c50 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_lifecycle.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_lifecycle.inc @@ -49,6 +49,31 @@ ~implementation() { + stop_workers(); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) + clear_media_bindings(); + media_initializer_key.Reset(); +#endif + dedicated_workers.clear(); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + // Stop native publication before releasing the dispatcher and isolate. + // Context disposal discards its JS promises; no late callback may enter it. + if(webgpu && isolate && !context.IsEmpty()) { + auto locker=lock_shared_isolate(); + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + v8::Context::Scope context_scope(context.Get(isolate)); +#if (defined(__APPLE__) || defined(_WIN32)) + gpu_canvases.clear(); +#endif + webgpu.reset(); + } + webgpu_dom_exception.Reset(); + webgpu_wake.reset(); + webgpu_navigator.Reset(); + graphics.reset(); + graphics_deliver = {}; +#endif // Preparation workers may still be waiting on a document or adding // newly discovered CSS/script prefetches. Join them before taking the // final snapshot of the resource prefetch table. @@ -117,6 +142,7 @@ media_query_lists.clear(); timers.clear(); pending_window_messages.clear(); + pending_dialog_close_events.clear(); pending_programmatic_scroll_events.clear(); pending_interop_promises.clear(); pending_callback_promises.clear(); @@ -170,7 +196,9 @@ element_template.Reset(); #if defined(WEBSCENE_NATIVE_ENGINE_GENERATED_DOM_BINDINGS) html_element_template.Reset(); + svg_element_template.Reset(); html_table_cell_element_template.Reset(); + html_dialog_element_template.Reset(); html_form_element_template.Reset(); html_select_element_template.Reset(); html_script_element_template.Reset(); @@ -187,6 +215,7 @@ event_target_template.Reset(); #endif frame_context.Reset(); + module_map.clear(); context.Reset(); release_external_dom_memory_accounting(); #if defined(WEBSCENE_NATIVE_ENGINE_CERTIFICATION) @@ -199,7 +228,7 @@ isolate->Dispose(); } isolate = nullptr; - delete allocator; + allocator.reset(); allocator = nullptr; // The final shared owner disposes the isolate. Release the recursive // V8 lock first so its destructor never observes an already-disposed diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_media.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_media.inc new file mode 100644 index 000000000..2f044cb33 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_media.inc @@ -0,0 +1,341 @@ +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) +#if defined(__APPLE__) +struct media_image_wake final : webscene::graphics::completion_wake { + std::atomic ready{}; + std::shared_ptr host; + void signal() noexcept override { + ready.store(true, std::memory_order_release); + if (host) + host->signal(); + } +}; +std::shared_ptr media_images_ready; +#endif +struct media_binding { + std::unique_ptr session; + std::shared_ptr control = + std::make_shared(); + v8::Global realm; + v8::Global deliver; + v8::Global owner; +#if defined(__APPLE__) + std::unique_ptr images; + bool image_pending{}; +#endif + uint64_t generation{}, version{}; + webscene::media::media_session::snapshot frame; +}; +std::unordered_map> media_bindings; +v8::Global media_initializer_key; +uint64_t media_dom_generation = UINT64_MAX; +std::atomic media_work_ready{false}; +bool media_presentation_ready{}; +double media_host_offset{}, media_last_host_timestamp{-1}, media_presentation_time{}; +std::array media_refresh_intervals{}; +size_t media_refresh_count{}, media_refresh_index{}; +void signal_media_presentation(double timestamp_ms) { + if (media_bindings.empty()) return; + const auto now = std::chrono::duration( + std::chrono::steady_clock::now().time_since_epoch()).count(); + const auto host = timestamp_ms / 1000.0; + if (media_last_host_timestamp < 0 || host < media_last_host_timestamp || + host - media_last_host_timestamp > .25) + media_host_offset = now - host; + const auto interval = host - media_last_host_timestamp; + if (interval >= .004 && interval <= .05) { + media_refresh_intervals[media_refresh_index++ % media_refresh_intervals.size()] = interval; + media_refresh_count = std::min(media_refresh_count + 1, media_refresh_intervals.size()); + } + media_last_host_timestamp = host; + auto intervals = media_refresh_intervals; + std::sort(intervals.begin(), intervals.begin() + media_refresh_count); + const auto refresh = media_refresh_count ? intervals[(media_refresh_count - 1) / 2] : 1. / 60.; + // Map the host clock once, rather than incorporating JS/task latency into + // every sample. The host currently supplies a vsync timestamp, not verified + // physical scanout feedback; retain that distinction in qualification. + // Select by coverage of the upcoming refresh interval. Its midpoint + // avoids unstable boundary decisions when source/display rates match. + media_presentation_time = host + media_host_offset + refresh * .5; + media_presentation_ready = true; +} +void clear_media_bindings() { + clear_audio_bindings(); + media_dom_generation = UINT64_MAX; + media_bindings.clear(); + media_work_ready = false; + media_presentation_ready = false; + media_last_host_timestamp = -1; + media_refresh_count = media_refresh_index = 0; +#if defined(__APPLE__) + media_images_ready.reset(); +#endif +} +static void media_native_call(const v8::FunctionCallbackInfo &info) { + auto *self = current(info.GetIsolate()); + if (!self) + return; + try { + auto realm = info.GetIsolate()->GetCurrentContext(); + if (info.Length() < 2 || !info[1]->IsObject()) + throw std::invalid_argument("Media element required"); + auto *node = unwrap_node(info[1].As()); + if (!node || (node->tag != "video" && node->tag != "audio")) + throw std::invalid_argument("Media element required"); + auto key = self->wrapper_key(*node); + auto op = to_utf8(info.GetIsolate(), info[0]); + if (op == "brand") + return; + if (op == "videoSupported") { + info.GetReturnValue().Set(webscene::media::native_video_decode_available()); + return; + } + if (op == "release") { + auto old = self->media_bindings.find(key); + if (old != self->media_bindings.end()) + old->second->control->set(0, 1, false, 1, false); + self->release_default_media_audio(key); + self->media_bindings.erase(key); + node->mutable_canvas().gpu_image.reset(); + self->document.mark_scene_changed(); + return; + } + if (op == "load") { + if (info.Length() < 5 || !info[2]->IsArrayBuffer() || !info[4]->IsFunction()) + throw std::invalid_argument("Invalid media load"); + auto buffer = info[2].As(); + auto backing = buffer->GetBackingStore(); + if (backing->ByteLength() > webscene::media::decode_limits{}.encoded_bytes) + throw std::length_error("Media input too large"); + auto source = std::make_shared(); + if (backing->ByteLength()) { + auto p = static_cast(backing->Data()); + source->bytes.assign(p, p + backing->ByteLength()); + } + source->extension = to_utf8(info.GetIsolate(), info[3]); + self->media_bindings.erase(key); + if (self->media_bindings.size() >= 32) + throw std::length_error("Media session limit reached"); + auto binding = std::make_unique(); + binding->realm.Reset(info.GetIsolate(), realm); + binding->owner.Reset(info.GetIsolate(), info[1].As()); + binding->deliver.Reset(info.GetIsolate(), info[4].As()); +#if defined(__APPLE__) + if (!self->media_images_ready) { + self->media_images_ready = std::make_shared(); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + self->media_images_ready->host = self->webgpu_wake; +#endif + } + binding->images = std::make_unique( + 128ULL * 1024 * 1024, self->media_images_ready); +#endif + binding->session = std::make_unique([self] { + self->media_work_ready.store(true, std::memory_order_release); + if (self->runtime_work_available) + self->runtime_work_available(); + }); + binding->session->load(std::move(source), node->tag == "video"); + self->media_bindings.emplace(key, std::move(binding)); + return; + } + auto found = self->media_bindings.find(key); + if (found == self->media_bindings.end()) + return; + if (op == "time") { + info.GetReturnValue().Set(found->second->control->time()); + return; + } + if (op == "state") { + if (info.Length() < 7) + throw std::invalid_argument("Invalid media control"); + found->second->control->set( + info[2]->NumberValue(realm).FromMaybe(0), info[3]->NumberValue(realm).FromMaybe(1), + info[4]->BooleanValue(info.GetIsolate()), info[5]->NumberValue(realm).FromMaybe(1), + info[6]->BooleanValue(info.GetIsolate())); + self->start_default_media_audio(key, *found->second); + return; + } + if (op == "seek") { + if (info.Length() < 3) + throw std::invalid_argument("Seek time required"); + found->second->session->seek(info[2]->NumberValue(realm).FromMaybe(-1)); + return; + } + throw std::invalid_argument("Unknown media operation"); + } catch (const std::exception &e) { + info.GetIsolate()->ThrowException(v8::Exception::TypeError(js_string(info.GetIsolate(), e.what()))); + } +} +void synchronize_media_elements() { + if (media_dom_generation == document.scene_generation() || media_initializer_key.IsEmpty()) + return; + media_dom_generation = document.scene_generation(); + if (document.media_elements().empty()) + return; + auto realm = context.Get(isolate); + v8::Context::Scope scope(realm); + v8::Local initializer; + if (!realm->Global()->GetPrivate(realm, media_initializer_key.Get(isolate)).ToLocal(&initializer) || + !initializer->IsFunction()) + return; + // Registry is O(media elements), never a traversal of the application's + // DOM. Runs only on native content changes, including parser mutations. + auto registry = document.media_elements(); + std::vector elements(registry.begin(),registry.end()); + for (auto *node : elements) { + if (node->namespace_uri() != dom_node::html_namespace_uri) + continue; + auto object = wrap_node(*node); + v8::Local args[] = {object, v8::Boolean::New(isolate, is_connected(*node))}; + auto result = initializer.As()->Call(realm, realm->Global(), 2, args); + static_cast(result); + } +} +bool drain_media() { + const bool presentation = std::exchange(media_presentation_ready, false); + bool ready = media_work_ready.exchange(false, std::memory_order_acq_rel) || presentation; +#if defined(__APPLE__) + if (media_images_ready) + ready = media_images_ready->ready.exchange(false, std::memory_order_acq_rel) || ready; +#endif + if (!ready) + return false; + drain_audio_decodes(); + perform_microtask_checkpoint(); + // Callback may call load()/release(); collect keys then reacquire each binding. + std::vector keys; + for (auto &[key, binding] : media_bindings) + keys.push_back(key); + for (auto key : keys) { + auto found = media_bindings.find(key); + if (found == media_bindings.end()) + continue; + auto &b = *found->second; + auto frame = presentation && b.control->playing.load() + ? b.session->present(b.control->time_at(media_presentation_time)) + : b.session->read(); + if (!frame.version) + continue; + const bool changed = frame.generation != b.generation || frame.version != b.version; +#if defined(__APPLE__) + if (!changed && !b.image_pending) + continue; +#else + if (!changed) + continue; +#endif + if (presentation && std::getenv("WEBSCENE_MEDIA_TRACE")) { + std::fprintf(stderr, "media_present host=%.6f media=%.6f pts=%.6f selected=%llu dropped=%llu repeated=%llu changed=%d\n", + media_presentation_time, b.control->time_at(media_presentation_time), frame.video.timestamp, + static_cast(frame.selected), static_cast(frame.dropped), + static_cast(frame.repeated), changed ? 1 : 0); + } + if (changed) { + b.generation = frame.generation; + b.version = frame.version; + b.frame = std::move(frame); + refresh_audio_source(key, b); + } +#if defined(__APPLE__) + try { + if (b.frame.video.native_surface) { + auto *node = unwrap_node(b.owner.Get(isolate)); + auto &backing = node->mutable_canvas().backing; + backing.claim_context(webscene::graphics::canvas_context_mode::media); + if (backing.width() != b.frame.video.width || backing.height() != b.frame.video.height) { + backing.reset_bitmap(b.frame.video.width, b.frame.video.height); + document.mark_dirty(); + } else + backing.publish_content(); + webscene::graphics::image_metadata metadata; + metadata.canvas = backing.identity(); + metadata.allocation_generation = backing.allocation_generation(); + metadata.content_serial = backing.content_serial(); + metadata.producer_timeline = backing.identity(); + metadata.producer_value = metadata.content_serial; + metadata.width = backing.width(); + metadata.height = backing.height(); + metadata.format = webscene::graphics::image_format::bgra8_unorm; + metadata.alpha = webscene::graphics::image_alpha::opaque; + auto color = webscene::graphics::iosurface_color::adopt_bgra8( + static_cast(b.frame.video.native_surface.get()), + b.frame.video.native_surface); + b.image_pending = true; + if (auto image = b.images->adopt(metadata, std::move(color))) { + document.publish_gpu_canvas_image( + *node, std::make_shared(std::move(*image))); + b.image_pending = false; + } + } + } catch (const std::exception &error) { + b.image_pending = false; + b.frame.ready = false; + b.frame.error = error.what(); + b.frame.video = {}; + } +#endif + if (!changed) + continue; + auto realm = b.realm.Get(isolate); + v8::Context::Scope scope(realm); + auto value = v8::Object::New(isolate); + auto put = [&](const char *name, v8::Local data) { + value->CreateDataProperty(realm, js_string(isolate, name), data).Check(); + }; + put("seeking", v8::Boolean::New(isolate, b.frame.seeking)); + put("ready", v8::Boolean::New(isolate, b.frame.ready)); + put("error", js_string(isolate, b.frame.error.c_str())); + put("duration", v8::Number::New(isolate, b.frame.duration)); + put("width", v8::Integer::NewFromUnsigned(isolate, b.frame.video.width)); + put("height", v8::Integer::NewFromUnsigned(isolate, b.frame.video.height)); + auto callback = b.deliver.Get(isolate); + auto owner = b.owner.Get(isolate); + v8::Local args[] = {value}; + v8::TryCatch caught(isolate); + auto ignored = callback->Call(realm, owner, 1, args); + static_cast(ignored); + if (caught.HasCaught()) + last_error = describe_exception(caught, realm); + perform_microtask_checkpoint(); + } + return true; +} +void install_media_globals(v8::Local realm) { + realm->Global() + ->Set(realm, js_string(isolate, "__websceneMedia"), + v8::Function::New(realm, media_native_call).ToLocalChecked()) + .Check(); + constexpr std::string_view source = +#include "media/media_platform.js.inc" + ; + auto script = v8::Script::Compile(realm, js_dom_string(isolate, std::string(source))).ToLocalChecked(); + script->Run(realm).ToLocalChecked(); + auto init = realm->Global()->Get(realm, js_string(isolate, "__websceneInitializeMedia")).ToLocalChecked(); + if (media_initializer_key.IsEmpty()) + media_initializer_key.Reset(isolate, v8::Private::New(isolate)); + realm->Global()->SetPrivate(realm, media_initializer_key.Get(isolate), init).Check(); + realm->Global()->Delete(realm, js_string(isolate, "__websceneInitializeMedia")).Check(); + install_audio_globals(realm); +} +void attach_media_prototype(v8::Local realm, dom_node &node, v8::Local object) { + if (node.namespace_uri() != dom_node::html_namespace_uri || + (node.tag != "audio" && node.tag != "video") || media_initializer_key.IsEmpty()) + return; + v8::Local initializer, prototype; + if (!realm->Global()->GetPrivate(realm, media_initializer_key.Get(isolate)).ToLocal(&initializer) || + !initializer->IsFunction()) + return; + if (!initializer.As() + ->Get(realm, js_string(isolate, node.tag == "video" ? "videoPrototype" : "audioPrototype")) + .ToLocal(&prototype) || + !prototype->IsObject()) + return; + object->SetPrototype(realm, prototype).Check(); + v8::Local args[] = {object, v8::Boolean::New(isolate, is_connected(node))}; + auto result = initializer.As()->Call(realm, realm->Global(), 2, args); + static_cast(result); +} + +#endif + +#include "webscene_v8_runtime_audio.inc" diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_modules.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_modules.inc new file mode 100644 index 000000000..bb3370687 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_modules.inc @@ -0,0 +1,122 @@ + struct module_entry { + std::string url; + v8::Global realm; + v8::Global module; + }; + std::vector module_map; + + std::string module_url(v8::Local module) { + for (auto& entry : module_map) + if (entry.module.Get(isolate) == module) return entry.url; + return {}; + } + + static v8::MaybeLocal resolve_module( + v8::Local realm, v8::Local specifier, + v8::Local attributes, v8::Local referrer) { + auto* self = current(v8::Isolate::GetCurrent()); + if (!self) return {}; + if (attributes->Length()) { + v8::Isolate::GetCurrent()->ThrowException(v8::Exception::TypeError( + js_dom_string(v8::Isolate::GetCurrent(), "Import attributes are not supported"))); + return {}; + } + return self->fetch_module(realm, to_utf8(self->isolate, specifier), self->module_url(referrer)); + } + + v8::MaybeLocal compile_module(v8::Local realm, + std::string_view source, const std::string& url) { + for (auto& entry : module_map) + if (entry.url == url && entry.realm.Get(isolate) == realm) + return entry.module.Get(isolate); + v8::ScriptOrigin origin(js_dom_string(isolate, url), 0, 0, false, -1, {}, false, false, true); + v8::ScriptCompiler::Source input(js_dom_string(isolate, std::string(source)), origin); + v8::Local result; + if (!v8::ScriptCompiler::CompileModule(isolate, &input).ToLocal(&result)) return {}; + module_map.push_back({url, v8::Global(isolate, realm), + v8::Global(isolate, result)}); + return result; + } + + v8::MaybeLocal fetch_module(v8::Local realm, + const std::string& specifier, const std::string& base) { + if (!(specifier.starts_with("./") || specifier.starts_with("../") + || specifier.starts_with("/") || specifier.find(':') != std::string::npos)) { + isolate->ThrowException(v8::Exception::TypeError(js_dom_string(isolate, + "Unable to resolve bare module specifier: " + specifier))); + return {}; + } + auto url = resolve_resource_url(specifier, base); + for (auto& entry : module_map) + if (entry.url == url && entry.realm.Get(isolate) == realm) + return entry.module.Get(isolate); + std::string source, resolved; + if (!load_text_resource(url, {}, WEBSCENE_RESOURCE_SCRIPT, source, resolved)) { + isolate->ThrowException(v8::Exception::TypeError(js_dom_string(isolate, + "Unable to load module: " + url))); + return {}; + } + return compile_module(realm, source, resolved); + } + + static void initialize_import_meta(v8::Local realm, + v8::Local module, v8::Local meta) { + auto* self = current(v8::Isolate::GetCurrent()); + if (self) meta->CreateDataProperty(realm, js_dom_string(self->isolate, "url"), + js_dom_string(self->isolate, self->module_url(module))).Check(); + } + + v8::MaybeLocal evaluate_module(v8::Local realm, + v8::Local module) { + if (module->GetStatus() == v8::Module::kUninstantiated + && !module->InstantiateModule(realm, resolve_module).FromMaybe(false)) return {}; + if (module->GetStatus() == v8::Module::kErrored) { + isolate->ThrowException(module->GetException()); return {}; + } + return module->Evaluate(realm); + } + + static v8::MaybeLocal import_module_dynamically( + v8::Local realm, v8::Local, + v8::Local resource, v8::Local specifier, + v8::Local attributes) { + auto* isolate = v8::Isolate::GetCurrent(); + auto* self = current(isolate); + v8::Local resolver; + if (!v8::Promise::Resolver::New(realm).ToLocal(&resolver)) return {}; + v8::TryCatch caught(isolate); + v8::Local module; + v8::Local evaluation; + if (self && attributes->Length() == 0 + && self->fetch_module(realm, to_utf8(isolate, specifier), + to_utf8(isolate, resource)).ToLocal(&module) + && self->evaluate_module(realm, module).ToLocal(&evaluation)) { + auto fulfilled = v8::Function::New(realm, [](const v8::FunctionCallbackInfo& info) { + info.GetReturnValue().Set(info.Data()); + }, module->GetModuleNamespace()).ToLocalChecked(); + if (evaluation->IsPromise()) return evaluation.As()->Then(realm, fulfilled); + resolver->Resolve(realm, module->GetModuleNamespace()).FromMaybe(false); + } else { + auto error = caught.HasCaught() ? caught.Exception() + : v8::Exception::TypeError(js_dom_string(isolate, "Unable to import module")); + resolver->Reject(realm, error).FromMaybe(false); + } + return resolver->GetPromise(); + } + + bool execute_module(v8::Local realm, std::string_view source, + const std::string& url, std::string& error) { + v8::Context::Scope scope(realm); + v8::TryCatch caught(isolate); + v8::Local module; + v8::Local evaluation; + if (!compile_module(realm, source, url).ToLocal(&module) + || !evaluate_module(realm, module).ToLocal(&evaluation)) { + error = describe_reported_exception(caught, realm); return false; + } + perform_microtask_checkpoint(); + if (evaluation->IsPromise() && evaluation.As()->State() == v8::Promise::kRejected) { + error = to_utf8(isolate, evaluation.As()->Result()); return false; + } + return true; + } diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_navigation.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_navigation.inc index b6ffd2a7d..6ecf6e9fb 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_navigation.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_navigation.inc @@ -1,3 +1,39 @@ +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + void retire_document_graphics() + { + if (graphics_transitioning || graphics_delivering) throw std::logic_error("Reentrant graphics document transition"); + if (!graphics) return; + struct transition_guard { + bool& active; + explicit transition_guard(bool& value) : active(value) { active=true; } + ~transition_guard() { active=false; } + } guard(graphics_transitioning); + // Detach admission before cancellation delivery can run JS. The local + // service survives dispatch and closes native publication before reset. + if(!webgpu_navigator.IsEmpty()) { + webgpu_navigator.Get(isolate)->Delete(context.Get(isolate),js_string(isolate,"gpu")).FromMaybe(false); + webgpu_navigator.Reset(); + for(const auto* name:webscene::graphics::webgpu_flag_namespaces) + context.Get(isolate)->Global()->Delete(context.Get(isolate),js_string(isolate,name)).FromMaybe(false); + context.Get(isolate)->Global()->Delete(context.Get(isolate),js_string(isolate,"GPUDeviceLostInfo")).FromMaybe(false); + for(const auto* name:webscene::graphics::webgpu_error_names) + context.Get(isolate)->Global()->Delete(context.Get(isolate),js_string(isolate,name)).FromMaybe(false); + } +#if (defined(__APPLE__) || defined(_WIN32)) + gpu_canvases.clear();gpu_rendering_opportunity=false; +#endif + webgpu_dom_exception.Reset(); + webgpu_wake.reset(); + webgpu.reset(); + auto previous=std::move(graphics); + auto deliver=std::move(graphics_deliver); + graphics_deliver={}; + previous->close(); + previous->pump(deliver,std::numeric_limits::max()); + perform_microtask_checkpoint(); + } +#endif + bool execute(const std::string& source, const std::string& document_name) { auto isolate_locker = lock_shared_isolate(); @@ -20,6 +56,9 @@ const std::string& url, std::vector document_start_scripts) { +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + if (graphics_transitioning || graphics_delivering) throw std::logic_error("Navigation during graphics completion delivery"); +#endif current_document_start_scripts = std::move(document_start_scripts); std::string html; std::string resolved_url; @@ -55,7 +94,18 @@ v8::HandleScope handle_scope(isolate); auto local_context = context.Get(isolate); v8::Context::Scope context_scope(local_context); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + retire_document_graphics(); +#endif + stop_workers(); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) + clear_media_bindings(); +#endif + module_map.clear(); set_context_location(local_context, local_context->Global(), resolved_url); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + if(webgpu_document_policy)webgpu_document_policy(resolved_url); +#endif auto outer_document = document_object.Get(isolate); outer_document->Set( local_context, @@ -238,7 +288,11 @@ css_variables.clear(); important_css_variables.clear(); +#if defined(WEBSCENE_NATIVE_ENGINE_HTML5EVER) + for (auto stylesheet : parsed_inline_stylesheets(html_element)) { +#else for (auto stylesheet : parse_inline_stylesheets(html)) { +#endif add_stylesheet(std::move(stylesheet), document_base_address); } for (const auto& href : stylesheets) { @@ -323,13 +377,11 @@ const auto script_source = immutable_source != nullptr ? immutable_source->view() : std::string_view(source); - if (!execute_in_context( - local_context, - script_source, - name, - error, - true, - std::move(immutable_source))) { + if (script.module) outer_document->Set(local_context, js_string(isolate, "currentScript"), v8::Null(isolate)).Check(); + const bool executed = script.module + ? execute_module(local_context, script_source, name, error) + : execute_in_context(local_context, script_source, name, error, true, std::move(immutable_source)); + if (!executed) { frame_last_error_value = "Document script #" + std::to_string(script.index) + " failed: " + error; ++frame_script_error_count; diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_resources.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_resources.inc index 185b88d5e..c26ed020b 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_resources.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_resources.inc @@ -255,10 +255,9 @@ bool settled = false; if (result.loaded) { auto value = v8::Object::New(isolate); - value->Set( - task_context, - js_string(isolate, "body"), - js_dom_string(isolate, result.body)).Check(); + auto bytes=v8::ArrayBuffer::New(isolate,result.body.size()); + if(!result.body.empty())std::memcpy(bytes->GetBackingStore()->Data(),result.body.data(),result.body.size()); + value->Set(task_context,js_string(isolate,"body"),bytes).Check(); value->Set( task_context, js_string(isolate, "url"), @@ -746,13 +745,10 @@ const auto script_source = immutable_source != nullptr ? immutable_source->view() : std::string_view(source); - if (!execute_in_context( - local_context, - script_source, - script_name, - error, - false, - std::move(immutable_source))) { + const auto type = node.attributes.find("type"); + const bool module = type != node.attributes.end() && lower_html_name(type->second) == "module"; + if (!(module ? execute_module(local_context,script_source,script_name,error) + : execute_in_context(local_context,script_source,script_name,error,false,std::move(immutable_source)))) { frame_last_error_value = "Connected script failed: " + error; ++frame_script_error_count; return false; diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_state.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_state.inc index 088a047bc..92bc3706a 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_state.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_state.inc @@ -11,7 +11,7 @@ std::unordered_map pending_frame_preparations; std::shared_ptr shared_isolate; - v8::ArrayBuffer::Allocator* allocator{nullptr}; + std::shared_ptr allocator; v8::Isolate* isolate{nullptr}; #if defined(WEBSCENE_NATIVE_ENGINE_CERTIFICATION) v8::CpuProfiler* cpu_profiler{nullptr}; @@ -39,7 +39,9 @@ v8::Global element_template; #if defined(WEBSCENE_NATIVE_ENGINE_GENERATED_DOM_BINDINGS) v8::Global html_element_template; + v8::Global svg_element_template; v8::Global html_table_cell_element_template; + v8::Global html_dialog_element_template; v8::Global html_form_element_template; v8::Global html_select_element_template; v8::Global html_script_element_template; @@ -119,6 +121,7 @@ std::vector> media_query_lists; std::vector timers; std::deque pending_window_messages; + std::vector pending_dialog_close_events; std::vector pending_programmatic_scroll_events; native_websocket_transport websocket_transport; @@ -153,6 +156,8 @@ // the authored Blob text, while an activation needs the exact // byte payload and MIME type. Keep both representations under the same URL. std::unordered_map object_url_download_payloads; + struct binary_object_url {std::string bytes,origin;}; + std::unordered_map object_url_binary; // Canvas.toBlob() intentionally carries no encoded bytes. Preserve its // retained canvas identity across URL.createObjectURL() so an anchor // download can ask the desktop compositor for the exact PNG pixels. @@ -339,6 +344,7 @@ float scrollbar_drag_thumb_travel{0}; dom_node* hover_target{nullptr}; uint32_t current_cursor_kind_value{WEBSCENE_CURSOR_DEFAULT}; + bool pointer_cursor_update_pending{false}; dom_node* current_related_target{nullptr}; double pointer_down_x{0}; double pointer_down_y{0}; diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_state_types.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_state_types.inc index 63b51eb37..b893583e7 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_state_types.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_state_types.inc @@ -26,6 +26,12 @@ std::string source_origin; }; + struct pending_dialog_close_event final { + uint32_t target_id; + v8::Global context; + v8::Global target; + }; + struct pending_programmatic_scroll_event final { uint32_t target_id; v8::Global context; @@ -66,6 +72,7 @@ std::string code; bool defer{false}; size_t index{0}; + bool module{false}; }; struct css_declaration final { @@ -531,7 +538,7 @@ using css_index_string_set = std::unordered_set; }; struct shared_isolate_state final { - v8::ArrayBuffer::Allocator* allocator{nullptr}; + std::shared_ptr allocator; v8::Isolate* isolate{nullptr}; size_t slot_index{0U}; std::atomic active_contexts{0U}; @@ -554,7 +561,7 @@ using css_index_string_set = std::unordered_set; } isolate->Dispose(); } - delete allocator; + allocator.reset(); } }; @@ -821,9 +828,9 @@ using css_index_string_set = std::unordered_set; if (first_empty_slot == config.maximum_count) return {}; auto state = std::make_shared(); state->slot_index = first_empty_slot; - state->allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + state->allocator.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator()); v8::Isolate::CreateParams params; - params.array_buffer_allocator = state->allocator; + params.array_buffer_allocator_shared = state->allocator; if (const auto maximum_heap_mib = unsigned_environment_value("WEBSCENE_V8_MAX_HEAP_MIB"); maximum_heap_mib.has_value() && *maximum_heap_mib > 0) { diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_support.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_support.inc index d66d2fcf7..450f9a087 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_support.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_support.inc @@ -694,6 +694,7 @@ display_mode default_display_for_node(const dom_node& node) : display_mode::none; } if (node.attributes.contains("hidden")) return display_mode::none; + if (node.tag == "dialog" && !node.attributes.contains("open")) return display_mode::none; if (node.tag == "input") { const auto type = node.attributes.find("type"); if (type != node.attributes.end()) { diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_tasks.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_tasks.inc index 701812274..ab999bc06 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_tasks.inc +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_tasks.inc @@ -443,6 +443,23 @@ return true; } + bool drain_dialog_close_event_task() + { + auto task = std::move(pending_dialog_close_events.front()); + pending_dialog_close_events.erase(pending_dialog_close_events.begin()); + auto local_context = task.context.Get(isolate); + auto* target = document.find_by_native_id(task.target_id); + if (target == nullptr || local_context.IsEmpty()) return true; + v8::Context::Scope context_scope(local_context); + activate_css_cascade(local_context); + webscene_input_event input{}; + v8::TryCatch try_catch(isolate); + if (dispatch_input_event_type(input, "close", *target)) return true; + last_error = "Dialog close dispatch failed: " + + describe_reported_exception(try_catch, local_context); + return false; + } + void enqueue_programmatic_scroll_event(dom_node& target) { auto local_context = isolate->GetCurrentContext(); @@ -527,6 +544,30 @@ // compilation and optimization work. An embedder must service that // queue; otherwise hot application code remains on its initial tier. pump_v8_platform_tasks(); +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) + if (graphics && !graphics_delivering && graphics->has_ready_work()) { + struct delivery_guard { + bool& active; + explicit delivery_guard(bool& value) : active(value) { active=true; } + ~delivery_guard() { active=false; } + } guard(graphics_delivering); + // execute() also reaches this path after its script context scope + // has unwound. Enter the owning context explicitly for delivery. + auto graphics_context = context.Get(isolate); + v8::Context::Scope graphics_context_scope(graphics_context); + // Bound each batch and continue task arbitration to avoid starving DOM. + if (graphics->pump(graphics_deliver, 64)) + perform_microtask_checkpoint(); + } +#endif +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS) && (defined(__APPLE__) || defined(_WIN32)) + publish_ready_gpu_canvases(); +#endif +#if defined(WEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA) + synchronize_media_elements(); + if(drain_media())return true; +#endif + if (has_worker_messages()) return drain_worker_message(); if (websocket_transport.has_pending_events()) { return drain_websocket_event(); } @@ -536,6 +577,9 @@ if (has_ready_fetch_task()) { return drain_fetch_task(); } + if (!pending_dialog_close_events.empty()) { + return drain_dialog_close_event_task(); + } if (!pending_programmatic_scroll_events.empty()) { return drain_programmatic_scroll_event_task(); } @@ -1385,6 +1429,7 @@ bool* default_prevented = nullptr, dom_node* submitter = nullptr) { + webscene_frame_trace::scope trace(frame_trace, "event-start", "event-end", input.sequence); const auto previous_context = isolate->GetCurrentContext(); auto local_context = context_for_node(target); v8::Context::Scope event_context_scope(local_context); @@ -1414,14 +1459,18 @@ event->Set(local_context, js_string(isolate, name), v8::Number::New(isolate, value)).Check(); }; const auto type_view = std::string_view(type); - const auto bubbles = type_view != "blur" + const auto bubbles = type_view != "beforetoggle" + && type_view != "close" + && type_view != "cancel" + && type_view != "blur" && type_view != "focus" && type_view != "mouseenter" && type_view != "mouseleave" && type_view != "pointerenter" && type_view != "pointerleave" && type_view != "scroll"; - const auto cancelable = type_view != "blur" + const auto cancelable = type_view != "close" + && type_view != "blur" && type_view != "focus" && type_view != "focusin" && type_view != "focusout" @@ -1438,6 +1487,11 @@ event->Set(local_context, js_string(isolate, "bubbles"), v8::Boolean::New(isolate, bubbles)).Check(); event->Set(local_context, js_string(isolate, "cancelable"), v8::Boolean::New(isolate, cancelable)).Check(); event->Set(local_context, js_string(isolate, "defaultPrevented"), v8::False(isolate)).Check(); + if (type_view == "beforetoggle") { + event->Set(local_context, js_string(isolate, "oldState"), js_string(isolate, "closed")).Check(); + event->Set(local_context, js_string(isolate, "newState"), js_string(isolate, "open")).Check(); + event->Set(local_context, js_string(isolate, "source"), v8::Null(isolate)).Check(); + } if (type_view == "submit") { event->Set( local_context, @@ -1800,6 +1854,7 @@ event->Set(local_context, js_string(isolate, "currentTarget"), v8::Null(isolate)).Check(); perform_microtask_checkpoint(); style_batch.finish(); + #if defined(WEBSCENE_NATIVE_ENGINE_CERTIFICATION) event_callback_counts[type] += input_callback_invocation_count - callback_count_before; @@ -1946,6 +2001,7 @@ bool is_effectively_visible_for_focus(const dom_node* node) { + if (node != nullptr && document.is_inert(*node)) return false; auto visibility_resolved = false; auto visibility_hidden = false; for (auto* current_node = node; current_node != nullptr; @@ -2184,6 +2240,7 @@ bool blur_active_element_before_detach(dom_node& detached_root) { + document.unregister_modal_subtree(detached_root); if (!subtree_contains(detached_root, active_element)) return true; webscene_input_event synthetic{}; if (!set_active_element(nullptr, synthetic)) return false; @@ -2253,6 +2310,9 @@ bool dispatch_keyboard_or_text(const webscene_input_event& input) { + if (active_element != nullptr && document.is_inert(*active_element)) { + if (!set_active_element(nullptr, input)) return false; + } auto* target = active_element == nullptr ? &active_root() : active_element; if (input.kind == WEBSCENE_INPUT_KEY_UP) { pending_text_input_from_keydown = false; @@ -2274,7 +2334,7 @@ target = find_node_by_native_id(active_root(), target_id); if (target == nullptr) return true; } - if (!is_text_control(target)) return true; + if (!is_text_control(target) || document.is_inert(*target)) return true; bool prevented = false; if (!dispatch_input_event_type(input, "beforeinput", *target, &prevented)) return false; if (prevented) return true; @@ -2729,7 +2789,7 @@ return std::nullopt; } - bool dispatch_input(const webscene_input_event& input) + bool dispatch_input(const webscene_input_event& input, bool defer_cursor_update = false) { current_input_sequence = input.sequence; auto isolate_locker = lock_shared_isolate(); @@ -2770,6 +2830,7 @@ hover_target = nullptr; current_related_target = nullptr; current_cursor_kind_value = WEBSCENE_CURSOR_DEFAULT; + pointer_cursor_update_pending = false; has_pointer_position = false; current_movement_x = current_movement_y = 0; recascade_hover_transition(previous, nullptr, "host-pointer-exit"); @@ -3279,6 +3340,11 @@ if (input.kind == WEBSCENE_INPUT_POINTER_MOVE || input.kind == WEBSCENE_INPUT_POINTER_DOWN || input.kind == WEBSCENE_INPUT_POINTER_UP) { + if (defer_cursor_update && input.kind == WEBSCENE_INPUT_POINTER_MOVE) { + pointer_cursor_update_pending = true; + return true; + } + pointer_cursor_update_pending = false; ensure_layout(); auto* cursor_target = document.hit_test( document.body(), @@ -3287,6 +3353,7 @@ current_cursor_kind_value = cursor_target == nullptr ? WEBSCENE_CURSOR_DEFAULT : cursor_kind_for(*cursor_target); + } return true; } diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_workers.inc b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_workers.inc new file mode 100644 index 000000000..beaad0c9e --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_v8_runtime_workers.inc @@ -0,0 +1,242 @@ + struct worker_state { + std::mutex mutex; + std::condition_variable ready; + std::deque incoming, outgoing; + std::deque errors; + bool stopped{false}; + bool wake_requested{false}; + v8::Isolate* running_isolate{}; + std::function notify_parent; + std::thread thread; + v8::Global wrapper; + v8::Global realm; + void stop() { + { + std::lock_guard lock(mutex); + stopped = true; + if (running_isolate) running_isolate->TerminateExecution(); + incoming.clear(); outgoing.clear(); errors.clear(); + } + ready.notify_all(); + if (thread.joinable()) thread.join(); + } + ~worker_state() { stop(); } + }; + std::vector> dedicated_workers; + worker_state* worker_owner{}; + bool force_dedicated_isolate{false}; + + static v8::MaybeLocal worker_transfer( + const v8::FunctionCallbackInfo& info) { + if (info.Length() < 2) return v8::Undefined(info.GetIsolate()); + if (info[1]->IsObject() && !info[1]->IsArray()) + return info[1].As()->Get(info.GetIsolate()->GetCurrentContext(), js_string(info.GetIsolate(), "transfer")); + return info[1]; + } + static void worker_post_message_impl(const v8::FunctionCallbackInfo& info) { + auto* state = static_cast(info.Data().As()->Value(v8::kExternalPointerTypeTagDefault)); + v8::Local transfer; + if (!worker_transfer(info).ToLocal(&transfer)) return; + clone_packet packet; + if (!serialize_clone(info, info[0], transfer, packet)) return; + { std::lock_guard lock(state->mutex); if (state->stopped) return; state->incoming.push_back(std::move(packet)); } + state->ready.notify_one(); + } + static void worker_post_parent_impl(const v8::FunctionCallbackInfo& info) { + auto* self = current(info.GetIsolate()); + if (!self || !self->worker_owner) return; + auto* state = self->worker_owner; + v8::Local transfer; + if (!worker_transfer(info).ToLocal(&transfer)) return; + clone_packet packet; + if (!serialize_clone(info, info[0], transfer, packet)) return; + { std::lock_guard lock(state->mutex); if (state->stopped) return; state->outgoing.push_back(std::move(packet)); } + if (state->notify_parent) state->notify_parent(); + } + static void worker_terminate(const v8::FunctionCallbackInfo& info) { + auto* state = static_cast(info.Data().As()->Value(v8::kExternalPointerTypeTagDefault)); + state->stop(); + } + static void worker_close(const v8::FunctionCallbackInfo& info) { + auto* self = current(info.GetIsolate()); + if (self && self->worker_owner) { + std::lock_guard lock(self->worker_owner->mutex); self->worker_owner->stopped = true; + } + } + bool dispatch_worker_message(v8::Local realm, + v8::Local target, const clone_packet* packet, const std::string& error = {}) { + v8::Context::Scope scope(realm); + v8::TryCatch caught(isolate); + auto event = v8::Object::New(isolate); + event->Set(realm, js_string(isolate,"type"), js_string(isolate, packet ? "message" : "error")).Check(); + if (packet) { + v8::Local value; + if (!deserialize_clone(isolate, realm, *packet).ToLocal(&value)) return false; + event->Set(realm, js_string(isolate,"data"), value).Check(); + } else event->Set(realm, js_string(isolate,"message"), js_dom_string(isolate,error)).Check(); + v8::Local dispatch; + if (!target->Get(realm, js_string(isolate,"dispatchEvent")).ToLocal(&dispatch) || !dispatch->IsFunction()) return false; + v8::Local args[]{event}; + if (dispatch.As()->Call(realm,target,1,args).IsEmpty()) { + last_error = describe_reported_exception(caught,realm); return false; + } + perform_microtask_checkpoint(); + return true; + } + bool has_worker_messages() const { + for (auto& state : dedicated_workers) { + std::lock_guard lock(state->mutex); + if (!state->outgoing.empty() || !state->errors.empty()) return true; + } + return false; + } + bool drain_worker_message() { + for (auto& state : dedicated_workers) { + std::optional packet; std::string error; + { + std::lock_guard lock(state->mutex); + if (!state->errors.empty()) { error = std::move(state->errors.front()); state->errors.pop_front(); } + else if (!state->outgoing.empty()) { packet = std::move(state->outgoing.front()); state->outgoing.pop_front(); } + else continue; + } + return dispatch_worker_message(state->realm.Get(isolate),state->wrapper.Get(isolate), + packet ? &*packet : nullptr,error); + } + return true; + } + void stop_workers() { for (auto& worker : dedicated_workers) worker->stop(); } + + static void worker_construct_impl(const v8::FunctionCallbackInfo& info) { + auto* self = current(info.GetIsolate()); if (!self) return; + auto* isolate = info.GetIsolate(); auto realm = isolate->GetCurrentContext(); + if (!info.IsConstructCall() || info.Length() < 1) { + isolate->ThrowException(v8::Exception::TypeError(js_string(isolate,"Worker requires new and a URL"))); return; + } + const auto url = resolve_resource_url(to_utf8(isolate,info[0]),self->current_base_address()); + // Until worker CORS/credentials are qualified, enforce a same-origin script. + auto origin = [](const std::string& value) { + auto scheme = value.find("://"); + return scheme == std::string::npos ? std::string{} : value.substr(0,value.find('/',scheme+3)); + }; + if (origin(url) != origin(self->current_base_address())) { + throw_dom_exception(info,"Worker script must be same origin","SecurityError"); return; + } + bool module = false; + std::string worker_name; + if (info.Length()>1 && info[1]->IsObject()) { + v8::Local type; + if (!info[1].As()->Get(realm,js_string(isolate,"type")).ToLocal(&type)) return; + if (!type->IsUndefined()) { + auto text=to_utf8(isolate,type); + if(text!="classic"&&text!="module"){ + isolate->ThrowException(v8::Exception::TypeError(js_string(isolate,"Invalid WorkerType")));return; + } + module=text=="module"; + } + v8::Local name; + if(!info[1].As()->Get(realm,js_string(isolate,"name")).ToLocal(&name))return; + if(!name->IsUndefined())worker_name=to_utf8(isolate,name); + } + if (self->dedicated_workers.size() >= 1024) { + isolate->ThrowException(v8::Exception::RangeError(js_string(isolate,"Worker capacity exhausted"))); return; + } + v8::Local target_constructor; + if (!realm->Global()->Get(realm,js_string(isolate,"EventTarget")).ToLocal(&target_constructor) || !target_constructor->IsFunction()) return; + v8::Local target; + if (!target_constructor.As()->NewInstance(realm,0,nullptr).ToLocal(&target)) return; + v8::Local prototype; + if(info.NewTarget().As()->Get(realm,js_string(isolate,"prototype")).ToLocal(&prototype) + &&prototype->IsObject())target->SetPrototype(realm,prototype).FromMaybe(false); + auto state = std::make_unique(); + auto* raw = state.get(); + raw->notify_parent = self->runtime_work_available; + raw->wrapper.Reset(isolate,target);raw->realm.Reset(isolate,realm); + auto data=v8::External::New(isolate,raw,v8::kExternalPointerTypeTagDefault); + target->Set(realm,js_string(isolate,"postMessage"),v8::Function::New(realm,worker_post_message,data,1).ToLocalChecked()).Check(); + target->Set(realm,js_string(isolate,"terminate"),v8::Function::New(realm,worker_terminate,data).ToLocalChecked()).Check(); + auto loader=self->load_resource_callback; + auto root=self->resource_root; + self->dedicated_workers.push_back(std::move(state)); + raw->thread=std::thread([raw,url,module,worker_name=std::move(worker_name),loader=std::move(loader),root=std::move(root)] { + auto report=[&](const std::string& text) { + {std::lock_guard lock(raw->mutex);if(raw->stopped)return;raw->errors.push_back(text);} + if(raw->notify_parent)raw->notify_parent(); + }; + try { + native_document document; + v8_dom_runtime runtime(document,[]{return v8_dom_runtime::viewport_metrics{1,1,1,0};},{},loader); + auto* child=runtime.impl_.get(); + struct running_guard { + worker_state* state;implementation* child; + ~running_guard(){ + std::lock_guard lock(state->mutex);state->running_isolate=nullptr; + if(child->isolate)child->isolate->CancelTerminateExecution(); + } + } guard{raw,child}; + child->force_dedicated_isolate=true;child->worker_owner=raw; + child->runtime_work_available=[raw]{ + {std::lock_guard lock(raw->mutex);raw->wake_requested=true;} + raw->ready.notify_one(); + }; + runtime.set_resource_root(root.string()); + if(!runtime.initialize())throw std::runtime_error(runtime.last_error()); + { + std::lock_guard lock(raw->mutex); + if(raw->stopped)return; + raw->running_isolate=child->isolate; + } + { + v8::Isolate::Scope entered(child->isolate);v8::HandleScope handles(child->isolate); + auto realm=child->context.Get(child->isolate);v8::Context::Scope scope(realm); + auto global=realm->Global(); + child->document_base_address=url; + child->set_context_location(realm,global,url); + global->Set(realm,js_string(child->isolate,"postMessage"),v8::Function::New(realm,worker_post_parent,{},1).ToLocalChecked()).Check(); + global->Set(realm,js_string(child->isolate,"close"),v8::Function::New(realm,worker_close).ToLocalChecked()).Check(); + global->Set(realm,js_string(child->isolate,"self"),global).Check(); + global->Set(realm,js_string(child->isolate,"name"),js_dom_string(child->isolate,worker_name)).Check(); + global->Delete(realm,js_string(child->isolate,"document")).FromMaybe(false); + global->Delete(realm,js_string(child->isolate,"window")).FromMaybe(false); + std::string source,resolved,error; + if(!child->load_text_resource(url,{},WEBSCENE_RESOURCE_SCRIPT,source,resolved))report("Unable to load worker: "+url); + else if(!(module ? child->execute_module(realm,source,resolved,error) + : child->execute_in_context(realm,source,resolved,error)))report(error); + } + while(true) { + std::optional packet; + { + std::unique_lock lock(raw->mutex); + raw->ready.wait_for(lock,runtime.recommended_idle_wait(std::chrono::milliseconds(1000)), + [&]{return raw->stopped||raw->wake_requested||!raw->incoming.empty();}); + raw->wake_requested=false; + if(raw->stopped)break; + if(!raw->incoming.empty()){packet=std::move(raw->incoming.front());raw->incoming.pop_front();} + } + v8::Isolate::Scope entered(child->isolate);v8::HandleScope handles(child->isolate); + auto realm=child->context.Get(child->isolate);v8::Context::Scope scope(realm); + if(packet&&!child->dispatch_worker_message(realm,realm->Global(),&*packet))report(child->last_error); + child->drain_tasks(); + } + {std::lock_guard lock(raw->mutex);raw->running_isolate=nullptr;} + child->isolate->CancelTerminateExecution(); + } catch(const std::exception& e) { + {std::lock_guard lock(raw->mutex);raw->running_isolate=nullptr;} + report(e.what()); + } + }); + info.GetReturnValue().Set(target); + } + static void worker_construct(const v8::FunctionCallbackInfo& info) { + try {worker_construct_impl(info);} + catch(const std::exception& e){ + info.GetIsolate()->ThrowException(v8::Exception::Error(js_string(info.GetIsolate(),e.what()))); + } + } + static void worker_post_message(const v8::FunctionCallbackInfo& info) { + try {worker_post_message_impl(info);} + catch(const std::exception& e){info.GetIsolate()->ThrowException(v8::Exception::RangeError(js_string(info.GetIsolate(),e.what())));} + } + static void worker_post_parent(const v8::FunctionCallbackInfo& info) { + try {worker_post_parent_impl(info);} + catch(const std::exception& e){info.GetIsolate()->ThrowException(v8::Exception::RangeError(js_string(info.GetIsolate(),e.what())));} + } diff --git a/experiments/WebScene.NativeEngine.Probe/tests/audio_graph_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/audio_graph_tests.cpp new file mode 100644 index 000000000..e605d967a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/audio_graph_tests.cpp @@ -0,0 +1,137 @@ +#include "audio_graph.h" +#include +#include +#include +#include +using namespace webscene::media; +void check(bool value, const char *why) { + if (!value) + throw std::runtime_error(why); +} +template void rejects(F f) { + bool rejected = false; + try { + f(); + } catch (const std::exception &) { + rejected = true; + } + check(rejected, "Expected rejection"); +} +int main() { + try { + audio_graph graph(false); + auto source = graph.create(audio_graph::kind::source), gain = graph.create(audio_graph::kind::gain), + meter = graph.create(audio_graph::kind::analyser), + capture = graph.create(audio_graph::kind::stream); + auto pcm = std::make_shared(); + pcm->channels = 2; + pcm->sample_rate = 48000; + pcm->samples.resize(48000 * 2); + for (size_t i = 0; i < 48000; ++i) { + pcm->samples[i * 2] = .5f; + pcm->samples[i * 2 + 1] = -.25f; + } + auto control = std::make_shared(); + control->set(0, 1, true, 1, false); + graph.set_source(source, pcm, control); + graph.connect(source, gain); + graph.connect(gain, meter); + graph.connect(meter, 0); + graph.connect(meter, capture); + auto recording = graph.capture(capture), sibling = recording->clone(); + graph.set_gain(gain, .5f, 0, 0); + graph.resume(); + std::array output{}; + graph.render(output.data(), 128); + for (size_t i = 0; i < 128; ++i) { + check(std::abs(output[i * 2] - .25) < 1e-6, "Left gain channel"); + check(std::abs(output[i * 2 + 1] + .125) < 1e-6, "Right gain channel"); + } + std::array samples{}; + graph.analyser(meter, samples); + for (float s : samples) + check(std::abs(s - .0625) < 1e-6, "Analyser did not contain actual mix"); + graph.analyser(capture, samples); + for (float s : samples) + check(std::abs(s - .0625) < 1e-6, "Capture bus did not contain actual mix"); + std::array recorded{}; + auto packet = recording->read(recorded); + check(packet.frames == 128 && packet.first_frame == 0 && packet.dropped == 0, + "Capture timestamp/frame count"); + for (size_t i = 0; i < 128; ++i) { + check(std::abs(recorded[i * 2] - .25) < 1e-6, "Recorded left channel"); + check(std::abs(recorded[i * 2 + 1] + .125) < 1e-6, "Recorded right channel"); + } + recording->stop(); + check(recording->ended() && !sibling->ended(), "Track stop affected sibling"); + check(sibling->read(recorded).frames == 128, "Independent capture cursor"); + const auto before = graph.time(); + graph.suspend(); + graph.render(output.data(), 128); + for (float f : output) + check(f == 0, "Suspended context not silent"); + check(graph.time() == before, "Suspended clock advanced"); + graph.resume(); + graph.set_gain(gain, 0, graph.time(), .01); + graph.render(output.data(), 128); + check(output[0] > .24 && output[254] < output[0] && output[254] > 0, + "Target automation did not ramp"); + control->set(0, 1, true, 1, true); + graph.render(output.data(), 128); + for (float f : output) + check(f == 0, "Muted media not silent"); + rejects([&] { graph.connect(meter, source); }); + rejects([&] { graph.set_gain(gain, 0, -1, .1); }); + rejects([&] { graph.connect(999, 0); }); + graph.suspend(); + for (int i = 0; i < 32; ++i) + graph.set_gain(gain, 1, 100 + i, 0); + rejects([&] { graph.set_gain(gain, 1, 200, 0); }); + graph.close(); + check(sibling->ended(), "Context close did not end capture"); + rejects([&] { graph.resume(); }); + { + audio_graph g(false); + auto input = g.create(audio_graph::kind::source); + auto data = std::make_shared(); + data->channels = 1; + data->sample_rate = 48000; + data->samples.resize(2048); + for (size_t i = 0; i < data->samples.size(); ++i) + data->samples[i] = float(i) / 2048; + auto clock = std::make_shared(); + clock->set(0, 2, true, 1, false); + clock->epoch = 0; + g.set_source(input, data, clock); + g.connect(input, 0); + g.resume(); + std::array block{}; + g.render_at(block.data(), 256, 0); + for (size_t i = 0; i < 256; ++i) { + check(std::abs(block[i * 2] - float(i * 2) / 2048) < 1e-6, "Rate/quantum continuity"); + check(block[i * 2] == block[i * 2 + 1], "Mono to stereo conversion"); + } + clock->set(.01, 1, true, 1, false); + clock->epoch = 0; + g.render_at(block.data(), 128, 0); + check(std::abs(block[0] - 480.f / 2048) < 1e-6, "Seek did not reset audio cursor"); + } + { + audio_capture ring(48000); + uint64_t cursor = 0; + std::array signal{}; + signal.fill(.2f); + for (int i = 0; i < 130; ++i) + ring.write(signal); + auto data = ring.read(cursor, signal); + check(data.dropped == 256 && data.first_frame == 256 && data.frames == 128, + "Capture overrun not reported"); + } + std::cout << "Audio graph: native mixing, gain automation, analyser/capture, suspend/mute, " + "cycle/limit and teardown passed\n"; + return 0; + } catch (const std::exception &e) { + std::cerr << e.what() << '\n'; + return 1; + } +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/fixtures/media/README.md b/experiments/WebScene.NativeEngine.Probe/tests/fixtures/media/README.md new file mode 100644 index 000000000..acecb9ad6 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/fixtures/media/README.md @@ -0,0 +1,16 @@ +# Synthetic video fixture + +`numbered-motion.mp4` is a two-second 64x48, 24 fps synthetic moving test pattern, +created for WebScene tests (no third-party footage). Generated with: + +``` +ffmpeg -f lavfi -i testsrc2=size=64x48:rate=24 -t 2 -c:v libx264 \ + -pix_fmt yuv420p -movflags +faststart numbered-motion.mp4 +``` + +The name denotes a frame-ordered motion fixture; it has no burned-in timecode. +FFmpeg is a fixture-generation tool only, not a WebScene runtime dependency. +Tests decode different timestamps and verify changed pixels and native frame +ownership. Generated stereo PCM fixtures in the test verify exact channel values. + +`flash-click.mp4` is a generated three-second 64x48/30fps H.264/AAC fixture: a white frame and 10ms/1kHz tone at each integer second, black/silence otherwise. It contains no third-party content. Native tests compare decoded frame timestamps with actual mixed/recorded PCM onset, with a 10ms tolerance; this measures pipeline timestamps, not physical speaker/display latency. diff --git a/experiments/WebScene.NativeEngine.Probe/tests/fixtures/media/flash-click.mp4 b/experiments/WebScene.NativeEngine.Probe/tests/fixtures/media/flash-click.mp4 new file mode 100644 index 000000000..9dc0288f1 Binary files /dev/null and b/experiments/WebScene.NativeEngine.Probe/tests/fixtures/media/flash-click.mp4 differ diff --git a/experiments/WebScene.NativeEngine.Probe/tests/fixtures/media/numbered-motion.mp4 b/experiments/WebScene.NativeEngine.Probe/tests/fixtures/media/numbered-motion.mp4 new file mode 100644 index 000000000..d5482c257 Binary files /dev/null and b/experiments/WebScene.NativeEngine.Probe/tests/fixtures/media/numbered-motion.mp4 differ diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_angle_context_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_angle_context_tests.cpp new file mode 100644 index 000000000..512979a8e --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_angle_context_tests.cpp @@ -0,0 +1,116 @@ +#include "graphics/angle_context.h" +#include "graphics/angle_display.h" +#include +#include +#include +#include +#include +#include +using namespace webscene::graphics; +void require(bool value, std::source_location where=std::source_location::current()) { if (!value) throw std::runtime_error("requirement failed at line "+std::to_string(where.line())); } +// Diagnostic readback proves storage isolation; it is not presentation transport. +GLuint make_texture(const std::array& pixel) { + GLuint texture=0;glGenTextures(1,&texture);glBindTexture(GL_TEXTURE_2D,texture); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,1,1,0,GL_RGBA,GL_UNSIGNED_BYTE,pixel.data()); + require(texture!=0 && glGetError()==GL_NO_ERROR);return texture; +} +void verify_texture(GLuint texture,const std::array& expected) { + GLuint framebuffer=0;glGenFramebuffers(1,&framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER,framebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,texture,0); + require(glCheckFramebufferStatus(GL_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE); + std::array pixels{}; + glReadPixels(0,0,1,1,GL_RGBA,GL_UNSIGNED_BYTE,pixels.data()); + require(glGetError()==GL_NO_ERROR && pixels==expected); + glBindFramebuffer(GL_FRAMEBUFFER,0);glDeleteFramebuffers(1,&framebuffer); +} +int main(int argc, char** argv) { + const EGLint major=argc==2 && std::string_view(argv[1])=="3" ? 3 : 2; +#if defined(__APPLE__) + constexpr EGLint backend=EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE; +#elif defined(_WIN32) + constexpr EGLint backend=EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE; +#else + constexpr EGLint backend=EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; +#endif + auto lease=angle_display::acquire(backend); + const auto display=lease->get(); + auto other_engine=angle_display::acquire(backend); + require(other_engine->get()==display); + other_engine.reset(); + require(eglQueryString(display,EGL_VERSION)!=nullptr); + const EGLint config_attrs[]={EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,EGL_RENDERABLE_TYPE,major==2 ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES3_BIT,EGL_NONE}; + EGLConfig config{}; EGLint count{}; + require(eglChooseConfig(display,config_attrs,&config,1,&count) && count==1); + angle_context first(lease,config,major); + GLuint retained_texture=0; + const std::array red{255,0,0,255},green{0,255,0,255}; + { + angle_context second(lease,config,major); + angle_context::scope active(first); + const auto first_context=eglGetCurrentContext(); + glClearColor(1,0,0,1); + retained_texture=make_texture(red); + verify_texture(retained_texture,red); + { + angle_context::scope other(second); + require(eglGetCurrentContext()!=first_context); + glClearColor(0,1,0,1); + require(!glIsTexture(retained_texture)); + const auto second_texture=make_texture(green); + verify_texture(second_texture,green); + glDeleteTextures(1,&second_texture); + } + require(eglGetCurrentContext()==first_context); + GLfloat color[4]{}; glGetFloatv(GL_COLOR_CLEAR_VALUE,color); + require(color[0]==1 && color[1]==0); + bool rejected=false; + std::thread wrong([&] { try { angle_context::scope invalid(first); } catch(const std::logic_error&) { rejected=true; } }); + wrong.join(); require(rejected); + } + { + angle_context::scope still_valid(first); + require(glGetError()==GL_NO_ERROR && glIsTexture(retained_texture)); + verify_texture(retained_texture,red); + glDeleteTextures(1,&retained_texture); + } + for (const bool nested : {false,true}) { + angle_context lost(lease,config,major); + angle_context::scope surviving(first); + const auto previous=eglGetCurrentContext(); + { + angle_context::scope losing(lost); + require(!lost.is_lost()); + using lose_proc=void (GL_APIENTRY *)(GLenum,GLenum); + const auto lose=reinterpret_cast(eglGetProcAddress("glLoseContextCHROMIUM")); + require(lose!=nullptr); + using request_proc=void (GL_APIENTRY *)(const GLchar*); + const auto request=reinterpret_cast(eglGetProcAddress("glRequestExtensionANGLE")); + require(request!=nullptr); + // WebGL-compatible contexts expose this diagnostic extension only + // after requesting it. Reset notification is configured by the owner. + require(glGetError()==GL_NO_ERROR); + request("GL_CHROMIUM_lose_context"); + require(glGetError()==GL_NO_ERROR); + if (nested) { + { + angle_context::scope same_context(lost); + lose(GL_GUILTY_CONTEXT_RESET_EXT,GL_INNOCENT_CONTEXT_RESET_EXT); + } + // A nested scope must not restore its own now-lost context. + require(lost.is_lost() && eglGetCurrentContext()==EGL_NO_CONTEXT); + } else { + lose(GL_GUILTY_CONTEXT_RESET_EXT,GL_INNOCENT_CONTEXT_RESET_EXT); + } + } + require(lost.is_lost() && eglGetCurrentContext()==previous); + bool rejected=false; + try { angle_context::scope invalid(lost); } catch(const std::runtime_error&) { rejected=true; } + require(rejected && eglGetCurrentContext()==previous && !first.poll_loss()); + const auto texture=make_texture(green);verify_texture(texture,green);glDeleteTextures(1,&texture); + } + require(eglGetCurrentContext()==EGL_NO_CONTEXT); + std::cout << "ANGLE isolated contexts, loss rejection, nested restoration and execution-thread checks passed\n"; +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_canvas_backing_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_canvas_backing_tests.cpp new file mode 100644 index 000000000..9c8456ca6 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_canvas_backing_tests.cpp @@ -0,0 +1,25 @@ +#include "graphics/canvas_backing.h" +#include +using namespace webscene::graphics; +void require(bool value) { if (!value) throw std::runtime_error("canvas backing requirement failed"); } +int main() { + canvas_backing a,b; + require(a.identity()!=b.identity()); + require(a.width()==300 && a.height()==150 && a.mode()==canvas_context_mode::none); + require(!a.claim_context(canvas_context_mode::none)); + require(a.claim_context(canvas_context_mode::webgpu)); + require(a.claim_context(canvas_context_mode::webgpu)); + require(!a.claim_context(canvas_context_mode::two_d) && !a.claim_context(canvas_context_mode::webgl2)); + auto identity=a.identity(),generation=a.allocation_generation(); + for (int i=0;i<1000;++i) a.publish_content(); + require(a.content_serial()==1000 && a.allocation_generation()==generation && a.identity()==identity); + a.reset_bitmap(300,150); + require(a.content_serial()==1001 && a.allocation_generation()==generation); + a.reset_bitmap(640,480); + require(a.width()==640 && a.height()==480 && a.allocation_generation()==generation+1); + require(a.identity()==identity && a.mode()==canvas_context_mode::webgpu); + a.reset_bitmap(0,0); + require(a.width()==0 && a.height()==0 && !a.claim_context(canvas_context_mode::two_d)); + require(b.claim_context(canvas_context_mode::two_d)); + std::cout << "canvas identity, exclusive context and independent content/allocation versions passed\n"; +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_completion_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_completion_tests.cpp new file mode 100644 index 000000000..31c511d92 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_completion_tests.cpp @@ -0,0 +1,177 @@ +#include "graphics/completion_mailbox.h" +#include "graphics/producer_completion_gate.h" +#include +using namespace webscene::graphics; +void require(bool value) { if (!value) throw std::runtime_error("requirement failed"); } +struct wake_counter : completion_wake { + std::atomic count{}; + void signal() noexcept override { ++count; } +}; +int main() { + // Hold either producer phase indefinitely. No readiness or release signal + // is permitted until the other phase arrives, including on validation error. + for(bool queue_first:{false,true})for(bool queue_ok:{false,true})for(bool validation_ok:{false,true}) { + producer_completion_gate gate; + const auto first=queue_first ? producer_completion_gate::phase::queue : producer_completion_gate::phase::validation; + const auto second=queue_first ? producer_completion_gate::phase::validation : producer_completion_gate::phase::queue; + require(!gate.validated_for_gpu_wait()); + require(!gate.finish(first,queue_first ? queue_ok : validation_ok)); + require(gate.validated_for_gpu_wait()==(!queue_first && validation_ok)); + for(int poll=0;poll<100;++poll)require(gate.state()==producer_completion_gate::result::pending); + require(!gate.finish(first,true)); // A duplicate cannot supply the missing phase. + require(gate.state()==producer_completion_gate::result::pending); + require(gate.finish(second,queue_first ? validation_ok : queue_ok)); + const auto expected=queue_ok&&validation_ok ? producer_completion_gate::result::success : producer_completion_gate::result::failure; + require(gate.state()==expected); + require(gate.validated_for_gpu_wait()==(queue_ok && validation_ok)); + require(!gate.finish(first,false)&&!gate.finish(second,false)); + require(gate.state()==expected); + } + producer_completion_gate rejected; + rejected.reject(); + require(!rejected.finish(producer_completion_gate::phase::queue,true)); + require(rejected.finish(producer_completion_gate::phase::validation,true)); + require(rejected.state()==producer_completion_gate::result::failure); + + auto wake = std::make_shared(); + completion_mailbox box(2, wake); + resource_owner a{new_owner_token(),new_owner_token(),0}; + auto b=a; b.device=new_owner_token(); + auto first=box.reserve(1,a).value(), second=box.reserve(2,b).value(); + require(!box.reserve(3,a) && box.has_pending()); + bool rejected_thread=false; + std::thread callback([&] { + require(box.publish(second,completion_status::success)); + require(!box.publish(second,completion_status::success)); + try { box.drain_one([](auto) {}); } catch(const std::logic_error&) { rejected_thread=true; } + }); callback.join(); + require(rejected_thread && wake->count==1 && box.has_pending()); + box.cancel_owner(a); + require(box.has_pending()); + require(!box.publish(first,completion_status::success)); + require(box.drain_one([](auto record) { require(record.operation==2 && record.status==completion_status::success); })); + require(box.drain_one([](auto record) { require(record.operation==1 && record.status==completion_status::cancelled); })); + auto reused=box.reserve(3,a).value(); + require(!box.publish(first,completion_status::success)); + require(box.has_pending()); + box.close(); + require(box.has_pending()); + require(!box.publish(reused,completion_status::success) && !box.reserve(4,b)); + require(box.drain_one([](auto record) { require(record.operation==3 && record.status==completion_status::cancelled); })); + require(!box.has_ready()); + auto counters=box.metrics(); + require(counters.pending==0 && counters.ready==0 && counters.high_water==2); + require(counters.admitted==3 && counters.delivered==3 && counters.saturated_reservations==1); + require(counters.rejected_publications==4 && counters.latency_samples==0); + completion_mailbox delayed(1,wake); + auto cancelled=delayed.reserve(1,a).value(); + delayed.cancel_owner(a); + require(delayed.drain_one([](auto record) { require(record.status==completion_status::cancelled); })); + require(delayed.has_pending() && !delayed.reserve(2,a)); + require(delayed.metrics().occupied==1 && delayed.metrics().native_pending==1); + std::thread late([&] { require(!delayed.publish(cancelled,completion_status::success)); }); + late.join(); + require(!delayed.has_pending() && delayed.metrics().occupied==0); + auto next=delayed.reserve(2,a).value(); + require(next.generation!=cancelled.generation); + require(!delayed.publish(cancelled,completion_status::success)); + require(delayed.publish(next,completion_status::success)); + require(delayed.drain_one([](auto) {})); + completion_mailbox dormant(2,wake); + auto lifetime=dormant.reserve(1,a,false).value(); + require(dormant.has_pending()&&!dormant.has_pollable_pending()&&!dormant.has_ready()); + auto active=dormant.reserve(2,b).value(); + require(dormant.has_pollable_pending()); + require(dormant.publish(active,completion_status::success)); + require(dormant.drain_one([](auto){})); + require(!dormant.has_pollable_pending()); + require(dormant.publish(lifetime,completion_status::success)&&dormant.has_ready()); + require(dormant.drain_one([](auto){})); + require(!dormant.has_pending()); + auto cancel_lifetime=dormant.reserve(3,a,false).value(); + dormant.cancel_owner(a); + require(dormant.has_ready()&&!dormant.has_pollable_pending()); + require(dormant.drain_one([](auto record){require(record.status==completion_status::cancelled);})); + require(!dormant.publish(cancel_lifetime,completion_status::success)&&!dormant.has_pending()); + completion_mailbox timed(1,wake,true); + auto measured=timed.reserve(1,a).value(); + require(timed.publish(measured,completion_status::success)); + require(timed.drain_one([](auto) {})); + auto timings=timed.metrics(); + require(timings.latency_samples==1 && timings.total_latency_ns==timings.max_latency_ns); + require(timings.pending==0 && timings.ready==0); + // Saturate reusable storage while driver completion races owner cancellation. + // Delivery stays on this engine thread; every native callback must retire, + // even if its logical cancellation was already delivered. + completion_mailbox stress(16,wake); + for(uint64_t round=0;round<1000;++round) { + std::vector tickets; + for(uint64_t index=0;index<16;++index) + tickets.push_back(stress.reserve(round*16+index,index%2 ? b : a).value()); + require(!stress.reserve(UINT64_MAX,a)); + std::atomic start{false}; + std::thread driver([&] { + while(!start.load(std::memory_order_acquire)) std::this_thread::yield(); + for(auto ticket:tickets) { + stress.publish(ticket,completion_status::success); + std::this_thread::yield(); + } + }); + start.store(true,std::memory_order_release); + stress.cancel_owner(a); + bool seen[16]{}; + size_t delivered=0; + const auto deliver=[&](auto record) { + require(record.operation/16==round); + const auto index=record.operation%16; + require(!seen[index]);seen[index]=true;++delivered; + require(record.status==(index%2 ? completion_status::success : completion_status::cancelled)); + }; + while(stress.drain_one(deliver)) {} + driver.join(); + while(stress.drain_one(deliver)) {} + require(delivered==16); + const auto metrics=stress.metrics(); + require(metrics.pending==0 && metrics.ready==0 && metrics.native_pending==0 && metrics.occupied==0); + require(!stress.publish(tickets.front(),completion_status::success)); + } + require(stress.metrics().admitted==16000 && stress.metrics().delivered==16000); + require(stress.metrics().high_water==16 && stress.metrics().saturated_reservations==1000); + // Shutdown can detach the wake target while the driver still owns mailbox + // tickets. Exercise both callback retirement orders across fresh mailboxes. + for(uint64_t round=0;round<500;++round) { + auto shutdown_wake=std::make_shared(); + std::weak_ptr weak_wake=shutdown_wake; + auto closing=std::make_shared(16,shutdown_wake); + std::vector tickets; + for(uint64_t index=0;index<16;++index) + tickets.push_back(closing->reserve(index,a).value()); + std::atomic start{false}; + std::thread driver([closing,&tickets,&start] { + while(!start.load(std::memory_order_acquire)) std::this_thread::yield(); + for(auto ticket:tickets) { + closing->publish(ticket,completion_status::device_lost); + std::this_thread::yield(); + } + }); + start.store(true,std::memory_order_release); + closing->close(); + shutdown_wake.reset(); + require(!closing->reserve(99,b)); + bool seen[16]{}; + size_t delivered=0; + while(closing->drain_one([&](auto record) { + require(record.operation<16 && !seen[record.operation]); + seen[record.operation]=true;++delivered; + require(record.status==completion_status::cancelled); + })) {} + require(delivered==16); + driver.join(); + require(weak_wake.expired()); + require(!closing->has_ready() && !closing->has_pending()); + const auto metrics=closing->metrics(); + require(metrics.native_pending==0 && metrics.occupied==0 && metrics.delivered==16); + for(auto ticket:tickets) require(!closing->publish(ticket,completion_status::success)); + } + std::cout << "completion capacity, engine affinity, isolation and late-callback cancellation passed\n"; +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_dawn_event_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_dawn_event_tests.cpp new file mode 100644 index 000000000..6e0ce08a5 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_dawn_event_tests.cpp @@ -0,0 +1,1051 @@ +#include "graphics/graphics_service.h" +#include "graphics/webgpu_adapter_options.h" +#include "graphics/webgpu_feature_names.h" +#include "graphics/webgpu_buffer_descriptor.h" +#include "graphics/engine_wake.h" +#include "graphics/dawn_canvas_images.h" +#include "graphics/dawn_dxgi_image.h" +#include +#include +using namespace webscene::graphics; +// Synthetic exports exercise atomic handle ownership, not native fence signaling. +struct fence_test_ops { + using handle_type=int; + static inline std::set live; + static inline int next=100,duplicate_calls=0,fail_at=0; + static int empty() { return 0; } + static bool valid(int value) { return value>0; } + static int duplicate(int) { + if (++duplicate_calls==fail_at) throw std::system_error(std::make_error_code(std::errc::too_many_files_open)); + live.insert(next); return next++; + } + static void close(int value) noexcept { if (live.erase(value)!=1) std::terminate(); } +}; +void test_dxgi_fence_ownership() { + auto require=[](bool value) { if (!value) throw std::runtime_error("DXGI fence handoff ownership failed"); }; + std::array fences{}; + std::array values{7,UINT64_MAX}; + std::vector> output; + auto exported=[](const wgpu::SharedFence&,int& handle) { handle=42; return true; }; + require(duplicate_dxgi_fences(fences,values,output,exported)==dxgi_fence_status::success); + require(output.size()==2 && output[0].value==7 && output[1].value==UINT64_MAX + && output[0].handle.get()!=output[1].handle.get() && fence_test_ops::live.size()==2); + // A replacement failure must discard old output and roll back earlier duplicates. + fence_test_ops::duplicate_calls=0; fence_test_ops::fail_at=2; + require(duplicate_dxgi_fences(fences,values,output,exported)==dxgi_fence_status::handle_failure); + require(output.empty() && fence_test_ops::live.empty()); + fence_test_ops::fail_at=0; + int calls=0; + auto unsupported=[&](const wgpu::SharedFence&,int& handle) { handle=42; return ++calls!=2; }; + require(duplicate_dxgi_fences(fences,values,output,unsupported)==dxgi_fence_status::unsupported_fence); + require(output.empty() && fence_test_ops::live.empty()); + require(duplicate_dxgi_fences(fences,{},output,exported)==dxgi_fence_status::invalid_argument); + require(duplicate_dxgi_fences({}, {},output,exported)==dxgi_fence_status::success); + require(output.empty() && fence_test_ops::live.empty()); +} +void test_canvas_consumer_pixels(dawn_event_service& service,const wgpu::Device& device) { + auto pool=std::make_unique(device,2*64*64*4); + auto frame=pool->acquire(image_metadata{300,0,1,1,400,1,64,64}); + auto queue=device.GetQueue(); + auto encoder=device.CreateCommandEncoder(); + wgpu::RenderPassColorAttachment color{}; + color.view=frame->texture.CreateView(); color.loadOp=wgpu::LoadOp::Clear; + // Exact UNORM byte values avoid implementation-dependent rounding of .5 + // while retaining an exact, channel-sensitive cross-backend pixel check. + color.storeOp=wgpu::StoreOp::Store; color.clearValue={64.0/255.0,128.0/255.0,191.0/255.0,1}; + wgpu::RenderPassDescriptor pass{}; pass.colorAttachmentCount=1; pass.colorAttachments=&color; + auto render=encoder.BeginRenderPass(&pass); render.End(); + auto producer_commands=encoder.Finish(); + auto submitted=pool->submit(std::move(*frame),producer_commands); frame.reset(); + if (!submitted) throw std::runtime_error("canvas submission unexpectedly saturated"); + auto producer_status=submitted->status; + auto consumer=submitted->image.begin_consumer(); + auto resized=pool->acquire(image_metadata{300,0,2,2,400,2,32,32}); + auto resized_encoder=device.CreateCommandEncoder(); + wgpu::RenderPassColorAttachment resized_color{}; + resized_color.view=resized->texture.CreateView(); resized_color.loadOp=wgpu::LoadOp::Clear; + resized_color.storeOp=wgpu::StoreOp::Store; resized_color.clearValue={1,0,0,1}; + wgpu::RenderPassDescriptor resized_pass{}; resized_pass.colorAttachmentCount=1; resized_pass.colorAttachments=&resized_color; + auto resized_render=resized_encoder.BeginRenderPass(&resized_pass); resized_render.End(); + auto resized_commands=resized_encoder.Finish(); + auto newer=pool->submit(std::move(*resized),resized_commands); resized.reset(); + if (!newer || newer->image.describe().allocation==submitted->image.describe().allocation + || newer->image.describe().allocation_generation!=2 || consumer->describe().width!=64) + throw std::runtime_error("resize reused a retained image allocation"); + auto resized_status=newer->status; + auto resized_consumer=newer->image.begin_consumer(); + // Submit to the same queue before processing any completion. Queue order, + // rather than a CPU wait or a pixel upload, makes producer writes visible. + auto source_texture=dawn_canvas_images::resolve(*consumer,device); + wgpu::BufferDescriptor buffer_descriptor{}; + buffer_descriptor.size=(64+32)*256; + buffer_descriptor.usage=wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead; + auto readback=device.CreateBuffer(&buffer_descriptor); + auto copy=device.CreateCommandEncoder(); + wgpu::TexelCopyTextureInfo source{}; source.texture=source_texture; + wgpu::TexelCopyBufferInfo destination{}; destination.buffer=readback; + destination.layout.bytesPerRow=256; destination.layout.rowsPerImage=64; + wgpu::Extent3D extent{64,64,1}; + copy.CopyTextureToBuffer(&source,&destination,&extent); + source.texture=dawn_canvas_images::resolve(*resized_consumer,device); + destination.layout.offset=64*256; destination.layout.rowsPerImage=32; + extent={32,32,1}; copy.CopyTextureToBuffer(&source,&destination,&extent); + auto consumer_commands=copy.Finish(); queue.Submit(1,&consumer_commands); + submitted.reset(); newer.reset(); pool.reset(); + bool completed=false,queue_success=false,mapped=false,map_success=false; + queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents, + [&](wgpu::QueueWorkDoneStatus status,wgpu::StringView) { + consumer->complete(); consumer.reset(); + resized_consumer->complete(); resized_consumer.reset(); + queue_success=status==wgpu::QueueWorkDoneStatus::Success; completed=true; + }); + readback.MapAsync(wgpu::MapMode::Read,0,(64+32)*256,wgpu::CallbackMode::AllowProcessEvents, + [&](wgpu::MapAsyncStatus status,wgpu::StringView) { map_success=status==wgpu::MapAsyncStatus::Success; mapped=true; }); + const auto deadline=std::chrono::steady_clock::now()+std::chrono::seconds(10); + while ((!completed || !mapped || producer_status->load()==dawn_canvas_images::submission_status::pending + || resized_status->load()==dawn_canvas_images::submission_status::pending) && std::chrono::steady_clock::now()load()!=dawn_canvas_images::submission_status::success + || resized_status->load()!=dawn_canvas_images::submission_status::success) + throw std::runtime_error("GPU image consumer completion failed"); + const auto* pixels=static_cast(readback.GetConstMappedRange(0,(64+32)*256)); + if (!pixels) throw std::runtime_error("GPU image diagnostic mapping failed"); + for (size_t i=0;i<64*64;++i) { + if (pixels[4*i]!=64 || pixels[4*i+1]!=128 || pixels[4*i+2]!=191 || pixels[4*i+3]!=255) + throw std::runtime_error("retained GPU image pixels differ from producer clear"); + } + for (size_t y=0;y<32;++y) for (size_t x=0;x<32;++x) { + const auto* pixel=pixels+64*256+y*256+x*4; + if (pixel[0]!=255 || pixel[1]!=0 || pixel[2]!=0 || pixel[3]!=255) + throw std::runtime_error("resized GPU image pixels differ from new producer clear"); + } + readback.Unmap(); +} +void test_canvas_storage(dawn_event_service& service,const wgpu::Device& device,const wgpu::Device& foreign_device) { + auto require=[](bool v) { if (!v) throw std::runtime_error("Dawn canvas storage requirement failed"); }; + dawn_canvas_images images(device,3*128*128*4); + image_metadata m{100,0,1,1,200,1,64,64}; + std::vector frames; + for (int i=0;i<3;++i) frames.push_back(std::move(images.acquire(m).value())); + require(!images.acquire(m) && images.created_images()==3 && images.resident_bytes()==3*64*64*4); + auto encoder=device.CreateCommandEncoder(); + std::vector retained; + for (auto& frame:frames) { + frame.producer.begin(); + wgpu::RenderPassColorAttachment attachment{}; + attachment.view=frame.texture.CreateView(); + attachment.loadOp=wgpu::LoadOp::Clear; attachment.storeOp=wgpu::StoreOp::Store; + attachment.clearValue={0.25,0.5,0.75,1}; + wgpu::RenderPassDescriptor pass{}; pass.colorAttachmentCount=1; pass.colorAttachments=&attachment; + auto render=encoder.BeginRenderPass(&pass); render.End(); + retained.push_back(std::move(frame.producer.publish().value())); + } + auto commands=encoder.Finish(); auto queue=device.GetQueue(); queue.Submit(1,&commands); + bool complete=false,success=false; + queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents, + [&](wgpu::QueueWorkDoneStatus status,wgpu::StringView) { + for (auto& frame:frames) frame.producer.complete(); + success=status==wgpu::QueueWorkDoneStatus::Success; complete=true; + }); + const auto deadline=std::chrono::steady_clock::now()+std::chrono::seconds(10); + while (!complete && std::chrono::steady_clock::now()metadata.allocation!=0); + // Metadata-only acquisition/cancellation performs no GPU submission. + } + require(images.created_images()==3 && images.resident_bytes()==3*64*64*4); + m.width=128; m.height=128; ++m.allocation_generation; + { auto resized=images.acquire(m); require(resized && images.created_images()==4); } + require(images.resident_bytes()==128*128*4+2*64*64*4); + m.width=512; m.height=512; + require(!images.acquire(m) && images.created_images()==4); + images.close(); require(!images.acquire(image_metadata{100,0,1,200,200,2,64,64})); + // A retained image must block an over-budget resize, but idle cache entries + // must not permanently strand it after that retained image is released. + auto capacity_signal=std::make_shared(); + dawn_canvas_images tight(device,3*64*64*4,128,capacity_signal); + auto small=image_metadata{500,0,1,1,600,1,64,64}; + std::vector cached; + for (int i=0;i<3;++i) cached.push_back(std::move(tight.acquire(small).value())); + cached[1].producer.begin(); auto busy=cached[1].producer.publish(); + cached[1].producer.complete(); cached.clear(); + auto large=small; large.width=96; large.height=96; ++large.allocation_generation; + capacity_signal->wait_for(std::chrono::milliseconds(0),[] { return false; }); + require(!tight.acquire(large) && tight.created_images()==3); + require(!capacity_signal->wait_for(std::chrono::milliseconds(0),[] { return false; })); + require(busy->describe().width==64 && tight.busy_images()==1); + busy.reset(); + auto replacement=tight.acquire(large); + require(replacement && tight.created_images()==4 && tight.resident_bytes()==96*96*4); + replacement.reset(); require(tight.busy_images()==0); + dawn_canvas_images admission(device,2*64*64*4,1,capacity_signal); + auto first=admission.acquire(small); + auto empty_commands=device.CreateCommandEncoder().Finish(); + auto accepted=admission.submit(std::move(*first),empty_commands); first.reset(); + require(accepted.has_value()); + auto refused=admission.acquire(small); + require(refused.has_value()); + bool foreign_submission=false; + try { images.submit(std::move(*refused),empty_commands); } + catch (const std::invalid_argument&) { foreign_submission=true; } + require(foreign_submission); // Rejection must preserve the caller's frame. + capacity_signal->wait_for(std::chrono::milliseconds(0),[] { return false; }); + require(!admission.submit(std::move(*refused),empty_commands)); refused.reset(); + require(admission.busy_images()==1); + require(!capacity_signal->wait_for(std::chrono::milliseconds(0),[] { return false; })); + const auto accepted_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(10); + while (accepted->status->load()==dawn_canvas_images::submission_status::pending + && std::chrono::steady_clock::now()status->load()==dawn_canvas_images::submission_status::success); + accepted.reset(); require(admission.busy_images()==0); + auto detached=std::make_unique(device,64*64*4); + auto frame=detached->acquire(image_metadata{101,0,1,1,200,3,64,64}); + const auto native_identity=frame->texture.Get(); + frame->producer.begin(); auto scene=frame->producer.publish(); + frame->producer.complete(); frame.reset(); + auto consumer=scene->begin_consumer(); + auto anchor=std::weak_ptr(consumer->provider()); + detached.reset(); scene.reset(); + require(!anchor.expired()); + bool rejected=false; + try { dawn_canvas_images::resolve(*consumer,foreign_device); } + catch (const std::invalid_argument&) { rejected=true; } + require(rejected); + std::thread presenter([&] { + { auto texture=dawn_canvas_images::resolve(*consumer,device); + require(texture.Get()==native_identity && texture.GetWidth()==64 && texture.CreateView()); } + consumer->complete(); + bool stale=false; + try { dawn_canvas_images::resolve(*consumer,device); } + catch (const std::invalid_argument&) { stale=true; } + require(stale); + }); + presenter.join(); consumer.reset(); require(anchor.expired()); +} +int main() try { + test_dxgi_fence_ownership(); + auto wake=std::make_shared(); + graphics_service root(wake),other_root(wake); + auto& service=root.dawn(); + auto mailbox=service.completions(); + resource_owner owner{new_owner_token(),new_owner_token(),0}; + auto ticket=mailbox->reserve(1,owner).value(); + struct result { wgpu::Adapter adapter; }; + auto state=std::make_shared(); + webgpu_adapter_options browser_options; + auto converted=make_dawn_adapter_options(browser_options); + if (!converted || converted->featureLevel!=wgpu::FeatureLevel::Core || + converted->backendType!=wgpu::BackendType::Undefined || converted->nextInChain || + converted->powerPreference!=wgpu::PowerPreference::Undefined || converted->forceFallbackAdapter) + throw std::runtime_error("Default browser adapter selection changed"); + browser_options.feature_level=u"compatibility"; + browser_options.power_preference=wgpu::PowerPreference::LowPower; + browser_options.force_fallback_adapter=true; + auto fallback=make_dawn_adapter_options(browser_options); + if (!fallback || fallback->featureLevel!=wgpu::FeatureLevel::Compatibility || + !fallback->forceFallbackAdapter || fallback->powerPreference!=wgpu::PowerPreference::LowPower) + throw std::runtime_error("Browser adapter preferences were lost"); + browser_options.feature_level=u"unknown"; + if (make_dawn_adapter_options(browser_options)) throw std::runtime_error("Unknown feature level accepted"); + browser_options.feature_level=u"core"; browser_options.xr_compatible=true; + if (make_dawn_adapter_options(browser_options)) throw std::runtime_error("Unsupported XR adapter accepted"); + // Exercise the converted browser defaults through actual asynchronous Dawn discovery. + auto options=*converted; +#if defined(__APPLE__) + options.backendType=wgpu::BackendType::Metal; +#elif defined(_WIN32) + options.backendType=wgpu::BackendType::D3D12; +#else + options.backendType=wgpu::BackendType::Vulkan; +#endif + service.instance().RequestAdapter(&options,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,ticket,state](wgpu::RequestAdapterStatus status,wgpu::Adapter adapter,wgpu::StringView) { + state->adapter=std::move(adapter); + mailbox->publish(ticket,status==wgpu::RequestAdapterStatus::Success + ? completion_status::success : completion_status::failed); + }); + bool done=false, success=false; + const auto deadline=std::chrono::steady_clock::now()+std::chrono::seconds(30); + while (!done && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[] { return false; }); + } + if (!done) { std::cerr << "Dawn headless completion timed out\n"; return 1; } + if (!success) { std::cerr << "Hardware adapter unavailable\n"; return 77; } + wgpu::AdapterInfo info{}; + if (state->adapter.GetInfo(&info)!=wgpu::Status::Success) return 1; + if (info.adapterType!=wgpu::AdapterType::IntegratedGPU && info.adapterType!=wgpu::AdapterType::DiscreteGPU) return 77; + if (webgpu_feature_from_name("SharedTextureMemoryIOSurface") + || webgpu_feature_from_name("shared-texture-memory-iosurface") + || webgpu_feature_from_name("SHADER-F16") + || webgpu_feature_to_name(wgpu::FeatureName::SharedTextureMemoryIOSurface) + || webgpu_feature_to_name(wgpu::FeatureName::SharedFenceMTLSharedEvent) + || webgpu_feature_to_name(static_cast(0xffffffff))) return 1; + bool null_features=false; + try { webgpu_supported_feature_names(wgpu::Adapter{}); } + catch (const std::invalid_argument&) { null_features=true; } + if (!null_features) return 1; + const auto adapter_features=webgpu_supported_feature_names(state->adapter); + std::set feature_names; + std::set native_features; + for (const auto& feature:webgpu_feature_names) { + if (!feature_names.insert(feature.name).second || !native_features.insert(feature.native).second + || webgpu_feature_from_name(feature.name)!=feature.native + || webgpu_feature_to_name(feature.native)!=feature.name + || (std::find(adapter_features.begin(),adapter_features.end(),feature.name)!=adapter_features.end()) + !=state->adapter.HasFeature(feature.native)) return 1; + } + auto adapter_handle=root.adopt_adapter(state->adapter); + bool foreign_adapter=false,active_destroy=false,active_close=false,stale_adapter=false; + try { other_root.with_adapter(adapter_handle,[](const auto&) {}); } + catch (const std::invalid_argument&) { foreign_adapter=true; } + wgpu::Adapter retained_adapter; + root.with_adapter(adapter_handle,[&](const auto& adapter) { + retained_adapter=adapter; // In-flight requests own a reference independent of the wrapper. + try { root.destroy_adapter(adapter_handle); } + catch (const std::logic_error&) { active_destroy=true; } + try { root.close(); } + catch (const std::logic_error&) { active_close=true; } + }); + root.destroy_adapter(adapter_handle); + auto replacement_adapter=root.adopt_adapter(retained_adapter); + try { root.with_adapter(adapter_handle,[](const auto&) {}); } + catch (const std::invalid_argument&) { stale_adapter=true; } + if (!foreign_adapter || !active_destroy || !active_close || !stale_adapter + || replacement_adapter.slot!=adapter_handle.slot + || replacement_adapter.generation==adapter_handle.generation + || root.metrics().live_adapters!=1) return 1; + auto adapter_release=graphics_service::deferred_adapter_release(replacement_adapter); + auto adapter_commands=root.command_endpoint(2,0); + std::thread adapter_finalizer([&] { + if (adapter_commands->enqueue(adapter_release)!=enqueue_result::accepted + || adapter_commands->enqueue(adapter_release)!=enqueue_result::accepted) std::terminate(); + }); + adapter_finalizer.join(); + root.drain_commands(); + if (root.live_adapters()!=0 || retained_adapter.GetInfo(&info)!=wgpu::Status::Success) return 1; + bool null_adapter=false,adapter_limit=false; + try { root.adopt_adapter({}); } + catch (const std::invalid_argument&) { null_adapter=true; } + std::vector> bounded_adapters; + for (size_t i=0;i<64;++i) bounded_adapters.push_back(root.adopt_adapter(retained_adapter)); + try { root.adopt_adapter(retained_adapter); } + catch (const std::length_error&) { adapter_limit=true; } + if (!null_adapter || !adapter_limit || root.live_adapters()!=64) return 1; + for (auto handle:bounded_adapters) root.destroy_adapter(handle); + if (root.live_adapters()!=0) return 1; + retained_adapter=nullptr; + struct device_result { wgpu::Device device; }; +#if !defined(_WIN32) + dawn_dxgi_image unopened; + wgpu::SharedTextureMemoryEndAccessState handoff; + if (unopened.begin(false)!=dxgi_access_status::invalid_state + || unopened.end(handoff)!=dxgi_access_status::invalid_state || unopened.abandon_lost_device()) return 1; + bool unopened_rejected=false; + try { unopened.texture(); } catch (const std::logic_error&) { unopened_rejected=true; } + if (!unopened_rejected) return 1; + std::unique_ptr unsupported; + if (dawn_dxgi_image::import({},nullptr,{},{},{},wgpu::TextureUsage::TextureBinding,unsupported) + !=dxgi_import_status::unsupported_platform || unsupported) return 1; +#endif + auto native_device=std::make_shared(); + auto device_ticket=mailbox->reserve(10,owner).value(); + wgpu::DeviceDescriptor device_descriptor{}; + state->adapter.RequestDevice(&device_descriptor,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,device_ticket,native_device](wgpu::RequestDeviceStatus status,wgpu::Device device,wgpu::StringView) { + native_device->device=std::move(device); + mailbox->publish(device_ticket,status==wgpu::RequestDeviceStatus::Success + ? completion_status::success : completion_status::failed); + }); + const auto device_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(30); + bool device_done=false; + while (!device_done && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[] { return false; }); + } + if (!device_done || !native_device->device) return 1; + for (uint32_t bit=0;bit<32;++bit) { + const auto usage=webgpu_buffer_usage(1u<nextInChain + || translated_buffer->label.length!=14 + || translated_buffer->label.data!=browser_buffer.label.data() + || translated_buffer->usage!=(wgpu::BufferUsage::MapWrite|wgpu::BufferUsage::CopySrc)) return 1; + auto created_buffer=native_device->device.CreateBuffer(&*translated_buffer); + if (!created_buffer || created_buffer.GetSize()!=64 + || created_buffer.GetUsage()!=translated_buffer->usage + || created_buffer.GetMapState()!=wgpu::BufferMapState::Mapped + || !created_buffer.GetMappedRange(0,64)) return 1; + created_buffer.Unmap(); + created_buffer.Destroy(); + browser_buffer.usage=0x400; + if (make_dawn_buffer_descriptor(browser_buffer)) return 1; + const auto device_features=webgpu_supported_feature_names(native_device->device); + for (const auto& feature:webgpu_feature_names) { + const bool exposed=std::find(device_features.begin(),device_features.end(),feature.name)!=device_features.end(); + if (exposed!=native_device->device.HasFeature(feature.native)) return 1; + // No optional features were requested on this device. Adapter support + // must not silently become device enablement. + if (exposed && feature.native!=wgpu::FeatureName::CoreFeaturesAndLimits) return 1; + } + imported_dxgi_fences imported; + imported.values.push_back(99); + if (import_dxgi_fences({}, {}, {}, imported)!=dxgi_fence_status::invalid_argument + || !imported.values.empty() || !imported.fences.empty()) return 1; + std::array invalid_handles{nullptr}; + std::array wait_values{5}; + if (import_dxgi_fences(native_device->device,invalid_handles,wait_values,imported) + !=dxgi_fence_status::invalid_argument) return 1; + if (import_dxgi_fences(native_device->device,invalid_handles,{},imported) + !=dxgi_fence_status::invalid_argument) return 1; + // The test device has no optional DXGI fence feature enabled. Refuse before + // calling ImportSharedFence, even if a caller supplies a non-null handle. + invalid_handles[0]=reinterpret_cast(uintptr_t{1}); + if (import_dxgi_fences(native_device->device,invalid_handles,wait_values,imported) + !=dxgi_fence_status::missing_device_feature || !imported.fences.empty()) return 1; + auto owned_device=root.adopt_device(state->adapter,native_device->device,{},1,1,1,1,1,1); + if (root.live_devices()!=1) return 1; + root.with_device(owned_device,[&](auto& device) { owner=device.owner(); }); + root.with_device(owned_device,[&](auto& device) { + wgpu::ShaderSourceWGSL source{};source.code="@compute @workgroup_size(1) fn main() {}"; + wgpu::ShaderModuleDescriptor descriptor{};descriptor.nextInChain=&source; + auto shader=device.create_shader_module(descriptor); + bool capacity=false; + try{device.create_shader_module(descriptor);}catch(const std::length_error&){capacity=true;} + if(!capacity || device.live_shader_modules()!=1)throw std::runtime_error("Shader capacity admission failed"); + device.with_shader_module(shader,[&](const auto& native) { + if(!native)throw std::runtime_error("Owned shader is null"); + bool release_guard=false,close_guard=false; + try{device.release_shader_module(shader);}catch(const std::logic_error&){release_guard=true;} + try{device.close();}catch(const std::logic_error&){close_guard=true;} + if(!release_guard || !close_guard)throw std::runtime_error("Shader borrowing did not guard lifetime"); + }); + device.release_shader_module(shader); + bool stale=false;try{device.with_shader_module(shader,[](const auto&){});}catch(const std::invalid_argument&){stale=true;} + auto replacement=device.create_shader_module(descriptor); + if(!stale || replacement.generation==shader.generation)throw std::runtime_error("Shader generation identity reused"); + device.release_shader_module(replacement); + }); + // An owned render pipeline must remain usable by an encoded command after + // its table reference and source shader have been released. + native_device->device.PushErrorScope(wgpu::ErrorFilter::Validation); + root.with_device(owned_device,[&](auto& device) { + wgpu::ShaderSourceWGSL source{}; + source.code=R"WGSL( + @vertex fn vs(@builtin(vertex_index) i:u32)->@builtin(position) vec4f { + let p=array(vec2f(-1,-1),vec2f(3,-1),vec2f(-1,3)); + return vec4f(p[i],0,1); + } + @fragment fn fs()->@location(0) vec4f {return vec4f(1,0,0,1);} + )WGSL"; + wgpu::ShaderModuleDescriptor shader_desc{};shader_desc.nextInChain=&source; + auto shader=device.create_shader_module(shader_desc); + wgpu::RenderPipelineDescriptor descriptor{}; + wgpu::ColorTargetState target{};target.format=wgpu::TextureFormat::RGBA8Unorm; + wgpu::FragmentState fragment{};fragment.entryPoint="fs";fragment.targetCount=1;fragment.targets=⌖ + descriptor.fragment=&fragment;descriptor.vertex.entryPoint="vs"; + resource_handle pipeline; + device.with_shader_module(shader,[&](const auto& module) { + descriptor.vertex.module=module;fragment.module=module; + pipeline=device.create_render_pipeline(descriptor); + bool full=false;try{device.create_render_pipeline(descriptor);}catch(const std::length_error&){full=true;} + if(!full || device.live_render_pipelines()!=1)throw std::runtime_error("Render pipeline capacity failed"); + }); + device.release_shader_module(shader); + wgpu::TextureDescriptor texture_desc{};texture_desc.size={4,4,1};texture_desc.format=target.format; + texture_desc.usage=wgpu::TextureUsage::RenderAttachment; + auto texture=device.create_texture(texture_desc); + auto view_handle=device.create_texture_view(texture,{});wgpu::TextureView view; + bool texture_full=false,view_full=false; + try{device.create_texture(texture_desc);}catch(const std::length_error&){texture_full=true;} + try{device.create_texture_view(texture,{});}catch(const std::length_error&){view_full=true;} + if(!texture_full || !view_full)throw std::runtime_error("Texture capacity admission failed"); + device.with_texture_view(view_handle,[&](const auto& native) { + view=native;bool release_guard=false,destroy_guard=false,close_guard=false; + try{device.release_texture_view(view_handle);}catch(const std::logic_error&){release_guard=true;} + try{device.destroy_texture(texture);}catch(const std::logic_error&){destroy_guard=true;} + try{device.close();}catch(const std::logic_error&){close_guard=true;} + if(!release_guard || !destroy_guard || !close_guard)throw std::runtime_error("Borrowed texture view lifetime unguarded"); + }); + device.release_texture(texture); // The view retains its source texture. + device.release_texture_view(view_handle); + bool stale_texture=false,stale_view=false; + try{device.with_texture(texture,[](const auto&){});}catch(const std::invalid_argument&){stale_texture=true;} + try{device.with_texture_view(view_handle,[](const auto&){});}catch(const std::invalid_argument&){stale_view=true;} + if(!stale_texture || !stale_view || device.live_textures() || device.live_texture_views())throw std::runtime_error("Texture references did not retire"); + wgpu::RenderPassColorAttachment attachment{};attachment.view=view; + attachment.loadOp=wgpu::LoadOp::Clear;attachment.storeOp=wgpu::StoreOp::Store; + wgpu::RenderPassDescriptor pass_desc{};pass_desc.colorAttachmentCount=1;pass_desc.colorAttachments=&attachment; + auto encoder=device.create_command_encoder({});auto pass=device.begin_render_pass(encoder,pass_desc); + bool encoder_full=false,pass_full=false; + try{device.create_command_encoder({});}catch(const std::length_error&){encoder_full=true;} + try{device.begin_render_pass(encoder,pass_desc);}catch(const std::length_error&){pass_full=true;} + if(!encoder_full || !pass_full)throw std::runtime_error("Command resource capacity failed"); + device.with_render_pipeline(pipeline,[&](const auto& native) { + bool guarded=false;try{device.release_render_pipeline(pipeline);}catch(const std::logic_error&){guarded=true;} + bool close_guarded=false;try{device.close();}catch(const std::logic_error&){close_guarded=true;} + if(!guarded || !close_guarded)throw std::runtime_error("Borrowed render pipeline lifetime unguarded"); + device.with_render_pass(pass,[&](const auto& native_pass) { + bool guarded=false;try{device.release_render_pass(pass);}catch(const std::logic_error&){guarded=true;} + if(!guarded)throw std::runtime_error("Borrowed render pass release unguarded"); + native_pass.SetPipeline(native);native_pass.Draw(3);native_pass.End(); + }); + }); + device.release_render_pass(pass); + auto command=device.finish_command_encoder(encoder,{}); + bool command_full=false;try{device.finish_command_encoder(encoder,{});}catch(const std::length_error&){command_full=true;} + if(!command_full)throw std::runtime_error("Command buffer capacity failed"); + device.release_command_encoder(encoder); + device.release_render_pipeline(pipeline); + bool stale=false;try{device.with_render_pipeline(pipeline,[](const auto&){});}catch(const std::invalid_argument&){stale=true;} + if(!stale || device.live_render_pipelines()!=0)throw std::runtime_error("Render pipeline stale handle accepted"); + device.with_command_buffer(command,[&](const auto& native_command) { + bool close_guard=false;try{device.close();}catch(const std::logic_error&){close_guard=true;} + if(!close_guard)throw std::runtime_error("Borrowed command buffer did not guard device close"); + device.native().GetQueue().Submit(1,&native_command); + }); + device.release_command_buffer(command); + bool stale_command=false;try{device.with_command_buffer(command,[](const auto&){});}catch(const std::invalid_argument&){stale_command=true;} + if(!stale_command || device.live_command_buffers() || device.live_command_encoders() || device.live_render_passes())throw std::runtime_error("Command handles did not retire"); + }); + bool render_checked=false; + native_device->device.PopErrorScope(wgpu::CallbackMode::AllowProcessEvents, + [&](wgpu::PopErrorScopeStatus status,wgpu::ErrorType type,wgpu::StringView) { + if(status!=wgpu::PopErrorScopeStatus::Success || type!=wgpu::ErrorType::NoError) + throw std::runtime_error("Owned render pipeline draw failed validation"); + render_checked=true; + }); + const auto render_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(10); + while(!render_checked && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[]{return false;}); + } + if(!render_checked)throw std::runtime_error("Render pipeline validation did not complete"); + root.with_device(owned_device,[&](auto& device) { + wgpu::TextureDescriptor descriptor{};descriptor.size={1,1,1};descriptor.format=wgpu::TextureFormat::RGBA8Unorm;descriptor.usage=wgpu::TextureUsage::RenderAttachment; + auto imported=device.native().CreateTexture(&descriptor); + bool missing_source=false;try{device.adopt_texture({},imported);}catch(const std::invalid_argument&){missing_source=true;} + if(!missing_source)throw std::runtime_error("Imported texture accepted without its source device"); + auto texture=device.adopt_texture(device.native(),imported); + device.with_texture(texture,[&](const auto& native){if(native.Get()!=imported.Get())throw std::runtime_error("Texture adoption changed native identity");}); + bool full=false;try{device.adopt_texture(device.native(),imported);}catch(const std::length_error&){full=true;} + if(!full||device.live_textures()!=1)throw std::runtime_error("Imported texture capacity admission failed"); + auto view=device.create_texture_view(texture,{}); + device.destroy_texture(texture);device.destroy_texture(texture); + if(device.live_textures()!=1 || device.live_texture_views()!=1)throw std::runtime_error("Texture destroy removed API handles"); + device.release_texture_view(view);device.release_texture(texture); + }); + auto shader_releases=root.release_endpoint(); + resource_handle retired_shader,reused_shader; + release_ticket retired_ticket; + root.with_device(owned_device,[&](auto& device) { + wgpu::ShaderSourceWGSL source{};source.code="@compute @workgroup_size(1) fn main() {}"; + wgpu::ShaderModuleDescriptor descriptor{};descriptor.nextInChain=&source; + retired_shader=device.create_shader_module(descriptor); + retired_ticket=shader_releases->reserve(graphics_service::deferred_shader_module_release(owned_device,retired_shader)).value(); + device.release_shader_module(retired_shader); + reused_shader=device.create_shader_module(descriptor); + }); + std::thread stale_shader_finalizer([&] {if(!shader_releases->publish(retired_ticket))std::terminate();}); + stale_shader_finalizer.join(); + root.drain_commands(); + root.with_device(owned_device,[&](auto& device) { + device.with_shader_module(reused_shader,[](const auto& native) {if(!native)throw std::runtime_error("Stale finalizer released replacement shader");}); + if(device.live_shader_modules()!=1)throw std::runtime_error("Stale shader release changed live count"); + }); + auto release_shader_ticket=shader_releases->reserve(graphics_service::deferred_shader_module_release(owned_device,reused_shader)).value(); + std::thread shader_finalizer([&] {if(!shader_releases->publish(release_shader_ticket))std::terminate();});shader_finalizer.join(); + root.with_device(owned_device,[&](auto& device) {if(device.live_shader_modules()!=1)throw std::runtime_error("Finalizer released native shader inline");}); + root.drain_commands(); + root.with_device(owned_device,[&](auto& device) {if(device.live_shader_modules()!=0)throw std::runtime_error("Shader finalizer did not retire handle");}); + if(shader_releases->occupied()!=0)return 1; + auto second_adapter=std::make_shared(); + auto adapter_ticket=mailbox->reserve(19,owner).value(); + service.instance().RequestAdapter(&options,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,adapter_ticket,second_adapter](wgpu::RequestAdapterStatus status,wgpu::Adapter adapter,wgpu::StringView) { + second_adapter->adapter=std::move(adapter); + mailbox->publish(adapter_ticket,status==wgpu::RequestAdapterStatus::Success + ? completion_status::success : completion_status::failed); + }); + bool adapter_done=false; + const auto adapter_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(30); + while (!adapter_done && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[] { return false; }); + } + if (!adapter_done || !second_adapter->adapter) return 1; + auto second_native=std::make_shared(); + auto second_ticket=mailbox->reserve(20,owner).value(); + wgpu::DeviceDescriptor second_descriptor{}; + auto second_loss=std::make_shared(wake); + device_loss_signal::configure(second_descriptor,second_loss); + second_adapter->adapter.RequestDevice(&second_descriptor,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,second_ticket,second_native](wgpu::RequestDeviceStatus status,wgpu::Device device,wgpu::StringView) { + second_native->device=std::move(device); + mailbox->publish(second_ticket,status==wgpu::RequestDeviceStatus::Success + ? completion_status::success : completion_status::failed); + }); + const auto second_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(30); + bool second_done=false; + while (!second_done && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[] { return false; }); + } + if (!second_done || !second_native->device) return 1; + auto second_owned=root.adopt_device(second_adapter->adapter,second_native->device,second_loss); + resource_owner second_owner{}; + root.with_device(second_owned,[&](auto& device) { second_owner=device.owner(); }); + if (root.live_devices()!=2 || owner==second_owner) return 1; + resource_handle managed_buffer; + wgpu::Buffer borrowed_buffer_reference; + root.with_device(owned_device,[&](auto& device) { + browser_buffer.usage=0x6; + auto descriptor=make_dawn_buffer_descriptor(browser_buffer); + managed_buffer=device.create_buffer(*descriptor); + bool capacity_rejected=false; + try { device.create_buffer(*descriptor); } + catch (const std::length_error&) { capacity_rejected=true; } + if (!capacity_rejected || device.live_buffers()!=1) throw std::runtime_error("Buffer capacity was not enforced"); + device.with_buffer(managed_buffer,[&](const auto& buffer) { + borrowed_buffer_reference=buffer; + bool destroy_guard=false,release_guard=false,close_guard=false; + try { device.destroy_buffer(managed_buffer); } catch (const std::logic_error&) { destroy_guard=true; } + try { device.release_buffer(managed_buffer); } catch (const std::logic_error&) { release_guard=true; } + try { device.close(); } catch (const std::logic_error&) { close_guard=true; } + if (!destroy_guard || !release_guard || !close_guard) throw std::runtime_error("Buffer execution guards failed"); + }); + }); + bool foreign_buffer=false; + root.with_device(second_owned,[&](auto& device) { + try { device.with_buffer(managed_buffer,[](const auto&) {}); } + catch (const std::invalid_argument&) { foreign_buffer=true; } + }); + if (!foreign_buffer) return 1; + auto buffer_release=root.release_endpoint(); + const auto buffer_release_ticket=buffer_release->reserve(graphics_service::deferred_buffer_release(owned_device,managed_buffer)); + if (!buffer_release_ticket) return 1; + std::thread buffer_finalizer([&] { + if (!buffer_release->publish(*buffer_release_ticket)) std::terminate(); + }); + buffer_finalizer.join(); + root.drain_commands(); + root.with_device(owned_device,[&](auto& device) { + bool stale=false; + try { device.with_buffer(managed_buffer,[](const auto&) {}); } + catch (const std::invalid_argument&) { stale=true; } + if (!stale || device.live_buffers()!=0 || borrowed_buffer_reference.GetMapState()!=wgpu::BufferMapState::Mapped + || !borrowed_buffer_reference.GetMappedRange(0,64)) throw std::runtime_error("Wrapper release destroyed native buffer"); + auto descriptor=make_dawn_buffer_descriptor(browser_buffer); + auto replacement=device.create_buffer(*descriptor); + if (replacement.slot!=managed_buffer.slot || replacement.generation==managed_buffer.generation) + throw std::runtime_error("Buffer slot generation was not advanced"); + device.destroy_buffer(replacement); + device.destroy_buffer(replacement); + device.with_buffer(replacement,[](const auto& buffer) { + if (buffer.GetSize()!=64 || buffer.GetMapState()!=wgpu::BufferMapState::Unmapped) + throw std::runtime_error("Destroyed buffer wrapper lost metadata"); + }); + device.release_buffer(replacement); + }); + borrowed_buffer_reference.Unmap(); + borrowed_buffer_reference=nullptr; + test_canvas_storage(service,native_device->device,second_native->device); + test_canvas_consumer_pixels(service,native_device->device); + bool foreign_rejected=false; + try { other_root.with_device(owned_device,[](auto&) {}); } + catch (const std::invalid_argument&) { foreign_rejected=true; } + if (!foreign_rejected) return 1; + // Resource-table destruction is logical until this device's GPU queue has + // completed. Exercise a real command buffer, rather than a synthetic serial. + resource_table buffers(2,owner); + wgpu::BufferDescriptor buffer_descriptor{}; + buffer_descriptor.size=4096; + buffer_descriptor.usage=wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; + auto buffer=buffers.insert(owner,std::make_unique( + native_device->device.CreateBuffer(&buffer_descriptor))); + auto encoder=native_device->device.CreateCommandEncoder(); + encoder.ClearBuffer(buffers.get(buffer,owner),0,4096); + auto commands=encoder.Finish(); + auto submitted=mailbox->reserve(11,owner).value(); + buffers.mark_used(buffer,owner,1); + auto queue=native_device->device.GetQueue(); + queue.Submit(1,&commands); + queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents, + [mailbox,submitted](wgpu::QueueWorkDoneStatus status,wgpu::StringView) { + mailbox->publish(submitted,status==wgpu::QueueWorkDoneStatus::Success + ? completion_status::success : completion_status::failed); + }); + buffers.destroy(buffer,owner); + if (buffers.resident_count()!=1 || buffers.deferred_count()!=1) return 1; + bool submission_done=false; + const auto submission_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(30); + while (!submission_done && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[] { return false; }); + } + if (!submission_done || buffers.resident_count()!=0 || buffers.deferred_count()!=0) return 1; + // Map completion must progress without presentation, including cancellation + // caused by destroying a buffer before ProcessEvents delivers the callback. + wgpu::BufferDescriptor map_descriptor{}; + map_descriptor.size=4096; + map_descriptor.usage=wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + for (const bool destroy_pending : {false,true}) { + auto mapped=native_device->device.CreateBuffer(&map_descriptor); + auto map_ticket=mailbox->reserve(destroy_pending ? 13 : 12,owner).value(); + auto map_status=std::make_shared(); + mapped.MapAsync(wgpu::MapMode::Read,0,4096,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,map_ticket,map_status](wgpu::MapAsyncStatus status,wgpu::StringView) { + *map_status=status; + mailbox->publish(map_ticket,status==wgpu::MapAsyncStatus::Success + ? completion_status::success : completion_status::cancelled); + }); + if (destroy_pending) mapped.Destroy(); + bool map_done=false; + const auto map_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(30); + while (!map_done && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[] { return false; }); + } + if (!map_done || *map_status!=(destroy_pending + ? wgpu::MapAsyncStatus::Aborted : wgpu::MapAsyncStatus::Success)) return 1; + if (!destroy_pending) { + const auto* bytes=static_cast(mapped.GetConstMappedRange(0,4096)); + if (!bytes) return 1; + for (size_t i=0;i<4096;++i) if (bytes[i]!=0) return 1; + mapped.Unmap(); + mapped.Destroy(); + } + if (mailbox->has_pending() || mailbox->has_ready()) return 1; + } + auto pending_device=mailbox->reserve(14,owner).value(); + auto pending_map=native_device->device.CreateBuffer(&map_descriptor); + struct cancelled_map_result { bool called{},accepted{}; }; + auto cancelled_map=std::make_shared(); + pending_map.MapAsync(wgpu::MapMode::Read,0,4096,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,pending_device,cancelled_map](wgpu::MapAsyncStatus,wgpu::StringView) { + cancelled_map->called=true; + cancelled_map->accepted=mailbox->publish(pending_device,completion_status::success); + }); + auto independent_owner=second_owner; + auto independent=mailbox->reserve(15,independent_owner).value(); + root.destroy_device(owned_device); + if (root.live_devices()!=1) return 1; + bool stale_rejected=false; + try { root.with_device(owned_device,[](auto&) {}); } + catch (const std::invalid_argument&) { stale_rejected=true; } + if (!stale_rejected || !mailbox->publish(independent,completion_status::success)) return 1; + size_t device_records=0; + service.pump([&](auto record) { + if ((record.operation==14 && record.status==completion_status::cancelled) + || (record.operation==15 && record.status==completion_status::success)) ++device_records; + else throw std::runtime_error("device cancellation crossed ownership boundary"); + }); + const auto cancellation_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(30); + while (!cancelled_map->called && std::chrono::steady_clock::now()called) wake->wait_for( + root.recommended_idle_wait(std::chrono::milliseconds(100)),[] { return false; }); + } + if (device_records!=2 || !cancelled_map->called || cancelled_map->accepted) return 1; + // The surviving native device must still execute an upload and map after + // its sibling was destroyed, with no presentation or animation-frame pump. + auto survivor_buffer=second_native->device.CreateBuffer(&map_descriptor); + std::vector upload(1024,0x13579bdfu); + second_native->device.GetQueue().WriteBuffer(survivor_buffer,0,upload.data(),4096); + std::fill(upload.begin(),upload.end(),0u); + auto survivor_ticket=mailbox->reserve(21,second_owner).value(); + survivor_buffer.MapAsync(wgpu::MapMode::Read,0,4096,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,survivor_ticket](wgpu::MapAsyncStatus status,wgpu::StringView) { + mailbox->publish(survivor_ticket,status==wgpu::MapAsyncStatus::Success + ? completion_status::success : completion_status::failed); + }); + bool survivor_done=false; + const auto survivor_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(30); + while (!survivor_done && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[] { return false; }); + } + if (!survivor_done) return 1; + const auto* uploaded=static_cast(survivor_buffer.GetConstMappedRange(0,4096)); + if (!uploaded) return 1; + for (size_t i=0;i<1024;++i) if (uploaded[i]!=0x13579bdfu) return 1; + survivor_buffer.Unmap(); + survivor_buffer.Destroy(); + wgpu::ShaderSourceWGSL wgsl{}; + wgsl.code="@compute @workgroup_size(1) fn main() {}"; + wgpu::ShaderModuleDescriptor shader_descriptor{}; + shader_descriptor.nextInChain=&wgsl; + auto shader=second_native->device.CreateShaderModule(&shader_descriptor); + wgpu::ComputePipelineDescriptor pipeline_descriptor{}; + pipeline_descriptor.compute.module=shader; + pipeline_descriptor.compute.entryPoint="main"; + auto pipeline_ticket=mailbox->reserve(24,second_owner).value(); + auto pipeline=std::make_shared(); + second_native->device.CreateComputePipelineAsync(&pipeline_descriptor,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,pipeline_ticket,pipeline](wgpu::CreatePipelineAsyncStatus status,wgpu::ComputePipeline result,wgpu::StringView) { + *pipeline=std::move(result); + mailbox->publish(pipeline_ticket,status==wgpu::CreatePipelineAsyncStatus::Success + ? completion_status::success : completion_status::failed); + }); + bool pipeline_done=false; + const auto pipeline_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(10); + while (!pipeline_done && std::chrono::steady_clock::now()wait_for(root.recommended_idle_wait(std::chrono::milliseconds(100)),[] { return false; }); + } + if (!pipeline_done || !*pipeline) return 1; + // Captured validation errors and empty scopes both complete independently + // of animation frames. A captured error must not poison the next scope. + for (const bool invalid : {true,false}) { + second_native->device.PushErrorScope(wgpu::ErrorFilter::Validation); + wgpu::BufferDescriptor scoped_descriptor{}; + scoped_descriptor.size=4; + scoped_descriptor.usage=invalid ? wgpu::BufferUsage::None : wgpu::BufferUsage::CopyDst; + auto scoped_buffer=second_native->device.CreateBuffer(&scoped_descriptor); + auto error_ticket=mailbox->reserve(invalid ? 25 : 26,second_owner).value(); + second_native->device.PopErrorScope(wgpu::CallbackMode::AllowProcessEvents, + [mailbox,error_ticket,invalid](wgpu::PopErrorScopeStatus status,wgpu::ErrorType type,wgpu::StringView message) { + const bool expected=status==wgpu::PopErrorScopeStatus::Success + && type==(invalid ? wgpu::ErrorType::Validation : wgpu::ErrorType::NoError) + && (!invalid || (message.data && message.length)); + mailbox->publish(error_ticket,expected ? completion_status::success : completion_status::failed); + }); + bool error_done=false; + const auto error_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while (!error_done && std::chrono::steady_clock::now()wait_for(root.recommended_idle_wait(std::chrono::milliseconds(100)),[] { return false; }); + } + if (!error_done || mailbox->metrics().occupied!=0) return 1; + } + for(const bool invalid:{false,true}) { + second_native->device.PushErrorScope(wgpu::ErrorFilter::Validation); + root.with_device(second_owned,[&](auto& device) { + wgpu::ShaderSourceWGSL source{}; + source.code=invalid?"@compute fn broken( {":"@compute @workgroup_size(1) fn main() {}"; + wgpu::ShaderModuleDescriptor descriptor{};descriptor.nextInChain=&source; + auto shader=device.create_shader_module(descriptor); + // Invalid shader modules remain valid API objects; validation is + // delivered through the native scope rather than a null wrapper. + device.with_shader_module(shader,[&](const auto& native) {if(!native)throw std::runtime_error("Shader module wrapper missing");}); + device.release_shader_module(shader); + }); + auto shader_ticket=mailbox->reserve(30,second_owner).value(); + second_native->device.PopErrorScope(wgpu::CallbackMode::AllowProcessEvents, + [mailbox,shader_ticket,invalid](wgpu::PopErrorScopeStatus status,wgpu::ErrorType type,wgpu::StringView) { + mailbox->publish(shader_ticket,status==wgpu::PopErrorScopeStatus::Success + && type==(invalid?wgpu::ErrorType::Validation:wgpu::ErrorType::NoError)?completion_status::success:completion_status::failed); + }); + bool compiled=false;auto deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(!compiled && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[]{return false;}); + } + if(!compiled)return 1; + } + // Keep another adopted device alive across the sibling ForceLoss. + auto isolation_adapter=std::make_shared(); + auto isolation_adapter_ticket=mailbox->reserve(40,second_owner).value(); + service.instance().RequestAdapter(&options,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,isolation_adapter_ticket,isolation_adapter](wgpu::RequestAdapterStatus status,wgpu::Adapter adapter,wgpu::StringView) { + isolation_adapter->adapter=std::move(adapter); + mailbox->publish(isolation_adapter_ticket,status==wgpu::RequestAdapterStatus::Success + ? completion_status::success : completion_status::failed); + }); + bool isolation_adapter_done=false; + const auto isolation_adapter_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(30); + while (!isolation_adapter_done && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[] { return false; }); + } + if (!isolation_adapter_done || !isolation_adapter->adapter) return 1; + auto isolation_native=std::make_shared(); + auto isolation_ticket=mailbox->reserve(41,second_owner).value(); + wgpu::DeviceDescriptor isolation_descriptor{}; + auto isolation_loss=std::make_shared(wake); + device_loss_signal::configure(isolation_descriptor,isolation_loss); + isolation_adapter->adapter.RequestDevice(&isolation_descriptor,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,isolation_ticket,isolation_native](wgpu::RequestDeviceStatus status,wgpu::Device device,wgpu::StringView message) { + if(status!=wgpu::RequestDeviceStatus::Success && message.data) std::cerr << "Isolation device request: " << std::string_view(message.data,message.length) << "\n"; + isolation_native->device=std::move(device); + mailbox->publish(isolation_ticket,status==wgpu::RequestDeviceStatus::Success + ? completion_status::success : completion_status::failed); + }); + const auto isolation_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(30); + bool isolation_done=false; + while (!isolation_done && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[] { return false; }); + } + if (!isolation_done || !isolation_native->device) return 1; + auto isolation_owned=root.adopt_device(isolation_adapter->adapter,isolation_native->device,isolation_loss); + resource_owner isolation_owner{}; + root.with_device(isolation_owned,[&](auto& device) { isolation_owner=device.owner(); }); + if (root.live_devices()!=2 || second_owner==isolation_owner) return 1; + auto loss_ticket=mailbox->reserve(22,second_owner).value(); + auto loss_buffer=second_native->device.CreateBuffer(&map_descriptor); + struct loss_map_result { bool called{},accepted{}; }; + auto loss_map=std::make_shared(); + loss_buffer.MapAsync(wgpu::MapMode::Read,0,4096,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,loss_ticket,loss_map](wgpu::MapAsyncStatus status,wgpu::StringView) { + loss_map->called=true; + loss_map->accepted=mailbox->publish(loss_ticket,status==wgpu::MapAsyncStatus::Success + ? completion_status::success : completion_status::failed); + }); + second_native->device.ForceLoss(wgpu::DeviceLostReason::Unknown,"G02 loss test"); + const auto loss_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while (!second_loss->lost.load(std::memory_order_acquire) && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[] { return false; }); + if (!second_loss->lost.load(std::memory_order_acquire) || !root.has_ready_work()) return 1; + bool loss_delivered=false; + root.pump([&](auto record) { + if (record.operation!=22 || record.status!=completion_status::device_lost) + throw std::runtime_error("device loss did not terminate its pending record"); + loss_delivered=true; + }); + bool lost_rejected=false; + root.with_device(second_owned,[&](auto& device) { + try { device.native(); } catch (const std::logic_error&) { lost_rejected=true; } + }); + const auto map_retirement_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while (!loss_map->called && std::chrono::steady_clock::now()called) wake->wait_for(root.recommended_idle_wait(std::chrono::milliseconds(100)),[] { return false; }); + } + if (!loss_delivered || !lost_rejected || !loss_map->called || loss_map->accepted + || mailbox->metrics().occupied!=0 || mailbox->metrics().native_pending!=0) return 1; + loss_buffer.Destroy(); + root.with_device(isolation_owned,[](auto& device) { (void)device.native(); }); + auto isolated_buffer=isolation_native->device.CreateBuffer(&map_descriptor); + std::vector isolated_upload(1024,0x13579bdfu); + isolation_native->device.GetQueue().WriteBuffer(isolated_buffer,0,isolated_upload.data(),4096); + std::fill(isolated_upload.begin(),isolated_upload.end(),0u); + auto isolated_ticket=mailbox->reserve(42,isolation_owner).value(); + isolated_buffer.MapAsync(wgpu::MapMode::Read,0,4096,wgpu::CallbackMode::AllowProcessEvents, + [mailbox,isolated_ticket](wgpu::MapAsyncStatus status,wgpu::StringView) { + mailbox->publish(isolated_ticket,status==wgpu::MapAsyncStatus::Success + ? completion_status::success : completion_status::failed); + }); + bool isolated_done=false; + const auto isolated_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(30); + while (!isolated_done && std::chrono::steady_clock::now()wait_for(std::chrono::milliseconds(1),[] { return false; }); + } + if (!isolated_done) return 1; + const auto* isolated_uploaded=static_cast(isolated_buffer.GetConstMappedRange(0,4096)); + if (!isolated_uploaded) return 1; + for (size_t i=0;i<1024;++i) if (isolated_uploaded[i]!=0x13579bdfu) return 1; + isolated_buffer.Unmap(); + isolated_buffer.Destroy(); + if(isolation_loss->lost.load(std::memory_order_acquire)) return 1; + root.destroy_device(isolation_owned); + if(root.live_devices()!=1 || mailbox->metrics().occupied!=0) return 1; + auto releases=root.command_endpoint(2,0); + auto release=graphics_service::deferred_device_release(second_owned); + std::thread finalizer([&] { + if (releases->enqueue(release)!=enqueue_result::accepted + || releases->enqueue(release)!=enqueue_result::accepted) std::terminate(); + }); + finalizer.join(); + if (root.live_devices()!=1) return 1; + root.pump([](auto) { throw std::runtime_error("unexpected release completion"); }); + if (root.live_devices()!=0 || mailbox->has_pending() || mailbox->has_ready()) return 1; + service.close(); + bool rejected=false; + try { service.instance(); } catch (const std::logic_error&) { rejected=true; } + if (!rejected) return 1; + // Close before processing the next native request. The promise-side record + // must terminate once; the backend callback may arrive only after teardown. + auto cancelled=std::make_unique(1,wake); + auto retained=cancelled->completions(); + auto pending=retained->reserve(2,owner).value(); + auto late_accepted=std::make_shared(false); + cancelled->instance().RequestAdapter(&options,wgpu::CallbackMode::AllowProcessEvents, + [retained,pending,late_accepted](wgpu::RequestAdapterStatus,wgpu::Adapter,wgpu::StringView) { + *late_accepted=retained->publish(pending,completion_status::success); + }); + cancelled->close(); + size_t terminated=0; + cancelled->pump([&](auto record) { + if (record.operation!=2 || record.status!=completion_status::cancelled) + throw std::runtime_error("pending operation not cancelled"); + ++terminated; + }); + cancelled.reset(); + if (terminated!=1 || *late_accepted || retained->has_ready() + || retained->publish(pending,completion_status::success)) return 1; + std::cout << "Native Dawn adapter/device/submission completion and deferred buffer release passed without RAF/UI\n"; +} catch(const std::exception& error) { + std::cerr << "Dawn event test failed: " << error.what() << '\n'; + return 1; +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_dxgi_contract_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_dxgi_contract_tests.cpp new file mode 100644 index 000000000..8ad2cba8d --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_dxgi_contract_tests.cpp @@ -0,0 +1,30 @@ +#include "graphics/dxgi_bridge_contract.h" +#include +using namespace webscene::graphics; +int main() { + auto require=[](bool value) { if (!value) throw std::runtime_error("DXGI negotiation requirement failed"); }; + dxgi_endpoint producer{{10,-1,true},dxgi_api::d3d12,0x1f,0x7,true,false}; + auto consumer=producer; consumer.api=dxgi_api::d3d11; + image_metadata image{1,2,1,1,3,1,64,64}; + auto choice=choose_dxgi_bridge(producer,consumer,image); + require(choice.status==dxgi_bridge_status::supported && choice.synchronization==dxgi_sync::shared_fence); + consumer.adapter.low=11; require(choose_dxgi_bridge(producer,consumer,image).status==dxgi_bridge_status::cross_adapter); + consumer.adapter=producer.adapter; consumer.adapter.valid=false; + require(choose_dxgi_bridge(producer,consumer,image).status==dxgi_bridge_status::unknown_adapter); + consumer=producer; consumer.color_formats=0; + require(choose_dxgi_bridge(producer,consumer,image).status==dxgi_bridge_status::unsupported_format); + consumer=producer; consumer.alpha_modes=0; + require(choose_dxgi_bridge(producer,consumer,image).status==dxgi_bridge_status::unsupported_alpha); + consumer=producer; require(choose_dxgi_bridge(producer,consumer,image,4).status==dxgi_bridge_status::needs_resolve); + consumer.shared_fence=false; consumer.keyed_mutex=true; producer.keyed_mutex=true; + require(choose_dxgi_bridge(producer,consumer,image).status==dxgi_bridge_status::unsupported_synchronization); + producer.api=consumer.api=dxgi_api::d3d11; + require(choose_dxgi_bridge(producer,consumer,image).synchronization==dxgi_sync::keyed_mutex); + consumer.api=static_cast(99); + require(choose_dxgi_bridge(producer,consumer,image).status==dxgi_bridge_status::unsupported_api); + consumer.api=dxgi_api::d3d11; + image.format=static_cast(UINT32_MAX); + require(choose_dxgi_bridge(producer,consumer,image).status==dxgi_bridge_status::unsupported_format); + image.format=image_format::rgba8_unorm; image.width=0; + require(choose_dxgi_bridge(producer,consumer,image).status==dxgi_bridge_status::invalid_dimensions); +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_fixture_dawn_clear.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_fixture_dawn_clear.h new file mode 100644 index 000000000..103d02be0 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_fixture_dawn_clear.h @@ -0,0 +1,217 @@ +#pragma once +#include "graphics/dawn_iosurface_submission.h" +#include "graphics/dawn_iosurface_canvas_host.h" +#include +#include + +// Synchronous diagnostic producer only; never part of ordinary presentation. +inline std::optional fixture_dawn_clear( + webscene::graphics::iosurface_canvas_images::frame&& frame) { + constexpr auto feature=wgpu::InstanceFeatureName::TimedWaitAny; + wgpu::InstanceDescriptor instanceDescription{}; + instanceDescription.requiredFeatureCount=1; + instanceDescription.requiredFeatures=&feature; + auto instance=wgpu::CreateInstance(&instanceDescription); + if (!instance) return {}; + auto wait=[&](wgpu::Future future) { + return instance.WaitAny(future,30'000'000'000ULL)==wgpu::WaitStatus::Success; + }; + auto adapter=std::make_shared(); + wgpu::RequestAdapterOptions options{}; options.backendType=wgpu::BackendType::Metal; + if (!wait(instance.RequestAdapter(&options,wgpu::CallbackMode::WaitAnyOnly, + [adapter](wgpu::RequestAdapterStatus status,wgpu::Adapter value,wgpu::StringView) { + if (status==wgpu::RequestAdapterStatus::Success) *adapter=std::move(value); + })) || !*adapter) return {}; + wgpu::AdapterInfo info{}; + if (adapter->GetInfo(&info)!=wgpu::Status::Success || + (info.adapterType!=wgpu::AdapterType::IntegratedGPU && info.adapterType!=wgpu::AdapterType::DiscreteGPU)) + return {}; + const wgpu::FeatureName features[]={wgpu::FeatureName::SharedTextureMemoryIOSurface, + wgpu::FeatureName::SharedFenceMTLSharedEvent}; + for (auto required:features) if (!adapter->HasFeature(required)) return {}; + auto error=std::make_shared>(false); + wgpu::DeviceDescriptor deviceDescription{}; + deviceDescription.requiredFeatureCount=2; deviceDescription.requiredFeatures=features; + deviceDescription.SetUncapturedErrorCallback( + [](const wgpu::Device&,wgpu::ErrorType,wgpu::StringView,std::atomic* state) { state->store(true); }, + error.get()); + struct device_storage { std::shared_ptr> error; wgpu::Device device; }; + auto storage=std::make_shared(device_storage{error,{}}); + auto device=std::shared_ptr(storage,&storage->device); + if (!wait(adapter->RequestDevice(&deviceDescription,wgpu::CallbackMode::WaitAnyOnly, + [device](wgpu::RequestDeviceStatus status,wgpu::Device value,wgpu::StringView) { + if (status==wgpu::RequestDeviceStatus::Success) *device=std::move(value); + })) || !*device) return {}; + // Recording rejection must release its slot without ever submitting GPU work. + using namespace webscene::graphics; + iosurface_canvas_images rejected(1024*1024); + auto empty=rejected.acquire(frame.metadata); + if (!empty || dawn_iosurface_submission::submit(std::move(*empty),*device, + [](const wgpu::Texture&) { return wgpu::CommandBuffer{}; },storage)) return {}; + empty.reset(); + if (rejected.busy_images()!=0) return {}; + auto throwing=rejected.acquire(frame.metadata); + if (!throwing) return {}; + bool caught=false; + try { + dawn_iosurface_submission::submit(std::move(*throwing),*device, + [](const wgpu::Texture&) -> wgpu::CommandBuffer { throw std::runtime_error("recording rejected"); },storage); + } catch (const std::runtime_error&) { caught=true; } + throwing.reset(); + if (!caught || rejected.busy_images()!=0) return {}; + struct counted_wake final : completion_wake { + std::atomic count{0}; + void signal() noexcept override { ++count; } + }; + auto invalid_frame=rejected.acquire(frame.metadata); + if (!invalid_frame) return {}; + auto invalid_wake=std::make_shared(); + auto invalid=dawn_iosurface_submission::submit(std::move(*invalid_frame),*device, + [&](const wgpu::Texture&) { + wgpu::BufferDescriptor bad{}; bad.size=4; bad.usage=wgpu::BufferUsage::None; + auto invalid_buffer=device->CreateBuffer(&bad); + return device->CreateCommandEncoder().Finish(); + },storage,invalid_wake); + invalid_frame.reset(); + if (!invalid || !wait(invalid->completion_future()) || !wait(invalid->validation_future()) || + invalid->state()!=dawn_iosurface_submission::status::failed || invalid->take_ready() || + invalid_wake->count.load()!=1 || rejected.busy_images()!=0) return {}; + auto ready_wake=std::make_shared(); + wgpu::Texture expired_texture; + wgpu::TextureDescriptor description{};description.dimension=wgpu::TextureDimension::e2D; + description.size={frame.metadata.width,frame.metadata.height,1};description.format=wgpu::TextureFormat::BGRA8Unorm;description.usage=wgpu::TextureUsage::RenderAttachment; + auto mismatch=description;mismatch.size.width++; + bool mismatch_rejected=false; + try{import_dawn_iosurface_canvas_texture(frame,*device,mismatch);} + catch(const std::invalid_argument&){mismatch_rejected=true;} + if(!mismatch_rejected)return {}; + auto shared=import_dawn_iosurface_canvas_texture(frame,*device,description); + if(shared&&(shared->texture().GetUsage()!=description.usage|| + !shared->matches(*device,frame.color->borrowed_handle())))return {}; + wgpu::SharedTextureMemoryBeginAccessDescriptor access{};access.initialized=false; + if(!shared||!shared->begin(access))return {}; + expired_texture=shared->texture(); + auto encoder=device->CreateCommandEncoder(); + wgpu::RenderPassColorAttachment color{};color.view=shared->texture().CreateView(); + color.loadOp=wgpu::LoadOp::Clear;color.storeOp=wgpu::StoreOp::Store;color.clearValue={0.2,0.4,0.6,1}; + wgpu::RenderPassDescriptor pass{};pass.colorAttachmentCount=1;pass.colorAttachments=&color; + auto recording=encoder.BeginRenderPass(&pass);recording.End();auto commands=encoder.Finish(); + // Model application-owned submission: handoff must not submit this again. + auto foreign=rejected.acquire(frame.metadata);if(!foreign)return {}; + bool foreign_rejected=false; + try{dawn_iosurface_submission::publish_submitted(std::move(*foreign),*device,shared,storage);} + catch(const std::invalid_argument&){foreign_rejected=true;} + if(!foreign_rejected||!foreign->color)return {};foreign.reset(); + device->GetQueue().Submit(1,&commands); + auto submitted=dawn_iosurface_submission::publish_submitted(std::move(frame),*device,shared,storage,ready_wake); + auto captured=submitted ? submitted->capture_snapshot() : nullptr; + if(!captured)return {}; + const auto captured_metadata=captured->describe(); + // EndAccess's local output has been destroyed. Captured ownership must retain + // every exported Metal event/value, independently of callback completion. + const auto& handoff=captured->producer_handoff(); + if(!handoff.initialized || !handoff.fenceCount || + handoff.fenceCount!=handoff.signaledValueCount) return {}; + for(size_t i=0;icompletion_future()) || !wait(submitted->validation_future()) || + (ready_wake->count.load()<1 || ready_wake->count.load()>2) || error->load()) return {}; + if(shared->begin(access)||!shared->expire_texture())return {}; + device->PushErrorScope(wgpu::ErrorFilter::Validation); + auto invalid_view=expired_texture.CreateView(); + auto expired_encoder=device->CreateCommandEncoder(); + wgpu::RenderPassColorAttachment expired_attachment{};expired_attachment.view=invalid_view; + expired_attachment.loadOp=wgpu::LoadOp::Clear;expired_attachment.storeOp=wgpu::StoreOp::Store; + expired_attachment.clearValue={1,0,1,1}; + wgpu::RenderPassDescriptor expired_pass{};expired_pass.colorAttachmentCount=1;expired_pass.colorAttachments=&expired_attachment; + auto expired_recording=expired_encoder.BeginRenderPass(&expired_pass);expired_recording.End(); + auto expired_commands=expired_encoder.Finish();device->GetQueue().Submit(1,&expired_commands); + auto expired_rejected=std::make_shared>(false); + if(!wait(device->PopErrorScope(wgpu::CallbackMode::WaitAnyOnly, + [expired_rejected](wgpu::PopErrorScopeStatus status,wgpu::ErrorType type,wgpu::StringView) { + expired_rejected->store(status==wgpu::PopErrorScopeStatus::Success && type==wgpu::ErrorType::Validation); + })) || !expired_rejected->load())return {}; + // Unconfigure/resize must retire submitted work without producing a scene + // lease. Even an uninitialized current texture is safe to discard. + for(bool submit_work:{false,true}) { + auto discarded_frame=rejected.acquire(frame.metadata); + if(!discarded_frame)return {}; + auto discarded_shared=import_dawn_iosurface_canvas_texture(*discarded_frame,*device,description); + if(!discarded_shared||!discarded_shared->begin(access))return {}; + if(submit_work) { + auto discard_encoder=device->CreateCommandEncoder(); + wgpu::RenderPassColorAttachment discard_color{};discard_color.view=discarded_shared->texture().CreateView(); + discard_color.loadOp=wgpu::LoadOp::Clear;discard_color.storeOp=wgpu::StoreOp::Store;discard_color.clearValue={1,0,1,1}; + wgpu::RenderPassDescriptor discard_pass{};discard_pass.colorAttachmentCount=1;discard_pass.colorAttachments=&discard_color; + auto discard_recording=discard_encoder.BeginRenderPass(&discard_pass);discard_recording.End(); + auto discard_commands=discard_encoder.Finish();device->GetQueue().Submit(1,&discard_commands); + } + auto discarded_wake=std::make_shared(); + auto discarded=dawn_iosurface_submission::publish_submitted(std::move(*discarded_frame),*device,discarded_shared,storage,discarded_wake,false); + discarded_frame.reset(); + if(!discarded||!wait(discarded->completion_future())||!wait(discarded->validation_future())|| + discarded->state()!=dawn_iosurface_submission::status::discarded||discarded->take_ready()|| + discarded_wake->count.load()!=1||rejected.busy_images()!=0||error->load())return {}; + if(discarded_shared->begin(access)||!discarded_shared->expire_texture())return {}; + } + dawn_iosurface_canvas_host canvas_provider(1024*1024); + auto host_texture=canvas_provider.acquire(frame.metadata,*device,description,storage); + if(!host_texture)return {}; + bool duplicate_acquire=false; + try{canvas_provider.acquire(frame.metadata,*device,description,storage);} + catch(const std::logic_error&){duplicate_acquire=true;} + if(!duplicate_acquire)return {}; + bool foreign_retirement=false; + try{canvas_provider.retire(expired_texture,false);} + catch(const std::invalid_argument&){foreign_retirement=true;} + if(!foreign_retirement)return {}; + canvas_provider.retire(host_texture,false); + if(canvas_provider.capture_latest_submission())return {}; + auto host_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(!canvas_provider.idle()&&std::chrono::steady_clock::now()take_ready(); + if (submitted->take_ready()) return {}; // A publication transfers once. + if(!image)return {}; + if(image->describe().allocation!=captured_metadata.allocation || + image->describe().content_serial!=captured_metadata.content_serial)return {}; + image.reset(); + // Draining the ordinary provider reference must not destroy a frozen + // scene's exact output. Its completion gate still resolves independently. + auto captured_image=captured->take_ready(); + if(!captured_image || captured->take_ready() || + captured_image->describe().allocation!=captured_metadata.allocation || + captured_image->describe().content_serial!=captured_metadata.content_serial)return {}; + captured_image.reset();captured.reset(); + host_texture=canvas_provider.acquire(frame.metadata,*device,description,storage); + if(!host_texture)return {}; + auto host_encoder=device->CreateCommandEncoder(); + color.view=host_texture.CreateView(); + auto host_recording=host_encoder.BeginRenderPass(&pass);host_recording.End(); + auto host_commands=host_encoder.Finish();device->GetQueue().Submit(1,&host_commands); + canvas_provider.retire(host_texture,true); + auto provider_capture=canvas_provider.capture_latest_submission(); + if(!provider_capture)return {}; + const auto provider_metadata=provider_capture->describe(); + host_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + do { + auto ready=canvas_provider.take_ready(); + if(ready) { + auto captured_ready=provider_capture->take_ready(); + if(!captured_ready || provider_capture->take_ready() || + captured_ready->describe().allocation!=ready->describe().allocation || + captured_ready->describe().content_serial!=provider_metadata.content_serial)return {}; + ready.reset(); + return captured_ready; + } + instance.ProcessEvents();std::this_thread::sleep_for(std::chrono::milliseconds(1)); + }while(std::chrono::steady_clock::now() +#include +using namespace webscene::graphics; +void require(bool v) { if (!v) throw std::runtime_error("image lease requirement failed"); } +template void rejects(F f) { bool failed=false; try { f(); } catch(const std::invalid_argument&) { failed=true; } require(failed); } +image_metadata metadata{1,2,1,3,4,5,640,480}; +image_write_token write(image_lease_pool& pool) { + auto writer=pool.acquire_write().value(); + auto physical=metadata; physical.allocation=10+writer.slot; + pool.set_metadata(writer,physical); + return writer; +} +std::optional submit(image_lease_pool& pool,image_write_token writer) { + pool.begin_producer(writer); + return pool.publish(writer); +} +void test_capacity_wake() { + struct capacity_wake final : completion_wake { + image_lease_pool* pool{}; + engine_wake latched; + size_t signals{},last_busy{}; + void signal() noexcept override { + last_busy=pool->busy_images(); // Must run outside the pool mutex. + ++signals; latched.signal(); + } + }; + auto wake=std::make_shared(); + image_lease_pool pool(1,wake); wake->pool=&pool; + auto writer=write(pool); auto retained=submit(pool,writer).value(); + require(!pool.retain(retained) && !pool.begin_consumer(retained)); + pool.finish_producer(writer); require(wake->signals==0); // No new capacity yet. + const auto cpu_only=pool.inspect_occupancy(); + require(cpu_only.busy==1 && cpu_only.producer_pending==0 + && cpu_only.retained==1 && cpu_only.consumer_pending==0); + std::thread release([&] { pool.release(retained); }); release.join(); + require(wake->signals==1 && wake->last_busy==0); + const auto idle=pool.inspect_occupancy(); + require(idle.busy==0 && idle.producer_pending==0 && idle.retained==0 && idle.consumer_pending==0); + require(wake->latched.wait_for(std::chrono::milliseconds(0),[] { return false; })); + auto abandoned=write(pool); pool.cancel_write(abandoned); + require(wake->signals==2); + writer=write(pool); retained=submit(pool,writer).value(); + pool.release(retained); require(wake->signals==3 && wake->last_busy==1); + pool.finish_producer(writer); require(wake->signals==4 && wake->last_busy==0); + rejects([&] { pool.finish_producer(writer); }); require(wake->signals==4); +} +void test_four_image_capacity() { + rejects([] { image_lease_pool invalid(128, {}, 5); }); + image_lease_pool pool(128, {}, 4); + std::array writers; + std::array scenes; + for (size_t i=0;i& destroyed; + explicit provider(std::atomic& count):destroyed(count) {} + ~provider() override { ++destroyed; } + }; + std::atomic destroyed=0; + auto backend=std::make_shared(destroyed); + std::weak_ptr weak=backend; + auto owner=std::make_unique(backend); + backend.reset(); + auto producer=owner->acquire(); + producer->set_metadata(metadata); producer->begin(); + auto scene=producer->publish(); + auto redraw=scene->retain(); + auto pending=scene->begin_consumer(); + const auto occupied=owner->inspect_occupancy(); + require(occupied.busy==1 && occupied.producer_pending==1 + && occupied.retained==1 && occupied.consumer_pending==1); + scene.reset(); + owner.reset(); // Engine/canvas ownership ends; GPU use is still outstanding. + require(!weak.expired() && destroyed==0); + producer->complete(); producer.reset(); + require(redraw->describe().allocation==metadata.allocation); + auto second=redraw->begin_consumer(); // Retained redraw after engine disposal. + redraw.reset(); + pending->complete(); pending.reset(); + require(!weak.expired() && destroyed==0); + std::thread completion([use=std::move(*second)]() mutable { + require(use.describe().content_serial==metadata.content_serial); + use.complete(); + rejects([&] { use.complete(); }); + }); + second.reset(); completion.join(); + require(weak.expired() && destroyed==1); + + auto bounded=std::make_unique(std::make_shared(destroyed),1); + { auto abandoned=bounded->acquire(); } // Unsubmitted writers cancel automatically. + require(bounded->busy_images()==0); + auto frame=bounded->acquire(); frame->set_metadata(metadata); frame->begin(); + auto held=frame->publish(); + require(!held->retain() && !held->begin_consumer()); // Ticket backpressure. + frame->complete(); frame.reset(); + require(bounded->busy_images()==1); + held.reset(); require(bounded->busy_images()==0); + bounded->close(); require(!bounded->acquire()); + bounded.reset(); require(destroyed==2); +} +void test_cache_eviction_reservations() { + auto provider=std::make_shared(); + owned_image_pool pool(provider); + auto producer=pool.acquire(); + const auto protected_slot=producer->slot(); + producer->set_metadata(metadata); producer->begin(); + auto scene=producer->publish(); + auto consumer=scene->begin_consumer(); + producer->complete(); producer.reset(); scene.reset(); + // A GPU consumer alone protects its allocation from cache eviction. + auto first=pool.acquire(); auto second=pool.acquire(); + require(first && second && first->slot()!=second->slot() + && first->slot()!=protected_slot && second->slot()!=protected_slot && !pool.acquire()); + first->cancel(false); second->cancel(false); + require(pool.busy_images()==1); + consumer->complete(); consumer.reset(); + auto reusable=pool.acquire(); + require(reusable && reusable->slot()==protected_slot); +} +int main() { + test_four_image_capacity(); + test_cache_eviction_reservations(); + test_capacity_wake(); + test_owned_lifetime(); + image_lease_pool pool; + auto a=write(pool),b=write(pool),c=write(pool); + require(!pool.acquire_write() && pool.busy_images()==3); + auto scene=submit(pool,a).value(); + auto retained=pool.retain(scene).value(); + auto gpu=pool.begin_consumer(scene).value(); + auto gpu_second=pool.begin_consumer(scene).value(); + require(pool.describe(gpu).allocation==10+a.slot); + rejects([&] { pool.set_metadata(a,metadata); }); + pool.release(scene); + rejects([&] { pool.describe(scene); }); + rejects([&] { pool.release(scene); }); + pool.finish_producer(a); + pool.release(retained); + require(pool.describe(gpu).width==640 && pool.describe(gpu).producer_timeline==4); + require(!pool.acquire_write()); // Scene release cannot finish consumer work. + rejects([&] { pool.release(gpu); }); + std::thread presenter([&] { pool.finish_consumer(gpu); }); presenter.join(); + rejects([&] { pool.finish_consumer(gpu); }); + require(!pool.acquire_write()); + pool.finish_consumer(gpu_second); + auto reused=write(pool); + require(reused.slot==a.slot && reused.generation!=a.generation); + rejects([&] { pool.finish_producer(a); }); + rejects([&] { pool.finish_consumer(gpu); }); + pool.cancel_write(reused); pool.cancel_write(b); pool.cancel_write(c); + require(pool.busy_images()==0); + auto frame=write(pool); + auto reference=submit(pool,frame).value(); + auto consumer=pool.begin_consumer(reference).value(); + pool.close(); + require(!pool.acquire_write()); + auto redraw=pool.retain(reference).value(); // Retained redraw survives close. + pool.release(reference); pool.release(redraw); pool.finish_consumer(consumer); + require(pool.busy_images()==1); // Producer notification can arrive last. + pool.finish_producer(frame); require(pool.busy_images()==0); + image_lease_pool bounded(1); + auto writer=write(bounded),waiting=write(bounded); + auto lease=submit(bounded,writer).value(); + require(!bounded.retain(lease) && !bounded.begin_consumer(lease) && !submit(bounded,waiting)); + bounded.release(lease); bounded.finish_producer(writer); + bounded.finish_producer(waiting); // GPU may finish before CPU publication. + auto published=bounded.publish(waiting).value(); + bounded.release(published); + require(bounded.busy_images()==0); + image_lease_pool invalid; + auto unconfigured=invalid.acquire_write().value(); + rejects([&] { invalid.publish(unconfigured); }); + auto invalid_metadata=metadata; invalid_metadata.width=0; + rejects([&] { invalid.set_metadata(unconfigured,invalid_metadata); }); + invalid_metadata=metadata; invalid_metadata.format=static_cast(999); + rejects([&] { invalid.set_metadata(unconfigured,invalid_metadata); }); + invalid.cancel_write(unconfigured); + auto abandoned=write(invalid); + rejects([&] { invalid.finish_producer(abandoned); }); + invalid.begin_producer(abandoned); + rejects([&] { invalid.begin_producer(abandoned); }); + rejects([&] { invalid.cancel_write(abandoned); }); + rejects([&] { invalid.set_metadata(abandoned,metadata); }); + invalid.close(); + require(invalid.busy_images()==1); + invalid.finish_producer(abandoned); + invalid.cancel_write(abandoned); + require(invalid.busy_images()==0); + image_lease_pool resize; + auto old_frame=write(resize); + auto old_scene=submit(resize,old_frame).value(); + auto new_frame=write(resize); + auto resized=metadata; resized.width=800; resized.allocation=6; resized.allocation_generation=2; + resize.set_metadata(new_frame,resized); + auto alias=resize.describe(old_scene); + alias.allocation_generation=99; + rejects([&] { resize.set_metadata(new_frame,alias); }); + auto new_scene=submit(resize,new_frame).value(); + require(resize.describe(old_scene).width==640 && resize.describe(old_scene).allocation_generation==1); + require(resize.describe(new_scene).width==800 && resize.describe(new_scene).allocation_generation==2); + rejects([&] { pool.describe(old_scene); }); + resize.release(old_scene); resize.finish_producer(old_frame); + resize.finish_producer(new_frame); resize.release(new_scene); + require(resize.busy_images()==0); + std::cout << "three-slot backpressure, retained scenes and independent GPU completion passed\n"; +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_iosurface_color_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_iosurface_color_tests.cpp new file mode 100644 index 000000000..31c805cd9 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_iosurface_color_tests.cpp @@ -0,0 +1,48 @@ +#include "graphics/iosurface_color.h" +#include "graphics/iosurface_canvas_images.h" +#include +int main() { + using webscene::graphics::iosurface_color; + if (iosurface_color::create_bgra8(0,4,1024*1024) || + iosurface_color::create_bgra8(17,0,1024*1024) || + iosurface_color::create_bgra8(UINT32_MAX,4,UINT64_MAX) || + iosurface_color::create_bgra8(17,4,17*4*4)) return 1; + auto image=iosurface_color::create_bgra8(17,4,1024*1024); + if (!image || image->allocation_bytes()>1024*1024 || + IOSurfaceGetWidth(image->borrowed_handle())!=17 || + IOSurfaceGetHeight(image->borrowed_handle())!=4) return 2; + const auto bytes=image->allocation_bytes(); + if (iosurface_color::create_bgra8(17,4,bytes-1)) return 3; + auto exact=iosurface_color::create_bgra8(17,4,bytes); + if (!exact || exact->allocation_bytes()!=bytes) return 4; + using namespace webscene::graphics; + auto pool=std::make_unique(3*bytes); + image_metadata first{700,0,1,1,701,1,17,4,image_format::bgra8_unorm}; + auto a=pool->acquire(first); + if (!a) return 5; + a->producer.begin(); + auto old=a->producer.publish(); + a->producer.complete(); a.reset(); + if (!old) return 6; + auto reader=old->begin_consumer(); + first.width=9; first.allocation_generation=2; first.content_serial=2; first.producer_value=2; + auto b=pool->acquire(first); + if (!b) return 7; + b->producer.begin(); + auto resized=b->producer.publish(); + b->producer.complete(); b.reset(); + auto newReader=resized->begin_consumer(); + old.reset(); resized.reset(); + pool.reset(); + bool retained=IOSurfaceGetWidth(iosurface_canvas_images::resolve(*reader).borrowed_handle())==17 && + IOSurfaceGetWidth(iosurface_canvas_images::resolve(*newReader).borrowed_handle())==9; + reader->complete(); newReader->complete(); + if (!retained) return 8; + iosurface_canvas_images bounded(3*bytes); + auto one=bounded.acquire(first), two=bounded.acquire(first), three=bounded.acquire(first); + if (!one || !two || !three || bounded.acquire(first)) return 9; + one.reset(); two.reset(); three.reset(); + if (bounded.busy_images()!=0) return 10; + std::cout << "IOSurface padded budget verified: " << bytes << " bytes\n"; + return 0; +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_iosurface_fixture.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_iosurface_fixture.cpp new file mode 100644 index 000000000..69e569aed --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_iosurface_fixture.cpp @@ -0,0 +1,125 @@ +#define GL_SILENCE_DEPRECATION +#include +#include +// Test-only provider factory. Never linked into or installed with the runtime. +#include "graphics/image_lease_abi.h" +#include "graphics/iosurface_canvas_images.h" +#include "graphics_fixture_dawn_clear.h" +namespace { +std::weak_ptr observed; +} +static uint8_t create_iosurface(webscene_gpu_image_lease_v3** result,bool paint) { + if (!result) return 0; + *result=nullptr; + try { + using namespace webscene::graphics; + iosurface_canvas_images pool(1024*1024); + auto frame=pool.acquire({700,0,1,1,701,1,17,4,image_format::bgra8_unorm}); + if (!frame) return 0; + auto image=[&]() -> std::optional { + if (paint) return fixture_dawn_clear(std::move(*frame)); + frame->producer.begin(); + auto unpainted=frame->producer.publish(); + frame->producer.complete(); + return unpainted; + }(); + frame.reset(); // The diagnostic wait has completed any submitted producer. + if (!image) return 0; + auto observer=image->begin_consumer(); + if (!observer) return 0; + observed=observer->provider(); + observer->complete(); + auto lease=std::make_unique(std::move(*image)); + *result=lease.release(); + return 1; + } catch (...) { return 0; } +} +extern "C" __attribute__((visibility("default"))) uint8_t webscene_test_create_iosurface(webscene_gpu_image_lease_v3** result) { + return create_iosurface(result,false); +} +extern "C" __attribute__((visibility("default"))) uint8_t webscene_test_create_dawn_iosurface(webscene_gpu_image_lease_v3** result) { + return create_iosurface(result,true); +} +extern "C" __attribute__((visibility("default"))) uint8_t webscene_test_iosurface_alive() { + return observed.expired() ? 0 : 1; +} + +// CGL context is confined to the test's calling thread. The fixture links only +// Apple's OpenGL for GL symbols, avoiding the runtime's separate ANGLE entrypoints. +#include +#include +namespace { +thread_local CGLContextObj fixture_context=nullptr, previous_context=nullptr; +thread_local GLuint fixture_texture=0, fixture_destination=0; +} +extern "C" __attribute__((visibility("default"))) void webscene_test_end_cgl() { + if (!fixture_context) return; + CGLSetCurrentContext(fixture_context); + glFinish(); // Diagnostic failure cleanup only. + if (fixture_texture) glDeleteTextures(1,&fixture_texture); + if (fixture_destination) glDeleteTextures(1,&fixture_destination); + CGLSetCurrentContext(previous_context); + CGLReleaseContext(fixture_context); + fixture_context=nullptr; previous_context=nullptr; fixture_texture=0; fixture_destination=0; +} +extern "C" __attribute__((visibility("default"))) uint8_t webscene_test_begin_cgl() { + if (fixture_context) return 0; + const CGLPixelFormatAttribute attributes[]={kCGLPFAAccelerated,kCGLPFAOpenGLProfile, + static_cast(kCGLOGLPVersion_3_2_Core), + static_cast(0)}; + CGLPixelFormatObj format=nullptr; GLint count=0; + if (CGLChoosePixelFormat(attributes,&format,&count)!=kCGLNoError || !format) return 0; + previous_context=CGLGetCurrentContext(); + const auto status=CGLCreateContext(format,nullptr,&fixture_context); + CGLReleasePixelFormat(format); + if (status!=kCGLNoError || !fixture_context) return 0; + if (CGLSetCurrentContext(fixture_context)!=kCGLNoError) { webscene_test_end_cgl(); return 0; } + glGenTextures(1,&fixture_texture); glBindTexture(GL_TEXTURE_RECTANGLE,fixture_texture); + if (!fixture_texture || glGetError()!=GL_NO_ERROR) { webscene_test_end_cgl(); return 0; } + return 1; +} +extern "C" __attribute__((visibility("default"))) uint8_t webscene_test_cgl_image_bound() { + if (!fixture_context || CGLGetCurrentContext()!=fixture_context) return 0; + GLint width=0,height=0; + glGetTexLevelParameteriv(GL_TEXTURE_RECTANGLE,0,GL_TEXTURE_WIDTH,&width); + glGetTexLevelParameteriv(GL_TEXTURE_RECTANGLE,0,GL_TEXTURE_HEIGHT,&height); + return width==17 && height==4 && glGetError()==GL_NO_ERROR; +} + + +// Queue an actual GPU read of the imported source before the managed fence. +// This is one GPU-local copy; there is no CPU transport between APIs. +extern "C" __attribute__((visibility("default"))) uint8_t webscene_test_cgl_copy() { + if (!fixture_context || CGLGetCurrentContext()!=fixture_context || fixture_destination) return 0; + glGenTextures(1,&fixture_destination); + glBindTexture(GL_TEXTURE_2D,fixture_destination); + glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,17,4,0,GL_RGBA,GL_UNSIGNED_BYTE,nullptr); + GLuint framebuffers[2]{}; glGenFramebuffers(2,framebuffers); + glBindFramebuffer(GL_READ_FRAMEBUFFER,framebuffers[0]); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_RECTANGLE,fixture_texture,0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER,framebuffers[1]); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,fixture_destination,0); + bool valid=glCheckFramebufferStatus(GL_READ_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE && + glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE; + if (valid) glBlitFramebuffer(0,0,17,4,0,0,17,4,GL_COLOR_BUFFER_BIT,GL_NEAREST); + valid &= glGetError()==GL_NO_ERROR; + glBindFramebuffer(GL_FRAMEBUFFER,0); glDeleteFramebuffers(2,framebuffers); + return valid ? 1 : 0; +} + +// Diagnostic readback occurs only after source-consumer fence retirement, +// from independently owned destination storage. +extern "C" __attribute__((visibility("default"))) uint8_t webscene_test_cgl_pixels() { + if (!fixture_context || CGLGetCurrentContext()!=fixture_context || !fixture_destination) return 0; + GLuint framebuffer=0; glGenFramebuffers(1,&framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER,framebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,fixture_destination,0); + bool valid=glCheckFramebufferStatus(GL_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE; + std::array pixels{}; + if (valid) glReadPixels(0,0,17,4,GL_RGBA,GL_UNSIGNED_BYTE,pixels.data()); + const int expected[]={51,102,153,255}; + for (size_t i=0;i +#import +#include +#include +#include +#include "../native/graphics/dawn_metal_producer_wait.h" + +// Hardware dependency test, not a presentation/FPS or Dawn/Skia interop test. +int main() { + @autoreleasepool { + id device=MTLCreateSystemDefaultDevice(); + if(!device) { std::fprintf(stderr,"Metal hardware unavailable\n"); return 1; } + id producer=[device newCommandQueue]; + id consumer=[device newCommandQueue]; + id gate=[device newSharedEvent]; + id ready=[device newSharedEvent]; + id buffer=[device newBufferWithLength:4096 options:MTLResourceStorageModeShared]; + if(!producer || !consumer || !gate || !ready || !buffer) return 2; + memset(buffer.contents,0,buffer.length); + id write=[producer commandBuffer]; + [write encodeWaitForEvent:gate value:1]; + id fill=[write blitCommandEncoder]; + [fill fillBuffer:buffer range:NSMakeRange(0,buffer.length) value:0x37]; + [fill endEncoding]; + [write encodeSignalEvent:ready value:7]; + [write commit]; + id second=[device newSharedEvent]; + const webscene::graphics::metal_producer_dependency invalid[]={{ready,7},{nil,1}}; + if(webscene::graphics::submit_metal_producer_waits(consumer,invalid)) { + gate.signaledValue=1; return 6; + } + const webscene::graphics::metal_producer_dependency dependencies[]={{ready,7},{second,11}}; + const wgpu::InstanceFeatureName timed=wgpu::InstanceFeatureName::TimedWaitAny; + wgpu::InstanceDescriptor instance_desc{}; instance_desc.requiredFeatureCount=1; instance_desc.requiredFeatures=&timed; + auto instance=wgpu::CreateInstance(&instance_desc); + wgpu::Adapter adapter; + wgpu::RequestAdapterOptions options{}; options.backendType=wgpu::BackendType::Metal; + auto request=instance.RequestAdapter(&options,wgpu::CallbackMode::WaitAnyOnly, + [&adapter](wgpu::RequestAdapterStatus status,wgpu::Adapter result,wgpu::StringView) { + if(status==wgpu::RequestAdapterStatus::Success) adapter=std::move(result); + }); + if(instance.WaitAny(request,5'000'000'000ULL)!=wgpu::WaitStatus::Success || !adapter) { + gate.signaledValue=1; return 9; + } + const wgpu::FeatureName feature=wgpu::FeatureName::SharedFenceMTLSharedEvent; + wgpu::DeviceDescriptor device_desc{}; device_desc.requiredFeatureCount=1; device_desc.requiredFeatures=&feature; + wgpu::Device dawn; + request=adapter.RequestDevice(&device_desc,wgpu::CallbackMode::WaitAnyOnly, + [&dawn](wgpu::RequestDeviceStatus status,wgpu::Device result,wgpu::StringView) { + if(status==wgpu::RequestDeviceStatus::Success) dawn=std::move(result); + }); + if(instance.WaitAny(request,5'000'000'000ULL)!=wgpu::WaitStatus::Success || !dawn) { + gate.signaledValue=1; return 10; + } + wgpu::SharedFence fences[2]; + uint64_t values[2]={7,11}; + for(size_t i=0;i<2;++i) { + wgpu::SharedFenceMTLSharedEventDescriptor metal; + metal.sharedEvent=(__bridge void*)dependencies[i].event; + wgpu::SharedFenceDescriptor desc{};desc.nextInChain=&metal; + fences[i]=dawn.ImportSharedFence(&desc); + } + if(webscene::graphics::submit_dawn_metal_producer_waits(consumer,fences,std::span(values,1))) { + gate.signaledValue=1;second.signaledValue=11;return 11; + } + auto barrier=webscene::graphics::submit_dawn_metal_producer_waits(consumer,fences,values); + if(!barrier) { gate.signaledValue=1; return 7; } + id read=[consumer commandBuffer]; + id result=[device newBufferWithLength:4096 options:MTLResourceStorageModeShared]; + id copy=[read blitCommandEncoder]; + [copy copyFromBuffer:buffer sourceOffset:0 toBuffer:result destinationOffset:0 size:4096]; + [copy endEncoding]; + [read commit]; + // CPU submission must return while the producer is deliberately blocked. + // The copy above is diagnostic verification only, never pixel transport. + std::this_thread::sleep_for(std::chrono::milliseconds(30)); + const bool completed_before_signal=read.status==MTLCommandBufferStatusCompleted || ready.signaledValue>=7; + gate.signaledValue=1; + if(completed_before_signal) { second.signaledValue=11; return 3; } + const auto producer_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(ready.signaledValue<7 && std::chrono::steady_clock::now()(result.contents); + for(unsigned i=0;i<4096;++i) if(bytes[i]!=0x37) return 5; + std::puts("Metal delayed producer dependency passed; CPU submission nonblocking; physical presentation unverified"); + return 0; + } +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_nt_handle_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_nt_handle_tests.cpp new file mode 100644 index 000000000..721cc06bc --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_nt_handle_tests.cpp @@ -0,0 +1,82 @@ +#include "graphics/nt_handle.h" +#include "graphics/dxgi_device_identity.h" +#include "graphics/d3d12_shared_color.h" +#include "graphics/d3d12_canvas_images.h" +#include "graphics/d3d12_fence_waits.h" +#include +using namespace webscene::graphics; +struct test_ops { + using handle_type=int; + static inline std::set live; + static inline int next=1,closed=0; + static inline bool fail=false; + static int empty() noexcept { return 0; } + static bool valid(int value) noexcept { return value>0; } + static int create() { live.insert(next); return next++; } + static int duplicate(int source) { + if (fail || !live.contains(source)) throw std::runtime_error("duplicate failed"); + return create(); + } + static void close(int value) noexcept { + if (live.erase(value)!=1) std::terminate(); + ++closed; + } +}; +int main() { + auto require=[](bool value) { if (!value) throw std::runtime_error("NT handle ownership requirement failed"); }; + using owned=unique_nt_handle; + const auto borrowed=test_ops::create(); + { + auto copy=owned::duplicate(borrowed); + require(copy.get()!=borrowed && test_ops::live.contains(borrowed)); + auto moved=std::move(copy); require(!copy && moved); + auto replacement=owned::adopt(test_ops::create()); + replacement=std::move(moved); require(!moved && test_ops::closed==1); + replacement.reset(); replacement.reset(); require(test_ops::closed==2); + test_ops::fail=true; + bool failed=false; + try { auto unexpected=owned::duplicate(borrowed); } + catch (const std::runtime_error&) { failed=true; } + require(failed && test_ops::live.size()==1); + test_ops::fail=false; + auto transferred=owned::duplicate(borrowed); + const auto raw=transferred.release(); require(!transferred); test_ops::close(raw); + } + require(test_ops::live.contains(borrowed) && test_ops::live.size()==1); + test_ops::close(borrowed); require(test_ops::live.empty()); +#if defined(_WIN32) + require(dxgi_color_format(image_format::rgba8_unorm)==DXGI_FORMAT_R8G8B8A8_UNORM + && dxgi_color_format(image_format::bgra8_unorm)==DXGI_FORMAT_B8G8R8A8_UNORM + && dxgi_color_format(image_format::rgba16_float)==DXGI_FORMAT_R16G16B16A16_FLOAT + && dxgi_color_format(image_format::rgba8_srgb)==DXGI_FORMAT_R8G8B8A8_UNORM_SRGB + && dxgi_color_format(image_format::bgra8_srgb)==DXGI_FORMAT_B8G8R8A8_UNORM_SRGB + && dxgi_color_format(static_cast(0))==DXGI_FORMAT_UNKNOWN); + uint32_t formats=31; + require(query_dxgi_color_formats(static_cast(nullptr),formats)==E_INVALIDARG && formats==0); + formats=31; + require(query_dxgi_color_formats(static_cast(nullptr),formats)==E_INVALIDARG && formats==0); + d3d12_fence_waits unopened_waits; + require(unopened_waits.enqueue()==E_UNEXPECTED); + std::unique_ptr waits; + require(d3d12_fence_waits::prepare(nullptr,{1,2,true},{},waits)==E_INVALIDARG && !waits); + bool invalid_pool=false; + try { d3d12_canvas_images invalid(nullptr,1024); } + catch (const std::invalid_argument&) { invalid_pool=true; } + require(invalid_pool); + std::unique_ptr color; + require(d3d12_shared_color::create(nullptr,{},1024,color)==E_INVALIDARG && !color); + adapter_luid identity{1,2,true}; + require(query_adapter_luid(static_cast(nullptr),identity)==E_INVALIDARG && !identity.valid); + identity={1,2,true}; + require(query_adapter_luid(static_cast(nullptr),identity)==E_INVALIDARG && !identity.valid); + dxgi_endpoint endpoint{{1,2,true},dxgi_api::d3d11,31,7,true,true}; + require(identify_dxgi_endpoint(static_cast(nullptr),endpoint)==E_INVALIDARG + && !endpoint.adapter.valid && endpoint.api==dxgi_api::d3d12 && !endpoint.color_formats + && !endpoint.alpha_modes && !endpoint.shared_fence && !endpoint.keyed_mutex); + auto original=owned_nt_handle::adopt(::CreateEventW(nullptr,TRUE,FALSE,nullptr)); + auto duplicate=owned_nt_handle::duplicate(original.get()); + original.reset(); + require(::SetEvent(duplicate.get())!=0 && ::WaitForSingleObject(duplicate.get(),0)==WAIT_OBJECT_0); + duplicate.reset(); +#endif +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_queue_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_queue_tests.cpp new file mode 100644 index 000000000..43adaabb9 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_queue_tests.cpp @@ -0,0 +1,84 @@ +#include "graphics/work_queue.h" +#include +#include +#include +#include +using namespace webscene::graphics; +void require(bool value) { if (!value) throw std::runtime_error("requirement failed"); } +int main() { + work_queue queue(2, 4); + std::array source{std::byte{42}}; + require(queue.try_push(1, source) == enqueue_result::accepted); + source[0] = std::byte{99}; + require(queue.try_push(2) == enqueue_result::accepted); + require(queue.try_push(3) == enqueue_result::full); + queue.consume_one([&](auto command, auto upload, auto serial) { + require(command == 1 && serial == 1 && upload[0] == std::byte{42}); + require(queue.try_push(3) == enqueue_result::full); + }); + require(queue.try_push(3) == enqueue_result::accepted); + queue.close(); + require(queue.try_push(4) == enqueue_result::closed); + for (uint64_t expected = 2; expected <= 3; ++expected) + require(queue.consume_one([&](auto command, auto, auto serial) { require(command == expected && serial == expected); })); + require(queue.metrics().depth == 0 && queue.metrics().high_water == 2 && queue.metrics().upload_bytes == 4); + // Saturated producer and consumer progress without RAF, UI or GPU callbacks. + work_queue concurrent(8, 0); + constexpr uint64_t count = 100000; + std::thread producer([&] { + for (uint64_t i = 1; i <= count; ++i) + while (concurrent.try_push(i) == enqueue_result::full) std::this_thread::yield(); + concurrent.close(); + }); + uint64_t expected = 1; + while (expected <= count) { + if (!concurrent.consume_one([&](auto command, auto, auto serial) { + require(command == expected && serial == expected); ++expected; + })) std::this_thread::yield(); + } + producer.join(); + require(concurrent.metrics().depth == 0 && concurrent.metrics().high_water <= 8); + // Multiple producers copy call-time bytes into bounded slots. Admission + // serials define the global order; each producer's own sequence stays FIFO. + work_queue uploads(8,32); + constexpr uint64_t producers=4, per_producer=5000; + std::vector writers; + for(uint64_t writer=0;writer bytes; + for(size_t index=0;index((command+index)%251); + enqueue_result result; + do { + result=uploads.try_push(command,bytes); + if(result==enqueue_result::full) std::this_thread::yield(); + } while(result==enqueue_result::full); + require(result==enqueue_result::accepted); + bytes.fill(std::byte{255}); // Mutation cannot change an accepted upload. + } + }); + std::array sequences{}; + uint64_t delivered=0; + while(delivered((command+index)%251)); + std::this_thread::yield(); // Slot must stay owned during consumption. + } + ++delivered; + })) std::this_thread::yield(); + } + for(auto& writer:writers) writer.join(); + uploads.close(); + const auto upload_metrics=uploads.metrics(); + require(upload_metrics.depth==0 && upload_metrics.high_water<=8); + require(upload_metrics.accepted==producers*per_producer); + require(upload_metrics.upload_bytes==producers*per_producer*32); + std::cout << "graphics queue call-time copy, FIFO, backpressure and concurrent stress passed\n"; +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_resource_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_resource_tests.cpp new file mode 100644 index 000000000..b8be9d94e --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_resource_tests.cpp @@ -0,0 +1,74 @@ +#include "graphics/resource_table.h" +#include +using namespace webscene::graphics; +void require(bool value) { if (!value) throw std::runtime_error("requirement failed"); } +template void rejects(F action) { + bool rejected = false; + try { action(); } catch (const std::exception&) { rejected = true; } + require(rejected); +} +struct tracked { + int& live; + explicit tracked(int& count) : live(count) { ++live; } + ~tracked() { --live; } +}; +int main() { + int live = 0; + resource_owner a{new_owner_token(), new_owner_token(), new_owner_token()}; + auto b = a; b.device = new_owner_token(); + resource_table table(2, a), foreign(2, b); + resource_table zero(0,a); + require(!zero.can_insert() && table.can_insert()); + auto retained=std::make_unique(live); + bool insertion_rejected=false; + std::thread bad_insert([&] { + try { table.insert(a,std::move(retained)); } + catch (const std::logic_error&) { insertion_rejected=true; } + }); + bad_insert.join(); + require(insertion_rejected && retained && live==1); + rejects([&] { table.insert(b,std::move(retained)); }); + require(retained && live==1); + auto first = table.insert(a,std::move(retained)); + require(!retained && live==1); + rejects([&] { table.get(first, b); }); + auto wrong_context = a; wrong_context.context = new_owner_token(); + rejects([&] { table.get(first, wrong_context); }); + rejects([&] { foreign.get(first, a); }); + bool wrong_thread = false; + std::thread worker([&] { try { table.get(first, a); } catch (const std::logic_error&) { wrong_thread = true; } }); + worker.join(); require(wrong_thread); + table.mark_used(first, a, 2); + table.destroy(first, a); + rejects([&] { table.get(first, a); }); + require(live == 1 && table.deferred_count() == 1); + rejects([&] { table.insert(b, std::make_unique(live)); }); + auto second = table.insert(a, std::make_unique(live)); + rejects([&] { table.insert(a, std::make_unique(live)); }); + require(!table.can_insert()); + auto overflow=std::make_unique(live); + rejects([&] { table.insert(a,std::move(overflow)); }); + require(overflow && live==3); + overflow.reset(); + table.complete(1); require(live == 2 && !table.can_insert()); + table.complete(2); require(live == 1 && table.deferred_count() == 0 && table.can_insert()); + auto reused = table.insert(a, std::make_unique(live)); + require(reused.slot == first.slot && reused.generation != first.generation); + rejects([&] { table.get(first, a); }); + table.destroy(reused, a); require(live == 1); + table.get(second, a); + rejects([&] { table.complete(1); }); + table.destroy(second, a); + require(live == 0 && table.resident_count() == 0); + auto device_a = table.insert(a, std::make_unique(live)); + auto device_b = foreign.insert(b, std::make_unique(live)); + table.mark_used(device_a, a, 3); + foreign.mark_used(device_b, b, 1); + table.destroy_owner(a); + foreign.destroy_owner(b); + table.complete(100); + require(live == 1 && foreign.deferred_count() == 1); + foreign.complete(1); + require(live == 0); + std::cout << "graphics resource lifetime and owner isolation passed\n"; +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_scene_abi_layout_tests.c b/experiments/WebScene.NativeEngine.Probe/tests/graphics_scene_abi_layout_tests.c new file mode 100644 index 000000000..96114a22d --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_scene_abi_layout_tests.c @@ -0,0 +1,18 @@ +#include "webscene_native_engine.h" +#include +_Static_assert(sizeof(webscene_scene_acquire_options_v3)==16,"scene options wire size"); +_Static_assert(offsetof(webscene_scene_acquire_options_v3,consumer_capabilities)==8,"scene capability alignment"); +_Static_assert(sizeof(webscene_scene_acquire_status)==4,"scene status wire size"); +_Static_assert(offsetof(webscene_scene_view_v3,required_capabilities)==8,"scene capability prefix"); +_Static_assert(offsetof(webscene_scene_view_v3,cpu_view)==16,"scene CPU view prefix"); +_Static_assert(offsetof(webscene_scene_view_v3,lease_token)==16+sizeof(void*),"scene lease offset"); +_Static_assert(sizeof(webscene_scene_view_v3)==16+2*sizeof(void*),"scene view wire size"); +_Static_assert(sizeof(webscene_gpu_image_info_v3)==80,"image metadata wire size"); +_Static_assert(offsetof(webscene_gpu_image_info_v3,canvas)==8,"image identity alignment"); +_Static_assert(offsetof(webscene_gpu_image_info_v3,width)==56,"image dimensions offset"); +_Static_assert(offsetof(webscene_gpu_metal_event_view_v3,borrowed_shared_event)==8,"Metal event pointer prefix"); +#if UINTPTR_MAX == UINT64_MAX +_Static_assert(sizeof(webscene_gpu_metal_event_view_v3)==24,"Metal event view wire size"); +_Static_assert(offsetof(webscene_gpu_metal_event_view_v3,signaled_value)==16,"Metal event timeline alignment"); +#endif +int main(void) { return WEBSCENE_SCENE_VIEW_VERSION_3==3U ? 0 : 1; } diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_scene_lease_tests.inc b/experiments/WebScene.NativeEngine.Probe/tests/graphics_scene_lease_tests.inc new file mode 100644 index 000000000..2b5482a26 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_scene_lease_tests.inc @@ -0,0 +1,486 @@ +// White-box fixture compiled only into the graphics runtime test executable. +// Exercises the production acquisition core and exported scene/lease operations. +#include +void test_native_gpu_scene_leases() +{ + const auto require=[](bool value, std::source_location location=std::source_location::current()) { + if (!value) { + const auto message="GPU scene lease requirement failed at line " + std::to_string(location.line()); + std::fprintf(stderr,"%s\n",message.c_str()); + throw std::runtime_error(message); + } + }; + // Producer dependency ownership must follow every retained image and consumer, + // even after the originating scene/snapshot has gone away. + { + struct provider final : webscene::graphics::image_provider_lifetime {}; + struct dependency final : webscene_gpu_producer_dependencies { + size_t count() const noexcept override { return 2; } + bool metal_event(size_t index,void*& event,uint64_t& value) const override { + event=index<2 ? const_cast(this) : nullptr; + value=index+7;return index<2; + } + }; + webscene::graphics::owned_image_pool pool(std::make_shared()); + auto writer=pool.acquire(); + writer->set_metadata({1,1,1,1,1,1,16,16}); + writer->begin();auto pixels=writer->publish();writer->complete();writer.reset(); + auto owner=std::make_shared(); + std::weak_ptr lifetime=owner; + auto source=std::make_unique(std::move(*pixels),owner,true); + owner.reset(); + webscene_gpu_image_lease_v3* retained=nullptr; + require(webscene_gpu_image_retain_v3(source.get(),&retained)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + source.reset();require(!lifetime.expired() && retained->dependencies); + require(retained->requires_producer_wait); + { + auto protected_scene=std::make_shared(); + protected_scene->gpu_images.push_back(std::make_shared( + retained->value.retain().value(),retained->dependencies,true)); + const webscene_scene_view_v3* view=nullptr; + require(acquire_scene_value_v3(protected_scene,{},WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES,&view)== + WEBSCENE_SCENE_ACQUIRE_UNSUPPORTED_CAPABILITIES && !view); + require(acquire_scene_value_v3(protected_scene,{},WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES | + WEBSCENE_SCENE_CAPABILITY_PRODUCER_GPU_WAITS,&view)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + require(view->required_capabilities & WEBSCENE_SCENE_CAPABILITY_PRODUCER_GPU_WAITS); + webscene_scene_release_v3(view); + } + webscene_gpu_image_consumer_v3* consumer=nullptr; + require(webscene_gpu_image_begin_consumer_v3(retained,&consumer)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + webscene_gpu_image_release_v3(retained); + require(!lifetime.expired() && consumer->dependencies); + uint32_t count=99; + require(!webscene_gpu_image_dependency_count_v3(nullptr,&count) && count==0); + require(!webscene_gpu_image_dependency_count_v3(consumer,nullptr)); + require(webscene_gpu_image_dependency_count_v3(consumer,&count) && count==2); + webscene_gpu_metal_event_view_v3 event{sizeof(event),3,nullptr,0}; + require(webscene_gpu_image_get_metal_event_v3(consumer,0,&event) && event.borrowed_shared_event && event.signaled_value==7); + require(webscene_gpu_image_get_metal_event_v3(consumer,1,&event) && event.signaled_value==8); + require(!webscene_gpu_image_get_metal_event_v3(consumer,2,&event) && !event.borrowed_shared_event && event.signaled_value==0); + event.version=4;require(!webscene_gpu_image_get_metal_event_v3(consumer,0,&event)); + event.version=3;event.struct_size=0;require(!webscene_gpu_image_get_metal_event_v3(consumer,0,&event)); + webscene_gpu_image_complete_consumer_v3(consumer); + require(lifetime.expired()); + } + // A controlled backend proves early commit is independently gated by + // consumer capability and producer validation. It performs no GPU reads. + { + webscene_engine engine(nullptr); + struct provider final : webscene::graphics::image_provider_lifetime {}; + struct output final : webscene_gpu_image_snapshot { + std::shared_ptr image; + bool validated=false; + webscene::graphics::image_metadata describe() const override { return image->value.describe(); } + status state() const override { return status::pending; } + std::shared_ptr resolve() override { return {}; } + std::shared_ptr resolve_with_gpu_waits() override { return validated?image:nullptr; } + }; + auto& canvas=engine.document_.create_element("canvas"); + require(engine.document_.append_child(engine.document_.body(),canvas)); + const auto& backing=canvas.mutable_canvas().backing; + require(canvas.mutable_canvas().backing.claim_context(webscene::graphics::canvas_context_mode::webgpu)); + webscene::graphics::owned_image_pool pool(std::make_shared()); + auto writer=pool.acquire(); + writer->set_metadata({backing.identity(),100,backing.allocation_generation(),backing.content_serial(),1,1,backing.width(),backing.height()}); + writer->begin();auto pixels=writer->publish();writer->complete();writer.reset(); + auto pending=std::make_shared(); + pending->image=std::make_shared(std::move(*pixels),nullptr,true); + canvas.mutable_canvas().gpu_snapshot=pending; + auto captured=std::make_shared(); + captured->header.revision=1;captured->header.viewport_width=engine.viewport_width_;captured->header.viewport_height=engine.viewport_height_; + captured->gpu_bindings.push_back({canvas.id,pending->describe(),pending,{}});captured->gpu_images.resize(1); + const auto commit=[&]{return engine.commit_captured_scene(captured,std::chrono::steady_clock::now());}; + using result=webscene_engine::publication_result; + require(commit()==result::deferred); + engine.set_producer_gpu_wait_consumer(true); + require(commit()==result::deferred); + pending->validated=true; + engine.set_producer_gpu_wait_consumer(false); + require(commit()==result::deferred); + engine.set_producer_gpu_wait_consumer(true); + require(commit()==result::published); + require(pending->state()==webscene_gpu_image_snapshot::status::pending); + require(engine.latest_->gpu_images[0]->requires_producer_wait); + } + // A presentation binding may retain an old bitmap only through its explicit + // owner and the generation of the resized canvas that captured it. + { + webscene_engine engine(nullptr); + struct provider final : webscene::graphics::image_provider_lifetime {}; + webscene::graphics::owned_image_pool pool(std::make_shared()); + auto& node=engine.document_.create_element("canvas"); + require(engine.document_.append_child(engine.document_.body(),node)); + auto& canvas=node.mutable_canvas(); + require(canvas.backing.claim_context(webscene::graphics::canvas_context_mode::webgpu)); + auto writer=pool.acquire(); + writer->set_metadata({canvas.backing.identity(),100,canvas.backing.allocation_generation(), + canvas.backing.content_serial(),1,1,canvas.backing.width(),canvas.backing.height()}); + writer->begin();auto image=writer->publish();writer->complete();writer.reset(); + auto retained=std::make_shared(std::move(*image));image.reset(); + canvas.backing.reset_bitmap(640,480); + canvas.gpu_presentation_image=retained; + webscene_native::gpu_canvas_scene_binding binding{node.id,retained->value.describe(),{},retained, + canvas.backing.allocation_generation()}; + require(engine.document_.validate_gpu_canvas_binding(binding)); + auto ordinary=binding;ordinary.presentation_generation=0; + require(!engine.document_.validate_gpu_canvas_binding(ordinary)); + canvas.backing.reset_bitmap(800,600); + require(!engine.document_.validate_gpu_canvas_binding(binding)); + binding.presentation_generation=canvas.backing.allocation_generation(); + require(engine.document_.validate_gpu_canvas_binding(binding)); + canvas.gpu_presentation_image.reset(); + require(!engine.document_.validate_gpu_canvas_binding(binding)); + require(binding.resolve()==retained); // Immutable capture still owns its pixels. + } + // Exercise the production commit boundary with deliberately delayed outputs. + // This constructor disables the worker only in this test executable. + { + webscene_engine engine(nullptr); + struct test_provider final : webscene::graphics::image_provider_lifetime {}; + webscene::graphics::owned_image_pool pool(std::make_shared()); + struct delayed_output final : webscene_gpu_image_snapshot { + std::shared_ptr image; + status completion=status::pending; + webscene::graphics::image_metadata describe()const override{return image->value.describe();} + status state()const override{return completion;} + std::shared_ptr resolve()override { + return completion==status::ready?image:nullptr; + } + }; + auto& canvas=engine.document_.create_element("canvas"); + require(engine.document_.append_child(engine.document_.body(),canvas)); + const auto& backing=canvas.mutable_canvas().backing; + require(canvas.mutable_canvas().backing.claim_context(webscene::graphics::canvas_context_mode::webgpu)); + auto writer=pool.acquire(); + writer->set_metadata({backing.identity(),100,backing.allocation_generation(),backing.content_serial(),1,1,backing.width(),backing.height()}); + writer->begin();auto retained=writer->publish();writer->complete();writer.reset(); + auto output=std::make_shared(); + output->image=std::make_shared(std::move(*retained));retained.reset(); + const auto make_capture=[&](uint64_t revision,uint64_t base) { + auto value=std::make_shared(); + value->header.revision=revision;value->header.base_revision=base; + value->header.viewport_width=static_cast(engine.viewport_width_); + value->header.viewport_height=static_cast(engine.viewport_height_); + value->captured_generation=engine.document_.scene_generation(); + value->gpu_bindings.push_back({canvas.id,output->describe(),output,{}}); + value->gpu_images.resize(1); + value->commands.push_back({});value->commands.back().x=static_cast(revision); + return value; + }; + const auto commit=[&](auto value){return engine.commit_captured_scene(value,std::chrono::steady_clock::now());}; + using result=webscene_engine::publication_result; + canvas.mutable_canvas().gpu_snapshot=output; + engine.document_.mark_scene_changed(); // Rendering opportunity submission. + const auto submitted_generation=engine.document_.scene_generation(); + output->completion=webscene_gpu_image_snapshot::status::ready; + engine.document_.publish_gpu_canvas_image(canvas,output->image); + require(engine.document_.scene_generation()==submitted_generation); + // A completed output without a captured dependency must still invalidate. + canvas.mutable_canvas().gpu_snapshot.reset();canvas.mutable_canvas().gpu_image.reset(); + engine.document_.publish_gpu_canvas_image(canvas,output->image); + require(engine.document_.scene_generation()==submitted_generation+1); + auto a=make_capture(1,0);output->completion=webscene_gpu_image_snapshot::status::ready; + require(commit(a)==result::published); + engine.acknowledgement_->pending_scenes.clear();engine.acknowledgement_->revision=1; + auto b=make_capture(2,1);output->completion=webscene_gpu_image_snapshot::status::pending; + require(commit(b)==result::deferred && engine.latest_->header.revision==1); + engine.document_.mark_scene_changed(); + auto c=make_capture(3,2); // Newer CPU capture must not replace frozen B. + require(commit(b)==result::deferred && engine.latest_->commands[0].x==1); + output->completion=webscene_gpu_image_snapshot::status::ready; + require(commit(b)==result::published && engine.latest_->commands[0].x==2); + require(engine.published_document_generation_!=engine.document_.scene_generation()); + require(commit(c)==result::deferred); // Mailbox is full; input capture remains owned. + engine.acknowledgement_->pending_scenes.clear();engine.acknowledgement_->revision=2; + require(commit(c)==result::published && engine.latest_->commands[0].x==3); + engine.acknowledgement_->pending_scenes.clear();engine.acknowledgement_->revision=3; + auto failed=make_capture(4,3);output->completion=webscene_gpu_image_snapshot::status::failed; + require(commit(failed)==result::discarded && engine.latest_->header.revision==3); + output->completion=webscene_gpu_image_snapshot::status::ready; + auto missing=make_capture(4,3); + auto missing_output=std::make_shared(output->describe()); + canvas.mutable_canvas().gpu_snapshot=missing_output; + canvas.mutable_canvas().gpu_image=output->image; // Older completed image must not be substituted. + engine.document_.layout(engine.viewport_width_,engine.viewport_height_); + engine.document_.build_gpu_canvas_bindings(missing->gpu_bindings); + require(missing->gpu_bindings.size()==1 && missing->gpu_bindings[0].pending==missing_output); + require(!missing->gpu_bindings[0].resolve()); + require(commit(missing)==result::discarded && engine.latest_->header.revision==3); + canvas.mutable_canvas().gpu_snapshot=output; + auto recovery=make_capture(4,3); + require(commit(recovery)==result::published && engine.latest_->header.revision==4); + engine.acknowledgement_->pending_scenes.clear();engine.acknowledgement_->revision=4; + auto stale=make_capture(5,2); + require(commit(stale)==result::deferred && !engine.staged_scene_ && engine.latest_->header.revision==4); + auto resized=make_capture(6,4);engine.viewport_width_+=1; + require(commit(resized)==result::deferred && engine.latest_->header.revision==4); + engine.viewport_width_-=1; + auto reset=make_capture(7,4); + canvas.mutable_canvas().backing.reset_bitmap(backing.width(),backing.height()); + require(commit(reset)==result::deferred && engine.latest_->header.revision==4); + } + // Full production capture + commit: A remains published while B is pending, + // even after live DOM and GPU state advance to C. No manual scene assembly. + { + webscene_engine engine(nullptr); + engine.native_scene_active_=true; + engine.ordered_scene_consumer_.store(true); + struct capture_provider final : webscene::graphics::image_provider_lifetime {}; + webscene::graphics::owned_image_pool pool(std::make_shared()); + struct captured_output final : webscene_gpu_image_snapshot { + std::shared_ptr image; + bool ready=false,failed=false; + webscene::graphics::image_metadata describe()const override{return image->value.describe();} + status state()const override{return failed?status::failed:(ready?status::ready:status::pending);} + std::shared_ptr resolve()override{return state()==status::ready?image:nullptr;} + }; + auto& node=engine.document_.create_element("canvas"); + require(engine.document_.append_child(engine.document_.body(),node)); + auto& backing=node.mutable_canvas().backing; + require(backing.claim_context(webscene::graphics::canvas_context_mode::webgpu)); + const auto submit=[&](uint32_t color) { + backing.publish_content(); + auto writer=pool.acquire();require(writer.has_value()); + writer->set_metadata({backing.identity(),100+backing.content_serial(),backing.allocation_generation(),backing.content_serial(),1,1,backing.width(),backing.height()}); + writer->begin();auto image=writer->publish();writer->complete();writer.reset(); + auto output=std::make_shared(); + output->image=std::make_shared(std::move(*image)); + node.mutable_canvas().gpu_snapshot=output; + engine.document_.body().style.background_rgba=color; + engine.document_.mark_dirty(); + return output; + }; + const auto check=[&](uint64_t serial,uint32_t color) { + require(engine.latest_ && engine.latest_->gpu_images.size()==1); + require(engine.latest_->gpu_images[0]->value.describe().content_serial==serial); + require(std::any_of(engine.latest_->commands.begin(),engine.latest_->commands.end(), + [&](const auto& command){return command.node_id==engine.document_.body().id && command.rgba==color;})); + }; + const auto acknowledge=[&] { + const webscene_scene_view_v3* view=nullptr; + require(acquire_scene_value_v3(engine.latest_,engine.acknowledgement_, + WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES|WEBSCENE_SCENE_CAPABILITY_ORDERED_CANVAS,&view)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + require(webscene_scene_acknowledge_v3(view));webscene_scene_release_v3(view); + }; + using result=webscene_engine::publication_result; + auto a=submit(0xff0000ff);a->ready=true; + require(engine.publish_scene()==result::published);check(1,0xff0000ff);acknowledge(); + auto b=submit(0x00ff00ff); + require(engine.publish_scene()==result::deferred);check(1,0xff0000ff); + auto c=submit(0x0000ffff); + require(engine.publish_scene()==result::deferred);check(1,0xff0000ff); + // A newer admitted GPU opportunity must not block the already captured B. + // Its own CPU/GPU state is frozen; C still waits for its rendering boundary. + struct publication_wake final : webscene::graphics::completion_wake { + void signal() noexcept override {} + }; + engine.runtime_=std::make_unique( + engine.document_,[]{return webscene_native::v8_dom_runtime::viewport_metrics{800,600,1,0};}); + require(engine.runtime_->initialize()); + require(engine.runtime_->install_webgpu(std::make_shared(),true, + webscene::graphics::webgpu_canvas_interop::none)); + engine.runtime_->signal_animation_frame(16); + require(engine.runtime_->has_open_gpu_output()); + b->ready=true; + require(engine.publish_scene()==result::published);check(2,0x00ff00ff); + require(engine.runtime_->has_open_gpu_output()); + require(!engine.staged_scene_); + require(engine.publish_scene()==result::deferred);check(2,0x00ff00ff); + require(!engine.staged_scene_); // C cannot be captured before its boundary. + require(engine.runtime_->pump_animation_frame_task()); + require(!engine.runtime_->has_open_gpu_output()); + engine.runtime_.reset(); + require(engine.published_document_generation_!=engine.document_.scene_generation()); + acknowledge(); + require(engine.publish_scene()==result::deferred);check(2,0x00ff00ff); + c->ready=true; + require(engine.publish_scene()==result::published);check(3,0x0000ffff);acknowledge(); + + // Add a first-ever pending second canvas. Neither completion order may + // expose a partially updated scene, and paint indices must remain exact. + a.reset();b.reset(); + auto& second=engine.document_.create_element("canvas"); + require(engine.document_.append_child(engine.document_.body(),second)); + auto& second_backing=second.mutable_canvas().backing; + require(second_backing.claim_context(webscene::graphics::canvas_context_mode::webgpu)); + webscene::graphics::owned_image_pool second_pool(std::make_shared()); + const auto submit_second=[&] { + second_backing.publish_content(); + auto writer=second_pool.acquire();require(writer.has_value()); + writer->set_metadata({second_backing.identity(),200+second_backing.content_serial(),second_backing.allocation_generation(),second_backing.content_serial(),1,1,second_backing.width(),second_backing.height()}); + writer->begin();auto image=writer->publish();writer->complete();writer.reset(); + auto output=std::make_shared(); + output->image=std::make_shared(std::move(*image)); + second.mutable_canvas().gpu_snapshot=output; + engine.document_.mark_scene_changed(); + return output; + }; + const auto check_pair=[&](uint64_t first_serial,uint64_t second_serial,uint32_t color) { + require(engine.latest_->gpu_images.size()==2); + for(const auto& command:engine.latest_->commands) { + if(command.kind!=WEBSCENE_SCENE_COMMAND_GPU_IMAGE)continue; + require(command.rgbagpu_images.size()); + const auto metadata=engine.latest_->gpu_images[command.rgba]->value.describe(); + require((command.node_id==node.id && metadata.canvas==backing.identity() && metadata.content_serial==first_serial) + || (command.node_id==second.id && metadata.canvas==second_backing.identity() && metadata.content_serial==second_serial)); + } + require(std::count_if(engine.latest_->commands.begin(),engine.latest_->commands.end(), + [](const auto& command){return command.kind==WEBSCENE_SCENE_COMMAND_GPU_IMAGE;})==2); + require(std::any_of(engine.latest_->commands.begin(),engine.latest_->commands.end(), + [&](const auto& command){return command.node_id==engine.document_.body().id && command.rgba==color;})); + }; + auto d=submit(0xffff00ff);auto second_a=submit_second(); + require(engine.publish_scene()==result::deferred);check(3,0x0000ffff); + require(engine.staged_scene_ && engine.staged_scene_->gpu_bindings.size()==2); + second_a->ready=true; + require(engine.publish_scene()==result::deferred);check(3,0x0000ffff); + d->ready=true; + require(engine.publish_scene()==result::published);check_pair(4,1,0xffff00ff);acknowledge(); + auto e=submit(0xff00ffff);auto second_b=submit_second(); + require(engine.publish_scene()==result::deferred);check_pair(4,1,0xffff00ff); + e->ready=true; + require(engine.publish_scene()==result::deferred);check_pair(4,1,0xffff00ff); + second_b->ready=true; + require(engine.publish_scene()==result::published);check_pair(5,2,0xff00ffff);acknowledge(); + c.reset();d.reset(); + auto f=submit(0xabcdefFF);auto second_c=submit_second(); + require(engine.publish_scene()==result::deferred);check_pair(5,2,0xff00ffff); + auto invalidated_capture=engine.staged_scene_; + second_c->failed=true; + require(engine.publish_scene()==result::discarded); + require(!engine.staged_scene_);check_pair(5,2,0xff00ffff); + second_backing.reset_bitmap(second_backing.width(),second_backing.height()); + require(engine.commit_captured_scene(invalidated_capture,std::chrono::steady_clock::now())==result::deferred); + require(!engine.staged_scene_);check_pair(5,2,0xff00ffff); + } + // Ordered Canvas2D placement is independently negotiated, including a diff + // whose unchanged command payload has been stripped after capability hashing. + auto ordered=std::make_shared(); + ordered->required_capabilities=scene_command_capabilities(WEBSCENE_SCENE_COMMAND_CANVAS_LAYER); + require(ordered->required_capabilities==WEBSCENE_SCENE_CAPABILITY_ORDERED_CANVAS); + require(scene_command_capabilities(1)==0); + require(scene_command_capabilities(WEBSCENE_SCENE_COMMAND_GPU_IMAGE)==WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES); + const webscene_scene_view_v3* ordered_view=nullptr; + for (const uint64_t offered : {uint64_t(0), uint64_t(WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES)}) { + require(acquire_scene_value_v3(ordered,{},offered,&ordered_view)== + WEBSCENE_SCENE_ACQUIRE_UNSUPPORTED_CAPABILITIES && !ordered_view); + } + require(acquire_scene_value_v3(ordered,{},WEBSCENE_SCENE_CAPABILITY_ORDERED_CANVAS,&ordered_view)== + WEBSCENE_SCENE_ACQUIRE_SUCCESS); + require(ordered_view->required_capabilities==WEBSCENE_SCENE_CAPABILITY_ORDERED_CANVAS); + webscene_scene_release_v3(ordered_view); + ordered->required_capabilities |= scene_command_capabilities(WEBSCENE_SCENE_COMMAND_GPU_IMAGE); + require(acquire_scene_value_v3(ordered,{},WEBSCENE_SCENE_CAPABILITY_ORDERED_CANVAS,&ordered_view)== + WEBSCENE_SCENE_ACQUIRE_UNSUPPORTED_CAPABILITIES && !ordered_view); + require(acquire_scene_value_v3(ordered,{},WEBSCENE_SCENE_CAPABILITY_ORDERED_CANVAS | + WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES,&ordered_view)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + webscene_scene_release_v3(ordered_view); + struct provider final : webscene::graphics::image_provider_lifetime {}; + auto backend=std::make_shared(); + auto weak=std::weak_ptr(backend); + auto pool=std::make_unique(backend); backend.reset(); + auto writer=pool->acquire(); + writer->set_metadata({1,2,1,1,3,1,64,64}); writer->begin(); + auto image=writer->publish(); writer->complete(); writer.reset(); + auto first=std::make_shared(); + first->header.revision=1; + first->header.flags=scene_flag_checkpoint | scene_flag_dom_replacement; + first->gpu_images.push_back(std::make_shared(std::move(*image))); image.reset(); + first->commands.push_back({WEBSCENE_SCENE_COMMAND_GPU_IMAGE,0,0,0,64,64,0,1,0,0,0,0,0}); + auto acknowledgement=std::make_shared(); + acknowledgement->pending_scenes.push_back(first); + const webscene_scene_view_v3* view=nullptr; + require(acquire_scene_value_v3(first,acknowledgement,0,&view)==WEBSCENE_SCENE_ACQUIRE_UNSUPPORTED_CAPABILITIES && !view); + require(acknowledgement->pending_scenes.size()==1 && acknowledgement->revision==0); + require(acquire_scene_value_v3(first,acknowledgement,WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES,&view)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + require(view->required_capabilities==WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES && webscene_scene_gpu_image_count_v3(view)==1); + webscene_gpu_image_lease_v3* retained=nullptr; + require(webscene_scene_retain_gpu_image_v3(view,1,&retained)==WEBSCENE_SCENE_ACQUIRE_INVALID_ARGUMENT && !retained); + require(webscene_scene_retain_gpu_image_v3(view,0,&retained)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + webscene_gpu_image_consumer_v3* consumer=nullptr; + require(webscene_gpu_image_begin_consumer_v3(retained,&consumer)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + auto removed=std::make_shared(); + removed->header.revision=2; removed->header.base_revision=1; + removed->header.flags=scene_flag_dom_replacement; + acknowledgement->pending_scenes.push_back(removed); + const webscene_scene_view_v3* premature=nullptr; + require(acquire_scene_value_v3(removed,acknowledgement,0,&premature)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + require(!webscene_scene_acknowledge_v3(premature) && acknowledgement->revision==0); + require(acknowledgement->presentation_images.empty()); + webscene_scene_release_v3(premature); + require(webscene_scene_acknowledge_v3(view) && acknowledgement->pending_scenes.size()==1); + require(acknowledgement->presentation_images==first->gpu_images); + webscene_scene_release_v3(view); first.reset(); + // A later ordered scene drops the GPU image. Both independent CPU retention and + // already-submitted consumer work must survive acknowledgement of removal. + require(acquire_scene_value_v3(removed,acknowledgement,0,&view)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + require(webscene_scene_gpu_image_count_v3(view)==0 && webscene_scene_acknowledge_v3(view)); + require(acknowledgement->presentation_images.empty()); + webscene_scene_release_v3(view); removed.reset(); + auto stale=std::make_shared(); + stale->header.revision=3; stale->header.base_revision=1; // Current base is 2. + stale->gpu_images.push_back(std::make_shared(retained->value.retain().value())); + acknowledgement->pending_scenes.push_back(stale); + const webscene_scene_view_v3* stale_view=nullptr; + require(acquire_scene_value_v3(stale,acknowledgement,WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES,&stale_view)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + require(!webscene_scene_acknowledge_v3(stale_view) && acknowledgement->revision==2); + require(acknowledgement->presentation_images.empty()); + acknowledgement->reset_for_checkpoint(); // Same reset used by the engine API. + require(acknowledgement->pending_scenes.empty() && acknowledgement->revision==0); + require(webscene_scene_gpu_image_count_v3(stale_view)==1); + auto recovery=std::make_shared(); + recovery->header.revision=4; + recovery->header.flags=scene_flag_checkpoint | scene_flag_dom_replacement; + recovery->gpu_images=stale->gpu_images; + acknowledgement->pending_scenes.push_back(recovery); + require(acquire_scene_value_v3(recovery,acknowledgement,WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES,&view)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + require(webscene_scene_acknowledge_v3(view) && acknowledgement->revision==4); + require(acknowledgement->presentation_images==recovery->gpu_images); + require(!webscene_scene_acknowledge_v3(stale_view)); + // An image-only diff updates presentation ownership without replacing the + // retained DOM comparison snapshot. It must not keep using that old table. + auto image_only=std::make_shared(); + image_only->header.revision=5; image_only->header.base_revision=4; + image_only->gpu_images.push_back(std::make_shared(retained->value.retain().value())); + acknowledgement->pending_scenes.push_back(image_only); + const webscene_scene_view_v3* image_only_view=nullptr; + require(acquire_scene_value_v3(image_only,acknowledgement,WEBSCENE_SCENE_CAPABILITY_GPU_IMAGES,&image_only_view)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + require(webscene_scene_acknowledge_v3(image_only_view)); + require(acknowledgement->value==recovery && acknowledgement->presentation_images==image_only->gpu_images); + acknowledgement->reset_for_checkpoint(); + require(acknowledgement->presentation_images.empty()); + require(webscene_scene_gpu_image_count_v3(image_only_view)==1); + webscene_scene_release_v3(image_only_view); image_only.reset(); + webscene_scene_release_v3(stale_view); stale.reset(); + webscene_scene_release_v3(view); recovery.reset(); acknowledgement.reset(); pool.reset(); + webscene_gpu_image_info_v3 info{}; info.struct_size=sizeof(info); info.version=3; + require(webscene_gpu_image_describe_v3(retained,&info) && info.content_serial==1 && !weak.expired()); + webscene_gpu_image_release_v3(retained); require(!weak.expired()); + webscene_gpu_iosurface_view_v3 native_view{sizeof(native_view),3,nullptr,0}; + require(!webscene_gpu_image_get_iosurface_v3(consumer,&native_view) && !native_view.borrowed_iosurface); + webscene_gpu_image_complete_consumer_v3(consumer); require(weak.expired()); +#if defined(__APPLE__) + auto surfaces=std::make_unique(1024*1024); + auto surface=surfaces->acquire({700,0,1,1,701,1,17,4,webscene::graphics::image_format::bgra8_unorm}); + require(surface.has_value()); + surface->producer.begin(); + auto published=surface->producer.publish(); + surface->producer.complete(); surface.reset(); + require(published.has_value()); + auto surface_lease=std::make_unique(std::move(*published)); + webscene_gpu_image_consumer_v3* surface_consumer=nullptr; + require(webscene_gpu_image_begin_consumer_v3(surface_lease.get(),&surface_consumer)==WEBSCENE_SCENE_ACQUIRE_SUCCESS); + surface_lease.reset(); surfaces.reset(); + require(webscene_gpu_image_get_iosurface_v3(surface_consumer,&native_view) && + native_view.borrowed_iosurface && native_view.allocation_bytes>=17*4*4); + require(IOSurfaceGetWidth(static_cast(native_view.borrowed_iosurface))==17); + native_view.version=2; + require(!webscene_gpu_image_get_iosurface_v3(surface_consumer,&native_view)); + native_view.version=3; native_view.struct_size=0; + require(!webscene_gpu_image_get_iosurface_v3(surface_consumer,&native_view)); + native_view.struct_size=sizeof(native_view); + require(!webscene_gpu_image_get_iosurface_v3(nullptr,&native_view) && + !native_view.borrowed_iosurface && !native_view.allocation_bytes); + webscene_gpu_image_complete_consumer_v3(surface_consumer); +#endif +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_service_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_service_tests.cpp new file mode 100644 index 000000000..11570cdac --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_service_tests.cpp @@ -0,0 +1,211 @@ +#include "graphics/graphics_service.h" +#include "graphics/engine_wake.h" +#include +#include +#include +using namespace webscene::graphics; +void require(bool value) { if (!value) throw std::runtime_error("requirement failed"); } +template void rejects(F action) { bool rejected=false; try { action(); } catch(const std::exception&) { rejected=true; } require(rejected); } +int executed_commands=0; +void set_color(graphics_service& service,std::span upload, + const graphics_command::arguments& values) noexcept { + resource_handle handle{values[0],values[1],static_cast(values[2])}; + float color[4]{}; + if (upload.size()!=sizeof(color)) std::terminate(); + std::memcpy(color,upload.data(),sizeof(color)); + service.with_angle_context(handle,[&] { glClearColor(color[0],color[1],color[2],color[3]); }); + ++executed_commands; +} +// Commands translate expected context loss without dropping later FIFO work. +std::array loss_order{}; +size_t loss_count=0; +void context_loss_command(graphics_service& service,std::span, + const graphics_command::arguments& values) noexcept { + resource_handle handle{values[0],values[1],static_cast(values[2])}; + bool entered=false; + try { + service.with_angle_context(handle,[&] { + entered=true; + if(values[3]==1) { + using request_proc=void (GL_APIENTRY *)(const GLchar*); + using lose_proc=void (GL_APIENTRY *)(GLenum,GLenum); + auto request=reinterpret_cast(eglGetProcAddress("glRequestExtensionANGLE")); + auto lose=reinterpret_cast(eglGetProcAddress("glLoseContextCHROMIUM")); + require(request && lose); + request("GL_CHROMIUM_lose_context"); + require(glGetError()==GL_NO_ERROR); + lose(GL_GUILTY_CONTEXT_RESET_EXT,GL_INNOCENT_CONTEXT_RESET_EXT); + } else { + glClearColor(0.25f,0.5f,0.75f,1); + GLfloat color[4]{};glGetFloatv(GL_COLOR_CLEAR_VALUE,color); + require(glGetError()==GL_NO_ERROR && color[1]==0.5f); + } + }); + loss_order.at(loss_count++)=3; + } catch(const angle_context_lost&) { + loss_order.at(loss_count++)=entered ? 1 : 2; + } +} +int main() { + auto wake=std::make_shared(); + graphics_service a(wake),b(wake); + require(!a.dawn_initialized() && !b.dawn_initialized()); + require(!a.has_ready_work()); + require(a.recommended_idle_wait(std::chrono::milliseconds(100))==std::chrono::milliseconds(100)); + require(a.pump([](auto) {})==0 && !a.dawn_initialized()); +#if defined(__APPLE__) + constexpr auto backend=EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE; +#elif defined(_WIN32) + constexpr auto backend=EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE; +#else + constexpr auto backend=EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; +#endif + auto first=a.create_angle_context(backend,2),second=b.create_angle_context(backend,2); + rejects([&] { b.with_angle_context(first,[] {}); }); + a.with_angle_context(first,[] { glClearColor(1,0,0,1); }); + b.with_angle_context(second,[] { glClearColor(0,1,0,1); }); + a.with_angle_context(first,[&] { + rejects([&] { a.destroy_angle_context(first); }); + rejects([&] { a.close(); }); + require(a.live_contexts()==1); + }); + rejects([&] { a.with_angle_context(first,[] { throw std::runtime_error("execution failed"); }); }); + a.destroy_angle_context(first); + rejects([&] { a.with_angle_context(first,[] {}); }); + a.close(); + b.with_angle_context(second,[] { GLfloat color[4]{}; glGetFloatv(GL_COLOR_CLEAR_VALUE,color); require(color[1]==1); }); + require(a.live_contexts()==0 && b.live_contexts()==1); + rejects([&] { a.dawn(); }); + auto endpoint=b.command_endpoint(2,sizeof(float)*4); + graphics_command command{set_color,{second.table,second.generation,second.slot}}; + std::array upload{1,0,0,1}; + std::thread producer([&] { + require(endpoint->enqueue(command,std::as_bytes(std::span(upload)))==enqueue_result::accepted); + upload={0,0,1,1}; + require(endpoint->enqueue(command,std::as_bytes(std::span(upload)))==enqueue_result::accepted); + require(endpoint->enqueue(command,std::as_bytes(std::span(upload)))==enqueue_result::full); + upload={0,0,0,0}; + }); + producer.join(); + require(b.has_ready_work() && executed_commands==0); + require(b.drain_commands(1)==1); + b.with_angle_context(second,[] { GLfloat color[4]{}; glGetFloatv(GL_COLOR_CLEAR_VALUE,color); require(color[0]==1 && color[2]==0); }); + require(b.drain_commands(1)==1); + b.with_angle_context(second,[] { GLfloat color[4]{}; glGetFloatv(GL_COLOR_CLEAR_VALUE,color); require(color[0]==0 && color[2]==1); }); + auto queue_counters=b.metrics().commands; + require(queue_counters.depth==0 && queue_counters.high_water==2 && queue_counters.upload_bytes==32); + require(executed_commands==2); + b.dawn(); require(b.dawn_initialized() && !b.has_ready_work()); + require(b.recommended_idle_wait(std::chrono::milliseconds(100))==std::chrono::milliseconds(100)); + auto mailbox=b.dawn().completions(); + resource_owner owner{b.engine_identity(),new_owner_token(),0}; + auto cancelled_ticket=mailbox->reserve(2,owner).value(); + mailbox->cancel_owner(owner); + require(b.pump([](auto record) { require(record.status==completion_status::cancelled); })==1); + require(!mailbox->has_ready() && mailbox->has_pending()); + require(b.recommended_idle_wait(std::chrono::milliseconds(100))<=std::chrono::milliseconds(1)); + require(!mailbox->publish(cancelled_ticket,completion_status::success)); + require(!b.has_ready_work()); + require(b.recommended_idle_wait(std::chrono::milliseconds(100))==std::chrono::milliseconds(100)); + auto ticket=mailbox->reserve(1,owner).value(); + require(mailbox->has_pending()); + require(b.recommended_idle_wait(std::chrono::milliseconds(100))<=std::chrono::milliseconds(1)); + b.pump([](auto) {}); + require(b.recommended_idle_wait(std::chrono::milliseconds(100))<=std::chrono::milliseconds(1)); + require(endpoint->enqueue(command,std::as_bytes(std::span(upload)))==enqueue_result::accepted); + b.close(); require(b.live_contexts()==0); + require(executed_commands==3 && endpoint->metrics().depth==0); + require(endpoint->enqueue(command)==enqueue_result::closed); + require(b.has_ready_work()); + require(b.recommended_idle_wait(std::chrono::milliseconds(100))==std::chrono::milliseconds::zero()); + require(!mailbox->publish(ticket,completion_status::success)); + require(b.pump([](auto record) { require(record.status==completion_status::cancelled); })==1); + require(!b.has_ready_work()); + require(b.recommended_idle_wait(std::chrono::milliseconds(100))==std::chrono::milliseconds(100)); + graphics_service gc(wake); + auto gc_context=gc.create_angle_context(backend,2); + auto gc_commands=gc.command_endpoint(2,sizeof(float)*4); + auto gc_releases=gc.release_endpoint(1); + auto release_record=graphics_service::deferred_context_release(gc_context); + auto registration=gc_releases->reserve(release_record).value(); + require(!gc_releases->reserve(release_record)); + graphics_command gc_command{set_color,{gc_context.table,gc_context.generation,gc_context.slot}}; + require(gc_commands->enqueue(gc_command,std::as_bytes(std::span(upload)))==enqueue_result::accepted); + require(gc_commands->enqueue(gc_command,std::as_bytes(std::span(upload)))==enqueue_result::accepted); + require(gc_commands->enqueue(gc_command)==enqueue_result::full); + std::thread gc_finalizer([&] { require(gc_releases->publish(registration)); }); + gc_finalizer.join(); + require(!gc_releases->publish(registration)); + require(gc.live_contexts()==1 && gc.metrics().release_registrations==1); + require(gc.drain_commands(1)==1 && gc.live_contexts()==1); + require(gc.drain_commands(1)==1 && gc.live_contexts()==0); + require(gc.metrics().release_registrations==0); + auto abandoned=gc_releases->reserve(release_record).value(); + require(abandoned.generation!=registration.generation); + require(!gc_releases->publish(registration)); + gc.close(); + require(!gc_releases->publish(abandoned) && gc_releases->occupied()==0); + // A delayed finalizer may refer to a slot reused by a new native context. + // Repeat real context teardown/recreation while forcing slot reuse. + graphics_service recycled(wake,1); + recycled.command_endpoint(1,0); + auto recycled_releases=recycled.release_endpoint(1); + for (int iteration=0;iteration<64;++iteration) { + auto old=recycled.create_angle_context(backend,2); + auto late=recycled_releases->reserve(graphics_service::deferred_context_release(old)).value(); + recycled.destroy_angle_context(old); + auto replacement=recycled.create_angle_context(backend,2); + require(replacement.slot==old.slot && replacement.generation!=old.generation); + require(recycled_releases->publish(late)); + recycled.pump([](auto) { throw std::runtime_error("unexpected context completion"); }); + require(recycled.live_contexts()==1); + recycled.with_angle_context(replacement,[] { + glClearColor(0.25f,0.5f,0.75f,1); + GLfloat color[4]{}; + glGetFloatv(GL_COLOR_CLEAR_VALUE,color); + require(color[0]==0.25f && color[1]==0.5f && color[2]==0.75f); + }); + recycled.destroy_angle_context(replacement); + auto baseline=recycled.metrics(); + require(baseline.live_contexts==0 && baseline.release_registrations==0 && baseline.commands.depth==0); + } + recycled.close(); + // Loss in one queued command is reported immediately, subsequent commands + // for that context reject before execution, and independent work continues. + graphics_service loss_service(wake); + auto lost_context=loss_service.create_angle_context(backend,2); + auto surviving_context=loss_service.create_angle_context(backend,2); + auto loss_queue=loss_service.command_endpoint(4,0); + const auto enqueue_loss=[&](auto handle,uint64_t inject) { + require(loss_queue->enqueue({context_loss_command, + {handle.table,handle.generation,handle.slot,inject}})==enqueue_result::accepted); + }; + enqueue_loss(lost_context,1); + enqueue_loss(lost_context,0); + enqueue_loss(surviving_context,0); + enqueue_loss(lost_context,0); + require(loss_service.drain_commands(4)==4); + require(loss_count==4 && loss_order==std::array{1,2,3,2}); + require(eglGetCurrentContext()==EGL_NO_CONTEXT); + loss_service.destroy_angle_context(lost_context); + loss_service.destroy_angle_context(surviving_context); + require(loss_service.live_contexts()==0 && loss_queue->metrics().depth==0); + loss_service.close(); + graphics_service delivery(wake); + auto delivery_mailbox=delivery.dawn().completions(); + auto delivery_ticket=delivery_mailbox->reserve(1,{delivery.engine_identity(),new_owner_token(),0}).value(); + require(delivery_mailbox->publish(delivery_ticket,completion_status::success)); + rejects([&] { + delivery.pump([&](auto) { + rejects([&] { delivery.close(); }); + rejects([&] { delivery.pump([](auto) {}); }); + throw std::runtime_error("delivery exception"); + }); + }); + delivery.close(); // Exception unwinding must release the active pump guard. + auto disposed=std::make_unique(wake); + auto late_endpoint=disposed->command_endpoint(1,0); + disposed.reset(); + require(late_endpoint->enqueue(command)==enqueue_result::closed); + std::cout << "lazy graphics service and cross-engine ANGLE lifetime isolation passed\n"; +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_iosurface_canvas_host.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_iosurface_canvas_host.h new file mode 100644 index 000000000..1ad7fb744 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_iosurface_canvas_host.h @@ -0,0 +1,75 @@ +#pragma once +#include "graphics/v8_webgpu_iosurface_canvas_host.h" +#include "graphics/v8_webgpu_realm.h" +#if defined(__APPLE__) +template +void test_v8_iosurface_canvas_host(v8::Isolate* isolate,v8::Local context, + graphics_service& service,Run run) { + auto exception=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As(); + auto realm=std::make_unique(isolate,context,service,exception,webgpu_canvas_interop::iosurface,wgpu::BackendType::Metal); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"sharedCanvasGPU"),realm->object()).FromMaybe(false),"Shared discovery publication failed"); + require(run("globalThis.sharedCanvasAdapterPromise=sharedCanvasGPU.requestAdapter();"),"Shared canvas adapter request failed"); + auto adapter_promise=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"sharedCanvasAdapterPromise")).ToLocalChecked().As(); + auto adapter_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(adapter_promise->State()==v8::Promise::kPending&&std::chrono::steady_clock::now()complete(completion),"Shared adapter completion not routed");}); + if(adapter_promise->State()==v8::Promise::kPending)std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + require(adapter_promise->State()==v8::Promise::kFulfilled&&adapter_promise->Result()->IsObject(),"Shared canvas Metal adapter unavailable"); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"sharedCanvasAdapter"),adapter_promise->Result()).FromMaybe(false),"Shared adapter publication failed"); + require(run("globalThis.privateCanvasFeaturePromise=sharedCanvasAdapter.requestDevice({requiredFeatures:['shared-texture-memory-iosurface']});"),"Private feature rejection dispatch failed"); + auto rejected=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"privateCanvasFeaturePromise")).ToLocalChecked().As(); + rejected->MarkAsHandled(); + require(rejected->State()==v8::Promise::kRejected&&rejected->Result().As()->Get(context,v8::String::NewFromUtf8Literal(isolate,"name")).ToLocalChecked()->StrictEquals(v8::String::NewFromUtf8Literal(isolate,"TypeError")),"Host policy allowed JavaScript to request a native feature"); + require(run("globalThis.sharedCanvasDevicePromise=sharedCanvasAdapter.requestDevice();"),"Shared canvas requestDevice failed"); + auto promise=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"sharedCanvasDevicePromise")).ToLocalChecked().As(); + auto deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(promise->State()==v8::Promise::kPending&&std::chrono::steady_clock::now()complete(completion),"Shared device completion not routed");}); + if(promise->State()==v8::Promise::kPending)std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + require(promise->State()==v8::Promise::kFulfilled,"Shared canvas device promise failed"); + auto device=promise->Result().As(); + auto native=v8_webgpu_devices::native_reference(device); + for(auto feature:{wgpu::FeatureName::SharedTextureMemoryIOSurface,wgpu::FeatureName::SharedFenceMTLSharedEvent})require(native.HasFeature(feature),"Host sharing feature was not provisioned"); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"sharedCanvasDevice"),device).FromMaybe(false),"Shared device publication failed"); + auto provider=std::make_shared(1024*1024); + uint64_t serial=0; + auto host=make_iosurface_webgpu_canvas_host(provider,[&]{image_metadata metadata;metadata.canvas=123;metadata.allocation_generation=7;metadata.content_serial=++serial;metadata.producer_timeline=456;metadata.producer_value=serial;return metadata;}); + auto canvas=std::make_unique(isolate,context,v8::Object::New(isolate),exception,4,2,std::move(host)); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"sharedCanvas"),canvas->object()).FromMaybe(false),"Shared canvas publication failed"); + require(run(R"JS( + (()=>{ + if(sharedCanvasDevice.features.has('shared-texture-memory-iosurface')||sharedCanvasDevice.features.has('shared-fence-mtl-shared-event'))throw new Error('private feature exposed'); + sharedCanvas.configure({device:sharedCanvasDevice,format:sharedCanvasGPU.getPreferredCanvasFormat()}); + const texture=sharedCanvas.getCurrentTexture(); + if(texture!==sharedCanvas.getCurrentTexture())throw new Error('shared texture identity'); + const encoder=sharedCanvasDevice.createCommandEncoder(); + const pass=encoder.beginRenderPass({colorAttachments:[{view:texture.createView(),loadOp:'clear',storeOp:'store',clearValue:[1,0,0,1]}]}); + pass.end();sharedCanvasDevice.queue.submit([encoder.finish()]); + })(); + )JS"),"JavaScript shared canvas clear failed"); + canvas->end_frame(true); + std::optional image; + deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(!image&&std::chrono::steady_clock::now()take_ready();if(ready)image.emplace(std::move(*ready)); + else{service.dawn().instance().ProcessEvents();std::this_thread::sleep_for(std::chrono::milliseconds(1));} + } + require(image.has_value(),"JavaScript shared canvas image unavailable"); + auto metadata=image->describe();require(metadata.canvas==123&&metadata.allocation_generation==7&&metadata.content_serial==1&&metadata.width==4&&metadata.height==2,"Shared canvas frame identity changed"); + auto consumer=image->begin_consumer();require(consumer.has_value(),"Shared canvas consumer unavailable"); + auto surface=iosurface_canvas_images::resolve(*consumer).borrowed_handle(); + // Explicit diagnostic CPU inspection only, after the producer's GPU work. + require(IOSurfaceLock(surface,kIOSurfaceLockReadOnly,nullptr)==kIOReturnSuccess,"Shared canvas diagnostic lock failed"); + auto pixels=static_cast(IOSurfaceGetBaseAddress(surface));auto stride=IOSurfaceGetBytesPerRow(surface); + bool correct=pixels!=nullptr; + if(pixels)for(size_t y=0;y<2;++y)for(size_t x=0;x<4;++x){auto pixel=pixels+y*stride+x*4;correct&=pixel[0]==0&&pixel[1]==0&&pixel[2]==255&&pixel[3]==255;} + auto unlocked=IOSurfaceUnlock(surface,kIOSurfaceLockReadOnly,nullptr);consumer->complete();image.reset(); + require(correct&&unlocked==kIOReturnSuccess,"JavaScript IOSurface canvas pixel mismatch"); + require(run("sharedCanvas.unconfigure();delete globalThis.sharedCanvas;delete globalThis.sharedCanvasDevice;delete globalThis.sharedCanvasAdapter;delete globalThis.sharedCanvasDevicePromise;delete globalThis.privateCanvasFeaturePromise;delete globalThis.sharedCanvasAdapterPromise;"),"Shared canvas cleanup failed"); + canvas.reset();require(provider->idle()&&provider->busy_images()==0,"Shared canvas provider retained completed frame"); + realm.reset(); + require(run("(()=>{let expired=false;try{sharedCanvasGPU.getPreferredCanvasFormat()}catch(e){expired=e instanceof TypeError}if(!expired)throw new Error('retired GPU realm callable');delete globalThis.sharedCanvasGPU;})();"),"GPU realm teardown left live discovery receiver"); +} +#endif diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_modules.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_modules.h new file mode 100644 index 000000000..a0166874c --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_modules.h @@ -0,0 +1,119 @@ +void test_modules_and_clone() { + webscene_native::native_document document; + unsigned dependency_loads = 0; + webscene_native::v8_dom_runtime runtime(document, + []{return webscene_native::v8_dom_runtime::viewport_metrics{640,480,1,0};}, {}, + [&](uint32_t, const std::string& url, const auto&, const std::string&, int64_t, auto& response) { + if (url.ends_with("/index.html")) response.content = R"HTML( + + )HTML"; + else if (url.ends_with("/main.js")) response.content = R"JS( + import {count, increment} from './dep.js'; + import './cycle.js'; + globalThis.mainUrl=import.meta.url; + increment(); + globalThis.moduleResult=count; + globalThis.moduleCurrentScript=document.currentScript; + executionOrder.push('module'); + import('./dep.js').then(m=>globalThis.dynamicResult=m.count); + )JS"; + else if (url.ends_with("/dep.js")) { + ++dependency_loads; + response.content = "export let count=40;export function increment(){count+=2;}"; + } else if (url.ends_with("/cycle.js")) response.content = "import './main.js';export const cycle=true;"; + else return false; + return true; + }); + require(runtime.initialize(), "Module runtime initialization failed"); + require(runtime.load_url("https://modules.test/index.html"), "Module navigation failed"); + require(runtime.execute(R"JS( + if(moduleResult!==42||dynamicResult!==42)throw Error('module live binding/dynamic import'); + if(mainUrl!=='https://modules.test/main.js'||moduleCurrentScript!==null)throw Error('module metadata'); + if(executionOrder.join(',')!=='classic,module')throw Error('module defer'); + const x={date:new Date(123),map:new Map([['key',42]]),set:new Set([1,2])};x.self=x; + const copy=structuredClone(x); + if(copy===x||copy.self!==copy||copy.date.getTime()!==123||copy.map.get('key')!==42||!copy.set.has(2))throw Error('structured clone'); + const bytes=new Uint8Array([1,2,3]);const moved=structuredClone({bytes},{transfer:[bytes.buffer]}); + if(bytes.byteLength!==0||moved.bytes[2]!==3)throw Error('buffer transfer'); + const b=new ArrayBuffer(8);let duplicate=false; + try{structuredClone(b,{transfer:[b,b]})}catch(e){duplicate=e.name==='DataCloneError'} + if(!duplicate||b.byteLength!==8)throw Error('duplicate transfer atomicity'); + let invalid=false;try{structuredClone(()=>{})}catch(e){invalid=e.name==='DataCloneError'} + if(!invalid)throw Error('uncloneable function'); + import('unmapped').then(()=>globalThis.bareRejected=false,()=>globalThis.bareRejected=true); + )JS","https://modules.test/test.js"), "Modules/clone regression failed"); + require(dependency_loads==1, "Module dependency was fetched more than once"); + require(runtime.execute("if(!bareRejected)throw Error('bare import did not reject');", "assert"), "Dynamic import rejection failed"); +} +void test_dedicated_module_worker() { + webscene_native::native_document document; + webscene_native::v8_dom_runtime runtime(document, + []{return webscene_native::v8_dom_runtime::viewport_metrics{64,64,1,0};}, {}, + [](uint32_t, const std::string& url, const auto&, const std::string&, int64_t, auto& response) { + if(url.ends_with("/index.html"))response.content=R"HTML()HTML"; + else if(url.ends_with("/worker.js"))response.content=R"JS( + import {factor} from './factor.js'; + self.onmessage=e=>{ + const array=e.data.array; + array[1]*=factor; + postMessage({array,documentType:typeof document,url:import.meta.url},[array.buffer]); + const workerOwned=new Uint8Array([31,47]); + postMessage({detached:array.byteLength===0,workerOwned},[workerOwned.buffer]); + }; + )JS"; + else if(url.ends_with("/busy.js"))response.content="while(true){}"; + else if(url.ends_with("/factor.js"))response.content="export const factor=3;"; + else return false; + return true; + }); + require(runtime.initialize()&&runtime.load_url("https://workers.test/index.html"),"Worker startup failed"); + for(unsigned i=0;i<100;++i){runtime.pump_task();std::this_thread::sleep_for(std::chrono::milliseconds(5));} + if(!runtime.execute(R"JS( + if(workerError)throw Error(workerError); + if(!parentRemainedResponsive||workerMessages.length!==2)throw Error('missing ordered worker replies: '+workerMessages.length); + if(workerMessages[0].array[1]!==24||workerMessages[0].documentType!=='undefined' + ||workerMessages[0].url!=='https://workers.test/worker.js'||!workerMessages[1].detached)throw Error('worker transfer/isolation'); + worker.terminate();worker.terminate(); + // Originating isolate and allocator owner are gone; transferred memory remains valid. + if(workerMessages[1].workerOwned[1]!==47)throw Error('worker-owned buffer lifetime'); + globalThis.busyWorker=new Worker('./busy.js'); + )JS","worker-assert"))throw std::runtime_error(runtime.last_error()); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + require(runtime.load_url("https://workers.test/index.html"),"Worker navigation shutdown failed"); +} +void test_secure_context_reporting() { + webscene_native::native_document document; + webscene_native::v8_dom_runtime runtime(document, + []{return webscene_native::v8_dom_runtime::viewport_metrics{64,64,1,0};},{}, + [](uint32_t,const std::string&,const auto&,const std::string&,int64_t,auto& response){ + response.content="origin test";return true; + }); + require(runtime.initialize(),"Secure context runtime initialization failed"); + for(auto [url,secure]:{ + std::pair{"https://example.test/index.html",true}, + std::pair{"http://example.test/index.html",false}, + std::pair{"http://localhost:4173/index.html",true}, + std::pair{"http://sub.localhost/index.html",true}, + std::pair{"http://127.0.0.1:4173/index.html",true}, + std::pair{"http://127.1.2.3/index.html",true}, + std::pair{"http://[::1]:4173/index.html",true}, + std::pair{"http://localhost.evil.test/index.html",false}, + std::pair{"http://127.evil.test/index.html",false}, + std::pair{"http://localhost@evil.test/index.html",false} + }){ + require(runtime.load_url(url),"Origin navigation failed"); + require(runtime.execute(std::string("if(isSecureContext!==")+(secure?"true":"false")+")throw Error('secure context classification');","secure-context"),"Secure context classification mismatch"); + require(runtime.execute("if('gpu' in navigator)throw Error('origin reporting bypassed host GPU policy');","gpu-policy"),"Origin reporting granted GPU admission"); + } +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_runtime_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_runtime_tests.cpp new file mode 100644 index 000000000..1aa3eecdf --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_runtime_tests.cpp @@ -0,0 +1,2472 @@ +#include "webscene_v8_runtime.h" +#include "webscene_native_dom.h" +#include "graphics/graphics_service.h" +#include "graphics/engine_wake.h" +#include "graphics/v8_release_registry.h" +#include "graphics/v8_webgpu_adapter_request.h" +#include "graphics/v8_webgpu_device_request.h" +#include "graphics/webgpu_prepared_device_descriptor.h" +#include "graphics/v8_webgpu_buffers.h" +#include "graphics/v8_webgpu_devices.h" +#include "graphics/v8_webgpu_shaders.h" +#include "graphics/webgpu_compilation_info.h" +#include "graphics/v8_webgpu_render_pipelines.h" +#include "graphics/v8_webgpu_bind_group_layouts.h" +#include "graphics/v8_webgpu_pipeline_layouts.h" +#include "graphics/v8_webgpu_bind_groups.h" +#include "graphics/v8_webgpu_texture_views.h" +#include "graphics/v8_webgpu_adapters.h" +#include "graphics/v8_webgpu_discovery.h" +#include "graphics/v8_webgpu_canvas_context.h" +#include "graphics/webgpu_adapter_info.h" +#include "graphics/v8_webgpu_mapped_ranges.h" +#include "graphics/v8_webgpu_map_request.h" +#include "graphics/image_lease_abi.h" +#include +#include +using namespace webscene::graphics; +void require(bool value,const char* message) { if (!value) throw std::runtime_error(message); } +#include "graphics_v8_webgpu_options.h" +#include "graphics_v8_webgpu_buffer_descriptor.h" +#include "graphics_v8_webgpu_device_descriptor.h" +#include "graphics_v8_webgpu_shader_descriptor.h" +#include "graphics_v8_webgpu_programmable_stage.h" +#include "graphics_v8_webgpu_render_state.h" +#include "graphics_v8_webgpu_texture_descriptor.h" +#include "graphics_v8_webgpu_render_pass_descriptor.h" +#include "graphics_v8_webgpu_canvas_configuration.h" +#include "graphics_v8_iosurface_canvas_host.h" +int weak_releases=0; +void test_native_gpu_scene_leases(); +void test_device_loss_signal() { + resource_owner owner{new_owner_token(),new_owner_token(),0}; + for(bool early:{false,true}) { + auto mailbox=std::make_shared(1,nullptr); + auto signal=std::make_shared(nullptr); + auto ticket=mailbox->reserve(new_owner_token(),owner,false).value(); + std::string message="driver loss"; + auto publish=[&]{signal->publish(wgpu::DeviceLostReason::Unknown,wgpu::StringView(message.data(),message.size()));}; + if(early){std::thread callback(publish);callback.join();} + signal->subscribe(mailbox,ticket); + if(!early){std::thread callback(publish);callback.join();} + message[0]='X'; + require(signal->lost.load()&&signal->result()->message=="driver loss","Loss callback did not own its snapshot"); + unsigned count=0;require(mailbox->drain_one([&](auto record){++count;require(record.status==completion_status::success,"Loss completion failed");}),"Loss notification missing"); + signal->publish(wgpu::DeviceLostReason::Destroyed,wgpu::StringView("duplicate")); + require(count==1&&!mailbox->has_ready(),"Loss delivered twice"); + } +} +void test_compilation_info_snapshot() { + std::string text="diagnostic"; + wgpu::CompilationMessage message{}; + message.message=wgpu::StringView(text.data(),text.size()); + message.type=wgpu::CompilationMessageType::Error; + message.lineNum=2;message.linePos=3;message.offset=4;message.length=5; + wgpu::CompilationInfo info{};info.messageCount=1;info.messages=&message; + wgpu::DawnCompilationMessageUtf16 utf16{}; + utf16.linePos=2;utf16.offset=3;utf16.length=4; + message.nextInChain=&utf16; + auto snapshot=webgpu_compilation_info::copy(info); + text[0]='X'; + require(snapshot.messages.size()==1&&snapshot.messages[0].message=="diagnostic" + &&snapshot.messages[0].offset==4&&snapshot.messages[0].length==5 + &&snapshot.messages[0].has_utf16&&snapshot.messages[0].utf16_offset==3 + &&snapshot.messages[0].utf16_line_pos==2&&snapshot.messages[0].utf16_length==4, + "Compilation diagnostics did not retain callback data"); + bool bounded=false;try{webgpu_compilation_info::copy(info,1,2);}catch(const std::length_error&){bounded=true;} + require(bounded,"Compilation diagnostic byte budget ignored"); + bounded=false;try{webgpu_compilation_info::copy(info,0);}catch(const std::length_error&){bounded=true;} + require(bounded,"Compilation diagnostic count budget ignored"); + message.message=wgpu::StringView("terminated"); + require(webgpu_compilation_info::copy(info).messages[0].message=="terminated","NUL-terminated diagnostic copy failed"); +} +void test_image_lease_abi() { + struct provider final : image_provider_lifetime {}; + auto native=std::make_shared(); + std::weak_ptr alive=native; + auto pool=std::make_unique(native,3); native.reset(); + auto writer=pool->acquire(); + writer->set_metadata({1,2,3,4,5,6,640,480}); writer->begin(); + auto frame=writer->publish(); + auto* original=new webscene_gpu_image_lease_v3(std::move(*frame)); frame.reset(); + webscene_gpu_image_lease_v3* retained=nullptr; + require(webscene_gpu_image_retain_v3(original,&retained)==WEBSCENE_SCENE_ACQUIRE_SUCCESS,"ABI retain failed"); + webscene_gpu_image_consumer_v3* consumer=nullptr; + require(webscene_gpu_image_begin_consumer_v3(retained,&consumer)==WEBSCENE_SCENE_ACQUIRE_SUCCESS,"ABI consumer failed"); + webscene_gpu_image_lease_v3* saturated=nullptr; + require(webscene_gpu_image_retain_v3(retained,&saturated)==WEBSCENE_SCENE_ACQUIRE_BACKPRESSURE && !saturated,"ABI backpressure failed"); + pool.reset(); writer->complete(); writer.reset(); + webscene_gpu_image_release_v3(original); + webscene_gpu_image_info_v3 info{}; info.struct_size=sizeof(info); info.version=3; + require(webscene_gpu_image_describe_v3(retained,&info) && info.width==640 && info.allocation_generation==3,"ABI metadata failed"); + info.version=99; require(!webscene_gpu_image_describe_v3(retained,&info),"ABI metadata version accepted"); + webscene_gpu_image_release_v3(retained); + require(!alive.expired(),"ABI released pending GPU provider"); + std::thread completion([&] { webscene_gpu_image_complete_consumer_v3(consumer); }); completion.join(); + require(alive.expired(),"ABI completion leaked provider"); +} +void test_scene_acquisition_v3() { + std::unique_ptr engine(webscene_engine_create(64),webscene_engine_destroy); + require(engine!=nullptr,"scene ABI engine creation failed"); + webscene_scene_acquire_options_v3 options{sizeof(options),WEBSCENE_SCENE_VIEW_VERSION_3,0}; + const webscene_scene_view_v3* view=nullptr; + auto invalid=options; invalid.scene_version=99; + require(webscene_engine_acquire_next_scene_v3(engine.get(),&invalid,&view)==WEBSCENE_SCENE_ACQUIRE_UNSUPPORTED_VERSION && !view,"scene version rejection failed"); + invalid=options; invalid.struct_size=0; + require(webscene_engine_acquire_next_scene_v3(engine.get(),&invalid,&view)==WEBSCENE_SCENE_ACQUIRE_INVALID_ARGUMENT && !view,"scene options size rejection failed"); + require(webscene_engine_acquire_next_scene_v3(nullptr,&options,&view)==WEBSCENE_SCENE_ACQUIRE_INVALID_ARGUMENT,"null scene engine accepted"); + auto status=webscene_engine_acquire_next_scene_v3(engine.get(),&options,&view); + if (view) { webscene_scene_release_v3(view); view=nullptr; } + constexpr char source[]="document.body.innerHTML='
scene lease ABI
';"; + require(webscene_engine_execute_script(engine.get(),source,sizeof(source)-1,"scene-v3",8),"scene ABI script failed"); + const auto deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + do { + status=webscene_engine_acquire_next_scene_v3(engine.get(),&options,&view); + if (status==WEBSCENE_SCENE_ACQUIRE_EMPTY) std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } while (status==WEBSCENE_SCENE_ACQUIRE_EMPTY && std::chrono::steady_clock::now()struct_size==sizeof(*view) && view->scene_version==3 && view->required_capabilities==0,"invalid versioned scene layout"); + require(view->cpu_view && view->cpu_view->abi_version==2,"CPU scene compatibility view missing"); + const auto* legacy=webscene_engine_acquire_latest_scene(engine.get()); + require(legacy && legacy->abi_version==2,"legacy scene acquisition regressed"); + webscene_scene_release(legacy); + auto incompatible=*view; + incompatible.struct_size=sizeof(uint32_t); + require(!webscene_scene_acknowledge_v3(&incompatible),"short scene view acknowledged"); + webscene_scene_release_v3(&incompatible); + incompatible=*view; incompatible.scene_version=99; + require(!webscene_scene_acknowledge_v3(&incompatible),"unknown scene view acknowledged"); + webscene_scene_release_v3(&incompatible); + const webscene_scene_view_v3* latest=nullptr; + require(webscene_engine_acquire_latest_scene_v3(engine.get(),&options,&latest)==WEBSCENE_SCENE_ACQUIRE_SUCCESS && latest, + "latest versioned acquisition failed"); + webscene_scene_release_v3(latest); + require(webscene_scene_acknowledge_v3(view),"versioned acknowledgement failed"); + const auto revision=view->cpu_view->header.revision; + engine.reset(); + require(view->cpu_view->header.revision==revision,"retained scene did not survive engine disposal"); + webscene_scene_release_v3(view); +} +void test_navigation_stylesheet_raw_text_isolation() { + webscene_native::native_document document; + webscene_native::v8_dom_runtime runtime(document, + []{return webscene_native::v8_dom_runtime::viewport_metrics{640,480,1,0};}, + {},[](uint32_t,const std::string&,const auto&,const std::string&,int64_t,auto& response){ + response.content=R"HTML( + + + + + )HTML"; + return true; + }); + require(runtime.initialize(),"Stylesheet navigation runtime failed"); + require(runtime.load_url("https://graphics.test/style-isolation"),"Stylesheet navigation failed"); + require(runtime.execute(R"JS( + if(getComputedStyle(document.getElementById('icon')).backgroundColor!=='rgb(10, 20, 30)')throw new Error('Inert markup activated stylesheet'); + if(getComputedStyle(document.getElementById('control')).position!=='static')throw new Error('Print preview repositioned document controls'); + )JS","style-isolation"),"Navigation leaked raw-text/comment/template CSS"); +} + +void test_inline_canvas_intrinsic_layout() { + webscene_native::native_document document; + webscene_native::v8_dom_runtime runtime(document, + []{return webscene_native::v8_dom_runtime::viewport_metrics{640,480,1,0};}); + require(runtime.initialize(),"Inline canvas runtime failed"); + require(runtime.execute(R"JS( + document.body.innerHTML='fallback'; + )JS","inline-canvas"),"Inline canvas setup failed"); + document.layout(640,480); + auto* canvas=document.find_by_id("intrinsic"); + require(canvas&&canvas->layout.width==256&&canvas->layout.height==128,"Inline canvas lost intrinsic dimensions"); + require(runtime.execute("document.getElementById('intrinsic').removeAttribute('width');document.getElementById('intrinsic').removeAttribute('height');","default-canvas"),"Canvas dimension removal failed"); + document.layout(640,480); + require(canvas->layout.width==300&&canvas->layout.height==150,"Inline canvas defaults lost"); +} +void test_runtime_webgpu_document_policy() { +#if defined(__APPLE__) + webscene_native::native_document document; + std::vector decisions; + webscene_native::v8_dom_runtime runtime(document, + []{return webscene_native::v8_dom_runtime::viewport_metrics{64,64,1,0};}, + {},[](uint32_t,const std::string& url,const auto&,const std::string&,int64_t,auto& response){ + if(url=="https://graphics.test/missing")return false; + response.content=""; + return true; + }); + runtime.set_webgpu_policy(std::make_shared(),[&](const std::string& url){ + decisions.push_back(url); + return url=="https://graphics.test/allowed" ? webgpu_canvas_interop::iosurface : webgpu_canvas_interop::none; + }); + require(runtime.initialize(),"Policy runtime initialization failed"); + require(decisions==std::vector{"about:blank"},"Initial document policy missing"); + require(runtime.execute("if('gpu' in navigator)throw new Error('blank admission');","blank-policy"),"Blank policy denied incorrectly"); + require(runtime.load_url("https://graphics.test/allowed"),"Allowed policy navigation failed"); + require(runtime.execute("if(!policySeenByScript||!navigator.gpu)throw new Error('late admission');","allowed-policy"),"Policy ran after application script"); + require(!runtime.load_url("https://graphics.test/missing"),"Missing policy resource loaded"); + require(decisions.size()==2,"Failed navigation changed admission"); + require(runtime.execute("if(!navigator.gpu)throw new Error('failed navigation retired GPU');","failed-policy"),"Failed navigation changed GPU exposure"); + require(runtime.load_url("https://graphics.test/denied"),"Denied policy navigation failed"); + require(runtime.execute("if(policySeenByScript||('gpu' in navigator))throw new Error('stale admission');","denied-policy"),"Denied navigation retained GPU exposure"); + require(decisions==std::vector{"about:blank","https://graphics.test/allowed","https://graphics.test/denied"},"Document policy URL sequence incorrect"); +#endif +} +void test_positioned_auto_margins() { + using namespace webscene_native; + for(auto display : {display_mode::block,display_mode::grid}) { + native_document document; + auto& parent=document.create_element("div"); + auto& box=document.create_element("div"); + require(document.append_child(document.body(),parent)&&document.append_child(parent,box),"Auto margin fixture failed"); + parent.style.display=display; + box.style.position=position_mode::fixed; + box.style.width={40,length_unit::pixels};box.style.height={20,length_unit::pixels}; + box.style.left=box.style.right=box.style.top=box.style.bottom={0,length_unit::pixels}; + box.style.margin_left_auto=box.style.margin_right_auto=true; + box.style.margin_top_auto=box.style.margin_bottom_auto=true; + for(const auto size : {std::pair{200.F,100.F},std::pair{300.F,180.F},std::pair{100.F,60.F}}) { + document.layout(size.first,size.second); + require(std::abs(box.layout.x-(size.first-40)/2)<0.01F + &&std::abs(box.layout.y-(size.second-20)/2)<0.01F,"Positioned auto margins did not center across resize"); + } + box.style.margin_right_auto=false;box.style.margin_right={10,length_unit::pixels}; + document.mark_dirty();document.layout(200,100); + require(std::abs(box.layout.x-150)<0.01F,"Single auto margin did not absorb remaining width"); + box.style.margin_right_auto=true;box.style.margin_right={}; + box.style.width={240,length_unit::pixels};box.style.height={140,length_unit::pixels}; + document.mark_dirty();document.layout(200,100); + require(std::abs(box.layout.x)<0.01F&&std::abs(box.layout.y+20)<0.01F,"Oversized positioned auto margins failed"); + } +} + +void test_native_modal_ordering() { + webscene_native::native_document document; + auto& scope=document.body(); + auto& host=document.create_element("div"); + auto& first=document.create_element("dialog"); + auto& second=document.create_element("dialog"); + auto& control=document.create_element("input"); + auto& background=document.create_element("button"); + require(document.append_child(scope,host)&&document.append_child(host,first) + &&document.append_child(first,control)&&document.append_child(scope,second) + &&document.append_child(scope,background),"Modal fixture tree failed"); + for(auto* dialog : {&first,&second}) { + dialog->style.position=webscene_native::position_mode::fixed; + dialog->style.left={0,webscene_native::length_unit::pixels}; + dialog->style.top={0,webscene_native::length_unit::pixels}; + dialog->style.width={60,webscene_native::length_unit::pixels}; + dialog->style.height={40,webscene_native::length_unit::pixels}; + } + control.style.width={10,webscene_native::length_unit::pixels}; + control.style.height={10,webscene_native::length_unit::pixels}; + background.style.position=webscene_native::position_mode::fixed; + background.style.left={0,webscene_native::length_unit::pixels}; + background.style.top={0,webscene_native::length_unit::pixels}; + background.style.width={100,webscene_native::length_unit::pixels}; + background.style.height={100,webscene_native::length_unit::pixels}; + background.style.z_index=1000000; + first.style.background_rgba=0x112233FF; + second.style.background_rgba=0x445566FF; + background.style.background_rgba=0x778899FF; + document.layout(200,200); + const auto hit_x=control.layout.x+control.layout.width/2; + const auto hit_y=control.layout.y+control.layout.height/2; + host.attributes["inert"]=""; + require(document.is_inert(control),"Baseline inherited inert state missing"); + require(document.register_modal_dialog(scope,first),"First modal registration failed"); + require(document.active_modal_dialog(scope)==&first&&!document.is_inert(control) + &&document.is_inert(background),"Modal did not escape inert ancestor or block background"); + require(document.hit_test(scope,hit_x,hit_y)==&control,"Active modal control was not hit above background"); + require(document.hit_test(scope,150,150)==nullptr,"Modal background remained hit-testable"); + first.attributes["inert"]=""; + require(document.is_inert(control),"Explicit modal inert state was escaped"); + require(document.hit_test(scope,hit_x,hit_y)==nullptr,"Explicitly inert modal accepted pointer input"); + first.attributes.erase("inert"); + require(document.register_modal_dialog(scope,second),"Second modal registration failed"); + require(document.active_modal_dialog(scope)==&second&&document.is_inert(control) + &&!document.is_inert(second),"Second modal ordering failed"); + require(document.hit_test(scope,hit_x,hit_y)==&second,"Top modal lost pointer ordering"); + for(bool ordered_canvas : {false,true}) { + std::vector commands; + std::vector strings; + std::vector bytes; + document.build_scene(commands,strings,bytes,ordered_canvas); + const auto painted_index=[&](const auto& node) { + size_t index=commands.size();unsigned count=0; + for(size_t i=0;i(); + require(!runtime.install_webgpu(wake,false,webgpu_canvas_interop::none),"Insecure WebGPU installation accepted"); + require(runtime.execute("if('gpu' in navigator)throw new Error('insecure GPU exposure');","denied-gpu"),"Denied GPU exposure failed"); +#if defined(__APPLE__) + constexpr auto runtime_interop=webgpu_canvas_interop::iosurface; +#else + constexpr auto runtime_interop=webgpu_canvas_interop::none; +#endif + require(runtime.install_webgpu(wake,true,runtime_interop),"Secure WebGPU installation failed"); + require(runtime.execute(R"JS( + if(navigator.gpu!==navigator.gpu)throw new Error('GPU identity'); + navigator.gpu.requestAdapter().then(adapter=>{ + if(!adapter)throw new Error('adapter unavailable'); + return adapter.requestDevice(); + }).then(device=>{ + globalThis.installedDevice=device; + const ready=document.createElement('div');ready.id='gpu-installed-ready';document.body.appendChild(ready); + }); + )JS","installed-gpu"),"Installed WebGPU request failed"); + auto deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(!document.find_by_id("gpu-installed-ready")&&std::chrono::steady_clock::now()++windowCalls; + window.addEventListener('probe',windowListener); + installedDevice.dispatchEvent(new Event('probe')); + window.removeEventListener('probe',windowListener); + if(windowCalls)throw new Error('GPUDevice dispatched to window'); + } + )JS","device-events"),"GPUDevice EventTarget integration failed"); + require(runtime.execute(R"JS( + (async()=>{ + if(!(installedDevice.lost instanceof Promise)||installedDevice.lost!==installedDevice.lost)throw new Error('lost SameObject promise'); + const adapter=await navigator.gpu.requestAdapter();const device=await adapter.requestDevice(); + const promise=device.lost;device.destroy();device.destroy(); + const info=await promise; + if(!(info instanceof GPUDeviceLostInfo)||info.reason!=='destroyed'||typeof info.message!=='string')throw new Error('destroyed loss result'); + if(Object.prototype.toString.call(info)!=='[object GPUDeviceLostInfo]')throw new Error('lost info tag'); + if(device.lost!==promise||await device.lost!==info)throw new Error('lost identity changed'); + let rejected=false;try{new GPUDeviceLostInfo()}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('lost info constructible'); + const getter=Object.getOwnPropertyDescriptor(GPUDeviceLostInfo.prototype,'reason').get; + rejected=false;try{getter.call({})}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('lost info receiver accepted'); + const node=document.createElement('div');node.id='loss-ready';document.body.appendChild(node); + })(); + )JS","device-lost"),"GPUDevice lost request failed"); + deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(!document.find_by_id("loss-ready")&&std::chrono::steady_clock::now(){ + const queue=installedDevice.queue; + if(queue.writeBuffer.length!==3)throw new Error('writeBuffer arity'); + const buffer=installedDevice.createBuffer({size:32,usage:9}); + const source=new Uint32Array([11,22,33,44]); + queue.writeBuffer(buffer,0,source.subarray(1),1,1); + source.fill(99); + const bytes=new Uint8Array([1,2,3,4,5,6,7,8]); + queue.writeBuffer(buffer,4,new DataView(bytes.buffer,2,6),1,4); + queue.writeBuffer(buffer,8,bytes.buffer,4,4); + const shared=new SharedArrayBuffer(8);new Uint32Array(shared).set([55,66]); + queue.writeBuffer(buffer,12,new Uint32Array(shared),1,1); + queue.writeBuffer(buffer,16,shared,0,4); + new Uint32Array(shared).fill(0); // Submitted shared bytes are call-time data. + const detachable=new Uint32Array([0x12345678]); + queue.writeBuffer(buffer,20,detachable); + new Uint8Array(detachable.buffer.transfer()).fill(0); + if(detachable.byteLength!==0)throw new Error('source view was not detached'); + const direct=new Uint32Array([0x23456789]).buffer; + queue.writeBuffer(buffer,24,direct); + new Uint8Array(direct.transfer()).fill(0); + if(direct.byteLength!==0)throw new Error('source buffer was not detached'); + queue.writeBuffer(buffer,32,new ArrayBuffer(0)); + await buffer.mapAsync(1); + const result=new DataView(buffer.getMappedRange()); + if(result.getUint32(0,true)!==33||result.getUint32(4,true)!==0x07060504 + ||result.getUint32(8,true)!==0x08070605||result.getUint32(12,true)!==66||result.getUint32(16,true)!==55 + ||result.getUint32(20,true)!==0x12345678||result.getUint32(24,true)!==0x23456789) + throw new Error('writeBuffer uploaded wrong bytes'); + buffer.unmap(); + for(const args of [[buffer,0,bytes,9],[buffer,0,bytes,0,3],[buffer,0,bytes,4,8]]) { + let rejected=false;try{queue.writeBuffer(...args)}catch(e){rejected=e instanceof DOMException&&e.name==='OperationError'} + if(!rejected)throw new Error('invalid source range accepted'); + } + for(const args of [[],[{},0,bytes],[buffer,-1,bytes],[buffer,0,{}],[buffer,0,bytes,-1]]) { + let rejected=false;try{queue.writeBuffer(...args)}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('invalid writeBuffer conversion accepted'); + } + const detached=new ArrayBuffer(8);detached.transfer(); + const resizable=new ArrayBuffer(8,{maxByteLength:16}); + for(const source of [detached,resizable]) { + let rejected=false;try{queue.writeBuffer(buffer,0,source)}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('invalid backing store accepted'); + } + const reentrant=new ArrayBuffer(8);let detachedRejected=false; + try{queue.writeBuffer(buffer,0,reentrant,{valueOf(){reentrant.transfer();return 0}})}catch(e){detachedRejected=e instanceof TypeError} + if(!detachedRejected)throw new Error('detachment during conversion accepted'); + const sentinel={};let propagated=false; + try{queue.writeBuffer(buffer,{valueOf(){throw sentinel}},bytes)}catch(e){propagated=e===sentinel} + if(!propagated)throw new Error('writeBuffer conversion exception lost'); + installedDevice.pushErrorScope('validation'); + queue.writeBuffer(buffer,2,new Uint32Array([1])); + if(!(await installedDevice.popErrorScope() instanceof GPUValidationError))throw new Error('unaligned destination did not reach Dawn validation'); + const node=document.createElement('div');node.id='write-ready';document.body.appendChild(node); + })(); + )JS","queue-write-buffer"),"writeBuffer request failed"); + deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(!document.find_by_id("write-ready")&&std::chrono::steady_clock::now(){ + if(event.bubbles||event.cancelable||event.target!==closingDialog)throw new Error('close event flags'); + dialogEvents.push('close'); + }); + closingDialog.returnValue='initial'; + closingDialog.close('ignored'); + if(closingDialog.returnValue!=='initial')throw new Error('closed dialog result changed'); + closingDialog.open=true; + closingDialog.addEventListener('cancel',event=>{ + if(event.bubbles||!event.cancelable)throw new Error('cancel event flags'); + dialogEvents.push('cancel');event.preventDefault(); + },{once:true}); + closingDialog.requestClose('cancelled'); + if(!closingDialog.open||closingDialog.returnValue!=='initial'||dialogEvents.join()!=='cancel')throw new Error('requestClose cancellation'); + const openAttribute=Array.from(closingDialog.attributes).find(attribute=>attribute.name==='open'); + closingDialog.requestClose('accepted'); + if(openAttribute.ownerElement!==null||openAttribute.value!=='')throw new Error('close did not detach Attr'); + if(closingDialog.open||closingDialog.returnValue!=='accepted'||dialogEvents.join()!=='cancel')throw new Error('close was not asynchronous'); + closingDialog.close('duplicate'); + if(closingDialog.returnValue!=='accepted')throw new Error('duplicate close changed result'); + )JS","dialog-close-lifecycle"); + if(!dialog_close_ok)throw std::runtime_error("Dialog closing failed: "+runtime.last_error()); + for(int task=0;task<32&&runtime.has_pending_tasks();++task) + require(runtime.pump_task(),"Dialog close task failed"); + require(runtime.execute(R"JS( + if(dialogEvents.join()!=='cancel,close')throw new Error('queued close missing or duplicated'); + closingDialog.open=true;closingDialog.close(undefined); + if(closingDialog.returnValue!=='accepted')throw new Error('omitted close value was overwritten'); + let rejected=false;try{closingDialog.close(Symbol())}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('close DOMString validation'); + rejected=false;try{HTMLDialogElement.prototype.close.call(document.body)}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('close receiver validation'); + closingDialog.remove(); + )JS","dialog-close-result"),"Dialog close result failed"); + const bool inert_setup_ok=runtime.execute(R"JS( + globalThis.inertHost=document.createElement('div'); + inertHost.innerHTML=''; + document.body.append(inertHost); + globalThis.outsideInert=document.createElement('button');outsideInert.textContent='Outside';document.body.append(outsideInert); + outsideInert.focus(); + inertHost.inert=true; + if(!inertHost.hasAttribute('inert')||!inertHost.inert||document.getElementById('inert-input').inert)throw new Error('inert reflection'); + document.getElementById('inert-input').focus(); + if(document.activeElement!==outsideInert)throw new Error('inert focus accepted'); + const shadowHost=document.createElement('div');inertHost.append(shadowHost); + const shadow=shadowHost.attachShadow({mode:'open'});shadow.innerHTML=''; + shadow.querySelector('button').focus(); + if(document.activeElement!==outsideInert)throw new Error('inert shadow focus accepted'); + inertHost.inert=false; + document.getElementById('inert-input').focus(); + if(document.activeElement!==document.getElementById('inert-input'))throw new Error('removing inert did not restore focus'); + inertHost.setAttribute('inert',''); + inertHost.getBoundingClientRect(); + )JS","inert-input-setup"); + if(!inert_setup_ok)throw std::runtime_error("Inert setup failed: "+runtime.last_error()); + auto* inert_input=document.find_by_id("inert-input"); + auto* inert_button=document.find_by_id("inert-button"); + require(inert_input&&inert_button&&document.is_inert(*inert_input),"Native inert state missing"); + require(document.hit_test(document.body(),5,5)!=inert_input,"Inert fixed input remained hit-testable"); + require(document.hit_test(document.body(),5,25)!=inert_button,"Inert fixed button remained hit-testable"); + webscene_input_event inert_text{};inert_text.kind=WEBSCENE_INPUT_TEXT;inert_text.x=65; + require(runtime.dispatch_input(inert_text),"Inert text dispatch failed"); + webscene_input_event inert_tab{};inert_tab.kind=WEBSCENE_INPUT_KEY_DOWN;inert_tab.x=9; + require(runtime.dispatch_input(inert_tab),"Inert Tab dispatch failed"); + require(runtime.execute("if(document.activeElement!==outsideInert)throw new Error('Tab selected inert control');","inert-tab"),"Inert Tab validation failed"); + require(runtime.execute(R"JS( + if(document.getElementById('inert-input').value!=='')throw new Error('inert input was edited'); + inertHost.removeAttribute('inert'); + inertHost.getBoundingClientRect(); + )JS","inert-text"),"Inert text validation failed"); + require(document.hit_test(document.body(),5,5)==inert_input,"Removing inert did not restore hit testing"); + require(runtime.execute("inertHost.remove();outsideInert.remove();","inert-cleanup"),"Inert cleanup failed"); + require(runtime.execute(R"JS( + if(installedDevice.createBindGroupLayout.length!==1)throw new Error('binding layout arity'); + globalThis.bindingLayout=installedDevice.createBindGroupLayout({label:'camera',entries:new Set([{binding:0,visibility:1,buffer:{}}])}); + if(Object.prototype.toString.call(bindingLayout)!=='[object GPUBindGroupLayout]'||bindingLayout.label!=='camera')throw new Error('binding layout wrapper'); + for(const descriptor of [{},{entries:[{visibility:1}]},{entries:[{binding:0}]},{entries:[{binding:0,visibility:1,buffer:{type:'bad'}}]}]){ + let rejected=false;try{installedDevice.createBindGroupLayout(descriptor)}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('binding descriptor accepted'); + } + bindingLayout.label='updated';if(bindingLayout.label!=='updated')throw new Error('binding layout label'); + )JS","binding-layout"),"Binding layout creation failed"); + require(runtime.execute(R"JS( + { + if(installedDevice.createBindGroup.length!==1)throw new Error('bind group arity'); + const buffer=installedDevice.createBuffer({size:64,usage:64}); + const order=[]; + const binding=new Proxy({buffer,offset:0,size:64},{get(target,key){order.push('buffer.'+key);return target[key]}}); + const entry=new Proxy({binding:0,resource:binding},{get(target,key){order.push('entry.'+key);return target[key]}}); + const descriptor=new Proxy({label:'camera group',entries:new Set([entry]),layout:bindingLayout},{get(target,key){order.push(key);return target[key]}}); + const group=installedDevice.createBindGroup(descriptor); + if(order.join(',')!=='label,entries,entry.binding,entry.resource,buffer.buffer,buffer.offset,buffer.size,layout')throw new Error('binding conversion order: '+order); + if(Object.prototype.toString.call(group)!=='[object GPUBindGroup]'||group.label!=='camera group')throw new Error('bind group wrapper'); + group.label='renamed';if(group.label!=='renamed')throw new Error('bind group label'); + installedDevice.createBindGroup({layout:bindingLayout,entries:[{binding:0,resource:buffer}]}); + for(const bad of [{},{entries:[]},{layout:bindingLayout,entries:[{binding:0}]}, + {layout:group,entries:[]},{layout:bindingLayout,entries:[{binding:0,resource:{buffer:{}}}]}, + {layout:bindingLayout,entries:[{binding:0,resource:{buffer,offset:-1}}]}]) { + let rejected=false;try{installedDevice.createBindGroup(bad)}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('invalid bind group accepted'); + } + const texture=installedDevice.createTexture({size:[2,2],format:'rgba8unorm',usage:4}); + const textureLayout=installedDevice.createBindGroupLayout({entries:[{binding:0,visibility:2,texture:{}}]}); + for(const resource of [texture,texture.createView()]) + installedDevice.createBindGroup({layout:textureLayout,entries:[{binding:0,resource}]}); + const sentinel={};let propagated=false; + try{installedDevice.createBindGroup({get entries(){throw sentinel}})}catch(e){propagated=e===sentinel} + if(!propagated)throw new Error('binding getter exception lost'); + let receiverRejected=false;try{installedDevice.createBindGroup.call({}, {})}catch(e){receiverRejected=e instanceof TypeError} + if(!receiverRejected)throw new Error('binding receiver accepted'); + } + )JS","binding-group"),"Bind group creation failed"); + require(runtime.execute(R"JS( + { + if(installedDevice.createPipelineLayout.length!==1)throw new Error('pipeline layout arity'); + const order=[]; + const descriptor=new Proxy({label:'explicit layout',bindGroupLayouts:new Set([bindingLayout]),immediateSize:0}, + {get(target,key){order.push(key);return target[key]}}); + const layout=installedDevice.createPipelineLayout(descriptor); + if(order.join(',')!=='label,bindGroupLayouts,immediateSize')throw new Error('pipeline layout conversion order'); + if(Object.prototype.toString.call(layout)!=='[object GPUPipelineLayout]'||layout.label!=='explicit layout')throw new Error('pipeline layout wrapper'); + layout.label='updated';if(layout.label!=='updated')throw new Error('pipeline layout label'); + installedDevice.createPipelineLayout({bindGroupLayouts:[null,undefined]}); + for(const bad of [{},{bindGroupLayouts:[{}]},{bindGroupLayouts:[layout]}, + {bindGroupLayouts:[],immediateSize:-1},{bindGroupLayouts:[],immediateSize:Infinity}]) { + let rejected=false;try{installedDevice.createPipelineLayout(bad)}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('invalid pipeline layout accepted'); + } + const sentinel={};let propagated=false; + try{installedDevice.createPipelineLayout({get bindGroupLayouts(){throw sentinel}})}catch(e){propagated=e===sentinel} + if(!propagated)throw new Error('pipeline layout getter exception lost'); + const module=installedDevice.createShaderModule({code:'@vertex fn main()->@builtin(position) vec4f{return vec4f(0,0,0,1);}',compilationHints:[{entryPoint:'main',layout}]}); + const pipeline=installedDevice.createRenderPipeline({layout,vertex:{module},primitive:{topology:'point-list'}}); + if(Object.prototype.toString.call(pipeline)!=='[object GPURenderPipeline]')throw new Error('explicit pipeline creation'); + } + )JS","pipeline-layout"),"Pipeline layout creation failed"); + require(runtime.execute(R"JS( + const namespaces={GPUBufferUsage:{MAP_READ:1,MAP_WRITE:2,COPY_SRC:4,COPY_DST:8,INDEX:16,VERTEX:32,UNIFORM:64,STORAGE:128,INDIRECT:256,QUERY_RESOLVE:512}, + GPUTextureUsage:{COPY_SRC:1,COPY_DST:2,TEXTURE_BINDING:4,STORAGE_BINDING:8,RENDER_ATTACHMENT:16,TRANSIENT_ATTACHMENT:32}, + GPUMapMode:{READ:1,WRITE:2},GPUShaderStage:{VERTEX:1,FRAGMENT:2,COMPUTE:4},GPUColorWrite:{RED:1,GREEN:2,BLUE:4,ALPHA:8,ALL:15}}; + for(const [name,values] of Object.entries(namespaces)){ + const object=globalThis[name];if(Object.prototype.toString.call(object)!=='[object '+name+']')throw new Error('namespace brand'); + for(const [key,value] of Object.entries(values)){const d=Object.getOwnPropertyDescriptor(object,key); + if(d.value!==value||d.writable||d.configurable||!d.enumerable)throw new Error('flag descriptor');} + } + )JS","gpu-constants"),"GPU flag namespaces failed"); + require(runtime.execute(R"JS( + (async()=>{ + if(installedDevice.popErrorScope.length!==0)throw new Error('pop scope arity'); + for(const Type of [GPUValidationError,GPUOutOfMemoryError,GPUInternalError]) { + const error=new Type('diagnostic'); + if(!(error instanceof GPUError)||!(error instanceof Type)||error.message!=='diagnostic')throw new Error('GPUError inheritance'); + if(Object.prototype.toString.call(error)!=='[object '+Type.name+']')throw new Error('GPUError tag'); + let rejected=false;try{Type('message')}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('error constructor callable'); + } + let rejected=false;try{new GPUError()}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('base error constructible'); + const getter=Object.getOwnPropertyDescriptor(GPUError.prototype,'message').get; + rejected=false;try{getter.call({})}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('GPUError getter accepted wrong receiver'); + const wrong=installedDevice.popErrorScope.call({}); + if(!(wrong instanceof Promise))throw new Error('pop receiver did not return promise'); + rejected=false;try{await wrong}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('pop receiver accepted'); + rejected=false;try{await installedDevice.popErrorScope()}catch(e){rejected=e instanceof DOMException&&e.name==='OperationError'} + if(!rejected)throw new Error('empty scope accepted'); + installedDevice.pushErrorScope('validation'); + installedDevice.pushErrorScope('out-of-memory'); + installedDevice.createBuffer({size:16,usage:0}); + const clean=installedDevice.popErrorScope(); + const captured=installedDevice.popErrorScope(); + if(clean===captured)throw new Error('scope promises reused'); + if(await clean!==null)throw new Error('wrong scope captured validation'); + const error=await captured; + if(!(error instanceof GPUValidationError)||!error.message.length)throw new Error('native validation missing'); + installedDevice.pushErrorScope('validation'); + if(await installedDevice.popErrorScope()!==null)throw new Error('clean scope failed'); + const node=document.createElement('div');node.id='scope-ready';document.body.appendChild(node); + })(); + )JS","pop-error-scope"),"GPU popErrorScope request failed"); + deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(!document.find_by_id("scope-ready")&&std::chrono::steady_clock::now()installedDevice.pushErrorScope(),()=>installedDevice.pushErrorScope(undefined), + ()=>installedDevice.pushErrorScope('Validation'),()=>installedDevice.pushErrorScope(null), + ()=>installedDevice.pushErrorScope(Symbol()),()=>installedDevice.pushErrorScope.call({},'validation') + ]){let rejected=false;try{call()}catch(e){rejected=e instanceof TypeError}if(!rejected)throw new Error('scope filter accepted');} + let conversions=0; + installedDevice.pushErrorScope({toString(){++conversions;return 'validation';}}); + if(conversions!==1)throw new Error('scope conversion count'); + const sentinel={};let propagated=false; + try{installedDevice.pushErrorScope({toString(){throw sentinel;}})}catch(e){propagated=e===sentinel} + if(!propagated)throw new Error('scope conversion exception lost'); + )JS","push-error-scope"),"GPU pushErrorScope binding failed"); + + require(runtime.execute(R"JS( + (async()=>{ + const valid=installedDevice.createShaderModule({code:'@compute @workgroup_size(1) fn main() {}'}); + if(valid.getCompilationInfo.length!==0)throw new Error('compilation info arity'); + const first=valid.getCompilationInfo(),second=valid.getCompilationInfo(); + if(first===second)throw new Error('compilation promises reused'); + if((await first).messages.length||(await second).messages.length)throw new Error('valid shader diagnostics'); + let wrong=false;try{await valid.getCompilationInfo.call({})}catch(e){wrong=e instanceof TypeError} + if(!wrong)throw new Error('compilation receiver accepted'); + const invalid=installedDevice.createShaderModule({code:'/* 😀 */ this is invalid WGSL'}); + const info=await invalid.getCompilationInfo(); + if(!Object.isFrozen(info.messages)||!info.messages.some(m=>m.type==='error'&&m.message.length&&m.offset>0)) + throw new Error('invalid shader diagnostics absent'); + const node=document.createElement('div');node.id='compilation-ready';document.body.appendChild(node); + })(); + )JS","compilation-info"),"Compilation info request script failed"); + deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(!document.find_by_id("compilation-ready")&&std::chrono::steady_clock::now(){ + const d=installedDevice; + const module=d.createShaderModule({code:` + @group(0) @binding(0) var values:array; + @compute @workgroup_size(1) fn main(@builtin(global_invocation_id) id:vec3){ + values[id.x]=id.x*3u+7u; + }`}); + const pipeline=await d.createComputePipelineAsync({layout:'auto',compute:{module,entryPoint:'main'}}); + const buffer=d.createBuffer({size:16,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST}); + const readback=d.createBuffer({size:16,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}); + const group=d.createBindGroup({layout:pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer}}]}); + const encoder=d.createCommandEncoder();encoder.clearBuffer(buffer); + const pass=encoder.beginComputePass();pass.setPipeline(pipeline);pass.setBindGroup(0,group);pass.dispatchWorkgroups(4);pass.end(); + encoder.copyBufferToBuffer(buffer,0,readback,0,16);d.queue.submit([encoder.finish()]); + await d.queue.onSubmittedWorkDone();await readback.mapAsync(GPUMapMode.READ); + const values=new Uint32Array(readback.getMappedRange()); + if(values.join(',')!=='7,10,13,16')throw Error('Compute dispatch readback mismatch: '+values); + readback.unmap();readback.destroy();buffer.destroy(); + let invalid=false; + try{await d.createComputePipelineAsync({layout:'auto',compute:{module,entryPoint:'missing'}})} + catch(e){invalid=e.name==='GPUPipelineError'&&e.reason==='validation'} + if(!invalid)throw Error('Invalid async pipeline did not reject'); + const texture=d.createTexture({size:[2,1],format:'rgba8unorm',usage:GPUTextureUsage.COPY_DST|GPUTextureUsage.COPY_SRC}); + d.queue.writeTexture({texture},new Uint8Array([255,0,0,255,0,255,0,255]),{bytesPerRow:8},[2,1]); + const pixels=d.createBuffer({size:256,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}); + const copy=d.createCommandEncoder();copy.copyTextureToBuffer({texture},{buffer:pixels,bytesPerRow:256},[2,1]); + d.queue.submit([copy.finish()]);await pixels.mapAsync(GPUMapMode.READ); + if(new Uint8Array(pixels.getMappedRange()).slice(0,8).join(',')!=='255,0,0,255,0,255,0,255')throw Error('Texture transfer mismatch'); + pixels.unmap();pixels.destroy();texture.destroy(); + const ready=document.createElement('div');ready.id='compute-copy-ready';document.body.appendChild(ready); + })(); + )JS","compute-copy"),"Compute/copy regression execution failed"); + deadline=std::chrono::steady_clock::now()+std::chrono::seconds(10); + while(!document.find_by_id("compute-copy-ready")&&std::chrono::steady_clock::now()canvas().gpu_image&&std::chrono::steady_clock::now()canvas().gpu_image; + require(published!=nullptr,"Completed DOM GPU image did not publish"); + auto captured_output=published_node->canvas().gpu_snapshot; + require(captured_output!=nullptr,"Runtime did not capture submitted GPU output"); + auto captured_image=captured_output->resolve(); + require(captured_image&&captured_image==captured_output->resolve(),"Captured output resolution was not stable"); + require(captured_image->value.describe().allocation==published->value.describe().allocation&& + captured_output->describe().content_serial==published->value.describe().content_serial,"Runtime capture changed output identity"); + auto consumer=published->value.begin_consumer();require(consumer.has_value(),"Published DOM canvas consumer unavailable"); + auto surface=iosurface_canvas_images::resolve(*consumer).borrowed_handle(); + require(IOSurfaceLock(surface,kIOSurfaceLockReadOnly,nullptr)==kIOReturnSuccess,"DOM canvas pixel lock failed"); + auto pixels=static_cast(IOSurfaceGetBaseAddress(surface));auto stride=IOSurfaceGetBytesPerRow(surface);bool correct=pixels!=nullptr; + if(pixels)for(size_t y=0;y<2;++y)for(size_t x=0;x<8;++x){auto pixel=pixels+y*stride+x*4;correct&=pixel[0]==0&&pixel[1]==0&&pixel[2]==255&&pixel[3]==255;} + auto unlocked=IOSurfaceUnlock(surface,kIOSurfaceLockReadOnly,nullptr);consumer->complete(); + require(correct&&unlocked==kIOReturnSuccess,"Published DOM canvas pixel mismatch"); + require(runtime.host_animation_frame_demand()==0,"Published unchanged GPU canvas kept requesting frames"); + require(runtime.execute("if(domGPUContext.getCurrentTexture()===publicationTexture)throw new Error('frame texture not expired');","gpu-publication-expire"),"GPU publication expiration failed"); + require(published_node->canvas().backing.accepts_completed_content(published->value.describe().content_serial), + "Acquiring the next frame invalidated completed canvas content"); + document.publish_gpu_canvas_image(*published_node,published); + require(runtime.execute("domGPUCanvas.width=10;globalThis.presentationRaf=requestAnimationFrame(()=>{});","gpu-presentation-resize"),"Presentation resize setup failed"); + runtime.update_gpu_presentation_images({published}); + require(published_node->canvas().gpu_presentation_image==published, + "Resize did not retain its compositor-accepted image"); + require(!runtime.has_open_gpu_output(),"Retained presentation image still blocked shell publication"); + require(runtime.execute("domGPUContext.unconfigure();","gpu-unconfigure"),"GPU unconfigure failed"); + require(!published_node->canvas().gpu_presentation_image,"Unconfigure retained presentation fallback"); + require(runtime.execute("cancelAnimationFrame(presentationRaf);","gpu-presentation-cleanup"),"Presentation RAF cleanup failed"); + bool invalidated_image_rejected=false; + try { document.publish_gpu_canvas_image(*published_node,published); } + catch(const std::invalid_argument&) { invalidated_image_rejected=true; } + require(invalidated_image_rejected,"Unconfigure accepted a previous completed image"); + require(!published_node->canvas().gpu_image&&!published_node->canvas().gpu_snapshot,"Unconfigure retained the displayed GPU output"); + require(captured_output->resolve()==captured_image,"Unconfigure invalidated a frozen capture's ownership"); + require(runtime.execute(R"JS( + domGPUContext.configure({device:installedDevice,format:navigator.gpu.getPreferredCanvasFormat()}); + requestAnimationFrame(()=>{globalThis.rafCanvasTexture=domGPUContext.getCurrentTexture();}); + requestAnimationFrame(()=>{if(domGPUContext.getCurrentTexture()!==rafCanvasTexture)throw new Error('texture expired between RAF callbacks');}); + )JS","gpu-raf-group"),"GPU RAF setup failed"); + runtime.signal_animation_frame(116); + require(runtime.pump_animation_frame_task()&&runtime.has_pending_animation_frame_task(),"First GPU RAF lost remaining rendering work"); + require(runtime.pump_animation_frame_task()&&!runtime.has_pending_animation_frame_task(),"GPU RAF group did not finish"); + require(runtime.execute("domGPUContext.unconfigure();","gpu-raf-cleanup"),"GPU RAF cleanup failed"); + require(runtime.execute(R"JS( + domGPUContext.configure({device:installedDevice,format:navigator.gpu.getPreferredCanvasFormat()}); + domGPUCanvas.width=12; + )JS","gpu-resize-no-redraw"),"Resize setup failed"); + require(!runtime.has_open_gpu_output(),"Bitmap reset without redraw indefinitely held scene publication"); + runtime.update_gpu_presentation_images({published}); + require(!published_node->canvas().gpu_presentation_image, + "Resize resurrected a compositor image from an earlier configuration"); + + require(runtime.execute("requestAnimationFrame(()=>requestAnimationFrame(()=>{}));","gpu-resize-redraw"),"Resize redraw setup failed"); + require(runtime.has_open_gpu_output(),"Queued resize redraw allowed an intermediate blank scene"); + runtime.signal_animation_frame(132); + require(runtime.pump_animation_frame_task(),"Resize opportunity failed"); + require(!runtime.has_open_gpu_output(),"Resize without drawing held publication beyond its rendering opportunity"); + runtime.signal_animation_frame(148); + require(runtime.pump_animation_frame_task(),"Nested resize RAF failed"); + require(runtime.execute("domGPUCanvas.height=3;requestAnimationFrame(()=>{});domGPUContext.unconfigure();","gpu-resize-unconfigure"),"Resize unconfigure setup failed"); + require(!runtime.has_open_gpu_output(),"Unconfigured canvas held a resize redraw boundary"); + runtime.signal_animation_frame(164); + require(runtime.pump_animation_frame_task(),"Unconfigured resize callback failed"); + require(runtime.execute(R"JS( + domGPUContext.configure({device:installedDevice,format:navigator.gpu.getPreferredCanvasFormat()}); + requestAnimationFrame(()=>{ + domGPUCanvas.width=14; + domGPUContext.getCurrentTexture(); + requestAnimationFrame(()=>{}); + }); + )JS","gpu-reset-within-frame"),"Within-frame reset setup failed"); + runtime.signal_animation_frame(180); + require(runtime.pump_animation_frame_task(),"Within-frame reset redraw failed"); + require(!runtime.has_open_gpu_output(),"Submitted replacement retained the resize hold"); + runtime.signal_animation_frame(196); + require(runtime.pump_animation_frame_task(),"Following frame callback failed"); + require(runtime.execute("domGPUContext.unconfigure();","gpu-reset-within-frame-cleanup"),"Within-frame reset cleanup failed"); + + +#endif + require(runtime.load_url("https://graphics.test/webgpu-next"),"WebGPU navigation failed"); + require(runtime.execute("if('gpu' in navigator||'GPUBufferUsage' in globalThis||'GPUDeviceLostInfo' in globalThis||'GPUError' in globalThis||'GPUValidationError' in globalThis||'GPUOutOfMemoryError' in globalThis||'GPUInternalError' in globalThis)throw new Error('GPU policy survived navigation');","navigated-gpu"),"Navigation retained GPU exposure"); + require(runtime.install_webgpu(wake,true,webgpu_canvas_interop::none),"Navigated GPU reinstall failed"); + require(runtime.execute("globalThis.retiredGPU=navigator.gpu;","retain-gpu"),"GPU retention failed"); + runtime.shutdown_graphics(); + require(runtime.execute("let invalidated=false;try{retiredGPU.getPreferredCanvasFormat()}catch(e){invalidated=e instanceof TypeError}if(!invalidated)throw new Error('shutdown GPU callable');","shutdown-gpu"),"GPU shutdown failed to invalidate receiver"); + // Direct runtime destruction must enter the isolate before cancelling a + // live realm; hosts are not required to call shutdown_graphics separately. + webscene_native::native_document direct_document; + webscene_native::v8_dom_runtime direct(direct_document,[]{return webscene_native::v8_dom_runtime::viewport_metrics{64,64,1,0};}); + require(direct.initialize()&&direct.install_webgpu(wake,true,webgpu_canvas_interop::none),"Direct disposal GPU setup failed"); + require(direct.execute("navigator.gpu.requestAdapter();","pending-disposal-gpu"),"Direct disposal request failed"); +} +#include "graphics_v8_modules.h" +int main() { + std::exception_ptr failure; + std::thread worker([&] { + try { + test_secure_context_reporting(); + test_modules_and_clone(); + test_dedicated_module_worker(); + test_device_loss_signal(); + test_compilation_info_snapshot(); + test_navigation_stylesheet_raw_text_isolation(); + test_inline_canvas_intrinsic_layout(); + test_runtime_webgpu_document_policy(); + test_positioned_auto_margins(); + test_native_modal_ordering(); + test_runtime_webgpu_installation(); + webscene_native::native_document document; + webscene_native::v8_dom_runtime runtime(document,[] { + return webscene_native::v8_dom_runtime::viewport_metrics{640,480,1,0}; + },{},[](uint32_t,const std::string& url,const auto&,const std::string&,int64_t,auto& response) { + if (url!="https://graphics.test/next") return false; + response.content="next"; + return true; + }); + require(runtime.initialize(),"runtime initialization failed"); + require(runtime.execute("globalThis.gpuDone=0; globalThis.rafDone=0; new Promise(r=>globalThis.gpuResolve=r).then(()=>globalThis.gpuDone=1); requestAnimationFrame(()=>globalThis.rafDone=1);","graphics-test"),"promise setup failed"); + require(runtime.execute("globalThis.canvasProbe=document.createElement('canvas'); canvasProbe.id='backing-probe'; document.body.appendChild(canvasProbe); globalThis.contextProbe=canvasProbe.getContext('2d');","backing-setup"),"canvas backing setup failed"); + auto* canvas_node=document.find_by_id("backing-probe"); + require(canvas_node!=nullptr,"canvas backing node missing"); + const auto& backing=canvas_node->canvas().backing; + const auto backing_id=backing.identity(),allocation=backing.allocation_generation(),content=backing.content_serial(); + require(runtime.execute("contextProbe.fillRect(0,0,10,10);","backing-draw"),"canvas draw failed"); + require(backing.content_serial()>content && backing.allocation_generation()==allocation,"draw changed allocation generation"); + const auto after_draw=backing.content_serial(); + require(runtime.execute("canvasProbe.style.width='600px';","backing-css-size"),"canvas CSS resize failed"); + require(backing.content_serial()==after_draw && backing.allocation_generation()==allocation,"CSS resize changed bitmap backing"); + require(runtime.execute("canvasProbe.width=300;","backing-reset"),"canvas reset failed"); + require(backing.content_serial()>after_draw,"same-size bitmap reset did not change content"); + require(backing.identity()==backing_id && backing.allocation_generation()==allocation,"same-size reset changed allocation identity"); + require(runtime.execute("canvasProbe.width=640;","backing-resize"),"canvas bitmap resize failed"); + require(backing.width()==640 && backing.height()==150 && backing.allocation_generation()==allocation+1,"bitmap resize missed backing generation"); + require(backing.mode()==canvas_context_mode::two_d,"bitmap reset released context ownership"); + struct canvas_provider final : image_provider_lifetime {}; + owned_image_pool images(std::make_shared()); + auto image_writer=images.acquire(); + image_writer->set_metadata({backing.identity(),100,backing.allocation_generation(),backing.content_serial(),1,1,backing.width(),backing.height()}); + image_writer->begin(); auto image_frame=image_writer->publish(); + image_writer->complete(); + auto canvas_image=std::make_shared(std::move(*image_frame)); image_frame.reset(); + document.layout(640,480); + // The first submitted image can be pending while its CPU paint + // commands are captured. Never substitute live canvas state later. + { + struct delayed_image final : webscene_gpu_image_snapshot { + std::shared_ptr image; + bool ready=false; + explicit delayed_image(std::shared_ptr value):image(std::move(value)) {} + image_metadata describe()const override{return image->value.describe();} + status state()const override{return ready?status::ready:status::pending;} + std::shared_ptr resolve()override{return ready?image:nullptr;} + }; + auto delayed=std::make_shared(canvas_image); + canvas_node->mutable_canvas().gpu_snapshot=delayed; + std::vector bindings; + document.build_gpu_canvas_bindings(bindings); + require(bindings.size()==1&&!bindings[0].resolve(),"Pending canvas binding escaped readiness gate"); + std::vector pending_paint; + std::vector pending_strings; + std::vector pending_bytes; + document.build_scene(pending_paint,pending_strings,pending_bytes,true,true); + require(std::any_of(pending_paint.begin(),pending_paint.end(),[&](const auto& command){return command.kind==WEBSCENE_SCENE_COMMAND_GPU_IMAGE&&command.node_id==canvas_node->id;}),"First pending image had no GPU paint placeholder"); + canvas_node->mutable_canvas().gpu_snapshot.reset(); + delayed->ready=true; + require(bindings[0].resolve()==canvas_image,"Frozen binding consulted replacement live canvas state"); + } + const auto before_publication=document.scene_generation(); + document.publish_gpu_canvas_image(*canvas_node,canvas_image); + require(document.scene_generation()==before_publication+1 && !document.dirty(), + "GPU publication did not request a scene independently of layout"); + document.publish_gpu_canvas_image(*canvas_node,canvas_image); + require(document.scene_generation()==before_publication+1,"same image requested redundant scene"); + webscene_native::native_document foreign_document; + bool foreign_image_rejected=false; + try { foreign_document.publish_gpu_canvas_image(*canvas_node,canvas_image); } + catch (const std::invalid_argument&) { foreign_image_rejected=true; } + require(foreign_image_rejected,"foreign document accepted GPU canvas"); + + require(runtime.execute("globalThis.paintBefore=document.createElement('div'); paintBefore.id='gpu-before'; paintBefore.style.cssText='width:20px;height:20px;background:red'; document.body.insertBefore(paintBefore,canvasProbe); globalThis.paintAfter=document.createElement('div'); paintAfter.id='gpu-after'; paintAfter.style.cssText='width:20px;height:20px;background:blue'; document.body.appendChild(paintAfter);","gpu-paint-order"),"GPU paint siblings failed"); + document.layout(640,480); + std::vector> captured; + document.build_gpu_canvas_images(captured); + require(captured.size()==1 && captured[0]==canvas_image,"canvas GPU image capture failed"); + std::vector paint; + std::vector paint_strings; + std::vector paint_bytes; + document.build_scene(paint,paint_strings,paint_bytes); + const auto gpu_paint=std::find_if(paint.begin(),paint.end(),[](const auto& c) { + return c.kind==WEBSCENE_SCENE_COMMAND_GPU_IMAGE; + }); + require(gpu_paint!=paint.end() && gpu_paint->node_id==canvas_node->id + && gpu_paint->x==canvas_node->layout.x && gpu_paint->width==canvas_node->layout.width, + "GPU image paint placement missing"); + const auto before_id=document.find_by_id("gpu-before")->id; + const auto after_id=document.find_by_id("gpu-after")->id; + const auto before_paint=std::find_if(paint.begin(),paint.end(),[&](const auto& c) { return (c.kind==1 || c.kind==9) && c.node_id==before_id; }); + const auto after_paint=std::find_if(paint.begin(),paint.end(),[&](const auto& c) { return (c.kind==1 || c.kind==9) && c.node_id==after_id; }); + require(before_paintid; + const auto mixed_marker=std::find_if(paint.begin(),paint.end(),[&](const auto& c) { + return c.kind==WEBSCENE_SCENE_COMMAND_CANVAS_LAYER && c.node_id==mixed_id; + }); + const auto mixed_gpu=std::find_if(paint.begin(),paint.end(),[](const auto& c) { + return c.kind==WEBSCENE_SCENE_COMMAND_GPU_IMAGE; + }); + const auto mixed_after=std::find_if(paint.begin(),paint.end(),[&](const auto& c) { + return (c.kind==1 || c.kind==9) && c.node_id==after_id; + }); + require(mixed_marker!=paint.end() && mixed_after!=paint.end() && mixed_gpuid) continue; + switch (command.kind) { + case 12: ++clips; if (command.radius_top_left!=8) throw std::runtime_error("GPU clip radius="+std::to_string(command.radius_top_left)+" bounds="+std::to_string(command.width)+"x"+std::to_string(command.height)); break; + case 13: --clips; break; + case 15: ++scales; require(command.width==0.75F,"GPU scale missing"); break; + case 16: --scales; break; + case 19: ++rotations; require(command.stroke_width==15,"GPU rotation missing"); break; + case 20: --rotations; break; + case 30: ++opacity_groups; require(command.rgba==128,"GPU group opacity missing"); break; + case 31: --opacity_groups; break; + case WEBSCENE_SCENE_COMMAND_GPU_IMAGE: + ++gpu_draws; + require(clips==1 && scales==1 && rotations==1 && opacity_groups==1, + "GPU sampling escaped its paint scopes"); break; + } + require(clips>=0 && scales>=0 && rotations>=0 && opacity_groups>=0,"GPU paint scope underflow"); + } + require(gpu_draws==1 && clips==0 && scales==0 && rotations==0 && opacity_groups==0, + "GPU paint scopes did not balance"); + require(backing.content_serial()==before_effects,"CSS paint effects changed GPU bitmap content"); + require(runtime.execute("canvasProbe.remove();","gpu-remove"),"canvas removal failed"); + std::vector> detached; + document.build_gpu_canvas_images(detached); + require(detached.empty() && captured[0]->value.describe().width==640,"detachment lost retained image"); + require(runtime.execute("document.body.appendChild(canvasProbe);","gpu-reinsert"),"canvas reinsertion failed"); + document.layout(640,480); document.build_gpu_canvas_images(detached); + require(detached.size()==1,"reinserted canvas lost image"); + require(runtime.execute("canvasProbe.width=800;","gpu-resize"),"GPU canvas reset failed"); + document.build_gpu_canvas_images(detached); + require(detached.empty() && !canvas_node->canvas().gpu_image,"reset kept stale canvas image"); + document.build_scene(paint,paint_strings,paint_bytes); + require(std::none_of(paint.begin(),paint.end(),[](const auto& c) { + return c.kind==WEBSCENE_SCENE_COMMAND_GPU_IMAGE; + }),"reset kept stale GPU paint operation"); + require(captured[0]->value.describe().width==640,"resize mutated retained frame"); + bool rejected=false; + try { document.publish_gpu_canvas_image(*canvas_node,canvas_image); } + catch (const std::invalid_argument&) { rejected=true; } + require(rejected,"stale image generation accepted"); + image_writer.reset(); + captured.clear(); canvas_image.reset(); + require(images.busy_images()==0,"captured image leaked pool slot"); + auto verify_attribute_reset=[&](const char* script,uint32_t expected_width,uint32_t expected_height,bool changes_size) { + const auto generation=backing.allocation_generation(),serial=backing.content_serial(); + auto before=images.acquire(); + before->set_metadata({backing.identity(),101,generation,serial,1,2,backing.width(),backing.height()}); + before->begin(); auto ticket=before->publish(); before->complete(); before.reset(); + auto old_image=std::make_shared(std::move(*ticket)); ticket.reset(); + document.publish_gpu_canvas_image(*canvas_node,old_image); + require(runtime.execute(script,"canvas-attribute-reset"),"canvas attribute operation failed"); + require(backing.width()==expected_width && backing.height()==expected_height + && backing.content_serial()==serial+1 + && backing.allocation_generation()==generation+(changes_size ? 1 : 0), + "canvas attribute reset missed bitmap version"); + require(!canvas_node->canvas().gpu_image && old_image->value.describe().content_serial==serial, + "attribute reset kept current image or mutated retained content"); + }; + verify_attribute_reset("canvasProbe.setAttribute('width','800');",800,150,false); + verify_attribute_reset("canvasProbe.setAttribute('height','200');",800,200,true); + verify_attribute_reset("canvasProbe.removeAttribute('width');",300,200,true); + verify_attribute_reset("canvasProbe.setAttributeNS(null,'width','400');",400,200,true); + verify_attribute_reset("canvasProbe.removeAttributeNS(null,'width');",300,200,true); + verify_attribute_reset("globalThis.widthAttr=document.createAttribute('width'); widthAttr.value='500'; canvasProbe.setAttributeNode(widthAttr);",500,200,true); + verify_attribute_reset("widthAttr.value='600';",600,200,true); + verify_attribute_reset("canvasProbe.removeAttributeNode(widthAttr);",300,200,true); + verify_attribute_reset("canvasProbe.toggleAttribute('width',true);",300,200,false); + verify_attribute_reset("canvasProbe.toggleAttribute('width',false);",300,200,false); + const auto removed_serial=backing.content_serial(); + require(runtime.execute("canvasProbe.removeAttribute('width'); canvasProbe.toggleAttribute('width',false); widthAttr.value='700';","detached-attribute"),"detached attribute mutation failed"); + require(backing.content_serial()==removed_serial,"absent or detached attribute reset canvas"); + runtime.set_visible(false); + auto wake=std::make_shared(); + const auto owner_thread=std::this_thread::get_id(); + bool delivered=false, adapter_delivered=false, adapter_cancelled=false; + graphics_service* adapter_service=nullptr; + std::unique_ptr adapter_request, cancelled_adapter; + std::array,2> failed_wrappers; + size_t failed_wrapper_count=0; + bool buffer_wrappers_tested=false; + std::unique_ptr device_request,failed_device_request,cancelled_device_request; + bool cancelled_device_retired=false; + bool device_failure_seen=false; + auto buffer_test_device=std::make_shared(); + resource_handle discovered_adapter; + std::shared_ptr releases; + std::unique_ptr wrappers; + std::unique_ptr gc_buffers,async_buffers; + size_t binding_map_completions=0; + std::unique_ptr device_registry; + v8::Global retired_device_probe; + bool device_map_retired=false; + bool buffer_validation_seen=false; + resource_handle gc_buffer_device; + std::array,3> map_requests; + size_t map_completions=0; + std::array test_operations; + for(auto& operation:test_operations)operation=new_owner_token(); + auto& graphics=runtime.initialize_graphics(wake,[&](completion_record record) { + if (device_registry && device_registry->complete(record)) { + auto* isolate=v8::Isolate::GetCurrent(); auto context=isolate->GetCurrentContext(); + device_registry.reset(); + { + v8::TryCatch caught(isolate); + auto object=retired_device_probe.Get(isolate); + auto method=object->Get(context,v8::String::NewFromUtf8Literal(isolate,"destroy")).ToLocalChecked().As(); + require(method->Call(context,object,0,nullptr).IsEmpty() && caught.HasCaught(),"Retired device wrapper retained native access"); + } + retired_device_probe.Reset(); device_map_retired=true; + return; + } + if (record.operation==test_operations[13]) { + auto* isolate=v8::Isolate::GetCurrent(); + require(!cancelled_device_request->pending(),"Cancelled device request remained pending"); + require(!cancelled_device_request->complete(isolate,isolate->GetCurrentContext(),record,[](wgpu::Device) -> v8::Local { + throw std::runtime_error("Cancelled device wrapped after native callback"); + }),"Cancelled device completion was consumed twice"); + cancelled_device_request.reset(); cancelled_device_retired=true; return; + } + if (record.operation==test_operations[11]) { + auto* isolate=v8::Isolate::GetCurrent(); auto context=isolate->GetCurrentContext(); + require(record.status==completion_status::failed,"Impossible device limit unexpectedly accepted"); + require(failed_device_request->complete(isolate,context,record,[](wgpu::Device) -> v8::Local { + throw std::runtime_error("Failed device was wrapped"); + }),"Device failure promise did not settle"); + auto promise=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"failedDevicePromise")).ToLocalChecked().As(); + require(promise->State()==v8::Promise::kRejected,"Device failure did not reject"); + auto name=promise->Result().As()->Get(context,v8::String::NewFromUtf8Literal(isolate,"name")).ToLocalChecked(); + require(name->StrictEquals(v8::String::NewFromUtf8Literal(isolate,"OperationError")),"Device failure has wrong exception type"); + failed_device_request.reset();device_failure_seen=true;return; + } + if (record.operation==test_operations[10]) { + require(record.status==completion_status::success,"Invalid browser usage did not generate native validation"); + buffer_validation_seen=true; return; + } + if (async_buffers && async_buffers->complete(record)) { ++binding_map_completions; return; } + if (record.operation==test_operations[6] || record.operation==test_operations[7] || record.operation==test_operations[8]) { + auto& request=map_requests[std::find(test_operations.begin()+6,test_operations.begin()+9,record.operation)-(test_operations.begin()+6)]; + require(request->complete(record,[&](const auto& buffer,auto) { + if (record.operation==test_operations[8]) throw std::bad_alloc(); + require(record.operation==test_operations[6],"Canceled mapping attached native memory"); + require(buffer.GetMapState()==wgpu::BufferMapState::Mapped && buffer.GetMappedRange(8,16),"Asynchronous subrange mapping failed"); + }),"Map promise completion was not handled"); + require(!request->pending(),"Map promise remained pending"); + require(!request->complete(record,[](const auto&,auto) { throw std::runtime_error("Map attached twice"); }),"Duplicate map completion was accepted"); + request.reset(); + ++map_completions; + return; + } + if (record.operation==test_operations[4]) { + auto* isolate=v8::Isolate::GetCurrent(); + auto context=isolate->GetCurrentContext(); + bool wrong_realm=false; + try { device_request->complete(isolate,v8::Context::New(isolate),record,[](wgpu::Device) -> v8::Local { + throw std::runtime_error("Wrong realm wrapped device"); + }); } catch (const std::logic_error&) { wrong_realm=true; } + require(wrong_realm && device_request->pending(),"Wrong realm consumed device completion"); + require(device_request->complete(isolate,context,record,[&](wgpu::Device device) -> v8::Local { + *buffer_test_device=std::move(device); + return v8::Object::New(isolate); + }),"Native device promise completion failed"); + require(!device_request->pending(),"Native device promise remained pending"); + require(!device_request->complete(isolate,context,record,[](wgpu::Device) -> v8::Local { + throw std::runtime_error("Device was wrapped twice"); + }),"Duplicate device completion accepted"); + auto settled=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"deviceRequestPromise")).ToLocalChecked().As(); + require(settled->State()==v8::Promise::kFulfilled && settled->Result()->IsObject(),"Device request did not fulfill with wrapper"); + device_request.reset(); + require(record.status==completion_status::success && *buffer_test_device,"Buffer wrapper fixture device failed"); + wgpu::Adapter adapter; + adapter_service->with_adapter(discovered_adapter,[&](const auto& native) { adapter=native; }); + auto device_handle=adapter_service->adopt_device(std::move(adapter),std::move(*buffer_test_device)); + resource_handle buffer_handle; + adapter_service->with_device(device_handle,[&](auto& device) { + wgpu::BufferDescriptor descriptor{}; + descriptor.size=64; descriptor.usage=wgpu::BufferUsage::CopyDst; + descriptor.mappedAtCreation=true; + buffer_handle=device.create_buffer(descriptor); + }); + auto buffer_registry=std::make_unique(isolate,context,1,context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As()); + auto object=buffer_registry->wrap(context,*adapter_service,device_handle,buffer_handle).ToLocalChecked(); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"bufferProbe"),object).FromMaybe(false),"Buffer wrapper publication failed"); + bool duplicate_rejected=false,realm_rejected=false; + try { buffer_registry->wrap(context,*adapter_service,device_handle,buffer_handle); } + catch (const std::invalid_argument&) { duplicate_rejected=true; } + try { buffer_registry->wrap(v8::Context::New(isolate),*adapter_service,device_handle,buffer_handle); } + catch (const std::logic_error&) { realm_rejected=true; } + require(duplicate_rejected && realm_rejected,"Buffer ownership or realm identity was duplicated"); + + auto run=[&](const char* source) { + v8::Local script; + return v8::Script::Compile(context,v8::String::NewFromUtf8(isolate,source).ToLocalChecked()).ToLocal(&script) + && !script->Run(context).IsEmpty(); + }; + adapter_service->with_device(device_handle,[&](auto& device) { + device.with_buffer(buffer_handle,[&](const auto& buffer) { + auto* data=buffer.GetMappedRange(0,64); + require(data!=nullptr,"Mapped fixture memory unavailable"); + v8_webgpu_mapped_ranges ranges(isolate,context,object,data,0,64); + auto first=ranges.create(context,0,16).ToLocalChecked(); + auto second=ranges.create(context,16,16).ToLocalChecked(); + require(first->Data()==data && second->Data()==static_cast(data)+16,"Mapped range copied native storage"); + for (auto request:std::array,4>{{{8,8},{4,4},{32,6},{64,4}}}) { + bool rejected=false; + try { ranges.create(context,request.first,request.second); } + catch (const std::invalid_argument&) { rejected=true; } + require(rejected,"Invalid mapped range accepted"); + } + require(!ranges.create(context,0,0).IsEmpty(),"Empty mapped range rejected"); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"mappedProbe"),first).FromMaybe(false),"Mapped view publication failed"); + require(run("globalThis.mappedWords=new Uint32Array(mappedProbe);mappedWords[0]=0x12345678;"),"Mapped JS write failed"); + uint32_t word{}; std::memcpy(&word,data,sizeof(word)); + require(word==0x12345678,"JavaScript write did not reach Dawn mapped memory"); + { + v8::TryCatch caught(isolate); + require(first->Detach(v8::Undefined(isolate)).IsNothing() && caught.HasCaught() + && !first->WasDetached(),"Mapped view allowed foreign detachment"); + } + ranges.detach(); + require(run("if(mappedProbe.byteLength!==0||mappedWords.length!==0)throw new Error('mapped views not detached');delete globalThis.mappedProbe;delete globalThis.mappedWords;"),"Mapped view detachment failed"); + bool detached_rejected=false; + try { ranges.create(context,32,4); } catch (const std::invalid_argument&) { detached_rejected=true; } + require(detached_rejected,"Detached mapping accepted a new view"); + }); + }); + require(run(R"JS( + globalThis.publicMapped=bufferProbe.getMappedRange(32,16); + globalThis.publicMappedWords=new Uint32Array(publicMapped); + publicMappedWords[0]=0x87654321; + if(bufferProbe.getMappedRange(48).byteLength!==16)throw new Error('default mapped size'); + for(let args of [[32,8],[4,4],[64,4]]){ + let rejected=false;try{bufferProbe.getMappedRange(...args)}catch(e){rejected=e instanceof DOMException&&e.name==='OperationError'} + if(!rejected)throw new Error('mapped range validation'); + } + for(let args of [[-1],[Infinity],[1n],[0,9007199254740992]]){ + let rejected=false;try{bufferProbe.getMappedRange(...args)}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('mapped range WebIDL'); + } + )JS"),"JavaScript getMappedRange failed"); + adapter_service->with_device(device_handle,[&](auto& device) { device.with_buffer(buffer_handle,[&](const auto& buffer) { + uint32_t word{}; std::memcpy(&word,static_cast(buffer.GetConstMappedRange(0,64))+32,sizeof(word)); + require(word==0x87654321,"Public mapped range write missed native memory"); + }); }); + require(run("if(bufferProbe.size!==64||bufferProbe.usage!==8||bufferProbe.mapState!=='mapped')throw new Error('buffer metadata'); let p=Object.getPrototypeOf(bufferProbe); for(let f of [p.destroy,Object.getOwnPropertyDescriptor(p,'size').get,Object.getOwnPropertyDescriptor(p,'usage').get,Object.getOwnPropertyDescriptor(p,'mapState').get]){let ok=false;try{f.call({})}catch(e){ok=e instanceof TypeError}if(!ok)throw new Error('buffer brand')} bufferProbe.destroy();bufferProbe.destroy();if(bufferProbe.size!==64||bufferProbe.mapState!=='unmapped')throw new Error('destroy metadata/state');"),"Native buffer wrapper behavior failed"); + require(run("if(publicMapped.byteLength!==0||publicMappedWords.length!==0)throw new Error('destroy did not detach');delete globalThis.publicMapped;delete globalThis.publicMappedWords;bufferProbe.unmap();"),"Destroy mapping detachment failed"); + auto unmap_registry=std::make_unique(isolate,context,1,context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As()); + adapter_service->with_device(device_handle,[&](auto& device) { + wgpu::BufferDescriptor descriptor{}; descriptor.size=32; descriptor.usage=wgpu::BufferUsage::CopyDst; descriptor.mappedAtCreation=true; + auto handle=device.create_buffer(descriptor); + auto wrapped=unmap_registry->wrap(context,*adapter_service,device_handle,handle).ToLocalChecked(); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"unmapProbe"),wrapped).FromMaybe(false),"Unmap fixture publication failed"); + }); + require(run(R"JS( + { + let view=unmapProbe.getMappedRange(), bytes=new Uint8Array(view); + if(view.byteLength!==32)throw new Error('default full mapped range'); + let conversionUnmapped=false; + try{unmapProbe.getMappedRange({valueOf(){unmapProbe.unmap();return 0}})}catch(e){conversionUnmapped=e instanceof DOMException&&e.name==='OperationError'} + if(!conversionUnmapped)throw new Error('mapping state not rechecked after conversion'); + unmapProbe.unmap(); + if(view.byteLength!==0||bytes.length!==0||unmapProbe.mapState!=='unmapped')throw new Error('unmap did not detach'); + let rejected=false;try{unmapProbe.getMappedRange()}catch(e){rejected=e instanceof DOMException&&e.name==='OperationError'} + if(!rejected)throw new Error('unmapped range accepted'); + delete globalThis.unmapProbe; + } + )JS"),"JavaScript unmap failed"); + unmap_registry.reset(); + require(run(R"JS( + if(bufferProbe.label!=='')throw new Error('label default'); + bufferProbe.label='a\0\ud800'; + if(bufferProbe.label!=='a\0\ufffd')throw new Error('label USVString'); + let labelError={}; + try {bufferProbe.label={toString(){throw labelError}}}catch(e){if(e!==labelError)throw e} + if(bufferProbe.label!=='a\0\ufffd')throw new Error('failed label mutation'); + let symbolRejected=false;try{bufferProbe.label=Symbol()}catch(e){symbolRejected=e instanceof TypeError} + if(!symbolRejected)throw new Error('symbol label accepted'); + bufferProbe.label={toString(){bufferProbe.destroy();return 'after destroy'}}; + if(bufferProbe.label!=='after destroy')throw new Error('reentrant label'); + let brandRejected=false,coerced=false; + try{Object.getOwnPropertyDescriptor(Object.getPrototypeOf(bufferProbe),'label').set.call({}, {toString(){coerced=true;return ''}})}catch(e){brandRejected=e instanceof TypeError} + if(!brandRejected||coerced)throw new Error('label brand ordering'); + )JS"),"Buffer label behavior failed"); + buffer_registry.reset(); + require(run("let stale=false;try{bufferProbe.destroy()}catch(e){stale=e instanceof TypeError}if(!stale)throw new Error('stale buffer realm');delete globalThis.bufferProbe;"),"Buffer wrapper teardown left native access"); + gc_buffer_device=device_handle; + gc_buffers=std::make_unique(isolate,context,1,context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As()); + adapter_service->with_device(device_handle,[&](auto& device) { + wgpu::BufferDescriptor descriptor{}; + descriptor.size=32; descriptor.usage=wgpu::BufferUsage::CopyDst; descriptor.mappedAtCreation=true; + auto collectible=device.create_buffer(descriptor); + auto gc_object=gc_buffers->wrap(context,*adapter_service,device_handle,collectible).ToLocalChecked(); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"gcBufferProbe"),gc_object).FromMaybe(false),"Collectible buffer wrapper failed"); + auto get_range=gc_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"getMappedRange")).ToLocalChecked().template As(); + auto held_range=get_range->Call(context,gc_object,0,nullptr).ToLocalChecked(); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"gcMappedProbe"),held_range).FromMaybe(false),"Retained mapped view publication failed"); + auto overflow=device.create_buffer(descriptor); + require(gc_buffers->wrap(context,*adapter_service,device_handle,overflow).IsEmpty(),"Buffer wrapper registry was not bounded"); + device.release_buffer(overflow); // Failed wrap did not take ownership. + }); + require(run("globalThis.mapExceptionSentinel={};globalThis.throwingMapException=class {constructor(){throw mapExceptionSentinel}};"),"Map exception fixture setup failed"); + adapter_service->with_device(device_handle,[&](auto& device) { + for (size_t i=0;i(isolate,context,v8::Object::New(isolate), + context->Global()->Get(context,v8::String::NewFromUtf8(isolate,i==1 ? "throwingMapException" : "DOMException").ToLocalChecked()).ToLocalChecked().As(), + std::move(buffer),device.owner(),test_operations[6+i]); + auto promise=map_requests[i]->start(adapter_service->dawn().completions(),wgpu::MapMode::Write,8,16).ToLocalChecked(); + require(context->Global()->Set(context,v8::String::NewFromUtf8(isolate,i==0 ? "asyncMapProbe" : i==1 ? "cancelMapProbe" : "allocationMapProbe").ToLocalChecked(),promise).FromMaybe(false),"Map promise publication failed"); + } + }); + require(run("globalThis.asyncMapDone=false;globalThis.cancelMapDone=false;globalThis.allocationMapDone=false;allocationMapProbe.catch(e=>{if(!(e instanceof RangeError))throw e;allocationMapDone=true});asyncMapProbe.then(v=>{if(v!==undefined)throw new Error('map result');asyncMapDone=true});cancelMapProbe.catch(e=>{if(e!==mapExceptionSentinel)throw e;cancelMapDone=true});"),"Map promise observers failed"); + require(map_requests[1]->cancel() && !map_requests[1]->pending() && !map_requests[1]->cancel(),"Map cancellation with throwing exception factory failed"); + async_buffers=std::make_unique(isolate,context,4,context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As()); + adapter_service->with_device(device_handle,[&](auto& device) { + auto count=device.live_buffers(); + { + v8::TryCatch caught(isolate); + auto invalid_size=v8::Script::Compile(context,v8::String::NewFromUtf8Literal(isolate,"({size:3,usage:8,mappedAtCreation:true})")).ToLocalChecked()->Run(context).ToLocalChecked(); + require(async_buffers->create(context,*adapter_service,device_handle,invalid_size).IsEmpty() + && caught.HasCaught() && device.live_buffers()==count,"Misaligned mapped creation allocated a buffer"); + require(caught.Exception().As()->Get(context,v8::String::NewFromUtf8Literal(isolate,"name")).ToLocalChecked()->StrictEquals(v8::String::NewFromUtf8Literal(isolate,"RangeError")),"Mapped size error was not RangeError"); + } + device.native().PushErrorScope(wgpu::ErrorFilter::Validation); + auto invalid_usage=v8::Script::Compile(context,v8::String::NewFromUtf8Literal(isolate,"({size:64,usage:1024,mappedAtCreation:true,label:'invalid usage'})")).ToLocalChecked()->Run(context).ToLocalChecked(); + auto error_buffer=async_buffers->create(context,*adapter_service,device_handle,invalid_usage).ToLocalChecked(); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"errorBufferProbe"),error_buffer).FromMaybe(false),"Error buffer publication failed"); + require(run("if(errorBufferProbe.usage!==1024||errorBufferProbe.size!==64||errorBufferProbe.label!=='invalid usage'||errorBufferProbe.getMappedRange().byteLength!==64)throw new Error('invalid buffer metadata/mapping');errorBufferProbe.unmap();delete globalThis.errorBufferProbe;"),"Browser error buffer behavior failed"); + auto error_mailbox=adapter_service->dawn().completions(); + auto error_ticket=error_mailbox->reserve(test_operations[10],device.owner()).value(); + device.native().PopErrorScope(wgpu::CallbackMode::AllowSpontaneous, + [error_mailbox,error_ticket](wgpu::PopErrorScopeStatus status,wgpu::ErrorType type,wgpu::StringView) { + error_mailbox->publish(error_ticket,status==wgpu::PopErrorScopeStatus::Success && type==wgpu::ErrorType::Validation + ? completion_status::success : completion_status::failed); + }); + for (const char* name:{"bindingBuffer","bindingCancelBuffer","bindingReadBuffer"}) { + const bool read=std::string_view(name)=="bindingReadBuffer"; + v8::Local object; + if (read) { + wgpu::BufferDescriptor descriptor{}; descriptor.size=64; + descriptor.usage=wgpu::BufferUsage::MapRead|wgpu::BufferUsage::CopyDst; + auto handle=device.create_buffer(descriptor); + device.with_buffer(handle,[&](const auto& buffer) { + std::array data; data.fill(0x11223344); + device.native().GetQueue().WriteBuffer(buffer,0,data.data(),sizeof(data)); + }); + object=async_buffers->wrap(context,*adapter_service,device_handle,handle).ToLocalChecked(); + } else { + auto input=v8::Script::Compile(context,v8::String::NewFromUtf8Literal(isolate,"({size:64,usage:6,label:'created from JS'})")).ToLocalChecked()->Run(context).ToLocalChecked(); + object=async_buffers->create(context,*adapter_service,device_handle,input).ToLocalChecked(); + } + require(context->Global()->Set(context,v8::String::NewFromUtf8(isolate,name).ToLocalChecked(),object).FromMaybe(false),"Async binding buffer publication failed"); + } + }); + { + auto saturated_registry=std::make_unique(isolate,context,1,context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As()); + auto input=v8::Script::Compile(context,v8::String::NewFromUtf8Literal(isolate,"({size:4,usage:8})")).ToLocalChecked()->Run(context).ToLocalChecked(); + size_t before=0; adapter_service->with_device(device_handle,[&](auto& device) { before=device.live_buffers(); }); + v8::TryCatch caught(isolate); + require(saturated_registry->create(context,*adapter_service,device_handle,input).IsEmpty() && caught.HasCaught(),"Release saturation silently returned an empty wrapper"); + adapter_service->with_device(device_handle,[&](auto& device) { require(device.live_buffers()==before,"Wrapper registration failure leaked a native buffer"); }); + } + require(run(R"JS( + globalThis.bindingMapDone=false;globalThis.bindingCancelDone=false;globalThis.bindingRemapDone=false;globalThis.bindingConversionDone=false;globalThis.bindingReadDone=false; + { + if(bindingBuffer.label!=='created from JS'||bindingBuffer.usage!==6)throw new Error('JS-created buffer metadata');if(bindingBuffer.mapAsync.length!==1)throw new Error('mapAsync length'); + let conversion=bindingBuffer.mapAsync(1n); + if(!(conversion instanceof Promise))throw new Error('mapAsync conversion threw synchronously'); + conversion.catch(e=>{if(!(e instanceof TypeError))throw e;bindingConversionDone=true}); + let promise=bindingBuffer.mapAsync(2,8,16); + if(!(promise instanceof Promise)||bindingBuffer.mapState!=='pending')throw new Error('pending map state'); + promise.then(()=>{ + if(bindingBuffer.mapState!=='mapped')throw new Error('completed map state'); + let view=bindingBuffer.getMappedRange(8,16);new Uint32Array(view)[0]=42; + bindingBuffer.unmap(); + if(view.byteLength!==0||bindingBuffer.mapState!=='unmapped')throw new Error('async view detach'); + bindingMapDone=true;delete globalThis.bindingBuffer; + }); + bindingReadBuffer.mapAsync(1,8,16).then(()=>{ + let words=new Uint32Array(bindingReadBuffer.getMappedRange(8,16)); + if(words[0]!==0x11223344)throw new Error('read mapping data'); + words[0]=0xffffffff;bindingReadBuffer.unmap(); + return bindingReadBuffer.mapAsync(1,8,16); + }).then(()=>{ + if(new Uint32Array(bindingReadBuffer.getMappedRange(8,16))[0]!==0x11223344)throw new Error('READ writes reached GPU buffer'); + bindingReadBuffer.unmap();bindingReadDone=true;delete globalThis.bindingReadBuffer; + }); + bindingCancelBuffer.mapAsync(2,0,16).catch(e=>{ + if(!(e instanceof DOMException)||e.name!=='AbortError')throw e;bindingCancelDone=true; + }); + bindingCancelBuffer.unmap(); + bindingCancelBuffer.mapAsync(2,16,16).then(()=>{ + if(bindingCancelBuffer.getMappedRange(16,16).byteLength!==16)throw new Error('remap range'); + bindingCancelBuffer.unmap();bindingRemapDone=true;delete globalThis.bindingCancelBuffer; + }); + } + )JS"),"JavaScript mapAsync dispatch failed"); + graphics_service adapter_fixture(wake,2); + auto fresh_adapter=std::make_shared(); + auto fixture_mailbox=adapter_fixture.dawn().completions(); + auto adapter_ticket=fixture_mailbox->reserve(new_owner_token(),{adapter_fixture.engine_identity(),new_owner_token(),0}).value(); + auto adapter_options=make_dawn_adapter_options(webgpu_adapter_options{},wgpu::BackendType::Undefined).value(); + adapter_fixture.dawn().instance().RequestAdapter(&adapter_options,wgpu::CallbackMode::AllowSpontaneous, + [fresh_adapter,fixture_mailbox,adapter_ticket](wgpu::RequestAdapterStatus status,wgpu::Adapter adapter,wgpu::StringView) { + *fresh_adapter=std::move(adapter); + fixture_mailbox->publish(adapter_ticket,status==wgpu::RequestAdapterStatus::Success?completion_status::success:completion_status::failed); + }); + bool fresh_adapter_ready=false; + auto fresh_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(!fresh_adapter_ready && std::chrono::steady_clock::now()Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As(); + auto adapter_devices=std::make_unique(isolate,context,exception_constructor,2,4); + auto adapter_registry=std::make_unique(isolate,context,*adapter_devices,exception_constructor,1); + auto adapter_object=adapter_registry->wrap(context,adapter_fixture,adapter_handle).ToLocalChecked(); + bool duplicate_adapter=false,foreign_realm=false; + try { adapter_registry->wrap(context,adapter_fixture,adapter_handle); } catch (const std::invalid_argument&) { duplicate_adapter=true; } + try { adapter_registry->wrap(v8::Context::New(isolate),adapter_fixture,adapter_handle); } catch (const std::logic_error&) { foreign_realm=true; } + require(duplicate_adapter && foreign_realm,"Adapter identity or realm ownership duplicated"); + auto adapter_features=adapter_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"features")).ToLocalChecked(); + require(adapter_features->StrictEquals(adapter_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"features")).ToLocalChecked()),"Adapter feature identity changed"); + auto feature_has=adapter_features.As()->Get(context,v8::String::NewFromUtf8Literal(isolate,"has")).ToLocalChecked().As(); + adapter_fixture.with_adapter(adapter_handle,[&](const auto& native) { + verify_v8_limits(isolate,context,adapter_object,native); + auto info=read_webgpu_adapter_info(native); + require(webgpu_adapter_is_fallback(wgpu::BackendType::Vulkan,0x1ae0,0xc0de) + && !webgpu_adapter_is_fallback(wgpu::BackendType::Vulkan,0x1ae0,0) + && !webgpu_adapter_is_fallback(wgpu::BackendType::Metal,0x1ae0,0xc0de),"Fallback classification differs from pinned Dawn"); + auto exposed_info=adapter_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"info")).ToLocalChecked().As(); + for(const auto& field:std::array,4>{{{"vendor",&info.vendor},{"architecture",&info.architecture},{"device",&info.device},{"description",&info.description}}}) { + auto value=exposed_info->Get(context,v8::String::NewFromUtf8(isolate,field.first).ToLocalChecked()).ToLocalChecked(); + v8::String::Utf8Value text(isolate,value); + require(*text && std::string(*text,text.length())==*field.second,"Adapter info string differs from native snapshot"); + } + wgpu::AdapterInfo native_info{};require(native.GetInfo(&native_info)==wgpu::Status::Success,"Adapter info query failed"); + require(info.description==webgpu_info_string(native_info.description) && !info.is_fallback_adapter,"Adapter info snapshot incorrect"); + require(info.subgroup_min_size==(native.HasFeature(wgpu::FeatureName::Subgroups)?native_info.subgroupMinSize:4) + && info.subgroup_max_size==(native.HasFeature(wgpu::FeatureName::Subgroups)?native_info.subgroupMaxSize:128),"Adapter subgroup information incorrect"); + require(webgpu_info_identifier(wgpu::StringView("vendor-123"))=="vendor-123" + && webgpu_info_identifier(wgpu::StringView("Vendor Name")).empty() + && webgpu_info_identifier(wgpu::StringView("a--b")).empty(),"Adapter identifier normalization rules incorrect"); + for (const auto& feature:webgpu_feature_names) { + v8::Local name=v8::String::NewFromUtf8(isolate,feature.name.data(),v8::NewStringType::kNormal,static_cast(feature.name.size())).ToLocalChecked(); + require(feature_has->Call(context,adapter_features,1,&name).ToLocalChecked()->BooleanValue(isolate)==native.HasFeature(feature.native),"Adapter capability snapshot differs from Dawn"); + } + }); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"adapterWrapperProbe"),adapter_object).FromMaybe(false),"Adapter wrapper publication failed"); + require(run("globalThis.adapterDevicePromise=adapterWrapperProbe.requestDevice({label:'via adapter',defaultQueue:{label:'JS queue'}});"),"Adapter requestDevice call failed"); + auto requested_device_promise=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"adapterDevicePromise")).ToLocalChecked().As(); + auto request_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while (requested_device_promise->State()==v8::Promise::kPending && std::chrono::steady_clock::now()complete(completion),"Adapter request completion not routed"); }); + if (requested_device_promise->State()==v8::Promise::kPending) std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + require(requested_device_promise->State()==v8::Promise::kFulfilled,"Adapter request did not produce a native device wrapper"); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"adapterDeviceProbe"),requested_device_promise->Result()).FromMaybe(false),"Adapter device publication failed"); + test_v8_webgpu_canvas_configuration(isolate,context); + size_t canvas_acquisitions=0,canvas_retirements=0; + webgpu_canvas_host canvas_host; + canvas_host.validate=[](const auto& config){if(config.color_space!="srgb")throw std::invalid_argument("Diagnostic canvas supports sRGB");}; + canvas_host.acquire=[&](const auto& config,const auto& descriptor){++canvas_acquisitions;wgpu::Texture texture;descriptor.with_native([&](const auto& native){texture=config.device.CreateTexture(&native);});return texture;}; + canvas_host.retire=[&](const auto& texture,bool){++canvas_retirements;texture.Destroy();}; + auto canvas_context=std::make_unique(isolate,context,v8::Object::New(isolate),context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As(),4,2,std::move(canvas_host)); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"canvasContextProbe"),canvas_context->object()).FromMaybe(false),"Canvas context publication failed"); + require(run(R"JS( + if(canvasContextProbe.getConfiguration()!==null)throw new Error('initial canvas config'); + let unconfigured=false;try{canvasContextProbe.getCurrentTexture()}catch(e){unconfigured=e instanceof DOMException&&e.name==='InvalidStateError'}if(!unconfigured)throw new Error('unconfigured canvas texture'); + canvasContextProbe.configure({device:adapterDeviceProbe,format:'rgba8unorm',usage:17}); + const snapshot=canvasContextProbe.getConfiguration();snapshot.viewFormats.push('invalid');snapshot.toneMapping.mode='invalid'; + if(canvasContextProbe.getConfiguration().viewFormats.length!==0||canvasContextProbe.getConfiguration().toneMapping.mode!=='standard')throw new Error('canvas snapshot mutation'); + if(canvasContextProbe.canvas!==canvasContextProbe.canvas)throw new Error('canvas identity'); + )JS"),"Canvas configuration lifecycle failed"); + + { + auto device_object=requested_device_promise->Result().As();auto device=v8_webgpu_devices::native_reference(device_object); + webgpu_texture_descriptor metadata;metadata.size={4,2,1};metadata.format=wgpu::TextureFormat::RGBA8Unorm;metadata.usage=16;metadata.label="imported canvas"; + wgpu::Texture native;metadata.with_native([&](const auto& descriptor){native=device.CreateTexture(&descriptor);}); + auto mismatch=metadata;mismatch.size.width=8;bool rejected=false; + try{v8_webgpu_devices::adopt_canvas_texture(context,device_object,device,native,mismatch);}catch(const std::invalid_argument&){rejected=true;} + require(rejected,"Canvas adoption accepted mismatched metadata"); + auto wrapper=v8_webgpu_devices::adopt_canvas_texture(context,device_object,device,native,metadata).ToLocalChecked(); + require(v8_webgpu_textures::native_reference(wrapper).Get()==native.Get(),"Canvas adoption replaced native texture"); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"importedCanvasTexture"),wrapper).FromMaybe(false),"Imported texture publication failed"); + require(run("if(importedCanvasTexture.width!==4||importedCanvasTexture.label!=='imported canvas')throw new Error('imported metadata');const importedView=importedCanvasTexture.createView();if(Object.prototype.toString.call(importedView)!=='[object GPUTextureView]')throw new Error('imported view');delete globalThis.importedCanvasTexture;"),"Imported canvas texture JavaScript access failed"); + } + + require(run(R"JS( + if(adapterWrapperProbe.requestDevice.length!==0)throw new Error('requestDevice arity'); + const ai=adapterWrapperProbe.info,di=adapterDeviceProbe.adapterInfo; + if(ai!==adapterWrapperProbe.info||di!==adapterDeviceProbe.adapterInfo||Object.prototype.toString.call(ai)!=='[object GPUAdapterInfo]')throw new Error('adapter info identity'); + for(const key of ['vendor','architecture','device','description','subgroupMinSize','subgroupMaxSize','isFallbackAdapter'])if(ai[key]!==di[key])throw new Error('device adapter info mismatch'); + const savedVendor=ai.vendor;let infoReadOnly=false;try{(()=>{'use strict';ai.vendor='changed'})()}catch(e){infoReadOnly=e instanceof TypeError} + if(!infoReadOnly||ai.vendor!==savedVendor||'backendType' in ai||'vendorID' in ai)throw new Error('adapter info mutation or private fields'); + let infoBrand=false;try{Object.getOwnPropertyDescriptor(Object.getPrototypeOf(ai),'vendor').get.call({})}catch(e){infoBrand=e instanceof TypeError}if(!infoBrand)throw new Error('adapter info receiver'); + globalThis.retainedAdapterInfo=ai; + if(adapterDeviceProbe.label!=='via adapter')throw new Error('requested device label'); + if(adapterDeviceProbe.queue!==adapterDeviceProbe.queue||adapterDeviceProbe.queue.label!=='JS queue'||Object.prototype.toString.call(adapterDeviceProbe.queue)!=='[object GPUQueue]')throw new Error('device queue identity'); + adapterDeviceProbe.queue.label='updated queue';if(adapterDeviceProbe.queue.label!=='updated queue')throw new Error('queue label'); + {const encoder=adapterDeviceProbe.createCommandEncoder({label:'JS encoder'}); + if(Object.prototype.toString.call(encoder)!=='[object GPUCommandEncoder]'||encoder.label!=='JS encoder'||encoder.finish.length!==0)throw new Error('encoder wrapper'); + const sentinel={};let propagated=false;try{encoder.finish({get label(){throw sentinel}})}catch(e){propagated=e===sentinel}if(!propagated)throw new Error('finish exception'); + const command=encoder.finish({label:'JS commands'}); + if(Object.prototype.toString.call(command)!=='[object GPUCommandBuffer]'||command.label!=='JS commands')throw new Error('command buffer wrapper'); + command.label='updated commands';if(command.label!=='updated commands')throw new Error('command label'); + for(const call of [()=>adapterDeviceProbe.createCommandEncoder(1),()=>encoder.finish.call({}),()=>encoder.finish(1)]) { + let rejected=false;try{call()}catch(e){rejected=e instanceof TypeError}if(!rejected)throw new Error('invalid encoder call'); + } + const empty=adapterDeviceProbe.createCommandEncoder().finish();if(empty.label!=='')throw new Error('encoder defaults');} + + {const texture=canvasContextProbe.getCurrentTexture();texture.label='JS texture'; + if(texture!==canvasContextProbe.getCurrentTexture())throw new Error('current texture identity'); + if(texture.width!==4||texture.height!==2||texture.depthOrArrayLayers!==1||texture.mipLevelCount!==1||texture.sampleCount!==1||texture.dimension!=='2d'||texture.format!=='rgba8unorm'||texture.usage!==17)throw new Error('texture metadata'); + if(Object.prototype.toString.call(texture)!=='[object GPUTexture]'||texture.label!=='JS texture')throw new Error('texture wrapper'); + const view=texture.createView({label:'JS view'}); + if(Object.prototype.toString.call(view)!=='[object GPUTextureView]'||view.label!=='JS view')throw new Error('view wrapper'); + view.label='updated view';texture.label='updated texture'; + let readonly=false;try{(()=>{'use strict';texture.width=8})()}catch(e){readonly=e instanceof TypeError}if(!readonly||texture.width!==4)throw new Error('texture readonly metadata'); + for(const call of [()=>adapterDeviceProbe.createTexture(),()=>adapterDeviceProbe.createTexture({size:[],format:'rgba8unorm',usage:16}),()=>texture.createView.call({}),()=>texture.destroy.call({})]) { + let rejected=false;try{call()}catch(e){rejected=e instanceof TypeError}if(!rejected)throw new Error('invalid texture call'); + } + const shader=adapterDeviceProbe.createShaderModule({code:'@vertex fn vs(@location(0) p:vec2f)->@builtin(position) vec4f {return vec4f(p,0,1);} @group(0) @binding(0) var color:vec4f; @fragment fn fs()->@location(0) vec4f {return color;}'}); + const vertices=adapterDeviceProbe.createBuffer({size:32,usage:32,mappedAtCreation:true}); + new Float32Array(vertices.getMappedRange()).set([-1,-1,3,-1,-1,3],2);vertices.unmap(); + const uniform=adapterDeviceProbe.createBuffer({size:512,usage:64,mappedAtCreation:true}); + new Float32Array(uniform.getMappedRange()).set([1,0,0,1],64);uniform.unmap(); + const bindingLayout=adapterDeviceProbe.createBindGroupLayout({entries:[{binding:0,visibility:2,buffer:{hasDynamicOffset:true}}]}); + const layout=adapterDeviceProbe.createPipelineLayout({bindGroupLayouts:[bindingLayout]}); + const group=adapterDeviceProbe.createBindGroup({layout:bindingLayout,entries:[{binding:0,resource:{buffer:uniform,size:16}}]}); + const pipeline=adapterDeviceProbe.createRenderPipeline({layout,vertex:{module:shader,entryPoint:'vs',buffers:[{arrayStride:8,attributes:[{format:'float32x2',offset:0,shaderLocation:0}]}]},fragment:{module:shader,entryPoint:'fs',targets:[{format:'rgba8unorm'}]}}); + const drawEncoder=adapterDeviceProbe.createCommandEncoder(); + let shapeRejected=false;try{drawEncoder.beginRenderPass({colorAttachments:[{view,loadOp:'clear',storeOp:'store',clearValue:[0,0]}]})}catch(e){shapeRejected=e instanceof TypeError}if(!shapeRejected)throw new Error('clear color shape'); + const pass=drawEncoder.beginRenderPass({label:'triangle pass',colorAttachments:[{view,loadOp:'clear',storeOp:'store',clearValue:[0,0,0,1]}]}); + if(Object.prototype.toString.call(pass)!=='[object GPURenderPassEncoder]'||pass.label!=='triangle pass')throw new Error('render pass wrapper'); + for(const call of [()=>pass.draw(),()=>pass.draw(-1),()=>pass.draw(1n),()=>pass.setPipeline({}),()=>pass.end.call({})]) { + let rejected=false;try{call()}catch(e){rejected=e instanceof TypeError}if(!rejected)throw new Error('invalid pass call'); + } + if(pass.setBindGroup.length!==2)throw new Error('setBindGroup arity'); + for(const call of [()=>pass.setBindGroup(),()=>pass.setBindGroup(-1,group),()=>pass.setBindGroup(0,{}), + ()=>pass.setBindGroup(0,group,[-1]),()=>pass.setBindGroup(0,group,new Uint32Array(1),0), + ()=>pass.setBindGroup(0,group,[],0,1)]) { + let rejected=false;try{call()}catch(e){rejected=e instanceof TypeError}if(!rejected)throw new Error('invalid bind group call'); + } + for(const [start,count] of [[2,0],[0,2]]) { + let rejected=false;try{pass.setBindGroup(0,group,new Uint32Array(1),start,count)}catch(e){rejected=e instanceof RangeError} + if(!rejected)throw new Error('dynamic offset bounds accepted'); + } + const bindingSentinel={};let bindingException=false; + try{pass.setBindGroup(0,group,{[Symbol.iterator](){throw bindingSentinel}})}catch(e){bindingException=e===bindingSentinel} + if(!bindingException)throw new Error('binding iterator exception lost'); + if(pass.setVertexBuffer.length!==2)throw new Error('setVertexBuffer arity'); + for(const call of [()=>pass.setVertexBuffer(),()=>pass.setVertexBuffer(-1,vertices),()=>pass.setVertexBuffer(0,{}), + ()=>pass.setVertexBuffer(0,vertices,-1),()=>pass.setVertexBuffer(0,vertices,0,1n)]) { + let rejected=false;try{call()}catch(e){rejected=e instanceof TypeError}if(!rejected)throw new Error('invalid vertex buffer call'); + } + pass.setVertexBuffer(0,null); + pass.setVertexBuffer(0,vertices,8,24); + pass.setPipeline(pipeline); + pass.setBindGroup(0,null); + pass.setBindGroup(0,group,new Set([256]));pass.draw(3); + const dynamic=new Uint32Array([77,256,88]); + pass.setBindGroup(0,group,dynamic.subarray(1),0,1);pass.draw(3); + const sharedDynamic=new Uint32Array(new SharedArrayBuffer(8));sharedDynamic[1]=256; + pass.setBindGroup(0,group,sharedDynamic,1,1);pass.draw(3);pass.end(); + const drawCommands=drawEncoder.finish({label:'triangle commands'}); + if(drawCommands.label!=='triangle commands')throw new Error('recorded draw commands'); + for(const call of [()=>adapterDeviceProbe.queue.submit(),()=>adapterDeviceProbe.queue.submit([{}]),()=>adapterDeviceProbe.queue.submit.call({},[])]) { + let rejected=false;try{call()}catch(e){rejected=e instanceof TypeError}if(!rejected)throw new Error('invalid queue call'); + } + const queueSentinel={};let queueException=false; + try{adapterDeviceProbe.queue.submit({[Symbol.iterator](){throw queueSentinel}})}catch(e){queueException=e===queueSentinel} + if(!queueException)throw new Error('queue iterator exception'); + adapterDeviceProbe.queue.submit(new Set([drawCommands])); + globalThis.triangleTextureProbe=texture; + + // Keep the submitted texture alive for diagnostic pixel verification. + if(texture.width!==4||texture.label!=='updated texture'||view.label!=='updated view')throw new Error('destroyed texture metadata');} + + {let b=adapterDeviceProbe.createBuffer({size:16,usage:8,mappedAtCreation:true}); + let range=b.getMappedRange();new Uint32Array(range)[0]=123;b.unmap(); + if(range.byteLength!==0 || b.size!==16)throw new Error('adapter device buffer');b.destroy();} + globalThis.consumedAdapterPromise=adapterWrapperProbe.requestDevice(); + consumedAdapterPromise.catch(()=>{}); + globalThis.badAdapterReceiverPromise=adapterWrapperProbe.requestDevice.call({}); + badAdapterReceiverPromise.catch(()=>{}); + )JS"),"Adapter device resource operations failed"); + { + auto native_device=v8_webgpu_devices::native_reference(requested_device_promise->Result()); + auto native_texture=v8_webgpu_textures::native_reference(context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"triangleTextureProbe")).ToLocalChecked()); + wgpu::BufferDescriptor descriptor{};descriptor.size=512;descriptor.usage=wgpu::BufferUsage::CopyDst|wgpu::BufferUsage::MapRead; + auto readback=native_device.CreateBuffer(&descriptor);auto copy=native_device.CreateCommandEncoder(); + wgpu::TexelCopyTextureInfo source{};source.texture=native_texture; + wgpu::TexelCopyBufferInfo destination{};destination.buffer=readback;destination.layout.bytesPerRow=256;destination.layout.rowsPerImage=2; + wgpu::Extent3D extent{4,2,1};copy.CopyTextureToBuffer(&source,&destination,&extent); + auto commands=copy.Finish();native_device.GetQueue().Submit(1,&commands); + auto map_status=std::make_shared>(0); + readback.MapAsync(wgpu::MapMode::Read,0,512,wgpu::CallbackMode::AllowSpontaneous,[map_status](wgpu::MapAsyncStatus status,wgpu::StringView){map_status->store(status==wgpu::MapAsyncStatus::Success?1:-1);}); + auto deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(map_status->load()==0&&std::chrono::steady_clock::now()load()==1,"Triangle diagnostic readback did not complete"); + const auto* pixels=static_cast(readback.GetConstMappedRange(0,512));require(pixels!=nullptr,"Triangle readback mapping missing"); + for(size_t y=0;y<2;++y)for(size_t x=0;x<4;++x){const auto* pixel=pixels+y*256+x*4;require(pixel[0]==255&&pixel[1]==0&&pixel[2]==0&&pixel[3]==255,"JavaScript triangle pixel mismatch");} + readback.Unmap();readback.Destroy(); + require(run("triangleTextureProbe.destroy();triangleTextureProbe.destroy();if(triangleTextureProbe.width!==4)throw new Error('destroyed texture metadata');delete globalThis.triangleTextureProbe;"),"Triangle texture cleanup failed"); + } + require(canvas_acquisitions==1&&canvas_retirements==0,"Canvas acquired more than once per frame"); + canvas_context->end_frame(true); + require(canvas_retirements==1,"Canvas frame did not retire"); + canvas_context->resize(8,3); + require(run("const resized=canvasContextProbe.getCurrentTexture();if(resized.width!==8||resized.height!==3)throw new Error('canvas resize');canvasContextProbe.unconfigure();if(canvasContextProbe.getConfiguration()!==null)throw new Error('canvas unconfigure');"),"Canvas resize/unconfigure failed"); + require(canvas_acquisitions==2&&canvas_retirements==2,"Canvas texture replacement lifetime failed"); + canvas_context.reset(); + require(run("let releasedCanvas=false;try{canvasContextProbe.getCurrentTexture()}catch(e){releasedCanvas=e instanceof TypeError}if(!releasedCanvas)throw new Error('released canvas receiver');delete globalThis.canvasContextProbe;"),"Released canvas wrapper remained callable"); +#if defined(__APPLE__) + test_v8_iosurface_canvas_host(isolate,context,adapter_fixture,run); + adapter_fixture.drain_commands(); +#endif + for(const char* name:{"consumedAdapterPromise","badAdapterReceiverPromise"}) { + auto rejected=context->Global()->Get(context,v8::String::NewFromUtf8(isolate,name).ToLocalChecked()).ToLocalChecked().As(); + require(rejected->State()==v8::Promise::kRejected,"Invalid adapter request did not reject"); + auto error_name=rejected->Result().As()->Get(context,v8::String::NewFromUtf8Literal(isolate,"name")).ToLocalChecked(); + const char* expected=std::string_view(name)=="consumedAdapterPromise"?"OperationError":"TypeError"; + require(error_name->StrictEquals(v8::String::NewFromUtf8(isolate,expected).ToLocalChecked()),"Adapter rejection type incorrect"); + } + // Dispose the registry before delivering successful native + // device completion. Use a fresh, unconsumed adapter. + auto cancel_adapter=std::make_shared(); + const auto cancel_adapter_operation=new_owner_token(); + auto cancel_adapter_ticket=fixture_mailbox->reserve(cancel_adapter_operation,{adapter_fixture.engine_identity(),new_owner_token(),0}).value(); + adapter_fixture.dawn().instance().RequestAdapter(&adapter_options,wgpu::CallbackMode::AllowSpontaneous, + [cancel_adapter,fixture_mailbox,cancel_adapter_ticket](wgpu::RequestAdapterStatus status,wgpu::Adapter adapter,wgpu::StringView) { + *cancel_adapter=std::move(adapter); + fixture_mailbox->publish(cancel_adapter_ticket,status==wgpu::RequestAdapterStatus::Success?completion_status::success:completion_status::failed); + }); + bool cancel_adapter_ready=false; + auto cancel_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(!cancel_adapter_ready && std::chrono::steady_clock::now()(isolate,context,*adapter_devices,exception_constructor,1); + auto cancel_object=cancel_registry->wrap(context,adapter_fixture,cancel_handle).ToLocalChecked(); + auto request_method=cancel_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"requestDevice")).ToLocalChecked().As(); + const auto lifetime_completions=fixture_mailbox->metrics().occupied; + auto cancel_promise=request_method->Call(context,cancel_object,0,nullptr).ToLocalChecked().As(); + require(cancel_promise->State()==v8::Promise::kPending,"Teardown fixture did not admit native request"); + cancel_promise->MarkAsHandled(); + cancel_registry.reset(); + require(cancel_promise->State()==v8::Promise::kRejected,"Registry teardown left pending device promise"); + auto cancelled_reason=cancel_promise->Result(); + size_t successful_retirements=0; + cancel_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(fixture_mailbox->metrics().occupied>lifetime_completions && std::chrono::steady_clock::now()metrics().occupied>lifetime_completions)std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + require(successful_retirements==1 && fixture_mailbox->metrics().occupied==lifetime_completions + && adapter_fixture.live_devices()==1 && adapter_fixture.live_adapters()==1, + "Successful teardown race leaked completion or adopted an orphan device"); + require(cancel_promise->State()==v8::Promise::kRejected && cancel_promise->Result()->StrictEquals(cancelled_reason), + "Late native success replaced cancellation rejection"); + adapter_registry.reset(); + { v8::TryCatch caught(isolate); + require(adapter_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"features")).IsEmpty() && caught.HasCaught(),"Retired adapter retained native access"); + } + require(adapter_features.As()->Get(context,v8::String::NewFromUtf8Literal(isolate,"size")).ToLocalChecked()->IsUint32(),"Retained adapter features lost after disposal"); + adapter_devices.reset(); + require(run("if(typeof retainedAdapterInfo.vendor!=='string')throw new Error('retained adapter info');delete globalThis.retainedAdapterInfo;"),"Adapter info did not survive teardown"); + require(run("delete globalThis.adapterWrapperProbe;delete globalThis.adapterDeviceProbe;delete globalThis.adapterDevicePromise;delete globalThis.consumedAdapterPromise;delete globalThis.badAdapterReceiverPromise;"),"Adapter probe cleanup failed"); + require(adapter_fixture.live_adapters()==1,"Adapter disposal bypassed deferred release"); + adapter_fixture.pump([](completion_record) {}); + require(adapter_fixture.live_adapters()==0 && adapter_fixture.live_devices()==0,"Adapter/device deferred release leaked native handles"); + auto discovery_devices=std::make_unique(isolate,context,exception_constructor,2,4); + auto discovery_adapters=std::make_unique(isolate,context,*discovery_devices,exception_constructor,2); + auto discovery=std::make_unique(isolate,context,adapter_fixture,*discovery_adapters); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"gpuDiscoveryProbe"),discovery->object()).FromMaybe(false),"Discovery object publication failed"); + auto language_set=discovery->object()->Get(context,v8::String::NewFromUtf8Literal(isolate,"wgslLanguageFeatures")).ToLocalChecked().As(); + auto language_has=language_set->Get(context,v8::String::NewFromUtf8Literal(isolate,"has")).ToLocalChecked().As(); + size_t language_count=0; + for(const auto& feature:wgsl_language_feature_names) { + v8::Local name=v8::String::NewFromUtf8(isolate,feature.name.data(),v8::NewStringType::kNormal,static_cast(feature.name.size())).ToLocalChecked(); + bool expected=adapter_fixture.dawn().instance().HasWGSLLanguageFeature(feature.native); + require(language_has->Call(context,language_set,1,&name).ToLocalChecked()->BooleanValue(isolate)==expected,"WGSL capability differs from native instance"); + language_count+=expected; + } + require(language_set->Get(context,v8::String::NewFromUtf8Literal(isolate,"size")).ToLocalChecked()->Uint32Value(context).FromJust()==language_count,"WGSL snapshot count incorrect"); + { v8::TryCatch caught(isolate);v8::Local name=v8::String::NewFromUtf8Literal(isolate,"x"); + require(feature_has->Call(context,language_set,1,&name).IsEmpty() && caught.HasCaught(),"WGSL set accepted GPU feature-set receiver brand"); + } + require(run("{let f=gpuDiscoveryProbe.wgslLanguageFeatures;if(f!==gpuDiscoveryProbe.wgslLanguageFeatures||Object.prototype.toString.call(f)!=='[object WGSLLanguageFeatures]'||[...f].length!==f.size||f.has('chromium_testing_shipped')||f.has('chromium_print')||f.has('f16')||f.add)throw new Error('WGSL snapshot');}"),"WGSL snapshot semantics failed"); + require(run("if(gpuDiscoveryProbe.getPreferredCanvasFormat()!=='bgra8unorm'||gpuDiscoveryProbe.getPreferredCanvasFormat.length!==0)throw new Error('preferred format');let badFormatReceiver=false;try{gpuDiscoveryProbe.getPreferredCanvasFormat.call({})}catch(e){badFormatReceiver=e instanceof TypeError}if(!badFormatReceiver)throw new Error('format receiver');"),"Preferred canvas format behavior failed"); + bool invalid_format=false; + try { v8_webgpu_discovery invalid(isolate,context,adapter_fixture,*discovery_adapters,wgpu::BackendType::Undefined,wgpu::TextureFormat::RGBA16Float); } + catch(const std::invalid_argument&) { invalid_format=true; } + require(invalid_format,"Unsupported preferred format admitted"); + { v8_webgpu_discovery rgba(isolate,context,adapter_fixture,*discovery_adapters,wgpu::BackendType::Undefined,wgpu::TextureFormat::RGBA8Unorm); + auto object=rgba.object(); + auto method=object->Get(context,v8::String::NewFromUtf8Literal(isolate,"getPreferredCanvasFormat")).ToLocalChecked().As(); + require(method->Call(context,object,0,nullptr).ToLocalChecked()->StrictEquals(v8::String::NewFromUtf8Literal(isolate,"rgba8unorm")),"Host RGBA format selection ignored"); + } + require(run("globalThis.discoveryPromise=gpuDiscoveryProbe.requestAdapter();"),"JavaScript adapter discovery failed"); + auto discovery_promise=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"discoveryPromise")).ToLocalChecked().As(); + auto discovery_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(discovery_promise->State()==v8::Promise::kPending && std::chrono::steady_clock::now()complete(completion),"Discovery completion routing failed"); }); + if(discovery_promise->State()==v8::Promise::kPending)std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + require(discovery_promise->State()==v8::Promise::kFulfilled && discovery_promise->Result()->IsObject(),"Discovery did not return a hardware adapter wrapper"); + auto discovered_object=discovery_promise->Result().As(); + auto device_method=discovered_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"requestDevice")).ToLocalChecked().As(); + auto chain_device=device_method->Call(context,discovered_object,0,nullptr).ToLocalChecked().As(); + discovery_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(chain_device->State()==v8::Promise::kPending && std::chrono::steady_clock::now()complete(completion),"Discovered device completion routing failed"); }); + if(chain_device->State()==v8::Promise::kPending)std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + require(chain_device->State()==v8::Promise::kFulfilled,"Discovered adapter could not create device"); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"discoveryDevice"),chain_device->Result()).FromMaybe(false),"Discovered device publication failed"); + require(run("{let buffer=discoveryDevice.createBuffer({size:16,usage:8,mappedAtCreation:true});let view=buffer.getMappedRange();new Uint32Array(view)[0]=99;buffer.unmap();if(view.byteLength!==0)throw new Error('discovery mapping');buffer.destroy();}"),"Discovered device could not execute buffer operations"); + auto unavailable=run("globalThis.unavailableAdapter=gpuDiscoveryProbe.requestAdapter({featureLevel:'not-supported'});globalThis.invalidDiscovery=gpuDiscoveryProbe.requestAdapter({powerPreference:'invalid'});invalidDiscovery.catch(()=>{});"); + require(unavailable,"Discovery failure paths threw synchronously"); + auto unavailable_promise=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"unavailableAdapter")).ToLocalChecked().As(); + require(unavailable_promise->State()==v8::Promise::kFulfilled && unavailable_promise->Result()->IsNull(),"Unavailable adapter did not resolve null"); + auto invalid_discovery=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"invalidDiscovery")).ToLocalChecked().As(); + require(invalid_discovery->State()==v8::Promise::kRejected,"Invalid discovery options did not reject"); + discovery.reset(); discovery_adapters.reset(); discovery_devices.reset(); + require(run("delete globalThis.gpuDiscoveryProbe;delete globalThis.discoveryPromise;delete globalThis.discoveryDevice;delete globalThis.unavailableAdapter;delete globalThis.invalidDiscovery;"),"Discovery cleanup failed"); + adapter_fixture.pump([](completion_record) {}); + require(adapter_fixture.live_adapters()==0 && adapter_fixture.live_devices()==0,"Discovery chain leaked native ownership"); + buffer_wrappers_tested=true; + return; + } + if (record.operation==test_operations[2] || record.operation==test_operations[3]) { + auto* isolate=v8::Isolate::GetCurrent(); + auto context=isolate->GetCurrentContext(); + auto& request=failed_wrappers[std::find(test_operations.begin()+2,test_operations.begin()+4,record.operation)-(test_operations.begin()+2)]; + require(record.status==completion_status::success,"Wrapper failure test needs a hardware adapter"); + require(request->complete(isolate,context,record,[&](wgpu::Adapter) -> v8::MaybeLocal { + if (record.operation==test_operations[2]) throw std::length_error("resource table full"); + auto sentinel=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"wrapperSentinel")).ToLocalChecked(); + isolate->ThrowException(sentinel); + return {}; + }),"Wrapper failure did not reject its promise"); + require(!request->pending(),"Failed wrapper left request pending"); + require(!request->complete(isolate,context,record,[](wgpu::Adapter) -> v8::Local { + throw std::runtime_error("duplicate completion wrapped twice"); + }),"Duplicate completion was accepted"); + ++failed_wrapper_count; + return; + } + if (record.operation==test_operations[1]) { + auto* isolate=v8::Isolate::GetCurrent(); + require(record.status==completion_status::cancelled,"Adapter cancellation lost"); + require(cancelled_adapter->complete(isolate,isolate->GetCurrentContext(),record, + [](wgpu::Adapter) -> v8::Local { throw std::runtime_error("Cancelled adapter was wrapped"); }), + "Cancelled adapter promise did not resolve"); + adapter_cancelled=true; + return; + } + if (record.operation==test_operations[0]) { + auto* isolate=v8::Isolate::GetCurrent(); + auto context=isolate->GetCurrentContext(); + require(adapter_request->complete(isolate,context,record,[&](wgpu::Adapter adapter) -> v8::Local { + auto mailbox=adapter_service->dawn().completions(); + webgpu_device_descriptor requested; + auto js_descriptor=v8::Script::Compile(context,v8::String::NewFromUtf8Literal(isolate,"({label:'requested device',defaultQueue:{label:'requested queue'},requiredLimits:{maxBufferSize:8192}})")).ToLocalChecked()->Run(context).ToLocalChecked(); + require(read_webgpu_device_descriptor(isolate,context,js_descriptor,requested),"Device descriptor conversion failed"); + webgpu_device_request_error preparation_error; + auto prepared=webgpu_prepared_device_descriptor::prepare(requested,adapter,false,preparation_error); + require(prepared && preparation_error==webgpu_device_request_error::none,"Actual adapter device preparation failed"); + auto descriptor=prepared->native(); + requested.label="mutated source"; requested.queue_label="mutated queue"; + require(std::string_view(descriptor.label.data,descriptor.label.length)=="requested device" + && std::string_view(descriptor.defaultQueue.label.data,descriptor.defaultQueue.label.length)=="requested queue", + "Prepared device labels borrow mutable source storage"); + require(!webgpu_prepared_device_descriptor::prepare(requested,adapter,true,preparation_error) + && preparation_error==webgpu_device_request_error::operation_error,"Consumed adapter accepted"); + auto invalid_request=requested; invalid_request.required_features.push_back(wgpu::FeatureName::DawnInternalUsages); + require(!webgpu_prepared_device_descriptor::prepare(invalid_request,adapter,true,preparation_error) + && preparation_error==webgpu_device_request_error::unsupported_feature,"Private feature or feature-error precedence incorrect"); + invalid_request=requested; invalid_request.required_limits.emplace_back(u"unknownLimit",1); + require(!webgpu_prepared_device_descriptor::prepare(invalid_request,adapter,false,preparation_error) + && preparation_error==webgpu_device_request_error::operation_error,"Unknown required limit accepted"); + for (const auto& feature:webgpu_feature_names) { + auto feature_request=requested; feature_request.required_features={feature.native,feature.native}; + auto feature_prepared=webgpu_prepared_device_descriptor::prepare(feature_request,adapter,false,preparation_error); + if (adapter.HasFeature(feature.native)) { + require(feature_prepared && feature_prepared->native().requiredFeatureCount==1 + && feature_prepared->native().requiredFeatures[0]==feature.native,"Supported feature set preparation failed"); + } else require(!feature_prepared && preparation_error==webgpu_device_request_error::unsupported_feature,"Unsupported adapter feature accepted"); + } + for (const auto& [source,expected]:std::vector>{ + {"({requiredFeatures:['unknown-feature']})","TypeError"}, + {"({requiredLimits:{unknown:1}})","OperationError"}, + {"({get label(){throw globalThis.requestSentinel=new Error('sentinel')}})","Error"}}) { + auto input=v8::Script::Compile(context,v8::String::NewFromUtf8(isolate,source).ToLocalChecked()).ToLocalChecked()->Run(context).ToLocalChecked(); + const auto occupied=mailbox->metrics().occupied; + v8::Local rejected; + auto invalid=v8_webgpu_device_request::start_checked(isolate,context,input,[&] { return std::pair{adapter,false}; },mailbox, + {adapter_service->engine_identity(),new_owner_token(),0},new_owner_token(), + context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As(),rejected); + require(invalid && !invalid->pending() && rejected->State()==v8::Promise::kRejected + && mailbox->metrics().occupied==occupied,"Invalid descriptor reached native admission or failed to reject"); + rejected->MarkAsHandled(); + auto error_name=rejected->Result().As()->Get(context,v8::String::NewFromUtf8Literal(isolate,"name")).ToLocalChecked(); + require(error_name->StrictEquals(v8::String::NewFromUtf8(isolate,expected).ToLocalChecked()),"Checked device rejection type incorrect"); + if (std::string_view(expected)=="Error") require(rejected->Result()->StrictEquals(context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"requestSentinel")).ToLocalChecked()),"Descriptor exception identity lost"); + } + auto changing_input=v8::Script::Compile(context,v8::String::NewFromUtf8Literal(isolate,"({get label(){globalThis.adapterConsumedDuringConversion=true;return ''}})")).ToLocalChecked()->Run(context).ToLocalChecked(); + v8::Local consumed_promise; + bool consumed_observed=false; + auto consumed_request=v8_webgpu_device_request::start_checked(isolate,context,changing_input,[&] { + bool consumed=context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"adapterConsumedDuringConversion")).ToLocalChecked()->IsTrue(); + consumed_observed=consumed;return std::pair{adapter,consumed}; + },mailbox,{adapter_service->engine_identity(),new_owner_token(),0},new_owner_token(), + context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As(),consumed_promise); + require(consumed_observed && consumed_request && !consumed_request->pending() && consumed_promise->State()==v8::Promise::kRejected,"Consumed adapter did not reject"); + consumed_promise->MarkAsHandled(); + v8::Local promise; + device_request=v8_webgpu_device_request::start_checked(isolate,context,js_descriptor,[&] { return std::pair{adapter,false}; },mailbox, + {adapter_service->engine_identity(),new_owner_token(),0},test_operations[4], + context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As(),promise); + require(device_request && device_request->pending(),"Device promise did not start"); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"deviceRequestPromise"),promise).FromMaybe(false),"Device promise publication failed"); + wgpu::Limits impossible_limits{}; impossible_limits.maxBufferSize=uint64_t{1}<<63; + wgpu::DeviceDescriptor impossible_descriptor{}; impossible_descriptor.requiredLimits=&impossible_limits; + v8::Local failure_promise; + failed_device_request=v8_webgpu_device_request::start(isolate,context,impossible_descriptor,adapter,mailbox, + {adapter_service->engine_identity(),new_owner_token(),0},test_operations[11], + context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As(),failure_promise); + require(failed_device_request && failed_device_request->pending(),"Failure device promise did not start"); + failure_promise->MarkAsHandled(); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"failedDevicePromise"),failure_promise).FromMaybe(false),"Failure promise publication failed"); + v8::Local cancelled_promise; + cancelled_device_request=v8_webgpu_device_request::start(isolate,context,impossible_descriptor,adapter,mailbox, + {adapter_service->engine_identity(),new_owner_token(),0},test_operations[13], + context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As(),cancelled_promise); + require(cancelled_device_request && cancelled_device_request->pending(),"Cancellable device request did not start"); + bool wrong_cancel_realm=false; + try { cancelled_device_request->cancel(v8::Context::New(isolate)); } catch (const std::logic_error&) { wrong_cancel_realm=true; } + require(wrong_cancel_realm && cancelled_device_request->pending(),"Foreign cancellation consumed request"); + require(cancelled_device_request->cancel(context) && !cancelled_device_request->cancel(context),"Device cancellation was not idempotent"); + require(cancelled_promise->State()==v8::Promise::kRejected,"Cancellation left an unresolved device promise"); + cancelled_promise->MarkAsHandled(); + discovered_adapter=adapter_service->adopt_adapter(std::move(adapter)); + // Diagnostic wrapper only; the standards GPUAdapter registry is separate work. + return v8::Object::New(isolate); + }), "Adapter promise completion failed"); + require(!adapter_request->pending(), "Adapter promise remained pending"); + adapter_delivered=true; + return; + } + require(((record.operation==1 || record.operation==2) && record.status==completion_status::success) + || (record.operation==3 && record.status==completion_status::cancelled),"unexpected completion"); + if (record.operation==3) { + bool rejected=false; + try { runtime.initialize_graphics(wake,[](auto) {}); } + catch (const std::logic_error&) { rejected=true; } + require(rejected,"navigation allowed reentrant graphics initialization"); + rejected=false; + try { runtime.load_url("https://graphics.test/next"); } + catch (const std::logic_error&) { rejected=true; } + require(rejected,"cancellation delivery allowed reentrant navigation"); + } + require(std::this_thread::get_id()==owner_thread,"completion left runtime thread"); + auto* isolate=v8::Isolate::GetCurrent(); + require(isolate!=nullptr && isolate->InContext(),"completion has no V8 context"); + auto context=isolate->GetCurrentContext(); + if (record.operation==1) { + test_v8_webgpu_adapter_options(isolate,context); + test_v8_webgpu_buffer_descriptor(isolate,context); + test_v8_webgpu_device_descriptor(isolate,context); + test_v8_webgpu_shader_descriptor(isolate,context); + test_v8_webgpu_render_state(isolate,context); + test_v8_webgpu_texture_descriptor(isolate,context); + { + auto evaluate=[&](const char* source){return v8::Script::Compile(context,v8::String::NewFromUtf8(isolate,source).ToLocalChecked()).ToLocalChecked()->Run(context).ToLocalChecked();}; + webgpu_bind_group_layout_descriptor descriptor; + require(read_webgpu_bind_group_layout_descriptor(isolate,context,evaluate("({entries:[{binding:0,visibility:1,buffer:{}},{binding:1,visibility:2,sampler:{}},{binding:2,visibility:2,texture:{}},{binding:3,visibility:4,storageTexture:{format:'rgba8unorm'}},{binding:4,visibility:2,externalTexture:{}}]})"),descriptor),"Binding variant conversion failed"); + descriptor.with_native([&](const auto& native){ + require(native.entryCount==5&&native.entries[0].buffer.type==wgpu::BufferBindingType::Uniform + &&native.entries[1].sampler.type==wgpu::SamplerBindingType::Filtering + &&native.entries[2].texture.sampleType==wgpu::TextureSampleType::Float + &&native.entries[3].storageTexture.access==wgpu::StorageTextureAccess::WriteOnly + &&native.entries[4].nextInChain&&native.entries[4].nextInChain->sType==wgpu::SType::ExternalTextureBindingLayout, + "Binding defaults or external chain incorrect"); + }); + require(read_webgpu_bind_group_layout_descriptor(isolate,context,evaluate("(()=>{globalThis.bindingOrder=[];return {entries:[new Proxy({binding:0,visibility:1,buffer:new Proxy({},{get(o,k){bindingOrder.push('buffer.'+k);return o[k]}})},{get(o,k){bindingOrder.push(k);return o[k]}})]}})()"),descriptor) + &&evaluate("bindingOrder.join(',')==='binding,buffer,buffer.hasDynamicOffset,buffer.minBindingSize,buffer.type,externalTexture,sampler,storageTexture,texture,visibility'")->IsTrue(),"Binding dictionary conversion order incorrect"); + } + + test_v8_webgpu_render_pass_descriptor(isolate,context); + v8::Local promise; + webgpu_adapter_options options; + adapter_request=v8_webgpu_adapter_request::start(isolate,context,options, + adapter_service->dawn().instance(),adapter_service->dawn().completions(), + {adapter_service->engine_identity(),new_owner_token(),0},test_operations[0],wgpu::BackendType::Undefined,promise); + require(adapter_request && adapter_request->pending(), "Adapter request did not become pending"); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"adapterProbePromise"),promise).FromMaybe(false), + "Adapter promise publication failed"); + const resource_owner cancelled_owner{adapter_service->engine_identity(),new_owner_token(),0}; + v8::Local cancelled_promise; + cancelled_adapter=v8_webgpu_adapter_request::start(isolate,context,options, + adapter_service->dawn().instance(),adapter_service->dawn().completions(), + cancelled_owner,test_operations[1],wgpu::BackendType::Undefined,cancelled_promise); + require(cancelled_adapter && cancelled_adapter->pending(),"Cancelled request was not admitted"); + adapter_service->dawn().completions()->cancel_owner(cancelled_owner); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"cancelledAdapterPromise"),cancelled_promise).FromMaybe(false), + "Cancelled adapter promise publication failed"); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"wrapperSentinel"),v8::Object::New(isolate)).FromMaybe(false),"Sentinel publication failed"); + for (size_t i=0;i failed_promise; + failed_wrappers[i]=v8_webgpu_adapter_request::start(isolate,context,options, + adapter_service->dawn().instance(),adapter_service->dawn().completions(), + {adapter_service->engine_identity(),new_owner_token(),0},test_operations[2+i],wgpu::BackendType::Undefined,failed_promise); + require(failed_wrappers[i] && failed_wrappers[i]->pending(),"Failure test request was not admitted"); + require(context->Global()->Set(context,v8::String::NewFromUtf8(isolate,i==0 ? "nativeWrapperFailure" : "jsWrapperFailure").ToLocalChecked(),failed_promise).FromMaybe(false), + "Failure test promise publication failed"); + } + // Install rejection observers before returning to the event pump; + // spontaneous discovery may finish in this same delivery batch. + auto observers=v8::String::NewFromUtf8Literal(isolate,"globalThis.wrapperFailures=0; nativeWrapperFailure.catch(e=>{if(!(e instanceof Error))throw e;wrapperFailures++}); jsWrapperFailure.catch(e=>{if(e!==wrapperSentinel)throw e;wrapperFailures++}); globalThis.adapterPromiseDone=false; globalThis.cancelledAdapterDone=false; adapterProbePromise.then(a=>{if(!a)throw new Error('adapter absent');adapterPromiseDone=true}); cancelledAdapterPromise.then(a=>{if(a!==null)throw new Error('cancelled adapter present');cancelledAdapterDone=true});"); + v8::Local observer_script; + require(v8::Script::Compile(context,observers).ToLocal(&observer_script) + && !observer_script->Run(context).IsEmpty(),"Adapter observer failed"); + bool rejected=false; + try { runtime.load_url("https://graphics.test/next"); } + catch (const std::logic_error&) { rejected=true; } + require(rejected,"completion delivery allowed destructive navigation"); + rejected=false; + try { runtime.shutdown_graphics(); } + catch (const std::logic_error&) { rejected=true; } + require(rejected,"completion delivery allowed destructive shutdown"); + wrappers=std::make_unique(isolate,releases,1); + graphics_command release{[](graphics_service&,std::span,const graphics_command::arguments&) noexcept { ++weak_releases; }}; + auto weak_probe=v8::Object::New(isolate); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"weakReleaseProbe"),weak_probe).FromMaybe(false),"Weak probe root failed"); + require(wrappers->attach(weak_probe,release),"weak wrapper registration failed"); + require(!wrappers->attach(v8::Object::New(isolate),release),"weak wrapper capacity was not bounded"); + } + if (record.operation==2) { + adapter_service->with_device(gc_buffer_device,[&](auto& device) { + require(device.live_buffers()==0,"Collected buffer native handle survived engine drain"); + wgpu::BufferDescriptor descriptor{}; + descriptor.size=16; descriptor.usage=wgpu::BufferUsage::CopyDst; descriptor.mappedAtCreation=true; + auto replacement=device.create_buffer(descriptor); + auto replacement_object=gc_buffers->wrap(context,*adapter_service,gc_buffer_device,replacement).ToLocalChecked(); + auto method=replacement_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"getMappedRange")).ToLocalChecked().template As(); + auto view=method->Call(context,replacement_object,0,nullptr).ToLocalChecked().template As(); + require(view->ByteLength()==16,"Replacement buffer mapping failed"); + auto stale_device=gc_buffer_device; ++stale_device.generation; + require(gc_buffers->detach_device(*adapter_service,stale_device)==0 && view->ByteLength()==16,"Stale device detached a live mapping"); + require(gc_buffers->detach_device(*adapter_service,gc_buffer_device)==1 && view->ByteLength()==0,"Device-wide detachment failed"); + require(gc_buffers->detach_device(*adapter_service,gc_buffer_device)==0,"Device detachment was not idempotent"); + + }); + device_registry=std::make_unique(isolate,context,context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"DOMException")).ToLocalChecked().As(),1,2); + auto device_object=device_registry->wrap(context,*adapter_service,gc_buffer_device).ToLocalChecked(); + adapter_service->with_device(gc_buffer_device,[&](auto& owned) { verify_v8_limits(isolate,context,device_object,owned.native()); }); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"deviceProbe"),device_object).FromMaybe(false),"Device wrapper publication failed"); + auto feature_object=device_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"features")).ToLocalChecked().As(); + auto feature_has=feature_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"has")).ToLocalChecked().As(); + adapter_service->with_device(gc_buffer_device,[&](auto& owned) { + size_t enabled=0; + for (const auto& feature:webgpu_feature_names) { + v8::Local argument=v8::String::NewFromUtf8(isolate,feature.name.data(),v8::NewStringType::kNormal,static_cast(feature.name.size())).ToLocalChecked(); + const bool expected=owned.native().HasFeature(feature.native); + require(feature_has->Call(context,feature_object,1,&argument).ToLocalChecked()->BooleanValue(isolate)==expected,"Device feature differs from native enabled set"); + enabled+=expected; + } + require(feature_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"size")).ToLocalChecked()->Uint32Value(context).FromJust()==enabled,"Device feature count differs from native enabled set"); + }); + resource_handle shader_handle; + adapter_service->with_device(gc_buffer_device,[&](auto& owned) { + wgpu::ShaderSourceWGSL source{};source.code="@vertex fn vs()->@builtin(position) vec4f {return vec4f(0,0,0,1);}@fragment fn fs()->@location(0) vec4f {return vec4f(1,0,0,1);}"; + wgpu::ShaderModuleDescriptor descriptor{};descriptor.nextInChain=&source; + shader_handle=owned.create_shader_module(descriptor); + }); + auto shaders=std::make_unique(isolate,context,1); + auto shader_object=shaders->wrap(context,*adapter_service,gc_buffer_device,shader_handle,device_object,"initial shader").ToLocalChecked(); + bool duplicate_shader=false,foreign_shader_realm=false; + try{shaders->wrap(context,*adapter_service,gc_buffer_device,shader_handle,device_object);}catch(const std::invalid_argument&){duplicate_shader=true;} + try{shaders->wrap(v8::Context::New(isolate),*adapter_service,gc_buffer_device,shader_handle,device_object);}catch(const std::logic_error&){foreign_shader_realm=true;} + require(duplicate_shader && foreign_shader_realm,"Shader wrapper ownership duplicated"); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"shaderProbe"),shader_object).FromMaybe(false),"Shader wrapper publication failed"); + auto shader_script=v8::String::NewFromUtf8Literal(isolate,R"JS( + {if(Object.prototype.toString.call(shaderProbe)!=='[object GPUShaderModule]')throw new Error('shader tag');if(shaderProbe.label!=='initial shader')throw new Error('shader initial label'); + shaderProbe.label='shader\0\ud800';if(shaderProbe.label!=='shader\0\ufffd')throw new Error('shader USV label'); + let error={};try{shaderProbe.label={toString(){throw error}}}catch(e){if(e!==error)throw e} + if(shaderProbe.label!=='shader\0\ufffd')throw new Error('shader failed label committed'); + let symbolError=false;try{shaderProbe.label=Symbol()}catch(e){symbolError=e instanceof TypeError}if(!symbolError)throw new Error('shader symbol label'); + let brand=false;try{Object.getOwnPropertyDescriptor(Object.getPrototypeOf(shaderProbe),'label').get.call({})}catch(e){brand=e instanceof TypeError}if(!brand)throw new Error('shader receiver');} + )JS"); + require(!v8::Script::Compile(context,shader_script).ToLocalChecked()->Run(context).IsEmpty(),"Shader wrapper label behavior failed"); + test_v8_webgpu_programmable_stage(isolate,context); + { + resource_handle texture;resource_handle view; + adapter_service->with_device(gc_buffer_device,[&](auto& owned) { + wgpu::TextureDescriptor descriptor{};descriptor.size={4,4,1};descriptor.format=wgpu::TextureFormat::RGBA8Unorm;descriptor.usage=wgpu::TextureUsage::RenderAttachment; + texture=owned.create_texture(descriptor);view=owned.create_texture_view(texture,{}); + }); + auto views=std::make_unique(isolate,context,1); + auto object=views->wrap(context,*adapter_service,gc_buffer_device,view,device_object,"view").ToLocalChecked(); + auto retained=v8_webgpu_texture_views::native_reference(object); + bool wrong=false;try{v8_webgpu_shaders::native_reference(object);}catch(const std::invalid_argument&){wrong=true;} + require(wrong&&retained,"Texture view interface conversion failed"); + views.reset();bool retired=false; + try{v8_webgpu_texture_views::native_reference(object);}catch(const std::invalid_argument&){retired=true;} + adapter_service->with_device(gc_buffer_device,[&](auto& owned) {require(retired&&owned.live_texture_views()==1,"Texture view released inline");}); + adapter_service->drain_commands(); + adapter_service->with_device(gc_buffer_device,[&](auto& owned) {require(owned.live_texture_views()==0,"Texture view deferred release failed");owned.release_texture(texture);}); + } + auto retained_shader=v8_webgpu_shaders::native_reference(shader_object); + resource_handle pipeline_handle; + adapter_service->with_device(gc_buffer_device,[&](auto& owned) { + owned.with_shader_module(shader_handle,[&](const auto& native) {require(native.Get()==retained_shader.Get(),"Shader conversion changed native identity");}); + wgpu::ColorTargetState target{};target.format=wgpu::TextureFormat::RGBA8Unorm; + wgpu::FragmentState fragment{};fragment.module=retained_shader;fragment.entryPoint="fs";fragment.targetCount=1;fragment.targets=⌖ + wgpu::RenderPipelineDescriptor descriptor{};descriptor.vertex.module=retained_shader;descriptor.vertex.entryPoint="vs";descriptor.fragment=&fragment; + pipeline_handle=owned.create_render_pipeline(descriptor); + }); + auto pipelines=std::make_unique(isolate,context,1); + auto pipeline_object=pipelines->wrap(context,*adapter_service,gc_buffer_device,pipeline_handle,device_object,"render pipeline").ToLocalChecked(); + auto retained_pipeline=v8_webgpu_render_pipelines::native_reference(pipeline_object); + adapter_service->with_device(gc_buffer_device,[&](auto& owned) { + owned.with_render_pipeline(pipeline_handle,[&](const auto& native) {require(native.Get()==retained_pipeline.Get(),"Pipeline conversion changed native identity");}); + }); + bool wrong_shader=false,wrong_pipeline=false,forged_pipeline=false; + try{v8_webgpu_shaders::native_reference(pipeline_object);}catch(const std::invalid_argument&){wrong_shader=true;} + try{v8_webgpu_render_pipelines::native_reference(shader_object);}catch(const std::invalid_argument&){wrong_pipeline=true;} + try{v8_webgpu_render_pipelines::native_reference(v8::Object::New(isolate));}catch(const std::invalid_argument&){forged_pipeline=true;} + require(wrong_shader && wrong_pipeline && forged_pipeline,"GPU resource native conversion accepted wrong brand"); + require(context->Global()->Set(context,v8::String::NewFromUtf8Literal(isolate,"pipelineProbe"),pipeline_object).FromMaybe(false),"Pipeline publication failed"); + auto pipeline_script=v8::String::NewFromUtf8Literal(isolate,R"JS( + {if(Object.prototype.toString.call(pipelineProbe)!=='[object GPURenderPipeline]' || pipelineProbe.label!=='render pipeline')throw new Error('pipeline metadata'); + pipelineProbe.label='pipeline\0\ud800';if(pipelineProbe.label!=='pipeline\0\ufffd')throw new Error('pipeline label'); + let wrong=false;try{Object.getOwnPropertyDescriptor(Object.getPrototypeOf(pipelineProbe),'label').get.call(shaderProbe)}catch(e){wrong=e instanceof TypeError}if(!wrong)throw new Error('cross resource getter');} + )JS"); + require(!v8::Script::Compile(context,pipeline_script).ToLocalChecked()->Run(context).IsEmpty(),"Pipeline wrapper behavior failed"); + pipelines.reset(); + bool retired_pipeline=false; + try{v8_webgpu_render_pipelines::native_reference(pipeline_object);}catch(const std::invalid_argument&){retired_pipeline=true;} + require(retired_pipeline && retained_pipeline,"Disposed pipeline conversion remained available"); + adapter_service->with_device(gc_buffer_device,[&](auto& owned) {require(owned.live_render_pipelines()==1,"Pipeline disposal released native resources inline");}); + require(context->Global()->Delete(context,v8::String::NewFromUtf8Literal(isolate,"pipelineProbe")).FromMaybe(false),"Pipeline cleanup failed"); + shaders.reset(); + {v8::TryCatch caught(isolate);require(shader_object->Get(context,v8::String::NewFromUtf8Literal(isolate,"label")).IsEmpty()&&caught.HasCaught(),"Disposed shader retained native access");} + adapter_service->with_device(gc_buffer_device,[&](auto& owned) {require(owned.live_shader_modules()==1,"Shader registry released native module inline");}); + require(context->Global()->Delete(context,v8::String::NewFromUtf8Literal(isolate,"shaderProbe")).FromMaybe(false),"Shader probe cleanup failed"); + { + auto push=v8::String::NewFromUtf8Literal(isolate,"deviceProbe.pushErrorScope('validation');"); + require(!v8::Script::Compile(context,push).ToLocalChecked()->Run(context).IsEmpty(),"Native scope push script failed"); + auto native=v8_webgpu_devices::native_reference(device_object); + native.InjectError(wgpu::ErrorType::Validation,"scope capture sentinel"); + auto captured=std::make_shared>(0); + native.PopErrorScope(wgpu::CallbackMode::AllowSpontaneous, + [captured](wgpu::PopErrorScopeStatus status,wgpu::ErrorType type,wgpu::StringView message){ + const bool correct=status==wgpu::PopErrorScopeStatus::Success&&type==wgpu::ErrorType::Validation + && std::string_view(message.data,message.length).find("scope capture sentinel")!=std::string_view::npos; + captured->store(correct?1:-1); + }); + auto until=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(captured->load()==0&&std::chrono::steady_clock::now()pump([](auto){}); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + require(captured->load()==1,"JavaScript scope did not capture Dawn validation error"); + native.PushErrorScope(wgpu::ErrorFilter::Validation); + wgpu::ShaderSourceWGSL invalid_source{};invalid_source.code="/* 😀 */ this is invalid WGSL"; + wgpu::ShaderModuleDescriptor invalid_descriptor{};invalid_descriptor.nextInChain=&invalid_source; + auto invalid_shader=native.CreateShaderModule(&invalid_descriptor); + native.PopErrorScope(wgpu::CallbackMode::AllowSpontaneous, + [](wgpu::PopErrorScopeStatus,wgpu::ErrorType,wgpu::StringView){}); + auto diagnostics=std::make_shared>(0); + invalid_shader.GetCompilationInfo(wgpu::CallbackMode::AllowSpontaneous, + [diagnostics](wgpu::CompilationInfoRequestStatus status,const wgpu::CompilationInfo* info){ + try{ + if(status!=wgpu::CompilationInfoRequestStatus::Success||!info){diagnostics->store(-1);return;} + auto snapshot=webgpu_compilation_info::copy(*info); + bool error=false; + for(const auto& message:snapshot.messages) + error|=message.type==wgpu::CompilationMessageType::Error&&!message.message.empty()&&message.has_utf16&&message.offset>message.utf16_offset; + diagnostics->store(error?1:-1); + }catch(...){diagnostics->store(-1);} + }); + until=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while(diagnostics->load()==0&&std::chrono::steady_clock::now()pump([](auto){}); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + require(diagnostics->load()==1,"Invalid WGSL did not produce owned Dawn diagnostics"); + + } + { + resource_handle binding_handle; + resource_handle layout_handle; + adapter_service->with_device(gc_buffer_device,[&](auto& owned){ + wgpu::BindGroupLayoutEntry entry{};entry.binding=0; + entry.visibility=wgpu::ShaderStage::Vertex;entry.buffer.type=wgpu::BufferBindingType::Uniform; + wgpu::BindGroupLayoutDescriptor descriptor{};descriptor.entryCount=1;descriptor.entries=&entry; + binding_handle=owned.create_bind_group_layout(descriptor); + owned.with_bind_group_layout(binding_handle,[&](const auto& binding){ + bool guarded=false;try{owned.release_bind_group_layout(binding_handle);}catch(const std::logic_error&){guarded=true;} + require(guarded,"Binding layout released inside native access scope"); + wgpu::PipelineLayoutDescriptor pipeline{};pipeline.bindGroupLayoutCount=1;pipeline.bindGroupLayouts=&binding; + layout_handle=owned.create_pipeline_layout(pipeline); + }); + owned.release_bind_group_layout(binding_handle); + require(owned.live_bind_group_layouts()==0&&owned.live_pipeline_layouts()==1,"Layout handle ownership mismatch"); + owned.with_pipeline_layout(layout_handle,[&](const auto& layout){ + require(static_cast(layout),"Pipeline layout lost native dependency"); + bool guarded=false;try{owned.release_pipeline_layout(layout_handle);}catch(const std::logic_error&){guarded=true;} + require(guarded,"Pipeline layout released inside native access scope"); + guarded=false;try{owned.close();}catch(const std::logic_error&){guarded=true;} + require(guarded,"Device closed inside pipeline layout access scope"); + }); + }); + auto layouts=std::make_unique(isolate,context,1); + auto wrapper=layouts->wrap(context,*adapter_service,gc_buffer_device,layout_handle,device_object).ToLocalChecked(); + auto native=v8_webgpu_pipeline_layouts::native_reference(wrapper); + bool wrong=false;try{v8_webgpu_bind_group_layouts::native_reference(wrapper);}catch(const std::invalid_argument&){wrong=true;} + require(wrong,"Layout wrappers accepted the wrong interface brand"); + layouts.reset(); + adapter_service->with_device(gc_buffer_device,[&](auto& owned){require(owned.live_pipeline_layouts()==1,"Layout wrapper released native resource inline");}); + adapter_service->drain_commands(); + adapter_service->with_device(gc_buffer_device,[&](auto& owned){require(owned.live_pipeline_layouts()==0,"Deferred pipeline layout release failed");}); + require(static_cast(native),"Retained native layout reference lost"); + } + { + resource_handle group_handle; + adapter_service->with_device(gc_buffer_device,[&](auto& owned){ + wgpu::BufferDescriptor buffer_descriptor{}; + buffer_descriptor.size=64;buffer_descriptor.usage=wgpu::BufferUsage::Uniform; + auto buffer_handle=owned.create_buffer(buffer_descriptor); + wgpu::BindGroupLayoutEntry layout_entry{};layout_entry.binding=0; + layout_entry.visibility=wgpu::ShaderStage::Vertex; + layout_entry.buffer.type=wgpu::BufferBindingType::Uniform; + wgpu::BindGroupLayoutDescriptor layout_descriptor{}; + layout_descriptor.entryCount=1;layout_descriptor.entries=&layout_entry; + auto layout_handle=owned.create_bind_group_layout(layout_descriptor); + owned.with_bind_group_layout(layout_handle,[&](const auto& layout){ + owned.with_buffer(buffer_handle,[&](const auto& buffer){ + wgpu::BindGroupEntry entry{};entry.binding=0;entry.buffer=buffer;entry.size=64; + wgpu::BindGroupDescriptor descriptor{};descriptor.layout=layout; + descriptor.entryCount=1;descriptor.entries=&entry; + group_handle=owned.create_bind_group(descriptor); + }); + }); + owned.release_buffer(buffer_handle); + owned.release_bind_group_layout(layout_handle); + owned.with_bind_group(group_handle,[&](const auto& group){ + require(static_cast(group),"Bind group lost native dependencies"); + bool guarded=false;try{owned.release_bind_group(group_handle);}catch(const std::logic_error&){guarded=true;} + require(guarded,"Bind group released during native access"); + guarded=false;try{owned.close();}catch(const std::logic_error&){guarded=true;} + require(guarded,"Device closed during bind group access"); + }); + }); + auto groups=std::make_unique(isolate,context,1); + auto wrapper=groups->wrap(context,*adapter_service,gc_buffer_device,group_handle,device_object).ToLocalChecked(); + auto native=v8_webgpu_bind_groups::native_reference(wrapper); + bool wrong=false;try{v8_webgpu_bind_group_layouts::native_reference(wrapper);}catch(const std::invalid_argument&){wrong=true;} + require(wrong,"Bind group accepted as a bind group layout"); + groups.reset(); + adapter_service->with_device(gc_buffer_device,[&](auto& owned){require(owned.live_bind_groups()==1,"Bind group released inline");}); + adapter_service->drain_commands(); + adapter_service->with_device(gc_buffer_device,[&](auto& owned){require(owned.live_bind_groups()==0,"Deferred bind group release failed");}); + require(static_cast(native),"Retained native bind group reference lost"); + } + auto device_script=v8::String::NewFromUtf8Literal(isolate,R"JS( + { + if(deviceProbe.createShaderModule.length!==1)throw new Error('shader arity'); + const module=deviceProbe.createShaderModule({label:'JS shader',code:'@compute @workgroup_size(1) fn main() {} @vertex fn vs()->@builtin(position) vec4f {return vec4f(0,0,0,1);} @fragment fn fs()->@location(0) vec4f {return vec4f(1,0,0,1);}',compilationHints:[{entryPoint:'main',layout:'auto'}]}); + if(Object.prototype.toString.call(module)!=='[object GPUShaderModule]' || module.label!=='JS shader')throw new Error('shader creation'); + if(deviceProbe.createRenderPipeline.length!==1)throw new Error('pipeline arity'); + const pipeline=deviceProbe.createRenderPipeline({label:'JS pipeline',layout:'auto',vertex:{module,entryPoint:'vs'},fragment:{module,entryPoint:'fs',targets:[{format:'rgba8unorm'}]}}); + if(Object.prototype.toString.call(pipeline)!=='[object GPURenderPipeline]'||pipeline.label!=='JS pipeline')throw new Error('pipeline creation'); + pipeline.label='updated pipeline';if(pipeline.label!=='updated pipeline')throw new Error('pipeline label'); + for(const call of [()=>deviceProbe.createRenderPipeline(),()=>deviceProbe.createRenderPipeline({}),()=>deviceProbe.createRenderPipeline.call({},{}),()=>deviceProbe.createRenderPipeline({layout:'auto',vertex:{module:{}}})]) { + let rejected=false;try{call()}catch(e){rejected=e instanceof TypeError}if(!rejected)throw new Error('invalid pipeline call'); + } + module.label='updated shader';if(module.label!=='updated shader')throw new Error('created shader label'); + for(const call of [()=>deviceProbe.createShaderModule(),()=>deviceProbe.createShaderModule({}),()=>deviceProbe.createShaderModule.call({}, {code:''})]) { + let rejected=false;try{call()}catch(e){rejected=e instanceof TypeError}if(!rejected)throw new Error('shader invalid call'); + } + const shaderSentinel={};let propagated=false; + try{deviceProbe.createShaderModule({get code(){throw shaderSentinel}})}catch(e){propagated=e===shaderSentinel} + if(!propagated)throw new Error('shader getter exception'); + const limits=deviceProbe.limits;globalThis.retainedLimits=limits; + const originalLimit=limits.maxBufferSize; + if(Object.prototype.toString.call(limits)!=='[object GPUSupportedLimits]')throw new Error('limits tag'); + let readOnly=false;try{(()=>{'use strict';limits.maxBufferSize=1})()}catch(e){readOnly=e instanceof TypeError} + if(!readOnly||limits.maxBufferSize!==originalLimit)throw new Error('limits mutable'); + const getter=Object.getOwnPropertyDescriptor(Object.getPrototypeOf(limits),'maxBufferSize').get; + let wrongLimits=false;try{getter.call({})}catch(e){wrongLimits=e instanceof TypeError}if(!wrongLimits)throw new Error('limits receiver'); + const features=deviceProbe.features; + globalThis.retainedFeatures=features; + if(features!==deviceProbe.features)throw new Error('features identity'); + if(Object.prototype.toString.call(features)!=='[object GPUSupportedFeatures]')throw new Error('features brand'); + const names=[...features]; + if(names.length!==features.size || new Set(names).size!==names.length)throw new Error('features size'); + if(features.keys!==features.values || features.values!==features[Symbol.iterator])throw new Error('features iterator identity'); + if(features.has('dawn-internal-usages') || features.has('shared-texture-memory-iosurface'))throw new Error('private feature exposed'); + for(const name of names)if(!features.has({toString(){return name}}))throw new Error('feature coercion'); + for(const [a,b] of features.entries())if(a!==b || !features.has(a))throw new Error('feature entries'); + let count=0;const thisArg={};features.forEach(function(a,b,set){if(this!==thisArg || a!==b || set!==features)throw new Error('forEach arguments');count++},thisArg); + if(count!==features.size || features.add || features.delete || features.clear)throw new Error('immutable features'); + for(const operation of [()=>features.has(),()=>features.has(Symbol()),()=>features.has.call({},'x'),()=>features.forEach(null),()=>Set.prototype.add.call(features,'x')]) { + let rejected=false;try{operation()}catch(e){rejected=e instanceof TypeError}if(!rejected)throw new Error('features validation'); + } + const featureFailure={};let featureThrown=false; + try{features.has({toString(){throw featureFailure}})}catch(e){featureThrown=e===featureFailure} + if(!featureThrown)throw new Error('feature coercion exception'); + if(deviceProbe.label!=='')throw new Error('device label default'); + deviceProbe.label='device\0\ud800'; + if(deviceProbe.label!=='device\0\ufffd')throw new Error('device label conversion'); + let labelFailure={}; + try{deviceProbe.label={toString(){throw labelFailure}}}catch(e){if(e!==labelFailure)throw e} + if(deviceProbe.label!=='device\0\ufffd')throw new Error('failed device label changed value'); + let symbolRejected=false;try{deviceProbe.label=Symbol()}catch(e){symbolRejected=e instanceof TypeError} + if(!symbolRejected)throw new Error('device Symbol label accepted'); + if(deviceProbe.createBuffer.length!==1)throw new Error('createBuffer arity'); + for(let operation of [()=>deviceProbe.createBuffer(),()=>deviceProbe.createBuffer.call({},{}),()=>deviceProbe.destroy.call({})]) { + let rejected=false;try{operation()}catch(e){rejected=e instanceof TypeError} + if(!rejected)throw new Error('device brand or required descriptor'); + } + let buffer=deviceProbe.createBuffer({size:32,usage:8,mappedAtCreation:true,label:'via device'}); + if(buffer.size!==32||buffer.usage!==8||buffer.label!=='via device')throw new Error('device-created buffer metadata'); + let range=buffer.getMappedRange(),words=new Uint32Array(range);words[0]=123; + let pending=deviceProbe.createBuffer({size:16,usage:6}); + globalThis.deviceMapCancelled=false; + pending.mapAsync(2).catch(e=>{if(!(e instanceof DOMException)||e.name!=='AbortError')throw e;deviceMapCancelled=true}); + if(pending.mapState!=='pending')throw new Error('device map did not become pending'); + deviceProbe.destroy();deviceProbe.destroy(); + deviceProbe.label='destroyed device'; + if(deviceProbe.label!=='destroyed device')throw new Error('destroyed device label'); + if(pending.mapState!=='unmapped')throw new Error('device destroy did not cancel map'); + if(range.byteLength!==0||words.length!==0||buffer.mapState!=='unmapped'||buffer.size!==32)throw new Error('device destroy mapping lifetime'); + let rejected=false;try{buffer.getMappedRange()}catch(e){rejected=e instanceof DOMException&&e.name==='OperationError'} + if(!rejected)throw new Error('destroyed device mapping remained available'); + delete globalThis.deviceProbe; + } + )JS"); + size_t shaders_before_device_script=0,pipelines_before_device_script=0; + adapter_service->with_device(gc_buffer_device,[&](auto& owned){ + shaders_before_device_script=owned.live_shader_modules(); + pipelines_before_device_script=owned.live_render_pipelines(); + }); + v8::Local device_test; + require(v8::Script::Compile(context,device_script).ToLocal(&device_test) && !device_test->Run(context).IsEmpty(),"JavaScript device buffer creation/destruction failed"); + adapter_service->with_device(gc_buffer_device,[&](auto& owned) { + require(owned.live_shader_modules()==shaders_before_device_script+1,"JavaScript shader creation did not adopt exactly one native module"); + require(owned.live_render_pipelines()==pipelines_before_device_script+1,"JavaScript pipeline creation did not adopt exactly one native pipeline"); + }); + retired_device_probe.Reset(isolate,device_object); + async_buffers.reset(); + adapter_service->destroy_device(gc_buffer_device); + gc_buffers.reset(); // Delayed wrapper releases tolerate retired native devices. + // Retire disposed shader/pipeline fixture tickets before the + // next registration in this deliberately eight-ticket queue. + adapter_service->drain_commands(); + + graphics_command release{[](graphics_service&,std::span,const graphics_command::arguments&) noexcept { ++weak_releases; }}; + auto reachable=v8::Object::New(isolate); + require(wrappers->attach(reachable,release),"weak wrapper slot was not reusable"); + wrappers.reset(); + require(weak_releases==1,"registry disposal executed graphics inline"); + } + + auto key=v8::String::NewFromUtf8Literal(isolate,"gpuResolve"); + auto resolve=context->Global()->Get(context,key).ToLocalChecked().As(); + v8::Local outcome=v8::Integer::New(isolate,record.status==completion_status::cancelled ? 2 : 1); + require(!resolve->Call(context,context->Global(),1,&outcome).IsEmpty(),"promise resolution failed"); + delivered=true; + }); + bool wrong_thread_rejected=false; + std::thread wrong([&] { + try { runtime.initialize_graphics(wake,[](auto) {}); } + catch (const std::logic_error&) { wrong_thread_rejected=true; } + }); + wrong.join(); + require(wrong_thread_rejected,"wrong-thread initialization accepted"); + adapter_service=&graphics; + releases=graphics.release_endpoint(8); + auto mailbox=graphics.dawn().completions(); + auto ticket=mailbox->reserve(1,{graphics.engine_identity(),new_owner_token(),0}).value(); + std::thread native_callback([mailbox,ticket] { mailbox->publish(ticket,completion_status::success); }); + native_callback.join(); + require(!delivered,"native callback entered V8 directly"); + const auto deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while (!delivered && std::chrono::steady_clock::now()wait_for(runtime.recommended_idle_wait(std::chrono::milliseconds(100)),[] { return false; }); + } + require(delivered,"hidden completion did not progress"); + + while ((!adapter_delivered || !adapter_cancelled || !cancelled_device_retired || !device_failure_seen || !buffer_wrappers_tested || !buffer_validation_seen || binding_map_completions!=5 || map_completions!=3 || failed_wrapper_count!=2 || mailbox->metrics().native_pending!=0) && std::chrono::steady_clock::now()wait_for(runtime.recommended_idle_wait(std::chrono::milliseconds(100)),[] { return false; }); + } + require(adapter_delivered && discovered_adapter.table && adapter_cancelled,"Actual Dawn adapter discovery/cancellation failed"); + require(buffer_validation_seen && binding_map_completions==5 && map_completions==3 && mailbox->metrics().native_pending==0,"Buffer verification timed out before native retirement"); + require(runtime.execute("if(!allocationMapDone || !bindingReadDone || !bindingMapDone || !bindingCancelDone || !bindingRemapDone || !bindingConversionDone || !asyncMapDone || !cancelMapDone || !adapterPromiseDone || !cancelledAdapterDone || wrapperFailures!==2 || rafDone!==0)throw new Error('adapter promise did not progress while hidden');", "adapter-promise-check"),"Adapter promise checkpoint failed"); + adapter_service->with_adapter(discovered_adapter,[](const auto& adapter) { + require(static_cast(adapter),"Discovered adapter lost its native reference"); + }); + adapter_service->destroy_adapter(discovered_adapter); + adapter_request.reset(); cancelled_adapter.reset(); + for (auto& request:failed_wrappers) request.reset(); + require(runtime.execute("delete globalThis.gcBufferProbe;delete globalThis.weakReleaseProbe;", "buffer-drop-reference"),"Buffer reference removal failed"); + size_t buffers_before_gc=0; + adapter_service->with_device(gc_buffer_device,[&](auto& device) { buffers_before_gc=device.live_buffers(); }); + require(buffers_before_gc>=1,"Collectible buffer disappeared before GC"); + runtime.notify_low_memory(); + adapter_service->with_device(gc_buffer_device,[&](auto& device) { + require(device.live_buffers()==buffers_before_gc,"GC performed native buffer release inline"); + }); + require(weak_releases==0,"GC callback executed a native graphics release"); + require(runtime.has_pending_tasks(),"GC did not enqueue release work"); + require(runtime.pump_task(),"GC release task failed"); + require(weak_releases==1 && releases->occupied()==1,"Reachable mapped view did not retain its wrapper registration"); + adapter_service->with_device(gc_buffer_device,[&](auto& device) { + require(device.live_buffers()==1,"Mapped view lost its native buffer after wrapper references were dropped"); + }); + require(runtime.execute("if(gcMappedProbe.byteLength!==32)throw new Error('retained mapping detached');new Uint8Array(gcMappedProbe)[0]=91;delete globalThis.gcMappedProbe;", "mapped-owner-drop"),"Mapped view did not survive owner GC"); + runtime.notify_low_memory(); + adapter_service->with_device(gc_buffer_device,[&](auto& device) { + require(device.live_buffers()==1,"Mapped-view GC released native storage inline"); + }); + require(runtime.has_pending_tasks() && runtime.pump_task(),"Mapped-view release did not wake the engine"); + adapter_service->with_device(gc_buffer_device,[&](auto& device) { + require(device.live_buffers()==0 && releases->occupied()==0,"Mapped-view owner release did not drain"); + }); + require(runtime.execute("if(gpuDone!==1 || rafDone!==0) throw new Error('completion or RAF scheduling failed');","graphics-check"),"promise continuation failed without RAF"); + require(runtime.execute("globalThis.gpuDone=0; new Promise(r=>globalThis.gpuResolve=r).then(()=>globalThis.gpuDone=1);","graphics-second-setup"),"second promise setup failed"); + delivered=false; + auto second=mailbox->reserve(2,{graphics.engine_identity(),new_owner_token(),0}).value(); + require(mailbox->publish(second,completion_status::success),"second completion rejected"); + require(runtime.execute("void 0","graphics-execute-drain"),"execute completion drain failed"); + require(delivered,"execute did not drain completion"); + require(runtime.execute("if(gpuDone!==1 || rafDone!==0) throw new Error('execute checkpoint failed');","graphics-second-check"),"execute promise checkpoint failed"); + const auto device_map_deadline=std::chrono::steady_clock::now()+std::chrono::seconds(5); + while (!device_map_retired && std::chrono::steady_clock::now()wait_for(runtime.recommended_idle_wait(std::chrono::milliseconds(100)),[] { return false; }); + } + require(cancelled_device_retired,"Cancelled native device callback did not retire"); + require(device_failure_seen,"Device failure completion was not observed"); + require(device_map_retired,"Device map native completion did not retire"); + require(runtime.execute("if(!(retainedLimits.maxBufferSize>0))throw new Error('retained limits');delete globalThis.retainedLimits;if([...retainedFeatures].length!==retainedFeatures.size)throw new Error('retained features');delete globalThis.retainedFeatures;", "retained-features"),"Feature snapshot did not survive registry disposal"); + require(runtime.execute("if(!deviceMapCancelled)throw new Error('device cancellation promise not delivered');", "device-map-cancel-check"),"Device map rejection failed"); + require(weak_releases==2 && releases->occupied()==0,"registry disposal release did not drain"); + require(mailbox->metrics().occupied==0,"completion storage not reclaimed"); + require(!runtime.load_url("https://graphics.test/missing"),"missing navigation unexpectedly succeeded"); + const auto old_identity=graphics.engine_identity(); + auto endpoint=graphics.command_endpoint(1,0); + require(runtime.execute("globalThis.gpuDone=0; new Promise(r=>globalThis.gpuResolve=r).then(status=>globalThis.gpuDone=status);","navigation-setup"),"navigation promise setup failed"); + auto pending=mailbox->reserve(3,{old_identity,new_owner_token(),0}).value(); + require(runtime.load_url("https://graphics.test/next"),"navigation failed"); + require(runtime.execute("if(gpuDone!==2) throw new Error('navigation cancellation missing');","navigation-check"),"navigation did not terminate pending promise"); + require(!mailbox->publish(pending,completion_status::success),"old document callback delivered after navigation"); + graphics_command no_op{[](graphics_service&,std::span,const graphics_command::arguments&) noexcept {}}; + require(endpoint->enqueue(no_op)==enqueue_result::closed,"old document command endpoint remained open"); + bool disposed=false; + require(runtime.execute("globalThis.disposeDone=0; new Promise(r=>globalThis.disposeResolve=r).then(()=>globalThis.disposeDone=1);","dispose-setup"),"disposal promise setup failed"); + auto& next_graphics=runtime.initialize_graphics(wake,[&](auto record) { + require(record.operation==4 && record.status==completion_status::cancelled,"disposal cancellation missing"); + require(std::this_thread::get_id()==owner_thread,"disposal left engine thread"); + auto* isolate=v8::Isolate::GetCurrent(); + require(isolate && isolate->InContext(),"disposal has no context"); + auto context=isolate->GetCurrentContext(); + auto key=v8::String::NewFromUtf8Literal(isolate,"disposeResolve"); + auto resolve=context->Global()->Get(context,key).ToLocalChecked().As(); + require(!resolve->Call(context,context->Global(),0,nullptr).IsEmpty(),"disposal resolution failed"); + disposed=true; + }); + require(next_graphics.engine_identity()!=old_identity,"navigation reused graphics identity"); + auto final_mailbox=next_graphics.dawn().completions(); + auto final_ticket=final_mailbox->reserve(4,{next_graphics.engine_identity(),new_owner_token(),0}).value(); + runtime.shutdown_graphics(); + runtime.shutdown_graphics(); + require(disposed,"shutdown did not deliver cancellation"); + require(runtime.execute("if(disposeDone!==1) throw new Error('disposal checkpoint missing');","dispose-check"),"disposal checkpoint failed"); + require(!final_mailbox->publish(final_ticket,completion_status::success),"late disposal callback delivered"); + bool restart_rejected=false; + try { runtime.initialize_graphics(wake,[](auto) {}); } + catch (const std::logic_error&) { restart_rejected=true; } + require(restart_rejected,"disposed graphics runtime restarted"); + } catch (...) { failure=std::current_exception(); } + }); + worker.join(); + if (failure) { + try { std::rethrow_exception(failure); } + catch (const std::exception& error) { std::cerr << error.what() << '\n'; } + return 1; + } + test_native_gpu_scene_leases(); + test_image_lease_abi(); + test_scene_acquisition_v3(); + std::cout << "Hidden V8 graphics completion, context affinity and promise checkpoint passed without RAF\n"; +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_buffer_descriptor.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_buffer_descriptor.h new file mode 100644 index 000000000..22832f752 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_buffer_descriptor.h @@ -0,0 +1,33 @@ +#pragma once +#include "graphics/v8_webgpu_buffer_descriptor.h" +inline void test_v8_webgpu_buffer_descriptor(v8::Isolate* isolate,v8::Local context) { + using namespace webscene::graphics; + const auto evaluate=[&](const char* source) { + return v8::Script::Compile(context,v8::String::NewFromUtf8(isolate,source).ToLocalChecked()).ToLocalChecked()->Run(context).ToLocalChecked(); + }; + webgpu_buffer_descriptor descriptor; + require(read_webgpu_buffer_descriptor(isolate,context,evaluate("({size:16,usage:8})"),descriptor) + && descriptor.size==16 && descriptor.usage==8 && descriptor.label.empty() && !descriptor.mapped_at_creation,"Buffer defaults incorrect"); + require(read_webgpu_buffer_descriptor(isolate,context,evaluate("({size:9007199254740991,usage:4294967295,mappedAtCreation:{},label:'a\\u0000\\ud800\\ud83d\\ude00'})"),descriptor) + && descriptor.size==9007199254740991ull && descriptor.usage==0xffffffffu && descriptor.mapped_at_creation + && descriptor.label==std::string("a\0\xef\xbf\xbd\xf0\x9f\x98\x80",9),"Buffer boundaries or USVString incorrect"); + require(read_webgpu_buffer_descriptor(isolate,context,evaluate("Object.create({size:'12.9',usage:3.9,label:null})"),descriptor) + && descriptor.size==12 && descriptor.usage==3 && descriptor.label=="null","Buffer coercion/inheritance incorrect"); + require(read_webgpu_buffer_descriptor(isolate,context,evaluate("({size:-0.9,usage:null})"),descriptor) + && descriptor.size==0 && descriptor.usage==0,"Buffer truncation before range check incorrect"); + require(read_webgpu_buffer_descriptor(isolate,context,evaluate("(()=>{globalThis.bufferOrder=[];return new Proxy({size:4,usage:8},{get:(o,k)=>{bufferOrder.push(k);return o[k]}})})()"),descriptor) + && evaluate("bufferOrder.join(',')==='label,mappedAtCreation,size,usage'")->IsTrue(),"Buffer getter order incorrect"); + for (const char* source:{"undefined","null","{}","({size:4})","1","({size:undefined,usage:8})", + "({size:-1,usage:8})","({size:NaN,usage:8})","({size:Infinity,usage:8})","({size:9007199254740992,usage:8})", + "({size:1n,usage:8})","({size:Symbol(),usage:8})","({size:4,usage:4294967296})","({size:4,usage:-1})", + "({size:4,usage:NaN})","({size:4,usage:8,label:Symbol()})"}) { + v8::TryCatch caught(isolate); + descriptor.size=123; + require(!read_webgpu_buffer_descriptor(isolate,context,evaluate(source),descriptor) && caught.HasCaught() + && descriptor.size==123,"Invalid buffer descriptor accepted or partially committed"); + } + v8::TryCatch caught(isolate); + auto input=evaluate("(()=>{globalThis.bufferError={};return {size:{valueOf(){throw bufferError}},get usage(){throw 'wrong getter'}}})()"); + require(!read_webgpu_buffer_descriptor(isolate,context,input,descriptor) && caught.HasCaught() + && caught.Exception()->StrictEquals(context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"bufferError")).ToLocalChecked()),"Buffer coercion exception replaced"); +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_canvas_configuration.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_canvas_configuration.h new file mode 100644 index 000000000..d776c47c8 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_canvas_configuration.h @@ -0,0 +1,38 @@ +#pragma once +#include "graphics/v8_webgpu_canvas_configuration.h" +#include "graphics/webgpu_canvas_texture_descriptor.h" +inline void test_v8_webgpu_canvas_configuration(v8::Isolate* isolate,v8::Local context) { + const auto evaluate=[&](const char* source){return v8::Script::Compile(context,v8::String::NewFromUtf8(isolate,source).ToLocalChecked()).ToLocalChecked()->Run(context).ToLocalChecked();}; + const auto device=[](auto value){return v8_webgpu_devices::native_reference(value);}; + webgpu_canvas_configuration configuration; + require(read_webgpu_canvas_configuration(isolate,context,evaluate("({device:adapterDeviceProbe,format:'bgra8unorm'})"),configuration,device) + && configuration.device&&configuration.alpha_mode=="opaque"&&configuration.color_space=="srgb"&&configuration.tone_mapping=="standard" + && configuration.usage==16&&configuration.view_formats.empty(),"Canvas defaults failed"); + require(read_webgpu_canvas_configuration(isolate,context,evaluate("(()=>{globalThis.canvasOrder=[];return new Proxy({device:adapterDeviceProbe,format:'rgba16float',alphaMode:'premultiplied',colorSpace:'display-p3-linear',toneMapping:new Proxy({mode:'extended'},{get(o,k){canvasOrder.push(k);return o[k]}}),viewFormats:new Set(['rgba16float'])},{get(o,k){canvasOrder.push(k);return o[k]}})})()"),configuration,device) + && configuration.alpha_mode=="premultiplied"&&configuration.color_space=="display-p3-linear"&&configuration.tone_mapping=="extended" + && configuration.view_formats.size()==1 + && evaluate("canvasOrder.join(',')==='alphaMode,colorSpace,device,format,toneMapping,mode,usage,viewFormats'")->IsTrue(),"Canvas configuration order or requested modes changed"); + for(const char* source:{"undefined","{}","({device:{},format:'bgra8unorm'})","({device:adapterDeviceProbe,format:'invalid'})", + "({device:adapterDeviceProbe,format:'bgra8unorm',colorSpace:'invalid'})","({device:adapterDeviceProbe,format:'bgra8unorm',toneMapping:{mode:'invalid'}})", + "({device:adapterDeviceProbe,format:'bgra8unorm',usage:-1})","({device:adapterDeviceProbe,format:'bgra8unorm',viewFormats:null})"}) { + v8::TryCatch caught(isolate);configuration.alpha_mode="unchanged"; + require(!read_webgpu_canvas_configuration(isolate,context,evaluate(source),configuration,device)&&caught.HasCaught()&&configuration.alpha_mode=="unchanged","Invalid canvas configuration accepted or committed"); + } + for(auto format:{wgpu::TextureFormat::RGBA8Unorm,wgpu::TextureFormat::BGRA8Unorm,wgpu::TextureFormat::RGBA16Float}) { + configuration.format=format;configuration.usage=1; + validate_webgpu_canvas_format_usage(configuration); + auto texture=webgpu_canvas_texture_descriptor(configuration,0,17); + require(texture.size.width==0&&texture.size.height==17&&texture.size.depthOrArrayLayers==1&&texture.usage==1 + &&texture.mip_levels==1&&texture.samples==1&&texture.dimension==wgpu::TextureDimension::e2D + &&texture.format==format&&texture.view_formats==configuration.view_formats,"Canvas texture descriptor altered requested metadata"); + } + configuration.format=wgpu::TextureFormat::RGBA8UnormSrgb;bool bad_format=false; + try{validate_webgpu_canvas_format_usage(configuration);}catch(const std::invalid_argument&){bad_format=true;} + configuration.format=wgpu::TextureFormat::BGRA8Unorm;configuration.usage=0x30;bool transient=false; + try{validate_webgpu_canvas_format_usage(configuration);}catch(const std::invalid_argument&){transient=true;} + require(bad_format&&transient,"Canvas-only format/usage validation missing"); + auto throwing=evaluate("(()=>{globalThis.canvasConfigError={};return {get colorSpace(){throw canvasConfigError},get device(){throw 'wrong getter'}}})()"); + v8::TryCatch caught(isolate); + require(!read_webgpu_canvas_configuration(isolate,context,throwing,configuration,device)&&caught.HasCaught() + &&caught.Exception()->StrictEquals(evaluate("canvasConfigError")),"Canvas configuration exception replaced"); +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_device_descriptor.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_device_descriptor.h new file mode 100644 index 000000000..d9be9c003 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_device_descriptor.h @@ -0,0 +1,81 @@ +#pragma once +#include "graphics/v8_webgpu_device_descriptor.h" +#include "graphics/webgpu_limit_names.h" +#include "graphics/webgpu_required_limits.h" +inline void test_v8_webgpu_device_descriptor(v8::Isolate* isolate,v8::Local context) { + const auto evaluate=[&](const char* source) { + return v8::Script::Compile(context,v8::String::NewFromUtf8(isolate,source).ToLocalChecked()).ToLocalChecked()->Run(context).ToLocalChecked(); + }; + wgpu::Limits native_limits{}; + wgpu::CompatibilityModeLimits compatibility_limits{}; + for (const auto& limit:webgpu_limit_names) { + require(webgpu_limit_from_name(limit.name)==&limit,"Limit name roundtrip failed"); + require(limit.write(native_limits,compatibility_limits,1234) && limit.read(native_limits,compatibility_limits)==1234,"Native limit member mapping failed"); + const bool wide=std::holds_alternative(limit.member); + require(limit.write(native_limits,compatibility_limits,9007199254740991ull)==wide,"Limit width check failed"); + require(!limit.write(native_limits,compatibility_limits,UINT64_MAX),"Dawn undefined sentinel accepted as requested limit"); + if (!wide) require(!limit.write(native_limits,compatibility_limits,UINT32_MAX),"Dawn 32-bit undefined sentinel accepted"); + } + require(!webgpu_limit_from_name(u"maxPixelLocalStorageSize") && !webgpu_limit_from_name(u"unknown"),"Private/unknown native limit exposed"); + wgpu::Limits available{}; available.maxBufferSize=8192; available.minUniformBufferOffsetAlignment=256; + wgpu::CompatibilityModeLimits available_compatibility{}; available_compatibility.maxStorageBuffersInVertexStage=4; + std::vector requested{{u"maxBufferSize",4096},{u"minUniformBufferOffsetAlignment",512}, + {u"maxStorageBuffersInVertexStage",2},{u"unknown",std::nullopt}}; + require(prepare_webgpu_required_limits(requested,available,available_compatibility,native_limits,compatibility_limits) + && native_limits.maxBufferSize==4096 && native_limits.minUniformBufferOffsetAlignment==512 + && compatibility_limits.maxStorageBuffersInVertexStage==2,"Required limit mapping failed"); + for (const webgpu_required_limit invalid:std::vector{{u"unknown",0},{u"maxBufferSize",8193}, + {u"minUniformBufferOffsetAlignment",128},{u"minUniformBufferOffsetAlignment",257},{u"minUniformBufferOffsetAlignment",0}, + {u"minUniformBufferOffsetAlignment",uint64_t{1}<<32},{u"maxStorageBuffersInVertexStage",5}}) { + requested.push_back(invalid); + require(!prepare_webgpu_required_limits(requested,available,available_compatibility,native_limits,compatibility_limits) + && native_limits.maxBufferSize==4096 && compatibility_limits.maxStorageBuffersInVertexStage==2,"Invalid limits accepted or partially committed"); + requested.pop_back(); + } + webgpu_device_descriptor descriptor; + for (const char* source:{"undefined","null","{}"}) { + require(read_webgpu_device_descriptor(isolate,context,evaluate(source),descriptor) + && descriptor.label.empty() && descriptor.queue_label.empty() && descriptor.required_features.empty() + && descriptor.required_limits.empty(),"Device defaults incorrect"); + } + require(read_webgpu_device_descriptor(isolate,context,evaluate("({label:'a\\0\\ud800',defaultQueue:{label:null},requiredFeatures:new Set(['shader-f16','timestamp-query']),requiredLimits:{maxBufferSize:9007199254740991,unknown:undefined,'\\ud800':-0.9}})"),descriptor) + && descriptor.label==std::string("a\0\xef\xbf\xbd",5) && descriptor.queue_label=="null" + && descriptor.required_features==std::vector{wgpu::FeatureName::ShaderF16,wgpu::FeatureName::TimestampQuery} + && descriptor.required_limits.size()==3 && descriptor.required_limits[0].second==9007199254740991ull + && !descriptor.required_limits[1].second && descriptor.required_limits[2].first==std::u16string(1,char16_t{0xd800}) + && descriptor.required_limits[2].second==0,"Device descriptor conversion incorrect"); + require(read_webgpu_device_descriptor(isolate,context,evaluate("(()=>{globalThis.deviceOrder=[];return new Proxy({defaultQueue:new Proxy({},{get(o,k){deviceOrder.push('queue.'+k);return o[k]}})},{get(o,k){deviceOrder.push(k);return o[k]}})})()"),descriptor) + && evaluate("deviceOrder.join(',')==='label,defaultQueue,queue.label,requiredFeatures,requiredLimits'")->IsTrue(),"Device dictionary order incorrect"); + require(read_webgpu_device_descriptor(isolate,context,evaluate("({requiredLimits:Object.setPrototypeOf({get first(){Object.defineProperty(this,'second',{enumerable:false});return 12.9},second:4},{inherited:8})})"),descriptor) + && descriptor.required_limits.size()==1 && descriptor.required_limits[0].second==12,"Record enumeration mutation incorrect"); + require(read_webgpu_device_descriptor(isolate,context,evaluate("({requiredFeatures:{*[Symbol.iterator](){yield {toString(){return 'shader-f16'}};yield 'shader-f16'}}})"),descriptor) + && descriptor.required_features.size()==2,"Feature iterable conversion or duplicates incorrect"); + for (const char* source:{"1","({defaultQueue:1})","({label:Symbol()})","({requiredFeatures:null})", + "({requiredFeatures:'shader-f16'})","({requiredFeatures:[Symbol()]})","({requiredFeatures:['dawn-internal-usages']})", + "({requiredFeatures:{[Symbol.iterator]:3}})","({requiredFeatures:{[Symbol.iterator](){return 1}}})", + "({requiredFeatures:{[Symbol.iterator](){return {next(){return 1}}}}})","({requiredLimits:null})", + "({requiredLimits:{[Symbol()]:1}})","({requiredLimits:{x:NaN}})","({requiredLimits:{x:Infinity}})", + "({requiredLimits:{x:9007199254740992}})","({requiredLimits:{x:-1}})","({requiredLimits:{x:1n}})"}) { + v8::TryCatch caught(isolate); descriptor.label="unchanged"; + require(!read_webgpu_device_descriptor(isolate,context,evaluate(source),descriptor) && caught.HasCaught() + && descriptor.label=="unchanged","Invalid device descriptor accepted or partially committed"); + } + require(read_webgpu_device_descriptor(isolate,context,evaluate("(()=>{globalThis.limitOrder=[];return {requiredLimits:new Proxy({a:4,b:8},{ownKeys(o){limitOrder.push('keys');return Reflect.ownKeys(o)},getOwnPropertyDescriptor(o,k){limitOrder.push('desc:'+k);return Reflect.getOwnPropertyDescriptor(o,k)},get(o,k){limitOrder.push('get:'+k);return o[k]}})}})()"),descriptor) + && evaluate("limitOrder.join(',')==='keys,desc:a,get:a,desc:b,get:b'")->IsTrue(),"Record proxy trap order incorrect"); + auto input=evaluate("(()=>{globalThis.deviceDescriptorFailure={};return {requiredFeatures:{[Symbol.iterator](){return {next(){throw deviceDescriptorFailure}}}},get requiredLimits(){throw 'wrong getter'}}})()"); + v8::TryCatch caught(isolate); + require(!read_webgpu_device_descriptor(isolate,context,input,descriptor) && caught.HasCaught() + && caught.Exception()->StrictEquals(context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"deviceDescriptorFailure")).ToLocalChecked()),"Feature iterator exception replaced"); +} + +template void verify_v8_limits(v8::Isolate* isolate,v8::Local context,v8::Local wrapper,const Source& source) { + auto key=v8::String::NewFromUtf8Literal(isolate,"limits"); + auto snapshot=wrapper->Get(context,key).ToLocalChecked().As(); + require(snapshot->StrictEquals(wrapper->Get(context,key).ToLocalChecked()),"Limit snapshot identity changed"); + wgpu::Limits native{};wgpu::CompatibilityModeLimits compatibility{};native.nextInChain=&compatibility; + require(source.GetLimits(&native)==wgpu::Status::Success,"Test native limit query failed"); + for(const auto& limit:webgpu_limit_names) { + auto property=v8::String::NewFromTwoByte(isolate,reinterpret_cast(limit.name.data()),v8::NewStringType::kNormal,static_cast(limit.name.size())).ToLocalChecked(); + require(snapshot->Get(context,property).ToLocalChecked()->NumberValue(context).FromJust()==static_cast(limit.read(native,compatibility)),"JavaScript limit differs from native source"); + } +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_options.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_options.h new file mode 100644 index 000000000..72d82b3fb --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_options.h @@ -0,0 +1,44 @@ +#pragma once +#include "graphics/v8_webgpu_adapter_options.h" + +inline void test_v8_webgpu_adapter_options(v8::Isolate* isolate,v8::Local context) { + using namespace webscene::graphics; + auto evaluate=[&](const char* source) { + auto code=v8::String::NewFromUtf8(isolate,source).ToLocalChecked(); + return v8::Script::Compile(context,code).ToLocalChecked()->Run(context).ToLocalChecked(); + }; + for (const char* source : {"undefined","null","({})"}) { + webgpu_adapter_options options; + require(read_webgpu_adapter_options(isolate,context,evaluate(source),options),"adapter defaults rejected"); + require(options.feature_level==u"core" && !options.power_preference && + !options.force_fallback_adapter && !options.xr_compatible,"adapter defaults incorrect"); + } + webgpu_adapter_options options; + require(read_webgpu_adapter_options(isolate,context,evaluate( + "({featureLevel:'compatibility',powerPreference:'low-power',forceFallbackAdapter:1,xrCompatible:{}})"),options), + "adapter dictionary conversion failed"); + require(options.feature_level==u"compatibility" && options.power_preference==wgpu::PowerPreference::LowPower && + options.force_fallback_adapter && options.xr_compatible,"adapter converted values incorrect"); + require(read_webgpu_adapter_options(isolate,context,evaluate("({featureLevel:String.fromCharCode(0xd800)})"),options) && + options.feature_level.size()==1 && options.feature_level[0]==0xd800,"DOMString lost an unpaired surrogate"); + require(read_webgpu_adapter_options(isolate,context,evaluate("Object.create({powerPreference:'high-performance'})"),options) && + options.power_preference==wgpu::PowerPreference::HighPerformance,"inherited adapter option ignored"); + require(read_webgpu_adapter_options(isolate,context,evaluate( + "(()=>{globalThis.adapterOptionOrder=[];return new Proxy({},{get:(o,k)=>{adapterOptionOrder.push(k);return undefined}})})()"),options), + "adapter proxy conversion failed"); + require(evaluate("adapterOptionOrder.join(',')==='featureLevel,forceFallbackAdapter,powerPreference,xrCompatible'")->IsTrue(), + "adapter dictionary getter order incorrect"); + for (const char* source : {"1","'x'","true","({powerPreference:null})","({powerPreference:'LOW-POWER'})", + "({featureLevel:Symbol()})","({get forceFallbackAdapter(){throw new Error('getter')}})"}) { + v8::TryCatch caught(isolate); + auto input=evaluate(source); + options.feature_level=u"unchanged"; + require(!read_webgpu_adapter_options(isolate,context,input,options) && caught.HasCaught(),"invalid adapter options accepted"); + require(options.feature_level==u"unchanged","failed conversion changed native descriptor"); + } + v8::TryCatch caught(isolate); + auto input=evaluate("(()=>{globalThis.adapterGetterError={};return {get featureLevel(){throw adapterGetterError},get forceFallbackAdapter(){throw 'wrong getter'}}})()"); + require(!read_webgpu_adapter_options(isolate,context,input,options) && caught.HasCaught(),"throwing getter accepted"); + require(caught.Exception()->StrictEquals(context->Global()->Get(context, + v8::String::NewFromUtf8Literal(isolate,"adapterGetterError")).ToLocalChecked()),"getter exception replaced"); +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_programmable_stage.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_programmable_stage.h new file mode 100644 index 000000000..4c559a564 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_programmable_stage.h @@ -0,0 +1,61 @@ +#pragma once +#include "graphics/v8_webgpu_programmable_stage.h" +#include "graphics/v8_webgpu_vertex_state.h" +#include "graphics/v8_webgpu_render_descriptor.h" +inline void test_v8_webgpu_programmable_stage(v8::Isolate* isolate,v8::Local context) { + const auto evaluate=[&](const char* source){return v8::Script::Compile(context,v8::String::NewFromUtf8(isolate,source).ToLocalChecked()).ToLocalChecked()->Run(context).ToLocalChecked();}; + webgpu_programmable_stage stage; + require(read_webgpu_programmable_stage(isolate,context,evaluate("({module:shaderProbe})"),stage) + && stage.module && !stage.entry_point && stage.constants.empty(),"Programmable stage defaults failed"); + auto module=stage.module; + require(read_webgpu_programmable_stage(isolate,context,evaluate("({module:shaderProbe,entryPoint:'m\\0\\ud800',constants:{a:true,b:null,c:'2.5','\\ud800':1,'\\ud801':2}})"),stage) + && stage.module.Get()==module.Get() && stage.entry_point==std::string("m\0\xef\xbf\xbd",5) + && stage.constants.size()==4 && stage.constants[0].second==1 && stage.constants[1].second==0 + && stage.constants[2].second==2.5 && stage.constants[3].second==2,"Programmable stage USV record conversion failed"); + auto native=stage.native_constants(); + require(native.size()==4 && native[3].key.length==3 && native[3].value==2,"Pipeline native constants changed converted values"); + auto ordered=evaluate(R"JS((()=>{ + globalThis.stageOrder=[]; + let constants=new Proxy({a:1,b:2},{ownKeys(o){stageOrder.push('keys');return Reflect.ownKeys(o)},getOwnPropertyDescriptor(o,k){stageOrder.push('desc:'+k);return Reflect.getOwnPropertyDescriptor(o,k)},get(o,k){stageOrder.push('get:'+k);if(k==='a')delete o.b;return o[k]}}); + return new Proxy({module:shaderProbe,constants},{get(o,k){stageOrder.push(k);return o[k]}}); + })())JS"); + require(read_webgpu_programmable_stage(isolate,context,ordered,stage) && stage.constants.size()==1 + && evaluate("stageOrder.join(',')==='constants,keys,desc:a,get:a,desc:b,entryPoint,module'")->IsTrue(),"Pipeline constant record order or deletion semantics failed"); + for(const char* source:{"undefined","null","1","{}","({module:{}})","({module:shaderProbe,constants:null})", + "({module:shaderProbe,constants:{x:NaN}})","({module:shaderProbe,constants:{x:Infinity}})","({module:shaderProbe,constants:{x:1n}})", + "({module:shaderProbe,constants:{[Symbol()]:1}})","({module:shaderProbe,entryPoint:Symbol()})"}) { + v8::TryCatch caught(isolate);stage.entry_point="unchanged"; + require(!read_webgpu_programmable_stage(isolate,context,evaluate(source),stage) && caught.HasCaught() + && stage.entry_point=="unchanged","Invalid programmable stage accepted or partially committed"); + } + webgpu_vertex_state vertex; + require(read_webgpu_vertex_state(isolate,context,evaluate("({module:shaderProbe})"),vertex)&&vertex.buffers.empty(),"Vertex buffer defaults failed"); + require(read_webgpu_vertex_state(isolate,context,evaluate("({module:shaderProbe,buffers:new Set([null,undefined,{arrayStride:16,stepMode:'instance',attributes:[{format:'float32x3',offset:0,shaderLocation:2}]}])})"),vertex) + && vertex.buffers.size()==3 && !vertex.buffers[0] && !vertex.buffers[1] && vertex.buffers[2] + && vertex.buffers[2]->step_mode==wgpu::VertexStepMode::Instance && vertex.buffers[2]->native().attributeCount==1 + && vertex.buffers[2]->native().attributes[0].shaderLocation==2,"Vertex nullable iterable layout failed"); + for(const char* source:{"({module:shaderProbe,buffers:null})","({module:shaderProbe,buffers:[{}]})", + "({module:shaderProbe,buffers:[{arrayStride:9007199254740992,attributes:[]}]})", + "({module:shaderProbe,buffers:[{arrayStride:16,attributes:[{format:'float32',offset:0}]}]})", + "({module:shaderProbe,buffers:[{arrayStride:16,attributes:[{format:'float32',offset:-1,shaderLocation:0}]}]})"}) { + v8::TryCatch caught(isolate); + require(!read_webgpu_vertex_state(isolate,context,evaluate(source),vertex) && caught.HasCaught() && vertex.buffers.size()==3,"Invalid vertex layout accepted or committed"); + } + webgpu_vertex_buffer_layout layout; + require(read_webgpu_vertex_buffer_layout(isolate,context,evaluate("(()=>{globalThis.vertexOrder=[];const attr=new Proxy({format:'float32',offset:0,shaderLocation:0},{get(o,k){vertexOrder.push(k);return o[k]}});return new Proxy({arrayStride:4,attributes:[attr]},{get(o,k){vertexOrder.push(k);return o[k]}})})()"),layout) + && evaluate("vertexOrder.join(',')==='arrayStride,attributes,format,offset,shaderLocation,stepMode'")->IsTrue(),"Vertex layout property order failed"); + webgpu_render_descriptor render; + const auto no_layout=[](auto){return std::optional{};}; + require(read_webgpu_render_descriptor(isolate,context,evaluate("(()=>{globalThis.pipelineOrder=[];return new Proxy({layout:'auto',vertex:{module:shaderProbe,buffers:[null]},fragment:{module:shaderProbe,targets:[null,{format:'rgba8unorm',blend:{alpha:{},color:{}}}]}},{get(o,k){pipelineOrder.push(k);return o[k]}})})()"),render,no_layout) + && evaluate("pipelineOrder.join(',')==='label,layout,depthStencil,fragment,multisample,primitive,vertex'")->IsTrue(),"Render descriptor order failed"); + auto moved_render=std::move(render); + moved_render.with_native([&](const auto& native) { + require(!native.layout && native.vertex.bufferCount==1 && native.vertex.buffers[0].stepMode==wgpu::VertexStepMode::Undefined + && native.fragment && native.fragment->targetCount==2 && native.fragment->targets[0].format==wgpu::TextureFormat::Undefined + && native.fragment->targets[1].blend && native.fragment->targets[1].blend->color.srcFactor==wgpu::BlendFactor::One,"Render descriptor nested native storage failed"); + }); + auto throwing=evaluate("(()=>{globalThis.stageError={};return {constants:{get x(){throw stageError}},get module(){throw 'wrong getter'}}})()"); + v8::TryCatch caught(isolate); + require(!read_webgpu_programmable_stage(isolate,context,throwing,stage) && caught.HasCaught() + && caught.Exception()->StrictEquals(evaluate("stageError")),"Pipeline constant exception was replaced"); +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_render_pass_descriptor.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_render_pass_descriptor.h new file mode 100644 index 000000000..3ec36ac82 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_render_pass_descriptor.h @@ -0,0 +1,21 @@ +#pragma once +#include "graphics/v8_webgpu_render_pass_descriptor.h" +inline void test_v8_webgpu_render_pass_descriptor(v8::Isolate* isolate,v8::Local context) { + const auto evaluate=[&](const char* source){return v8::Script::Compile(context,v8::String::NewFromUtf8(isolate,source).ToLocalChecked()).ToLocalChecked()->Run(context).ToLocalChecked();}; + wgpu::Color color{};bool shape=false; + require(read_webgpu_color(isolate,context,evaluate("[1,.5,0,1]"),color,shape)&&shape&&color.g==.5,"Color sequence failed"); + require(read_webgpu_color(isolate,context,evaluate("({r:1,g:0,b:0,a:1})"),color,shape)&&shape&&color.r==1,"Color dictionary failed"); + require(read_webgpu_color(isolate,context,evaluate("[1,2]"),color,shape)&&!shape,"Color shape was lost"); + for(const char* source:{"{}","[1,2,3,Infinity]","({r:0,g:0,b:0,a:Symbol()})"}) { + v8::TryCatch caught(isolate);require(!read_webgpu_color(isolate,context,evaluate(source),color,shape)&&caught.HasCaught(),"Invalid color accepted"); + } + webgpu_render_pass_descriptor pass; + require(read_webgpu_render_pass_descriptor(isolate,context,evaluate("(()=>{globalThis.passOrder=[];return new Proxy({colorAttachments:[null],maxDrawCount:123},{get(o,k){passOrder.push(k);return o[k]}})})()"),pass) + && pass.colors.size()==1&&!pass.colors[0]&&pass.max_draw_count==123 + && evaluate("passOrder.join(',')==='label,colorAttachments,depthStencilAttachment,maxDrawCount,occlusionQuerySet,timestampWrites'")->IsTrue(),"Render pass descriptor order failed"); + pass.with_native([&](const auto& native){require(native.colorAttachmentCount==1&&!native.colorAttachments[0].view&&native.nextInChain,"Render pass native storage failed");}); + for(const char* source:{"{}","({colorAttachments:null})","({colorAttachments:[{}]})","({colorAttachments:[],occlusionQuerySet:{}})","({colorAttachments:[],timestampWrites:{}})"}) { + v8::TryCatch caught(isolate);pass.label="unchanged"; + require(!read_webgpu_render_pass_descriptor(isolate,context,evaluate(source),pass)&&caught.HasCaught()&&pass.label=="unchanged","Invalid pass accepted or committed"); + } +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_render_state.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_render_state.h new file mode 100644 index 000000000..93e20a4d6 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_render_state.h @@ -0,0 +1,55 @@ +#pragma once +#include "graphics/v8_webgpu_render_state.h" +inline void test_v8_webgpu_render_state(v8::Isolate* isolate,v8::Local context) { + const auto evaluate=[&](const char* source){return v8::Script::Compile(context,v8::String::NewFromUtf8(isolate,source).ToLocalChecked()).ToLocalChecked()->Run(context).ToLocalChecked();}; + wgpu::PrimitiveState primitive{}; + require(read_webgpu_primitive_state(isolate,context,v8::Undefined(isolate),primitive) + && primitive.topology==wgpu::PrimitiveTopology::TriangleList && primitive.frontFace==wgpu::FrontFace::CCW + && primitive.cullMode==wgpu::CullMode::None && primitive.stripIndexFormat==wgpu::IndexFormat::Undefined && !primitive.unclippedDepth,"Primitive defaults failed"); + require(read_webgpu_primitive_state(isolate,context,evaluate("(()=>{globalThis.renderOrder=[];return new Proxy({topology:'triangle-strip',stripIndexFormat:'uint16',frontFace:'cw',cullMode:'back',unclippedDepth:{}},{get(o,k){renderOrder.push(k);return o[k]}})})()"),primitive) + && primitive.topology==wgpu::PrimitiveTopology::TriangleStrip && primitive.stripIndexFormat==wgpu::IndexFormat::Uint16 + && primitive.unclippedDepth && evaluate("renderOrder.join(',')==='cullMode,frontFace,stripIndexFormat,topology,unclippedDepth'")->IsTrue(),"Primitive conversion order failed"); + wgpu::MultisampleState multisample{}; + require(read_webgpu_multisample_state(isolate,context,evaluate("({count:4.9,mask:'4294967295',alphaToCoverageEnabled:1})"),multisample) + && multisample.count==4 && multisample.mask==0xffffffff && multisample.alphaToCoverageEnabled,"Multisample integer conversion failed"); + for(const char* source:{"({count:NaN})","({count:4294967296})","({mask:-1})","({count:1n})"}) { + v8::TryCatch caught(isolate);multisample.count=17; + require(!read_webgpu_multisample_state(isolate,context,evaluate(source),multisample) && caught.HasCaught() && multisample.count==17,"Invalid multisample integer accepted or committed"); + } + wgpu::BlendState blend{}; + require(read_webgpu_blend_state(isolate,context,evaluate("({alpha:null,color:{srcFactor:'src-alpha',dstFactor:'one-minus-src-alpha',operation:'add'}})"),blend) + && blend.alpha.srcFactor==wgpu::BlendFactor::One && blend.alpha.dstFactor==wgpu::BlendFactor::Zero + && blend.color.srcFactor==wgpu::BlendFactor::SrcAlpha && blend.color.dstFactor==wgpu::BlendFactor::OneMinusSrcAlpha,"Blend conversion failed"); + for(const char* source:{"{}","({alpha:{}})","({alpha:{},color:{srcFactor:'undefined'}})","({alpha:{},color:{operation:Symbol()}})"}) { + v8::TryCatch caught(isolate);require(!read_webgpu_blend_state(isolate,context,evaluate(source),blend)&&caught.HasCaught(),"Invalid blend accepted"); + } + wgpu::StencilFaceState stencil{}; + require(read_webgpu_stencil_face(isolate,context,evaluate("({compare:'less-equal',depthFailOp:'increment-wrap',failOp:'replace',passOp:'invert'})"),stencil) + && stencil.compare==wgpu::CompareFunction::LessEqual && stencil.depthFailOp==wgpu::StencilOperation::IncrementWrap + && stencil.failOp==wgpu::StencilOperation::Replace && stencil.passOp==wgpu::StencilOperation::Invert,"Stencil conversion failed"); + wgpu::DepthStencilState depth{}; + require(read_webgpu_depth_stencil(isolate,context,evaluate("({format:'depth24plus'})"),depth) + && depth.depthWriteEnabled==wgpu::OptionalBool::Undefined && depth.depthCompare==wgpu::CompareFunction::Undefined + && depth.stencilFront.compare==wgpu::CompareFunction::Always && depth.stencilReadMask==0xffffffff,"Depth optional defaults lost"); + require(read_webgpu_depth_stencil(isolate,context,evaluate("({format:'depth32float',depthWriteEnabled:null,depthCompare:'less',depthBias:-2.9,depthBiasClamp:1.5,stencilBack:{passOp:'replace'}})"),depth) + && depth.depthWriteEnabled==wgpu::OptionalBool::False && depth.depthBias==-2 && depth.depthBiasClamp==1.5f + && depth.stencilBack.passOp==wgpu::StencilOperation::Replace,"Depth state conversion failed"); + for(const char* source:{"{}","({format:'depth24plus',depthBias:2147483648})","({format:'depth24plus',depthBias:-2147483649})", + "({format:'depth24plus',depthBiasClamp:Infinity})","({format:'depth24plus',depthBiasSlopeScale:1e100})"}) { + v8::TryCatch caught(isolate);depth.depthBias=123; + require(!read_webgpu_depth_stencil(isolate,context,evaluate(source),depth)&&caught.HasCaught()&&depth.depthBias==123,"Invalid depth state accepted or committed"); + } + webgpu_color_target target; + require(read_webgpu_color_target(isolate,context,evaluate("({format:'bgra8unorm'})"),target) + && target.format==wgpu::TextureFormat::BGRA8Unorm && !target.blend && target.write_mask==15,"Color target defaults failed"); + require(read_webgpu_color_target(isolate,context,evaluate("({format:'rgba8unorm',blend:{alpha:{},color:{srcFactor:'src-alpha'}},writeMask:16})"),target) + && target.blend && target.native().blend==&*target.blend && target.write_mask==16,"Color target storage or invalid-mask preservation failed"); + for(const char* source:{"{}","({format:'invalid'})","({format:'rgba8unorm',blend:null})","({format:'rgba8unorm',writeMask:-1})"}) { + v8::TryCatch caught(isolate);target.write_mask=123; + require(!read_webgpu_color_target(isolate,context,evaluate(source),target)&&caught.HasCaught()&&target.write_mask==123,"Invalid color target accepted or committed"); + } + auto throwing=evaluate("(()=>{globalThis.renderError={};return {get cullMode(){throw renderError},get topology(){throw 'wrong getter'}}})()"); + v8::TryCatch caught(isolate); + require(!read_webgpu_primitive_state(isolate,context,throwing,primitive) && caught.HasCaught() + && caught.Exception()->StrictEquals(evaluate("renderError")),"Render state exception replaced"); +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_shader_descriptor.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_shader_descriptor.h new file mode 100644 index 000000000..1a03fae09 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_shader_descriptor.h @@ -0,0 +1,25 @@ +#pragma once +#include "graphics/v8_webgpu_shader_descriptor.h" +inline void test_v8_webgpu_shader_descriptor(v8::Isolate* isolate,v8::Local context) { + const auto evaluate=[&](const char* source){return v8::Script::Compile(context,v8::String::NewFromUtf8(isolate,source).ToLocalChecked()).ToLocalChecked()->Run(context).ToLocalChecked();}; + const auto recognize=[](auto){return std::optional{};}; + webgpu_shader_descriptor output; + require(read_webgpu_shader_descriptor(isolate,context,evaluate("({code:'@compute @workgroup_size(1) fn main() {}'})"),output,recognize) + && output.label.empty() && output.hints.empty() && !output.code.empty(),"Shader descriptor defaults failed"); + require(read_webgpu_shader_descriptor(isolate,context,evaluate("({label:'s\\0\\ud800',code:null,compilationHints:new Set([{entryPoint:'main',layout:{toString(){return 'auto'}}},{entryPoint:'other'}])})"),output,recognize) + && output.label==std::string("s\0\xef\xbf\xbd",5) && output.code=="null" && output.hints.size()==2 + && output.hints[0].kind==webgpu_shader_hint::layout_kind::automatic && output.hints[1].kind==webgpu_shader_hint::layout_kind::omitted,"Shader hint union/default conversion failed"); + require(read_webgpu_shader_descriptor(isolate,context,evaluate("(()=>{globalThis.shaderOrder=[];return new Proxy({code:'',compilationHints:[new Proxy({entryPoint:'main'},{get(o,k){shaderOrder.push(k);return o[k]}})]},{get(o,k){shaderOrder.push(k);return o[k]}})})()"),output,recognize) + && evaluate("shaderOrder.join(',')==='label,code,compilationHints,entryPoint,layout'")->IsTrue(),"Shader dictionary access order failed"); + for(const char* source:{"undefined","null","{}","1","({code:Symbol()})","({code:'',compilationHints:null})", + "({code:'',compilationHints:[{}]})","({code:'',compilationHints:[{entryPoint:'main',layout:null}]})", + "({code:'',compilationHints:[{entryPoint:'main',layout:'bad'}]})","({code:'',compilationHints:[{entryPoint:Symbol()}]})"}) { + v8::TryCatch caught(isolate);output.code="unchanged"; + require(!read_webgpu_shader_descriptor(isolate,context,evaluate(source),output,recognize) && caught.HasCaught() + && output.code=="unchanged","Invalid shader descriptor accepted or partially committed"); + } + auto input=evaluate("(()=>{globalThis.shaderDescriptorError={};return {get code(){throw shaderDescriptorError},get compilationHints(){throw 'wrong getter'}}})()"); + v8::TryCatch caught(isolate); + require(!read_webgpu_shader_descriptor(isolate,context,input,output,recognize) && caught.HasCaught() + && caught.Exception()->StrictEquals(context->Global()->Get(context,v8::String::NewFromUtf8Literal(isolate,"shaderDescriptorError")).ToLocalChecked()),"Shader conversion exception replaced"); +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_texture_descriptor.h b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_texture_descriptor.h new file mode 100644 index 000000000..f31e99030 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_v8_webgpu_texture_descriptor.h @@ -0,0 +1,35 @@ +#pragma once +#include "graphics/v8_webgpu_texture_descriptor.h" +#include "graphics/v8_webgpu_texture_view_descriptor.h" +inline void test_v8_webgpu_texture_descriptor(v8::Isolate* isolate,v8::Local context) { + const auto evaluate=[&](const char* source){return v8::Script::Compile(context,v8::String::NewFromUtf8(isolate,source).ToLocalChecked()).ToLocalChecked()->Run(context).ToLocalChecked();}; + webgpu_texture_descriptor texture; + require(read_webgpu_texture_descriptor(isolate,context,evaluate("({size:[32],format:'rgba8unorm',usage:16})"),texture) + && texture.size.width==32&&texture.size.height==1&&texture.size.depthOrArrayLayers==1&&texture.valid_extent_shape,"Texture extent defaults failed"); + require(read_webgpu_texture_descriptor(isolate,context,evaluate("(()=>{globalThis.textureIteratorReads=0;return {size:{get [Symbol.iterator](){textureIteratorReads++;return function*(){yield 8;yield 4;yield 2}}},format:'rgba8unorm',usage:16,viewFormats:new Set(['rgba8unorm-srgb']),textureBindingViewDimension:'2d-array'}})()"),texture) + && texture.size.depthOrArrayLayers==2&&evaluate("textureIteratorReads===1")->IsTrue(),"Extent iterator was reacquired"); + texture.with_native([&](const auto& native) { + require(native.viewFormatCount==1&&native.viewFormats[0]==wgpu::TextureFormat::RGBA8UnormSrgb&&native.nextInChain,"Texture native storage failed"); + }); + require(read_webgpu_texture_descriptor(isolate,context,evaluate("(()=>{globalThis.textureOrder=[];return new Proxy({size:new Proxy({width:4},{get(o,k){textureOrder.push(typeof k==='symbol'?'iterator':k);return o[k]}}),format:'rgba8unorm',usage:16},{get(o,k){textureOrder.push(k);return o[k]}})})()"),texture) + && evaluate("textureOrder.join(',')==='label,dimension,format,mipLevelCount,sampleCount,size,iterator,depthOrArrayLayers,height,width,textureBindingViewDimension,usage,viewFormats'")->IsTrue(),"Texture dictionary order failed"); + require(read_webgpu_texture_descriptor(isolate,context,evaluate("({size:[1,2,3,4],format:'rgba8unorm',get usage(){globalThis.textureUsageRead=true;return 16}})"),texture) + && !texture.valid_extent_shape && evaluate("textureUsageRead")->IsTrue(),"Extent shape validation occurred before dictionary conversion finished"); + bool invalid_shape=false;try{texture.with_native([](const auto&){});}catch(const std::invalid_argument&){invalid_shape=true;} + require(invalid_shape,"Invalid extent reached native descriptor"); + for(const char* source:{"{}","({size:[],format:'invalid',usage:16})","({size:{},format:'rgba8unorm',usage:16})", + "({size:[-1],format:'rgba8unorm',usage:16})","({size:[1],format:'rgba8unorm'})","({size:[1],format:'rgba8unorm',usage:16,viewFormats:['invalid']})"}) { + v8::TryCatch caught(isolate);texture.label="unchanged"; + require(!read_webgpu_texture_descriptor(isolate,context,evaluate(source),texture)&&caught.HasCaught()&&texture.label=="unchanged","Invalid texture descriptor accepted or committed"); + } + webgpu_texture_view_descriptor view; + require(read_webgpu_texture_view_descriptor(isolate,context,v8::Undefined(isolate),view)&&!view.mip_count&&!view.layer_count&&view.swizzle==u"rgba","View defaults failed"); + require(read_webgpu_texture_view_descriptor(isolate,context,evaluate("({mipLevelCount:4294967295,swizzle:'bgra'})"),view)&&view.mip_count==4294967295u,"Explicit view sentinel lost"); + view.with_native([&](const auto& native){require(native.dimension==static_cast(0xffffffffu)&&native.nextInChain,"Invalid explicit count became unspecified");}); + require(read_webgpu_texture_view_descriptor(isolate,context,evaluate("({swizzle:'\\ud800gba'})"),view)&&view.swizzle[0]==0xd800,"Swizzle DOMString surrogate changed"); + for(const char* source:{"({mipLevelCount:-1})","({aspect:'invalid'})","({swizzle:Symbol()})"}) { + v8::TryCatch caught(isolate);view.label="unchanged"; + require(!read_webgpu_texture_view_descriptor(isolate,context,evaluate(source),view)&&caught.HasCaught()&&view.label=="unchanged","Invalid view accepted or committed"); + } + +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/graphics_wake_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/graphics_wake_tests.cpp new file mode 100644 index 000000000..fa920741a --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/graphics_wake_tests.cpp @@ -0,0 +1,23 @@ +#include "graphics/engine_wake.h" +#include +#include +using namespace webscene::graphics; +void require(bool value) { if (!value) throw std::runtime_error("requirement failed"); } +int main() { + auto wake=std::make_shared(); + wake->signal(); + require(wake->wait_for(std::chrono::milliseconds(0),[] { return false; })); + require(!wake->wait_for(std::chrono::milliseconds(0),[] { return false; })); + std::promise waiting; + auto future=std::async(std::launch::async,[wake,&waiting] { + waiting.set_value(); + return wake->wait_for(std::chrono::seconds(5),[] { return false; }); + }); + waiting.get_future().wait(); + wake->signal(); + require(future.get()); + std::shared_ptr late=wake; + wake.reset(); + late->signal(); + std::cout << "engine wake latching, headless progress and retained signal lifetime passed\n"; +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/media_decode_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/media_decode_tests.cpp new file mode 100644 index 000000000..41aad5056 --- /dev/null +++ b/experiments/WebScene.NativeEngine.Probe/tests/media_decode_tests.cpp @@ -0,0 +1,368 @@ +#include "audio_graph.h" +#include "decode_service.h" +#include "media_session.h" +#include +#if defined(__APPLE__) +#include "../native/graphics/iosurface_canvas_images.h" +#endif +#include +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#endif +using namespace webscene::media; +void check(bool value, const char *message) { + if (!value) + throw std::runtime_error(message); +} +template void rejects(F function) { + bool failed = false; + try { + function(); + } catch (const std::exception &) { + failed = true; + } + check(failed, "Expected decode rejection"); +} +std::vector wav() { + std::vector data(44 + 480 * 4); + auto word = [&](size_t i, uint16_t v) { + data[i] = v; + data[i + 1] = v >> 8; + }; + auto dword = [&](size_t i, uint32_t v) { + word(i, v); + word(i + 2, v >> 16); + }; + memcpy(data.data(), "RIFF", 4); + dword(4, data.size() - 8); + memcpy(data.data() + 8, "WAVEfmt ", 8); + dword(16, 16); + word(20, 1); + word(22, 2); + dword(24, 48000); + dword(28, 192000); + word(32, 4); + word(34, 16); + memcpy(data.data() + 36, "data", 4); + dword(40, data.size() - 44); + for (size_t i = 0; i < 480; ++i) { + word(44 + i * 4, 16384); + word(46 + i * 4, static_cast(-8192)); + } + return data; +} +std::shared_ptr load(const char *path, const char *extension) { + std::ifstream file(path, std::ios::binary); + check(bool(file), "Fixture missing"); + auto source = std::make_shared(); + source->extension = extension; + source->bytes.assign(std::istreambuf_iterator(file), {}); + return source; +} +void cadence_tests() { + playback_control clock; + clock.set(0, 1, true, 1, false); + clock.observe_output(clock.sequence.load(), 2., 100.); + check(std::abs(clock.time_at(100.01) - 2.01) < 1e-6, "Video did not follow rendered audio clock"); + clock.set(5, 1, false, 1, false); + check(clock.time_at(100.02) == 5, "Stale audio feedback survived pause/seek"); + for (const double fps : {24., 30., 60., 24000./1001., 60000./1001.}) { + for (const double hz : {60., 120., 60000./1001.}) { + std::deque queue; + video_frame current; + size_t next = 1; + // Ten minutes with bounded decode ahead. Selection must remain + // timestamp-correct without accumulated rounding error. + for (size_t refresh = 0; refresh < size_t(hz * 600); ++refresh) { + const auto time = double(refresh) / hz; + while (queue.size() < 4) { + video_frame frame; + frame.timestamp = double(next++) / fps; + queue.push_back(std::move(frame)); + } + select_video_frame(queue, time, current); + const auto expected = std::floor((time + 1e-7) * fps) / fps; + check(std::abs(current.timestamp - expected) < 1e-6, "Video cadence drift/skipped frame"); + } + } + } + std::deque queue; + video_frame current; + for (double pts : {.01, .05, .12}) { + video_frame frame; frame.timestamp = pts; queue.push_back(frame); + } + check(select_video_frame(queue, .009, current) == 0, "Future VFR frame displayed early"); + check(select_video_frame(queue, .08, current) == 2 && current.timestamp == .05, + "Late deadline did not discard obsolete frames"); + check(select_video_frame(queue, .10, current) == 0 && current.timestamp == .05, + "Decode stall must retain complete frame"); + check(select_video_frame(queue, .12, current) == 1 && current.timestamp == .12, + "VFR next interval not selected"); +} +int main(int argc, char **argv) { + try { + cadence_tests(); + auto bytes = wav(); + auto result = decode_audio(bytes); + check(result.channels == 2 && result.sample_rate == 48000 && result.frames() == 480, + "WAV format/frame count"); + for (size_t i = 0; i < result.frames(); ++i) { + check(std::abs(result.samples[i * 2] - .5f) < 1e-6, "Left channel wrong"); + check(std::abs(result.samples[i * 2 + 1] + .25f) < 1e-6, "Right channel wrong"); + } + rejects([&] { decode_audio({}); }); + rejects([&] { decode_audio(std::vector{1, 2, 3}); }); + decode_limits small; + small.decoded_audio_bytes = 16; + rejects([&] { decode_audio(bytes, small); }); + small = {}; + small.encoded_bytes = 4; + rejects([&] { decode_audio(bytes, small); }); + std::stop_source cancelled; + cancelled.request_stop(); + rejects([&] { decode_audio(bytes, {}, cancelled.get_token()); }); + std::future future; + { + decode_service service; + auto source = std::make_shared(); + source->bytes = bytes; + future = service.audio(source); + check(future.get().frames() == 480, "Asynchronous decode failed"); + service.close(); + rejects([&] { service.audio(source); }); + } + // Service destruction settles pending work instead of stranding futures. + { + decode_service service; + auto source = std::make_shared(); + source->bytes = bytes; + future = service.audio(source); + } + check(future.wait_for(std::chrono::seconds(0)) == std::future_status::ready, + "Future stranded by teardown"); + try { + future.get(); + } catch (const std::exception &) { + } + { + media_session session; + auto source = std::make_shared(); + source->bytes = bytes; + session.load(source, false); + auto ready = [&] { + for (int i = 0; i < 500; ++i) { + auto frame = session.read(); + if (frame.ready && !frame.seeking) + return frame; + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + throw std::runtime_error("Media session timeout"); + }; + check(ready().audio->frames() == 480, "Session PCM"); + for (int i = 0; i < 100; ++i) + session.seek(i * .00001); + check(ready().ready, "Coalesced seek"); + auto replacement = std::make_shared(); + replacement->bytes = bytes; + session.load(replacement, false); + check(ready().generation == 2, "Stale media generation"); + session.close(); + rejects([&] { session.seek(0); }); + } + if (argc > 1 && std::string(argv[1]) != "-") { + auto source = load(argv[1], ".wav"); + decode_service service; + auto audio = service.audio(source).get(); + check(audio.frames() > audio.sample_rate, "Demo score too short"); + double energy = 0; + for (float x : audio.samples) { + check(std::isfinite(x), "Nonfinite demo audio"); + energy += x * x; + } + check(energy > 1, "Silent demo audio"); + std::cout << "Original score: " << audio.frames() << " frames, " << audio.sample_rate << " Hz, " + << audio.channels << " channels\n"; + } +#if defined(__APPLE__) + if (argc > 2) { + auto source = load(argv[2], ".mp4"); + // Decode-ahead must remain bounded and must not advance the public + // frame until a presentation opportunity selects it. + { + media_session session; + session.load(source, true); + auto buffered = [&] { + for (int retry = 0; retry < 1000; ++retry) { + const auto state = session.read(); + if (!state.error.empty()) throw std::runtime_error(state.error); + if (state.ready && state.buffered_frames >= 2) return state; + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + throw std::runtime_error("Video decode-ahead timeout"); + }; + auto initial = buffered(); + check(initial.buffered_frames <= 4, "Unbounded video queue"); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + check(session.read().video.timestamp == initial.video.timestamp, + "Decoder completion advanced presentation"); + for (int tick = 0; tick < 60; ++tick) { + buffered(); + const auto target = double(tick) / 120.; + const auto state = session.present(target); + check(state.video.timestamp <= target + 1e-7, "Video frame selected early"); + check(target - state.video.timestamp < .05, "Queued video did not advance"); + } + session.seek(.1); + buffered(); + check(session.read().video.timestamp < .15, "Seek retained stale queued video"); + } + video_frame first, later; + { + decode_service service; + first = service.video(source, 0).get(); + later = service.video(source, 1).get(); + } + check(first.native_surface && later.native_surface && first.width && first.height, + "No native video surface"); + check(later.timestamp >= .95 && later.timestamp > first.timestamp, + "Seek did not advance decoded frame"); + auto checksum = [](const video_frame &f) { + auto pixel = static_cast(f.native_surface.get()); + CVPixelBufferLockBaseAddress(pixel, kCVPixelBufferLock_ReadOnly); + auto p = static_cast(CVPixelBufferGetBaseAddress(pixel)); + uint64_t hash = 1469598103934665603ULL; + for (size_t y = 0; y < f.height; ++y) + for (size_t x = 0; x < f.width * 4; ++x) + hash = (hash ^ p[y * CVPixelBufferGetBytesPerRow(pixel) + x]) * 1099511628211ULL; + CVPixelBufferUnlockBaseAddress(pixel, kCVPixelBufferLock_ReadOnly); + return hash; + }; + auto decoder = open_video(*source); + auto end = decoder->read(decoder->duration()); + check(end.native_surface && end.timestamp > 0, "End seek failed"); + auto back = decoder->read(0); + check(back.timestamp < .1, "Backward seek failed"); + using namespace webscene::graphics; + std::optional consumer; + std::weak_ptr lifetime; + { + iosurface_canvas_images pool(128ULL * 1024 * 1024); + auto decoded = decoder->read(.5); + lifetime = decoded.native_surface; + auto color = iosurface_color::adopt_bgra8( + static_cast(decoded.native_surface.get()), decoded.native_surface); + image_metadata metadata; + metadata.canvas = 1; + metadata.producer_timeline = 1; + metadata.producer_value = 1; + metadata.allocation_generation = 1; + metadata.content_serial = 1; + metadata.width = decoded.width; + metadata.height = decoded.height; + metadata.format = image_format::bgra8_unorm; + auto image = pool.adopt(metadata, std::move(color)); + check(bool(image), "No adopted decoder image"); + consumer.emplace(image->begin_consumer().value()); + decoder.reset(); + decoded = {}; + image.reset(); + check(!lifetime.expired(), "Decoder frame recycled before consumer completion"); + check(iosurface_canvas_images::resolve(*consumer).borrowed_handle() != nullptr, + "Adopted surface missing"); + } + { + struct wake final : completion_wake { + int count{}; + void signal() noexcept override { ++count; } + }; + auto signal = std::make_shared(); + iosurface_canvas_images pool(128ULL * 1024 * 1024, signal); + auto color = iosurface_color::adopt_bgra8( + static_cast(first.native_surface.get()), first.native_surface); + image_metadata m{1, 0, 1, 1, 1, 1, first.width, first.height, image_format::bgra8_unorm}; + std::vector held; + for (int i = 0; i < 3; ++i) { + ++m.content_serial; + held.push_back(pool.adopt(m, color).value()); + } + auto consuming = held.back().begin_consumer(); + held.pop_back(); + check(!pool.adopt(m, color), "Reused surface before GPU completion"); + auto before = signal->count; + consuming->complete(); + consuming.reset(); + check(signal->count > before, "GPU completion did not wake blocked image publication"); + check(bool(pool.adopt(m, color)), "Latest frame could not publish after backpressure"); + } + check(!lifetime.expired(), "Pool destruction released a live GPU frame"); + consumer->complete(); + consumer.reset(); + check(lifetime.expired(), "Completed GPU frame leaked"); + check(checksum(first) != checksum(later), "Video frames did not change"); + check(CVPixelBufferGetIOSurface(static_cast(first.native_surface.get())) != + nullptr, + "Missing shareable IOSurface"); + rejects([&] { decode_video_frame(*source, -1); }); + rejects([&] { decode_video_frame(*source, 0, {}, cancelled.get_token()); }); + std::cout << "Original video: " << first.width << "x" << first.height << ", timestamps " + << first.timestamp << " and " << later.timestamp + << ", retained IOSurface frames survive decoder teardown\n"; + } +#endif +#if defined(__APPLE__) + if (argc > 3) { + auto source = load(argv[3], ".mp4"); + auto decoder = open_video(*source); + auto pcm = std::make_shared(decoder->audio()); + check(pcm->sample_rate == 48000 && pcm->frames() >= 144000, "Flash/click audio decode"); + audio_graph graph(false); + auto input = graph.create(audio_graph::kind::source), + bus = graph.create(audio_graph::kind::stream); + auto track = graph.capture(bus); + auto control = std::make_shared(); + control->set(0, 1, true, 1, false); + control->epoch = 0; + graph.set_source(input, pcm, control); + graph.connect(input, bus); + graph.resume(); + std::vector mix(144000 * 2), packet(256); + double maximum = 0; + for (uint32_t offset = 0; offset < 144000; offset += 128) { + auto size = std::min(128U, 144000 - offset); + graph.render_at(mix.data() + offset * 2, size, double(offset) / 48000); + auto result = track->read(std::span(packet.data(), size * 2)); + check(result.frames == size && result.first_frame == offset && !result.dropped, + "Recording time continuity"); + for (uint32_t i = 0; i < size; ++i) + mix[(offset + i) * 2] = packet[i * 2]; + } + for (int second = 0; second < 3; ++second) { + auto frame = decoder->read(second); + auto pixel = static_cast(frame.native_surface.get()); + CVPixelBufferLockBaseAddress(pixel, kCVPixelBufferLock_ReadOnly); + auto value = static_cast(CVPixelBufferGetBaseAddress(pixel))[0]; + CVPixelBufferUnlockBaseAddress(pixel, kCVPixelBufferLock_ReadOnly); + check(value > 200, "Missing numbered flash"); + uint32_t onset = second * 48000; + while (onset < static_cast((second + .05) * 48000) && + std::abs(mix[onset * 2]) < .1f) + ++onset; + double drift = std::abs(double(onset) / 48000 - frame.timestamp); + maximum = std::max(maximum, drift); + check(drift < .01, "Decoded video / recorded audio drift exceeds 10ms"); + } + std::cout << "Flash/click decoded presentation versus recorded PCM max drift: " << maximum * 1000 + << " ms (10 ms limit; excludes physical display/device latency)\n"; + } +#endif + std::cout << "Native media decode tests passed\n"; + return 0; + } catch (const std::exception &e) { + std::cerr << e.what() << '\n'; + return 1; + } +} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_browser_dom_tests.inc b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_browser_dom_tests.inc index 298a46228..02844753d 100644 --- a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_browser_dom_tests.inc +++ b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_browser_dom_tests.inc @@ -1391,6 +1391,12 @@ void test_async_save_acknowledgement_publishes_without_pointer_input() }); })() )JS", "async-save-publication-setup.js"); + // Host scripts and input have separate queues. An input/frame consumption + // counter is not a barrier for installing the document and click listener. + require(evaluate(engine, + "document.getElementById('save').isConnected && __saveState.activations === 0", + "async-save-publication-ready.js") == "true", + "async-save fixture setup did not complete before activation"); animation_frame_and_wait(engine, 0.0, 9101U); const auto consumed_before = consumed_input_count(engine); diff --git a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_canvas_tests.inc b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_canvas_tests.inc index 4c8bcfafc..b0dbcdf40 100644 --- a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_canvas_tests.inc +++ b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_canvas_tests.inc @@ -1,3 +1,112 @@ +void test_canvas_prefix_hint_tracks_append_and_reset() +{ + { + webscene_native::native_document document; + auto& target = document.create_element("canvas"); + auto& source = document.create_element("canvas"); + auto& nested = document.create_element("canvas"); + document.append_child(document.body(), target); + webscene_canvas_command fill{}; fill.kind = 20; + target.mutable_canvas().commands.push_back(fill); + source.mutable_canvas().commands.push_back(fill); + nested.mutable_canvas().commands.push_back(fill); + document.layout(640, 360); + std::vector layers; + std::vector commands; + std::vector strings; + std::vector bytes; + const auto capture_dependencies = [&] { + document.build_canvas_display_lists(layers, commands, strings, bytes); + return layers.size(); + }; + require(capture_dependencies() == 1, "detached canvas retained without dependency"); + webscene_canvas_command image{}; image.kind = 27; image.resource_id = source.id; + target.mutable_canvas().commands.push_back(image); + image.resource_id = nested.id; + source.mutable_canvas().commands.push_back(image); + require(capture_dependencies() == 3, "appended transitive canvas dependency was lost"); + require(capture_dependencies() == 3, "cached transitive canvas dependency was lost"); + ++target.mutable_canvas().generation; + target.mutable_canvas().commands.clear(); + target.mutable_canvas().commands.push_back(fill); + require(capture_dependencies() == 1, "canvas reset retained stale dependencies"); + } + auto* engine=webscene_engine_create(0); + require(engine!=nullptr,"canvas prefix engine creation failed"); + resize(engine,640,360,1); + struct capture { uint64_t generation{};uint32_t flags{},node_id{}; + std::vector commands;std::vector strings; }; + const auto read=[&](auto accept) { + for(int attempt=0;attempt<250;++attempt) { + const webscene_scene_view_v3* lease=nullptr; + const webscene_scene_acquire_options_v3 options{sizeof(options),3,WEBSCENE_SCENE_CAPABILITY_CANVAS_CHECKPOINTS}; + if(webscene_engine_acquire_latest_scene_v3(engine,&options,&lease)==WEBSCENE_SCENE_ACQUIRE_SUCCESS) { + const auto* scene=lease->cpu_view; + capture result; + if(scene->header.canvas_layer_count==1) { + const auto& layer=scene->canvas_layers[0]; + result.generation=layer.generation;result.flags=layer.flags; + result.node_id=layer.node_id; + result.commands.assign(scene->canvas_commands+layer.command_offset, + scene->canvas_commands+layer.command_offset+layer.command_count); + for(uint32_t index=0;indexstrings[layer.string_offset+index]; + result.strings.emplace_back(scene->string_bytes+value.byte_offset,value.byte_length); + } + } + webscene_scene_acknowledge_v3(lease);webscene_scene_release_v3(lease); + if(!result.commands.empty() && accept(result))return result; + } + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + fail("canvas prefix scene was not published"); + }; + execute_and_wait(engine,R"JS(document.body.innerHTML=''; + globalThis.prefixCanvas=document.body.firstElementChild; + globalThis.prefixContext=prefixCanvas.getContext('2d');prefixContext.fillRect(0,0,20,10);)JS","prefix-setup.js"); + auto previous=read([](const capture&){return true;}); + for(int frame=0;frame<4;++frame) { + execute_and_wait(engine,R"JS(prefixContext.clearRect(0,0,19.5,10); + prefixContext.fillStyle='blue';prefixContext.fillRect(1,1,3,3);)JS","prefix-append.js"); + const auto next=read([&](const capture& value){return value.commands.size()>previous.commands.size();}); + require((next.flags&WEBSCENE_CANVAS_LAYER_UNCHANGED_PREFIX)!=0,"append lost prefix hint across a DOM-unchanged diff"); + require(next.generation==previous.generation,"partial clear reset canvas generation"); + require(std::memcmp(next.commands.data(),previous.commands.data(),previous.commands.size()*sizeof(webscene_canvas_command))==0, + "canvas prefix hint covered modified commands"); + require(next.strings.size()>=previous.strings.size() + && std::equal(previous.strings.begin(),previous.strings.end(),next.strings.begin()), + "canvas prefix hint covered modified strings"); + previous=next; + } + execute_and_wait(engine,"prefixCanvas.width=20;prefixContext.fillRect(0,0,20,10)","prefix-reset.js"); + auto reset=read([&](const capture& value){return value.generation!=previous.generation;}); + require((reset.flags&WEBSCENE_CANVAS_LAYER_UNCHANGED_PREFIX)==0,"bitmap reset claimed an unchanged prefix"); + execute_and_wait(engine,"prefixContext.clearRect(0,0,20,10);prefixContext.fillRect(1,1,3,3)","prefix-full-clear.js"); + auto cleared=read([&](const capture& value){return value.generation!=reset.generation;}); + require((cleared.flags&WEBSCENE_CANVAS_LAYER_UNCHANGED_PREFIX)==0,"full-overwrite compaction claimed an unchanged prefix"); + constexpr auto payload="native checkpoint protocol test"; + require(webscene_engine_submit_canvas_checkpoint_v3(engine,cleared.node_id,cleared.generation, + static_cast(cleared.commands.size()),payload,std::strlen(payload)),"checkpoint submission failed"); + execute_and_wait(engine,"prefixContext.fillText('checkpoint-tail',2,3)","checkpoint-tail.js"); + // Checkpoint installation can publish before the following script runs. + // Wait for the appended drawing command as well as the new generation; + // otherwise an intermediate checkpoint-only scene races this assertion. + auto checkpoint=read([&](const capture& value){ + return value.generation!=cleared.generation && value.commands.size()>1; + }); + require(checkpoint.commands.front().kind==WEBSCENE_CANVAS_COMMAND_RASTER_CHECKPOINT,"checkpoint prefix was not installed"); + require(checkpoint.strings.front()==payload,"checkpoint payload changed"); + require(std::find(checkpoint.strings.begin(),checkpoint.strings.end(),"checkpoint-tail")!=checkpoint.strings.end(), + "commands appended after checkpoint capture lost their resources"); + require(webscene_engine_acquire_latest_scene(engine)==nullptr,"legacy consumer accepted a raster checkpoint"); + require(webscene_engine_submit_canvas_checkpoint_v3(engine,cleared.node_id,cleared.generation, + static_cast(cleared.commands.size()),payload,std::strlen(payload)),"stale checkpoint could not be queued"); + execute_and_wait(engine,"prefixContext.fillRect(2,2,3,3)","checkpoint-stale.js"); + auto after_stale=read([&](const capture& value){return value.commands.size()>checkpoint.commands.size();}); + require(after_stale.generation==checkpoint.generation,"stale checkpoint replaced a newer canvas generation"); + webscene_engine_destroy(engine); +} + struct canvas_text_measurement_probe final { std::string text; std::string family; diff --git a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_frame_scheduling_tests.inc b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_frame_scheduling_tests.inc index 1bd40c0fe..7c06e4daf 100644 --- a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_frame_scheduling_tests.inc +++ b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_frame_scheduling_tests.inc @@ -835,11 +835,13 @@ void test_detached_dom_wrappers_do_not_permanently_root_nodes(webscene_engine* e if (after.dom_nodes <= before.dom_nodes + 8U) break; std::this_thread::sleep_for(std::chrono::milliseconds(2)); } - require( - after.dom_nodes <= before.dom_nodes + 8U, - "unreachable detached DOM nodes remained permanently owned after V8 collection (before=" + if (after.dom_nodes > before.dom_nodes + 8U) { + require(false, + "unreachable detached DOM nodes remained after the idle collection deadline (before=" + std::to_string(before.dom_nodes) - + ", after=" + std::to_string(after.dom_nodes) + ")"); + + ", after=" + std::to_string(after.dom_nodes) + "): " + + diagnostics(engine)); + } #if defined(WEBSCENE_NATIVE_ENGINE_CERTIFICATION) require( diagnostic_value("detached-dom-release-batches", release_batches_before + 2U) @@ -1522,6 +1524,12 @@ void test_animation_frame_callback_list_timestamp_and_cancellation(webscene_engi cancelled = requestAnimationFrame(timestamp => { __frameBatchEvents.push(['cancelled', timestamp]); }); + // A larger admitted batch must remain indivisible even when host + // evaluation work arrives while its callbacks are running. + for (let i = 0; i < 40; ++i) requestAnimationFrame(() => { + const started = performance.now(); + while (performance.now() - started < 0.2) {} + }); requestAnimationFrame(timestamp => { __frameBatchEvents.push(['last', timestamp]); }); diff --git a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_input_tests.inc b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_input_tests.inc index c53727dc9..d29057813 100644 --- a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_input_tests.inc +++ b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_input_tests.inc @@ -178,6 +178,10 @@ void test_host_pointer_exit_clears_tooltip_without_another_move() // Many chart gestures use document mousemove/up rather than DOM capture. pointer_move(engine, 20, 20, 9U); + // The down event flushes the pending frame-paced entry move. Install + // counters afterward so worker timing cannot count that setup movement + // as part of the outside-host drag being tested. + pointer_button(engine, WEBSCENE_INPUT_POINTER_DOWN, 20, 20, 10U, true); execute(engine, R"JS( globalThis.dragMoves = 0; globalThis.dragUps = 0; @@ -186,13 +190,13 @@ void test_host_pointer_exit_clears_tooltip_without_another_move() )JS", "host-exit-document-drag.js"); require(evaluate(engine, "dragUps", "host-exit-document-drag-ready.js") == "0", "document drag fixture not ready"); - pointer_button(engine, WEBSCENE_INPUT_POINTER_DOWN, 20, 20, 10U, true); exit(11U, 1U); pointer_move(engine, -20, -20, 12U, true); pointer_button(engine, WEBSCENE_INPUT_POINTER_UP, -20, -20, 13U, false); - require(evaluate(engine, "[dragMoves, dragUps, getComputedStyle(tip).display]", - "host-exit-document-drag-result.js") == R"([1,1,"none"])", - "host exit interrupted document-level drag movement or release"); + const auto drag_result = evaluate(engine, "[dragMoves, dragUps, getComputedStyle(tip).display]", + "host-exit-document-drag-result.js"); + require(drag_result == R"([1,1,"none"])", + "host exit interrupted document-level drag movement or release: " + drag_result); pointer_move(engine, 20, 20, 14U); execute(engine, R"JS( @@ -1205,7 +1209,36 @@ void test_pressed_drag_moves_remain_dispatchable_after_threshold() void test_mouse_moves_are_raf_aligned_at_compositor_cadence() { - auto* engine = webscene_engine_create(0); + struct pointer_handler_gate { + std::mutex mutex; + std::condition_variable changed; + bool entered{false}; + bool released{false}; + bool timed_out{false}; + } gate; + webscene_engine_options options{}; + options.struct_size = sizeof(options); + options.text_measure_user_data = &gate; + options.text_measure_callback = +[]( + void* user_data, const char* text, size_t length, const char*, size_t, + float font_size, int32_t, float, float, webscene_text_metrics* metrics) -> uint8_t { + if (text != nullptr && std::string_view(text, length) == "pointer-handoff-gate") { + auto& state = *static_cast(user_data); + std::unique_lock lock(state.mutex); + state.entered = true; + state.changed.notify_all(); + // Test-only rendezvous inside the synchronous pointer handler. + // Production text callbacks must not block. This holds the handler + // open without depending on relative JS/test-thread sleep durations. + if (!state.changed.wait_for(lock, std::chrono::seconds(5), + [&] { return state.released; })) state.timed_out = true; + } + metrics->advance_width = static_cast(length) * font_size * 0.5F; + metrics->ascent = font_size * 0.75F; + metrics->descent = font_size * 0.25F; + return 1U; + }; + auto* engine = webscene_engine_create_with_options(&options); require(engine != nullptr, "rAF-aligned mouse engine creation failed"); resize(engine, 640, 360, 1U); execute(engine, R"JS( @@ -1324,6 +1357,79 @@ void test_mouse_moves_are_raf_aligned_at_compositor_cadence() && after.coalesced_pointer_move_inputs - before.coalesced_pointer_move_inputs == 2U, "rAF-aligned mouse metrics did not report coalesced device samples"); + + // The host can enqueue the RAF belonging to a boundary after a newer + // device sample has already been retained. That old RAF must not release + // the sample; only a subsequent display boundary or discrete input may. + pointer_move(engine, 70, 20, 9U); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + animation_frame(engine, 1016.666, 10U); + wait_for_consumed_inputs(engine, after.consumed_inputs + 1U, + "already-observed RAF was not consumed"); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + require(consumed_input_count(engine) == after.consumed_inputs + 1U, + "already-observed RAF prematurely released a newer pointer sample"); + webscene_engine_observe_compositor_frame(engine, 1025.0); + wait_for_consumed_inputs(engine, after.consumed_inputs + 2U, + "next boundary did not release the retained sample after an old RAF"); + require(evaluate(engine, "globalThis.__pacedPointerMoves", + "native-frame-paced-old-raf.js") == "[30,40,60,70]", + "old RAF handling lost or duplicated pointer movement"); + + execute_and_wait(engine, R"JS(document.body.firstElementChild.addEventListener('pointermove', () => { + document.createElement('canvas').getContext('2d').measureText('pointer-handoff-gate'); + requestAnimationFrame(() => {}); + }, {once:true}))JS", "native-pointer-demand-handoff-setup.js"); + const auto handoff_inputs = consumed_input_count(engine); + pointer_move(engine, 80, 20, 11U); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + webscene_engine_observe_compositor_frame(engine, 1041.666); + { + std::unique_lock lock(gate.mutex); + require(gate.changed.wait_for(lock, std::chrono::seconds(5), + [&] { return gate.entered; }), "pointer handler did not enter its handoff gate"); + } + require((webscene_engine_requires_animation_frame(engine) & 4U) != 0, + "in-flight pointer handler temporarily lost its frame demand"); + // This sample arrives while the previous handler is busy, before the + // next boundary. Dequeuing it after that boundary must not make it wait + // for yet another refresh. No script barrier is used to flush the input. + pointer_move(engine, 90, 20, 12U); + webscene_engine_observe_compositor_frame(engine, 1058.333); + { + std::lock_guard lock(gate.mutex); + require(!gate.timed_out, "pointer handler handoff gate timed out"); + gate.released = true; + } + gate.changed.notify_all(); + wait_for_consumed_inputs(engine, handoff_inputs + 2, + "pointer handler did not finish its demand handoff"); + require(evaluate(engine, "globalThis.__pacedPointerMoves.slice(-2)", + "native-frame-paced-busy-worker.js") == "[80,90]", + "busy worker deferred an eligible sample into an extra display interval"); + require((webscene_engine_requires_animation_frame(engine) & 1U) != 0, + "pointer demand was cleared before publishing the handler's RAF demand"); +#if defined(_WIN32) + execute_and_wait(engine, R"JS( + document.body.firstElementChild.addEventListener('pointermove', event => { + event.target.style.cursor = 'ew-resize'; + requestAnimationFrame(() => { event.target.style.cursor = 'ns-resize'; }); + }, {once:true}); + )JS", "native-paced-cursor-setup.js"); + const auto cursor_inputs = consumed_input_count(engine); + pointer_move(engine, 100, 20, 13U); + webscene_engine_observe_compositor_frame(engine, 1075.0); + animation_frame(engine, 1075.0, 14U); + wait_for_consumed_inputs(engine, cursor_inputs + 2, + "paced cursor input and frame were not consumed"); + const auto cursor_deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2); + while (webscene_engine_get_cursor(engine) != WEBSCENE_CURSOR_NS_RESIZE + && std::chrono::steady_clock::now() < cursor_deadline) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + require(webscene_engine_get_cursor(engine) == WEBSCENE_CURSOR_NS_RESIZE, + "paced cursor was not resolved from the final animation/publication layout"); +#endif webscene_engine_destroy(engine); } diff --git a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_resource_tests.inc b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_resource_tests.inc index 48a04fa89..9cf7dcc89 100644 --- a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_resource_tests.inc +++ b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_resource_tests.inc @@ -1883,8 +1883,10 @@ void test_process_wide_resource_load_single_flight() const auto origin = "https://resource-single-flight-" + unique_suffix + ".test/"; const auto document_url = origin + "index.html"; const auto script_url = origin + "shared.js"; + const auto warmup_url = origin + "warmup.html"; resource_server server{ .content = { + {warmup_url, "Warmup"}, {document_url, "" ""}, @@ -1915,6 +1917,15 @@ void test_process_wide_resource_load_single_flight() std::array results; for (size_t index = 0U; index < engine_count; ++index) { workers[index] = std::thread([&, index] { + // Engine creation is lazy: initialize each V8 realm on its owning + // thread before the release barrier. Otherwise a slow cold isolate + // can miss the entire resource flight (observed on Windows CI). + // Keep the waiter assertion below: this test must exercise overlap, + // not merely sequential cache hits. + require(webscene_engine_load_url(engines[index], warmup_url.data(), warmup_url.size()) != 0, + "resource single-flight warmup navigation was rejected"); + require(evaluate(engines[index], "1", "single-flight-warmup.js") == "1", + "resource single-flight warmup was rejected"); ready.fetch_add(1U, std::memory_order_release); while (!start.load(std::memory_order_acquire)) { std::this_thread::yield(); @@ -1969,6 +1980,14 @@ void test_process_wide_resource_load_single_flight() memory.v8_external_script_source_bytes >= shared_script.size() + shared_ascii_script.size(), "large UTF-8 script source was copied into the V8 heap"); + // Memory telemetry is sampled, not refreshed synchronously by its getter. + // A warmed realm can finish navigation within the five-second sample window. + for (int attempt = 0; memory.process_resource_mapped_cache_bytes < shared_script.size() + && attempt < 600; ++attempt) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + evaluate(engines[index], "1", "single-flight-memory-sample.js"); + webscene_engine_get_memory_metrics(engines[index], &memory); + } mapped_resource_bytes = std::max( mapped_resource_bytes, memory.process_resource_mapped_cache_bytes); @@ -1977,9 +1996,9 @@ void test_process_wide_resource_load_single_flight() mapped_resource_bytes >= shared_script.size(), "large persisted script resource was not retained as file-backed memory"); require( - server.requests.load(std::memory_order_relaxed) == 3, - "identical resources were loaded more than once across four engines"); - require(leaders == 3U, "resource loads did not elect one producer per URL"); + server.requests.load(std::memory_order_relaxed) == 4, + "identical resources (including warmup) were loaded more than once across four engines"); + require(leaders == 4U, "resource loads did not elect one producer per URL"); require(waiters > 0U, "concurrent resource loads did not record waiters"); require(memory_hits > 0U, "resource waiters did not consume shared results"); require(shared_bytes > 0U, "resource waiters did not share immutable response bytes"); diff --git a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_tests.cpp index 45f850c46..24f31d183 100644 --- a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_tests.cpp +++ b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_tests.cpp @@ -124,6 +124,7 @@ int main() if (const auto* filter = std::getenv("WEBSCENE_NATIVE_ENGINE_TEST_FILTER"); filter != nullptr) { const auto selected = std::string_view(filter); + if (selected == "async-save-publication") { test_async_save_acknowledgement_publishes_without_pointer_input(); return 0; } if (selected == "youtube-embed") { test_youtube_embed_fallback(); return 0; } if (selected == "table-cell-copy") { test_table_cell_click_copies_text_to_host(); return 0; } if (selected == "resource-failure-diagnostics") { test_resource_failure_diagnostics(); return 0; } @@ -340,6 +341,7 @@ int main() auto* engine = webscene_engine_create(0); require(engine != nullptr, "inheritance engine creation failed"); test_dimension_custom_property_inheritance(engine); + test_geometry_variable_positions(engine); webscene_engine_destroy(engine); return 0; } @@ -375,6 +377,7 @@ int main() webscene_engine_destroy(focused_engine); return 0; } + if (selected == "canvas-prefix-hint") { test_canvas_prefix_hint_tracks_append_and_reset(); return 0; } if (selected == "canvas-text-metrics") { test_canvas_text_metrics_use_host_font_axes(); return 0; @@ -558,6 +561,7 @@ int main() test_mixed_continuous_input_backlog_is_coalesced(); test_pressed_drag_moves_remain_dispatchable_after_threshold(); test_mouse_moves_are_raf_aligned_at_compositor_cadence(); + test_canvas_prefix_hint_tracks_append_and_reset(); test_controlled_switch_native_activation_matches_browser_semantics(); test_tradingview_switch_repeated_transitions_publish_dense_scenes(); test_loaded_document_keeps_html_and_body_cascade_distinct(); diff --git a/packaging/WebScene.NativeEngine.Runtime/WebScene.NativeEngine.Runtime.csproj b/packaging/WebScene.NativeEngine.Runtime/WebScene.NativeEngine.Runtime.csproj index 1d5d7f405..039f36b91 100644 --- a/packaging/WebScene.NativeEngine.Runtime/WebScene.NativeEngine.Runtime.csproj +++ b/packaging/WebScene.NativeEngine.Runtime/WebScene.NativeEngine.Runtime.csproj @@ -2,6 +2,9 @@ net8.0 true + false + false + true false true WebScene.NativeEngine.Runtime.$(WebSceneNativeEngineRid) @@ -50,6 +53,7 @@ $(WebSceneNativePackageMetadataDir)V8-LICENSE.txt $(WebSceneNativePackageMetadataDir)ICU-LICENSE.txt $(WebSceneNativePackageMetadataDir)IXWebSocket-LICENSE.txt + $(WebSceneNativePackageMetadataDir)Miniaudio-LICENSE.txt $(WebSceneNativePackageMetadataDir)MbedTLS-LICENSE.txt $(WebSceneNativePackageMetadataDir)HTML-PARSER-THIRD-PARTY-NOTICES.md @@ -84,6 +88,8 @@ + + + @@ -109,6 +117,8 @@ Text="The configured WebScene native engine does not exist: $(WebSceneNativeEnginePath)" /> + + diff --git a/packaging/WebScene.NativeEngine.Runtime/WebScene.NativeEngine.Runtime.targets b/packaging/WebScene.NativeEngine.Runtime/WebScene.NativeEngine.Runtime.targets index 782f6047b..84ac9b113 100644 --- a/packaging/WebScene.NativeEngine.Runtime/WebScene.NativeEngine.Runtime.targets +++ b/packaging/WebScene.NativeEngine.Runtime/WebScene.NativeEngine.Runtime.targets @@ -41,6 +41,8 @@ + + diff --git a/scripts/build-native-engine-runtime.ps1 b/scripts/build-native-engine-runtime.ps1 index 923fb8c0e..ee40f55e8 100644 --- a/scripts/build-native-engine-runtime.ps1 +++ b/scripts/build-native-engine-runtime.ps1 @@ -10,6 +10,7 @@ param( [string] $V8Root, [string] $V8Workspace, [string] $V8Revision = "15.3.10", + [string] $GraphicsSdk, [ValidateSet("legacy", "html5ever")] [string] $HtmlParser = "html5ever", @@ -43,6 +44,12 @@ $buildVariant = "-$HtmlParser-$CssParser-$SelectorParser-$DomBindings-$V8Snapsho $buildVariant += if ($ThinLto) { "-thinlto" } else { "" } $buildVariant += if ($PartitionAlloc) { "-partitionalloc" } else { "" } $buildVariant += "-inspector" +$graphicsCMake = "OFF" +if ($GraphicsSdk) { + $GraphicsSdk = (Resolve-Path $GraphicsSdk).Path + $graphicsCMake = "ON" + $buildVariant += "-graphics" +} if (($CssParser -eq "cssparser" -or $SelectorParser -eq "servo") -and $HtmlParser -ne "html5ever") { throw "Servo CSS components require -HtmlParser html5ever." } @@ -199,6 +206,9 @@ $buildDir = if ([string]::IsNullOrWhiteSpace($BuildDirectory)) { & cmake -S (Join-Path $repoRoot "experiments/WebScene.NativeEngine.Probe") -B $buildDir ` -A $(if ($cpu -eq "arm64") { "ARM64" } else { "x64" }) ` -DWEBSCENE_NATIVE_ENGINE_ENABLE_V8=ON ` + -DWEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA=ON ` + "-DWEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS=$graphicsCMake" ` + "-DWEBSCENE_GRAPHICS_SDK_ROOT=$GraphicsSdk" ` -DWEBSCENE_NATIVE_ENGINE_ENABLE_V8_INSPECTOR=ON ` -DWEBSCENE_V8_POINTER_COMPRESSION=ON ` -DWEBSCENE_V8_POINTER_COMPRESSION_SHARED_CAGE=ON ` @@ -227,11 +237,15 @@ if ($V8Snapshot -eq "bootstrap") { Copy-Item $snapshotPath (Join-Path $buildDir "Release/webscene_bootstrap_snapshot.bin") -Force Copy-Item $snapshotMetadataPath (Join-Path $buildDir "Release/webscene_bootstrap_snapshot.meta") -Force } -& ctest --test-dir $buildDir -C Release --output-on-failure +$ctestArgs = @('--test-dir', $buildDir, '-C', 'Release', '--output-on-failure') +if ($env:WEBSCENE_NATIVE_SKIP_HARDWARE_TESTS -eq '1') { $ctestArgs += @('-LE', 'hardware') } +& ctest @ctestArgs if ($LASTEXITCODE -ne 0) { throw "Native WebScene engine tests failed." } $nativePath = Join-Path $buildDir "Release/webscene_native_engine.dll" if (-not (Test-Path $nativePath)) { throw "Native engine build did not produce '$nativePath'." } +$miniaudioLicense = Join-Path $buildDir "webscene-miniaudio-LICENSE" +if (-not (Test-Path $miniaudioLicense)) { throw "Miniaudio license is missing from the media-enabled native build." } $ixWebSocketLicense = Join-Path $buildDir "_deps/webscene_ixwebsocket-src/LICENSE.txt" $mbedTlsLicense = Join-Path $buildDir "_deps/webscene_mbedtls-src/LICENSE" if (-not (Test-Path $ixWebSocketLicense)) { @@ -247,6 +261,8 @@ $packArguments = @( "-p:WebSceneNativeEngineRid=$Rid", "-p:WebSceneNativeEnginePath=$nativePath", "-p:WebSceneNativeEngineIcuDataPath=$icuData", + "-p:WebSceneNativeEngineMedia=true", + "-p:WebSceneNativeEngineMiniaudioLicensePath=$miniaudioLicense", "-p:WebSceneNativeEngineV8LicensePath=$v8License", "-p:WebSceneNativeEngineIcuLicensePath=$icuLicense", "-p:WebSceneNativeEngineIXWebSocketLicensePath=$ixWebSocketLicense", @@ -273,6 +289,13 @@ if ($HtmlParser -eq "html5ever") { $packArguments += "-p:WebSceneNativeEngineHtmlParserNoticesPath=$(Join-Path $repoRoot 'experiments/WebScene.NativeEngine.Probe/native/html_parser/THIRD-PARTY-NOTICES.md')" } $packArguments += "-p:PackageVersion=$PackageVersion" +if ($GraphicsSdk) { + $graphicsStage = Join-Path $buildDir ("graphics-package-" + [guid]::NewGuid().ToString('N')) + & python (Join-Path $repoRoot 'eng/graphics/stage-runtime.py') --sdk $GraphicsSdk ` + --native (Join-Path $buildDir 'Release') --rid $Rid --output $graphicsStage + if ($LASTEXITCODE -ne 0) { throw 'Failed to stage verified graphics dependencies.' } + $packArguments += "-p:WebSceneGraphicsPackageProps=$(Join-Path $graphicsStage 'GraphicsPackage.props')" +} & dotnet @packArguments if ($LASTEXITCODE -ne 0) { throw "Failed to pack the native WebScene engine." } @@ -298,6 +321,11 @@ try { --output (Join-Path $buildDir "wpt-results") if ($LASTEXITCODE -ne 0) { throw "Required native compatibility profile failed." } + & dotnet run --project (Join-Path $repoRoot "tests/WebPlatformSubset/runner/WebScene.WebPlatformSubset.Runner.csproj") ` + -c Release -- --manifest (Join-Path $repoRoot "tests/WebPlatformSubset/webscene-media-runtime-profile.json") ` + --selection required --native-library $packageNativePath --output (Join-Path $buildDir "media-contracts") + if ($LASTEXITCODE -ne 0) { throw "Required native media/audio contracts failed." } + $previousNativeEnginePath = $env:WEBSCENE_NATIVE_ENGINE_PATH $previousTestNativeLibrary = $env:WEBSCENE_TEST_NATIVE_LIBRARY $env:WEBSCENE_TEST_NATIVE_LIBRARY = $packageNativePath @@ -354,6 +382,9 @@ try { if ($LASTEXITCODE -ne 0) { throw "Failed to build the native runtime package consumer." } $consumerOutput = Join-Path $consumerDir "bin/Release/net8.0/$Rid" $copiedAssets = @("webscene_native_engine.dll", "icudtl.dat", "webscene-native-runtime.json") + if ($GraphicsSdk) { + $copiedAssets += @("webgpu_dawn.dll", "d3dcompiler_47.dll", "libEGL.dll", "libGLESv2.dll", "webscene-graphics-runtime.json") + } if ($V8Snapshot -eq "bootstrap") { $copiedAssets += @("webscene_bootstrap_snapshot.bin", "webscene_bootstrap_snapshot.meta") } diff --git a/scripts/build-native-engine-runtime.sh b/scripts/build-native-engine-runtime.sh index 218fd2fda..bfc37a780 100755 --- a/scripts/build-native-engine-runtime.sh +++ b/scripts/build-native-engine-runtime.sh @@ -18,10 +18,11 @@ thin_lto=false upstream_v8=false disable_wasm=false partition_alloc=false +graphics_sdk= cmake_build_type=Release usage() { - echo "Usage: $0 --rid osx-arm64|osx-x64|linux-arm64|linux-x64 [--output DIR] [--package-version VERSION] [--v8-root DIR] [--v8-output-root DIR] [--v8-workspace DIR] [--v8-revision REVISION] [--html-parser legacy|html5ever] [--css-parser legacy|cssparser] [--selector-parser legacy|servo] [--dom-bindings legacy|generated] [--v8-snapshot none|bootstrap] [--cmake-build-type Release|RelWithDebInfo] [--upstream-v8] [--thin-lto] [--disable-wasm] [--partition-alloc]" >&2 + echo "Usage: $0 --rid osx-arm64|osx-x64|linux-arm64|linux-x64 [--output DIR] [--package-version VERSION] [--v8-root DIR] [--v8-output-root DIR] [--v8-workspace DIR] [--v8-revision REVISION] [--html-parser legacy|html5ever] [--css-parser legacy|cssparser] [--selector-parser legacy|servo] [--dom-bindings legacy|generated] [--v8-snapshot none|bootstrap] [--cmake-build-type Release|RelWithDebInfo] [--upstream-v8] [--thin-lto] [--disable-wasm] [--partition-alloc] [--graphics-sdk DIR]" >&2 } while (($# > 0)); do @@ -43,6 +44,7 @@ while (($# > 0)); do --thin-lto) thin_lto=true; shift ;; --disable-wasm) disable_wasm=true; shift ;; --partition-alloc) partition_alloc=true; shift ;; + --graphics-sdk) graphics_sdk="${2:-}"; shift 2 ;; -h|--help) usage; exit 0 ;; *) echo "Unknown option: $1" >&2; usage; exit 1 ;; esac @@ -88,6 +90,12 @@ fi v8_configuration=Release build_variant="-$html_parser-$css_parser-$selector_parser-$dom_bindings-$v8_snapshot" +graphics_cmake=OFF +if [[ -n "$graphics_sdk" ]]; then + graphics_sdk="$(cd "$graphics_sdk" && pwd)" + graphics_cmake=ON + build_variant+=-graphics +fi if [[ "$cmake_build_type" == RelWithDebInfo ]]; then build_variant+=-symbols fi @@ -298,6 +306,9 @@ cmake_args=( -B "$build_dir" -DCMAKE_BUILD_TYPE="$cmake_build_type" -DWEBSCENE_NATIVE_ENGINE_ENABLE_V8=ON + -DWEBSCENE_NATIVE_ENGINE_ENABLE_MEDIA=ON + -DWEBSCENE_NATIVE_ENGINE_ENABLE_GRAPHICS="$graphics_cmake" + -DWEBSCENE_GRAPHICS_SDK_ROOT="$graphics_sdk" -DWEBSCENE_NATIVE_ENGINE_ENABLE_V8_INSPECTOR=ON -DWEBSCENE_V8_POINTER_COMPRESSION=ON -DWEBSCENE_V8_POINTER_COMPRESSION_SHARED_CAGE=ON @@ -367,7 +378,13 @@ fi cmake "${cmake_args[@]}" cmake --build "$build_dir" --config "$cmake_build_type" --parallel cmake -E copy_if_different "$icu_data" "$build_dir/icudtl.dat" -ctest --test-dir "$build_dir" -C "$cmake_build_type" --output-on-failure +ctest_args=(--test-dir "$build_dir" -C "$cmake_build_type" --output-on-failure) +# Hosted package builders prove linkage and CPU contracts; real GPU execution +# remains mandatory on the explicitly enrolled hardware qualification runners. +if [[ "${WEBSCENE_NATIVE_SKIP_HARDWARE_TESTS:-0}" == 1 ]]; then + ctest_args+=(-LE hardware) +fi +ctest "${ctest_args[@]}" native_path="$build_dir/$native_name" if [[ ! -f "$native_path" ]]; then @@ -390,6 +407,11 @@ if [[ "$v8_snapshot" == bootstrap \ echo "Native engine build did not produce its bootstrap snapshot sidecars." >&2 exit 1 fi +miniaudio_license="$build_dir/webscene-miniaudio-LICENSE" +if [[ ! -f "$miniaudio_license" ]]; then + echo "Miniaudio license is missing from the media-enabled native build." >&2 + exit 1 +fi ixwebsocket_license="$build_dir/_deps/webscene_ixwebsocket-src/LICENSE.txt" if [[ ! -f "$ixwebsocket_license" ]]; then echo "IXWebSocket license was not found at '$ixwebsocket_license'." >&2 @@ -410,6 +432,8 @@ pack_args=( "-p:WebSceneNativeEngineRid=$rid" "-p:WebSceneNativeEnginePath=$native_path" "-p:WebSceneNativeEngineIcuDataPath=$icu_data" + "-p:WebSceneNativeEngineMedia=true" + "-p:WebSceneNativeEngineMiniaudioLicensePath=$miniaudio_license" "-p:WebSceneNativeEngineV8LicensePath=$v8_license" "-p:WebSceneNativeEngineIcuLicensePath=$icu_license" "-p:WebSceneNativeEngineIXWebSocketLicensePath=$ixwebsocket_license" @@ -428,6 +452,12 @@ pack_args=( "-p:WebSceneNativeEngineV8Snapshot=$v8_snapshot" "-p:WebSceneNativeEngineConfiguration=$cmake_build_type" ) +if [[ -n "$graphics_sdk" ]]; then + graphics_stage_root="$(mktemp -d "$build_dir/graphics-package.XXXXXX")" + python3 "$repo_root/eng/graphics/stage-runtime.py" --sdk "$graphics_sdk" --native "$build_dir" \ + --rid "$rid" --output "$graphics_stage_root/assets" + pack_args+=("-p:WebSceneGraphicsPackageProps=$graphics_stage_root/assets/GraphicsPackage.props") +fi if [[ "$v8_snapshot" == bootstrap ]]; then pack_args+=( "-p:WebSceneNativeEngineSnapshotPath=$snapshot_path" @@ -459,6 +489,16 @@ WEBSCENE_VARIABLE_FONT_INSTANCING=1 dotnet run \ --native-cache-directory "$build_dir/code-cache" \ --output "$build_dir/wpt-results" +media_profiles=(webscene-media-runtime-profile.json) +if [[ "$expected_kernel" == Darwin ]]; then + media_profiles+=(webscene-macos-video-runtime-profile.json) +fi +for profile in "${media_profiles[@]}"; do + dotnet run --project "$repo_root/tests/WebPlatformSubset/runner/WebScene.WebPlatformSubset.Runner.csproj" \ + -c Release -- --manifest "$repo_root/tests/WebPlatformSubset/$profile" --selection required \ + --native-library "$package_native_path" --output "$build_dir/$profile-results" +done + WEBSCENE_TEST_NATIVE_LIBRARY="$package_native_path" \ WEBSCENE_VARIABLE_FONT_INSTANCING=1 \ dotnet test "$repo_root/tests/WebScene.Backend.Avalonia.Tests/WebScene.Backend.Avalonia.Tests.csproj" \ @@ -498,6 +538,14 @@ NUGET_PACKAGES="$consumer_root/packages" dotnet restore \ NUGET_PACKAGES="$consumer_root/packages" dotnet build \ "$consumer_dir/consumer.csproj" -c Release -r "$rid" --no-restore copied_assets=("$native_name" icudtl.dat webscene-native-runtime.json) +if [[ -n "$graphics_sdk" ]]; then + graphics_suffix=.so + if [[ "$expected_kernel" == Darwin ]]; then graphics_suffix=.dylib; fi + copied_assets+=("libwebgpu_dawn$graphics_suffix" webscene-graphics-runtime.json) + if [[ "$expected_kernel" != Darwin ]]; then + copied_assets+=("libEGL$graphics_suffix" "libGLESv2$graphics_suffix") + fi +fi if [[ "$v8_snapshot" == bootstrap ]]; then copied_assets+=(webscene_bootstrap_snapshot.bin webscene_bootstrap_snapshot.meta) fi diff --git a/scripts/compare-native-resize-cadence.py b/scripts/compare-native-resize-cadence.py index 9996a4746..07833600c 100755 --- a/scripts/compare-native-resize-cadence.py +++ b/scripts/compare-native-resize-cadence.py @@ -17,7 +17,7 @@ def read_samples(directory: pathlib.Path, minimum: int) -> list[dict[str, Any]]: raise RuntimeError( f"{directory}: expected at least {minimum} JSON samples, found {len(paths)}") samples = [json.loads(path.read_text()) for path in paths] - if any(sample.get("schema") != "webscene-native-resize-cadence-v1" for sample in samples): + if any(sample.get("schema") != "webscene-native-resize-cadence-v2" for sample in samples): raise RuntimeError(f"{directory}: contains a non-resize-cadence sample") return samples @@ -54,7 +54,8 @@ def main() -> int: parser.add_argument("--output", required=True, type=pathlib.Path) parser.add_argument("--minimum-samples", type=int, default=10) parser.add_argument("--require-material-improvement", action="store_true") - parser.add_argument("--require-vsync", action="store_true") + parser.add_argument("--require-vsync", action="store_true", help="Require actual presentation evidence; headless samples cannot pass") + parser.add_argument("--require-cpu-cadence", action="store_true") args = parser.parse_args() control = read_samples(args.control_dir, args.minimum_samples) @@ -73,15 +74,15 @@ def main() -> int: "renderLatencyMilliseconds.p95", "publicationLatencyMilliseconds.p95", "publicationToRenderLatencyMilliseconds.p95", - "presentationIntervalMilliseconds.p95", - "presentationIntervalMilliseconds.maximum", + "drawCallbackIntervalMilliseconds.p95", + "drawCallbackIntervalMilliseconds.maximum", "dispatchMilliseconds.average", "normalizedProcessCpuPercent", "layoutPassesPerAppliedResize", ) higher_is_better = ( "renderedFramesPerSecond", - "presentationFramesPerSecond", + "drawCallbackCompletionsPerSecond", ) metrics: dict[str, Any] = {} failures: list[str] = [] @@ -112,17 +113,22 @@ def main() -> int: if args.require_material_improvement and not material: failures.append("candidate did not meet the material-improvement threshold") vsync_passes = sum( - sample["practicalVsyncGate"]["passed"] is True for sample in candidate) - if args.require_vsync and vsync_passes != len(candidate): + sample["cpuCadenceGate"]["passed"] is True for sample in candidate) + if args.require_cpu_cadence and vsync_passes != len(candidate): failures.append( - f"practical vsync gate passed in {vsync_passes}/{len(candidate)} candidate runs") + f"CPU cadence gate passed in {vsync_passes}/{len(candidate)} candidate runs") + + if args.require_vsync: + failures.append("Physical vsync is not measurable by this headless draw-callback benchmark") report = { - "schema": "webscene-native-resize-comparison-v1", + "measurementScope": "headless-cpu-draw-callback", + "physicalPresentationVerified": False, + "schema": "webscene-native-resize-comparison-v2", "controlSamples": len(control), "candidateSamples": len(candidate), "materialImprovement": material, - "candidateVsyncPasses": vsync_passes, + "candidateCpuCadencePasses": vsync_passes, "metrics": metrics, "failures": failures, "passed": not failures, diff --git a/scripts/test-windows-kestrel.ps1 b/scripts/test-windows-kestrel.ps1 new file mode 100644 index 000000000..c654c0c1e --- /dev/null +++ b/scripts/test-windows-kestrel.ps1 @@ -0,0 +1,65 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] [string] $NativeLibrary, + [string] $OutputDirectory, + [switch] $VSync, + [ValidateRange(1, 20)] [int] $StartupRepetitions = 3 +) + +$ErrorActionPreference = 'Stop' +if (-not [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform( + [System.Runtime.InteropServices.OSPlatform]::Windows)) { throw 'This probe requires Windows.' } +$repoRoot = Split-Path -Parent $PSScriptRoot +$NativeLibrary = (Resolve-Path -LiteralPath $NativeLibrary).Path +if (-not $OutputDirectory) { $OutputDirectory = Join-Path $repoRoot 'artifacts/windows-kestrel-verification' } +$OutputDirectory = [IO.Path]::GetFullPath($OutputDirectory) +New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null +$probeProject = Join-Path $repoRoot 'experiments/WebScene.GpuHost.Probe' +$probe = Join-Path $probeProject 'bin/Release/net10.0/WebScene.GpuHost.Probe.dll' +$fixture = Join-Path $repoRoot 'tests/GraphicsCompatibility/fixtures/Kestrel-CAD.zip' +& python (Join-Path $repoRoot 'tests/GraphicsCompatibility/prepare-kestrel.py') +if ($LASTEXITCODE -ne 0) { throw 'Immutable Kestrel fixture verification failed.' } +& dotnet build $probeProject -c Release *> (Join-Path $OutputDirectory 'build.log') +if ($LASTEXITCODE -ne 0) { throw 'GPU host probe build failed; see build.log.' } + +$workloads = @( + @{ Name = 'pan'; Arguments = @('--pan-kestrel'); Marker = 'Kestrel pan workload validated' }, + @{ Name = 'sidebar-resize'; Arguments = @('--sidebar-kestrel', '--resize-kestrel'); Marker = 'Kestrel sidebar workload validated' }, + @{ Name = 'continuous-resize'; Arguments = @('--continuous-resize-kestrel'); Marker = 'Kestrel continuous window resize workload validated' }, + @{ Name = 'edit'; Arguments = @('--edit-kestrel'); Marker = 'Kestrel edit:' } +) +for ($index = 1; $index -le $StartupRepetitions; $index++) { + $workloads += @{ Name = "startup-$index"; Arguments = @(); Marker = 'Kestrel WebGPU startup check passed' } +} +$previousLibrary = $env:WEBSCENE_TEST_NATIVE_LIBRARY +$results = @() +try { + $env:WEBSCENE_TEST_NATIVE_LIBRARY = $NativeLibrary + foreach ($workload in $workloads) { + $log = Join-Path $OutputDirectory ($workload.Name + '.log') + $hostMode = if ($VSync) { '--webgpu-vsync' } else { '--webgpu-document' } + $probeArguments = @($probe, $hostMode, '--kestrel', $fixture, '--verify-kestrel') + $workload.Arguments + & dotnet @probeArguments *> $log + $exitCode = $LASTEXITCODE + $passed = $exitCode -eq 0 -and + (Select-String -LiteralPath $log -SimpleMatch 'Kestrel WebGPU startup check passed' -Quiet) -and + (Select-String -LiteralPath $log -SimpleMatch $workload.Marker -Quiet) + $results += @{ workload = $workload.Name; exitCode = $exitCode; passed = $passed; log = $log } + Write-Host "$($workload.Name): $(if ($passed) {'passed'} else {'FAILED'})" + if (-not $passed) { throw "Kestrel workload failed: $log" } + } +} +finally { + $env:WEBSCENE_TEST_NATIVE_LIBRARY = $previousLibrary + @{ + scope = 'Windows Avalonia WebGPU workloads and awaited shutdown; not physical presentation or epic qualification' + capturedAtUtc = [DateTime]::UtcNow.ToString('o') + nativeLibrary = $NativeLibrary + hostMode = $(if ($VSync) { 'compositor-clock' } else { 'default' }) + incrementalCanvasGpu = ($env:WEBSCENE_INCREMENTAL_CANVAS_GPU -eq '1') + asynchronousCanvasPreparation = ($env:WEBSCENE_ASYNC_CANVAS_PREPARATION -eq '1') + nativeSha256 = (Get-FileHash -LiteralPath $NativeLibrary -Algorithm SHA256).Hash.ToLowerInvariant() + fixtureSha256 = (Get-FileHash -LiteralPath $fixture -Algorithm SHA256).Hash.ToLowerInvariant() + workloads = $results + } | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDirectory 'results.json') +} diff --git a/scripts/test_compare_native_resize_cadence.py b/scripts/test_compare_native_resize_cadence.py new file mode 100644 index 000000000..0b0cf4392 --- /dev/null +++ b/scripts/test_compare_native_resize_cadence.py @@ -0,0 +1,54 @@ +"""Keep headless callback measurements separate from physical presentation gates.""" +import contextlib +import importlib.util +import io +import json +from pathlib import Path +import sys +import tempfile +import unittest +from unittest.mock import patch + +spec = importlib.util.spec_from_file_location("resize_compare", Path(__file__).with_name("compare-native-resize-cadence.py")) +compare = importlib.util.module_from_spec(spec) +spec.loader.exec_module(compare) + + +class ResizeMeasurementScopeTests(unittest.TestCase): + def test_legacy_ambiguous_measurement_is_rejected(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + (root / "sample.json").write_text(json.dumps({"schema": "webscene-native-resize-cadence-v1"})) + with self.assertRaises(RuntimeError): + compare.read_samples(root, 1) + + def test_good_cpu_cadence_cannot_pass_physical_vsync(self): + sample = {"schema": "webscene-native-resize-cadence-v2", + "cpuCadenceGate": {"passed": True}, + "renderedFramesPerSecond": 60, + "drawCallbackCompletionsPerSecond": 60, + "normalizedProcessCpuPercent": 10, + "layoutPassesPerAppliedResize": 1, + "dispatchMilliseconds": {"average": 1}} + for name in ["renderLatencyMilliseconds", "publicationLatencyMilliseconds", + "publicationToRenderLatencyMilliseconds", "drawCallbackIntervalMilliseconds"]: + sample[name] = {"p95": 1, "maximum": 2} + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + for name in ["control", "candidate"]: + (root / name).mkdir() + (root / name / "sample.json").write_text(json.dumps(sample)) + output = root / "comparison.json" + args = ["compare", "--control-dir", str(root / "control"), + "--candidate-dir", str(root / "candidate"), "--minimum-samples", "1", + "--output", str(output), "--require-vsync"] + with patch.object(sys, "argv", args), contextlib.redirect_stdout(io.StringIO()): + self.assertEqual(compare.main(), 1) + report = json.loads(output.read_text()) + self.assertFalse(report["passed"]) + self.assertFalse(report["physicalPresentationVerified"]) + self.assertEqual(report["candidateCpuCadencePasses"], 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/verify-aot-warnings.py b/scripts/verify-aot-warnings.py new file mode 100644 index 000000000..b0d7426de --- /dev/null +++ b/scripts/verify-aot-warnings.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +"""Reject trimming/AOT warnings in production source reached by a published host. + +Probe-only diagnostics and third-party warnings remain visible in the build log; +this gate makes no claim about unreachable APIs or those external assemblies. +""" +import pathlib +import re +import sys + +lines = pathlib.Path(sys.argv[1]).read_text(encoding="utf-8", errors="replace").splitlines() +failures = [line for line in lines if re.search(r"(?:^|[/\\])src[/\\].*?: (?:Trim analysis |AOT analysis )warning IL\d+:", line)] +if failures: + print("Production NativeAOT/trim warnings must be resolved:\n" + "\n".join(failures)) + raise SystemExit(1) +print("No NativeAOT/trim warnings in reachable production source.") diff --git a/scripts/verify-release-packages.py b/scripts/verify-release-packages.py index f72cf4c4d..a566fb9a7 100755 --- a/scripts/verify-release-packages.py +++ b/scripts/verify-release-packages.py @@ -130,6 +130,34 @@ def required_text(name: str) -> str: } +def validate_graphics_runtime(archive: zipfile.ZipFile, rid: str) -> None: + expected = { + "osx-arm64": {"libwebgpu_dawn.dylib"}, + "win-x64": {"webgpu_dawn.dll", "libEGL.dll", "libGLESv2.dll", "d3dcompiler_47.dll"}, + }.get(rid) + if expected is None: + return # Existing Linux packages remain non-GPU in this milestone. + prefix = f"runtimes/{rid}/native/" + name = prefix + "webscene-graphics-runtime.json" + if name not in archive.namelist(): + raise RuntimeError(f"{rid}: missing graphics runtime manifest") + manifest = json.loads(archive.read(name)) + if manifest.get("rid") != rid or set(manifest.get("libraries", {})) != expected: + raise RuntimeError(f"{rid}: graphics runtime library set is incomplete or unexpected") + components = {"dawn"} if rid == "osx-arm64" else {"dawn", "angle"} + if set(manifest.get("components", {})) != components: + raise RuntimeError(f"{rid}: incorrect graphics SDK components") + targets = "buildTransitive/graphics/WebScene.NativeEngine.Graphics.targets" + if targets not in archive.namelist(): + raise RuntimeError(f"{rid}: missing transitive graphics publish targets") + for library, digest in manifest["libraries"].items(): + asset = prefix + library + if asset not in archive.namelist() or hashlib.sha256(archive.read(asset)).hexdigest() != digest: + raise RuntimeError(f"{rid}: missing or corrupt graphics asset {library}") + if rid == "osx-arm64" and any(prefix + name in archive.namelist() for name in ("libEGL.dylib", "libGLESv2.dylib")): + raise RuntimeError("macOS Metal runtime must not ship ANGLE") + + def validate_native_runtime( package: pathlib.Path, runtime_identifier: str, @@ -139,6 +167,7 @@ def validate_native_runtime( f"runtimes/{runtime_identifier}/native/webscene-native-runtime.json" ) with zipfile.ZipFile(package) as archive: + validate_graphics_runtime(archive, runtime_identifier) if manifest_name not in archive.namelist(): raise RuntimeError(f"{package}: missing {manifest_name}") manifest = json.loads(archive.read(manifest_name)) diff --git a/src/WebScene.Backend.Avalonia/AssemblyInfo.cs b/src/WebScene.Backend.Avalonia/AssemblyInfo.cs index a7179b839..e16068aa1 100644 --- a/src/WebScene.Backend.Avalonia/AssemblyInfo.cs +++ b/src/WebScene.Backend.Avalonia/AssemblyInfo.cs @@ -4,3 +4,5 @@ [assembly: InternalsVisibleTo("WebScene.NativeEngine.Benchmarks")] [assembly: InternalsVisibleTo("WebScene.WebPlatformSubset.Runner")] [assembly: InternalsVisibleTo("WebScene.GlyphDiagnostics")] + +[assembly: InternalsVisibleTo("WebScene.GpuHost.Probe")] diff --git a/src/WebScene.Backend.Avalonia/AvaloniaHostServices.cs b/src/WebScene.Backend.Avalonia/AvaloniaHostServices.cs index f8c268ea3..d2b59d4e5 100644 --- a/src/WebScene.Backend.Avalonia/AvaloniaHostServices.cs +++ b/src/WebScene.Backend.Avalonia/AvaloniaHostServices.cs @@ -12,6 +12,9 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Input; +#if WEBSCENE_AVALONIA12 +using Avalonia.Input.Platform; +#endif using Avalonia.Platform; using Avalonia.Threading; using WebScene.Core; @@ -348,20 +351,21 @@ public WebSceneTextResource LoadText(in WebSceneResourceRequest request) var specifier = request.Specifier; if (specifier.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) { - return new WebSceneTextResource(specifier, DecodeDataUri(specifier), specifier, null); + return new WebSceneTextResource(specifier, DecodeDataUri(specifier), specifier, null) { BinaryContent = request.Kind == WebSceneResourceKind.Data ? new ReadOnlyMemory(DecodeBinaryDataUri(specifier)) : (ReadOnlyMemory?)null }; } var resolved = ResolveAddress(specifier, request.BaseAddress); if (resolved.IsFile) { - return LoadFile(resolved.LocalPath); + return LoadFile(resolved.LocalPath, request.Kind == WebSceneResourceKind.Data); } if (resolved.Scheme.Equals("avares", StringComparison.OrdinalIgnoreCase)) { using var stream = AssetLoader.Open(resolved); - using var reader = new StreamReader(stream); - return new WebSceneTextResource(resolved.ToString(), reader.ReadToEnd(), resolved.ToString(), null); + using var memory = new MemoryStream();stream.CopyTo(memory);var bytes=memory.ToArray(); + return new WebSceneTextResource(resolved.ToString(), Encoding.UTF8.GetString(bytes), resolved.ToString(), null) + { BinaryContent = request.Kind == WebSceneResourceKind.Data ? new ReadOnlyMemory(bytes) : (ReadOnlyMemory?)null }; } if (resolved.Scheme is "http" or "https") @@ -386,11 +390,11 @@ public WebSceneTextResource LoadText(in WebSceneResourceRequest request) WebSceneTextResource resource; if (isSafeRead && TryResolveMountedResource(resolved, out var mountedPath)) { - resource = LoadFile(mountedPath); + resource = LoadFile(mountedPath, request.Kind == WebSceneResourceKind.Data); } else if (isSafeRead && TryResolvePackagedResource(resolved.AbsolutePath, out var packagedPath)) { - resource = LoadFile(packagedPath); + resource = LoadFile(packagedPath, request.Kind == WebSceneResourceKind.Data); } else { @@ -464,14 +468,16 @@ public WebSceneTextResource LoadText(in WebSceneResourceRequest request) null, response.StatusCode); } + var binaryBytes = request.Kind == WebSceneResourceKind.Data ? response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult() : null; resource = new WebSceneTextResource( resolved.ToString(), - request.Kind == WebSceneResourceKind.Image + binaryBytes is not null ? Encoding.UTF8.GetString(binaryBytes) : request.Kind == WebSceneResourceKind.Image ? Native.NativeImageResource.ToMarkup(response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult()) : response.Content.ReadAsStringAsync().GetAwaiter().GetResult(), resolved.ToString(), null) { + BinaryContent = binaryBytes is null ? (ReadOnlyMemory?)null : new ReadOnlyMemory(binaryBytes), EntityTag = responseEntityTag, LastModified = responseLastModified, FreshUntil = cachePolicy.FreshUntil, @@ -756,7 +762,7 @@ private Uri ResolveAddress(string specifier, string? baseAddress) return new Uri(Path.GetFullPath(Path.Combine(ScriptBaseDirectory, specifier))); } - private WebSceneTextResource LoadFile(string path) + private WebSceneTextResource LoadFile(string path, bool binary = false) { var fullPath = Path.GetFullPath(path); if (!File.Exists(fullPath) && string.IsNullOrEmpty(Path.GetExtension(fullPath)) && File.Exists(fullPath + ".js")) @@ -780,7 +786,9 @@ private WebSceneTextResource LoadFile(string path) _resourceSearchDirectories.Add(directory); } - return new WebSceneTextResource(fullPath, File.ReadAllText(fullPath), fullPath, directory); + var bytes = binary ? File.ReadAllBytes(fullPath) : null; + return new WebSceneTextResource(fullPath, bytes is null ? File.ReadAllText(fullPath) : Encoding.UTF8.GetString(bytes), fullPath, directory) + { BinaryContent = bytes is null ? (ReadOnlyMemory?)null : new ReadOnlyMemory(bytes) }; } private bool TryResolvePackagedResource(string resourcePath, out string fullPath) @@ -940,7 +948,11 @@ internal sealed class AvaloniaClipboard : IWebSceneClipboard { try { +#if WEBSCENE_AVALONIA12 + return _topLevel.Clipboard?.TryGetTextAsync().GetAwaiter().GetResult() ?? _lastText; +#else return _topLevel.Clipboard?.GetTextAsync().GetAwaiter().GetResult() ?? _lastText; +#endif } catch { @@ -974,6 +986,18 @@ public void SetData(string format, ReadOnlyMemory data) try { +#if WEBSCENE_AVALONIA12 + var item = new DataTransferItem(); + item.Set(DataFormat.CreateBytesPlatformFormat(format), bytes); + if (format.Equals("image/png", StringComparison.OrdinalIgnoreCase)) + { + item.Set(DataFormat.CreateBytesPlatformFormat("public.png"), bytes); + item.Set(DataFormat.CreateBytesPlatformFormat("PNG"), bytes); + } + var clipboardData = new DataTransfer(); + clipboardData.Add(item); + _topLevel.Clipboard?.SetDataAsync(clipboardData).GetAwaiter().GetResult(); +#else var clipboardData = new DataObject(); clipboardData.Set(format, bytes); if (format.Equals("image/png", StringComparison.OrdinalIgnoreCase)) @@ -984,6 +1008,7 @@ public void SetData(string format, ReadOnlyMemory data) clipboardData.Set("PNG", bytes); } _topLevel.Clipboard?.SetDataObjectAsync(clipboardData).GetAwaiter().GetResult(); +#endif } catch { diff --git a/src/WebScene.Backend.Avalonia/AvaloniaResourceArchive.cs b/src/WebScene.Backend.Avalonia/AvaloniaResourceArchive.cs index 82d0d01f6..8cf863a1d 100644 --- a/src/WebScene.Backend.Avalonia/AvaloniaResourceArchive.cs +++ b/src/WebScene.Backend.Avalonia/AvaloniaResourceArchive.cs @@ -1,11 +1,12 @@ using System.Security.Cryptography; using System.Text; using System.Text.Json; +using System.Text.Json.Serialization; using WebScene.Core; namespace WebScene.Backends.Avalonia; -internal sealed class AvaloniaResourceArchive +internal sealed partial class AvaloniaResourceArchive { private const int CurrentSchemaVersion = 2; private const string ManifestFileName = "manifest.json"; @@ -51,8 +52,8 @@ internal static AvaloniaResourceArchive OpenReplay(string directory) manifestPath); } - var manifest = JsonSerializer.Deserialize( - File.ReadAllText(manifestPath)) + var manifest = JsonSerializer.Deserialize( + File.ReadAllText(manifestPath), ArchiveJsonContext.Default.ResourceArchiveManifest) ?? throw new InvalidDataException( $"WebScene resource replay manifest '{manifestPath}' is empty."); if (manifest.SchemaVersion is < 1 or > CurrentSchemaVersion) @@ -90,7 +91,7 @@ internal void CaptureText( + "Use an empty WebScene resource cache for the capture run."); } - var content = Encoding.UTF8.GetBytes(resource.Content); + var content = resource.BinaryContent?.ToArray() ?? Encoding.UTF8.GetBytes(resource.Content); lock (_gate) { var key = TextKey(address, kind, context.Origin); @@ -127,6 +128,7 @@ internal WebSceneTextResource ReplayText( resource.DisplayName, resource.Directory) { + BinaryContent = kind == WebSceneResourceKind.Data ? resource.Content : (ReadOnlyMemory?)null, EntityTag = resource.EntityTag, LastModified = resource.LastModified, FreshUntil = resource.FreshUntil, @@ -234,7 +236,7 @@ internal void Flush() temporaryPath, JsonSerializer.Serialize( manifest, - new JsonSerializerOptions { WriteIndented = true })); + ArchiveJsonContext.Default.ResourceArchiveManifest)); File.Move(temporaryPath, manifestPath, overwrite: true); _dirty = false; } @@ -307,6 +309,10 @@ private static string LegacyTextKey(Uri address, WebSceneResourceKind kind) private static string BinaryKey(Uri address) => $"binary:{address}"; + [JsonSourceGenerationOptions(WriteIndented = true)] + [JsonSerializable(typeof(ResourceArchiveManifest))] + private partial class ArchiveJsonContext : JsonSerializerContext { } + private sealed class ResourceArchiveManifest { public int SchemaVersion { get; set; } diff --git a/src/WebScene.Backend.Avalonia/INativeRetainedGpuImage.cs b/src/WebScene.Backend.Avalonia/INativeRetainedGpuImage.cs new file mode 100644 index 000000000..16bdb9ebc --- /dev/null +++ b/src/WebScene.Backend.Avalonia/INativeRetainedGpuImage.cs @@ -0,0 +1,12 @@ +using Avalonia.Skia; +using SkiaSharp; +namespace WebScene.Backends.Avalonia.Native; +internal interface INativeRetainedGpuImage +{ + void Draw(ISkiaSharpApiLease lease, SKRect destination, SKPaint? paint = null); + void Retire(ISkiaSharpApiLease lease); + bool TryComplete(ISkiaSharpApiLease lease); + bool TryRetireWithoutVisual(); + // Called synchronously on the composition owner before detached polling. + void SealForDetachedRetirement() { TryRetireWithoutVisual(); } +} diff --git a/src/WebScene.Backend.Avalonia/NativeCanvasBacking.cs b/src/WebScene.Backend.Avalonia/NativeCanvasBacking.cs new file mode 100644 index 000000000..256f09439 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeCanvasBacking.cs @@ -0,0 +1,187 @@ +using SkiaSharp; + +#if WEBSCENE_UNO +namespace WebScene.Backends.Uno.Native; +#else +namespace WebScene.Backends.Avalonia.Native; +#endif + +internal sealed unsafe partial class NativeCanvasSceneRenderer +{ + internal long ResumedCanvasCompilations { get; private set; } + internal bool UseIncrementalCanvasBacking { get; set; } + = Environment.GetEnvironmentVariable("WEBSCENE_INCREMENTAL_CANVAS_GPU") == "1"; + + private RetainedLayer CompileIncrementalCanvasLayer(NativeSceneView* view, in NativeCanvasLayer layer) + { + var isolation = RequiresIsolation(view, layer); + var prefix = FindReplayPrefix(view, layer); + s_layers.TryGetValue(layer.NodeId, out var previous); + + var bounds = new SKRect(0, 0, Math.Max(1, layer.BitmapWidth), Math.Max(1, layer.BitmapHeight)); + using var recorder = new SKPictureRecorder(); + var canvas = recorder.BeginRecording(bounds); + var continuation = Replay(canvas, view, layer, !isolation, prefix, captureContinuation: true); + using var suffix = recorder.EndRecording(); + SKPicture? full = null, gpu = null; + var history = prefix is null ? new List() + : previous!.CpuHistory!.Select(node => node.Retain()).ToList(); + try + { + canvas = recorder.BeginRecording(bounds); + canvas.DrawPicture(suffix); + history.Add(new CanvasPictureNode(recorder.EndRecording(), 1)); + // A persistent balanced forest avoids a deep DrawPicture chain and + // periodic full recompilation. Export still sees the complete CPU + // history; appending only creates O(log n) lightweight picture nodes. + while (history.Count >= 2 && history[^1].Weight == history[^2].Weight) + { + var right = history[^1]; var left = history[^2]; + canvas = recorder.BeginRecording(bounds); + canvas.DrawPicture(left.Picture); canvas.DrawPicture(right.Picture); + var combined = new CanvasPictureNode(recorder.EndRecording(), left.Weight + right.Weight); + history.RemoveRange(history.Count - 2, 2); + left.Dispose(); right.Dispose(); history.Add(combined); + } + canvas = recorder.BeginRecording(bounds); + foreach (var node in history) canvas.DrawPicture(node.Picture); + full = recorder.EndRecording(); + if (prefix is not null) + { + ResumedCanvasCompilations++; + if (previous!.GpuPicture is not null) + { + canvas = recorder.BeginRecording(bounds); + canvas.DrawPicture(previous.GpuPicture); + canvas.DrawPicture(suffix); + gpu = recorder.EndRecording(); + } + } + var result = new RetainedLayer(layer.NodeId, layer.Generation, + layer.Reserved & ~OffscreenCanvasLayer, (layer.Reserved & OffscreenCanvasLayer) != 0, + layer.X, layer.Y, layer.Width, layer.Height, layer.BitmapWidth, layer.BitmapHeight, + layer.CommandCount, isolation, full) + { + ReplaySnapshot = continuation, GpuPicture = gpu, + GpuContext = gpu is null ? null : previous!.GpuContext, + CpuHistory = history.ToArray() + }; + history.Clear(); + full = null; gpu = null; continuation = null; + return result; + } + finally { foreach (var node in history) node.Dispose(); full?.Dispose(); gpu?.Dispose(); continuation?.Dispose(); } + } + + private CanvasReplaySnapshot? FindReplayPrefix(NativeSceneView* view, in NativeCanvasLayer layer) + => UseIncrementalCanvasBacking + && s_layers.TryGetValue(layer.NodeId, out var previous) + && previous.Generation == layer.Generation + && previous.BitmapWidth == layer.BitmapWidth && previous.BitmapHeight == layer.BitmapHeight + && previous.ReplaySnapshot is { } retained + && retained.Matches(view, layer, _presenterDeviceScaleFactor, s_revision) + ? retained : null; + + internal sealed class CanvasPictureNode(SKPicture picture, long weight) : IDisposable + { + internal readonly SKPicture Picture = picture; + internal readonly long Weight = weight; + private int _references = 1; + internal CanvasPictureNode Retain() { Interlocked.Increment(ref _references); return this; } + public void Dispose() { if (Interlocked.Decrement(ref _references) == 0) Picture.Dispose(); } + } + + // Called only while the owning graphics lease is current. All pixels stay + // on the GPU: the previous immutable image plus appended commands becomes + // the next backing image. The CPU picture remains intact for export and + // context-loss fallback. No native WebGPU image lifetime is changed here. + private static SKPicture? MaterializeCanvasBacking(RetainedLayer layer, GRContext context) + { + if (layer.ReplaySnapshot is null || !layer.RequiresIsolation || context.IsAbandoned + || (ulong)layer.BitmapWidth * layer.BitmapHeight > 4 * 1024 * 1024) return null; + if (layer.IsMaterialized && ReferenceEquals(layer.GpuContext, context)) return layer.GpuPicture; + using var surface = SKSurface.Create(context, false, new SKImageInfo( + checked((int)layer.BitmapWidth), checked((int)layer.BitmapHeight), + SKColorType.Rgba8888, SKAlphaType.Premul)); + if (surface is null) return null; + surface.Canvas.Clear(SKColors.Transparent); + surface.Canvas.DrawPicture(ReferenceEquals(layer.GpuContext, context) && layer.GpuPicture is not null + ? layer.GpuPicture : layer.Picture); + SKImage? image = surface.Snapshot(); + try + { + using var recorder = new SKPictureRecorder(); + var canvas = recorder.BeginRecording(new SKRect(0, 0, layer.BitmapWidth, layer.BitmapHeight)); + canvas.DrawImage(image, 0, 0); + var picture = recorder.EndRecording(); + layer.GpuPicture?.Dispose(); + layer.GpuPicture = picture; + layer.GpuContext = context; + layer.IsMaterialized = true; + layer.CheckpointImage?.Dispose(); + layer.CheckpointImage = image; + image = null; + return picture; + } + finally { image?.Dispose(); } + } + + // An exact immutable prefix is required; generation alone does not prove + // that commands or string resources remained unchanged. Active save/clip + // stacks and mutable canvas/image dependencies deliberately use full replay. + internal sealed class CanvasReplaySnapshot : IDisposable + { + internal readonly CanvasState State; + internal readonly SKMatrix Matrix; + internal readonly SKPath Path; + internal readonly bool HasDrawn; + internal readonly int CommandCount, Depth; + private readonly byte[][] _commandChunks; + private readonly bool _hasExactCommands; + private readonly string[] _strings; + private readonly float _scale; + private readonly long _fontVersion = NativeTextShaping.FontRegistrationVersion; + + internal CanvasReplaySnapshot(NativeSceneView* view, in NativeCanvasLayer layer, + CanvasState state, SKMatrix matrix, SKPath path, bool hasDrawn, float scale, CanvasReplaySnapshot? prefix) + { + State = state; Matrix = matrix; Path = new SKPath(path); HasDrawn = hasDrawn; + CommandCount = checked((int)layer.CommandCount); Depth = (prefix?.Depth ?? -1) + 1; _scale = scale; + _hasExactCommands = (layer.Flags & LayerUnchangedPrefix) == 0 + && (prefix is null || prefix._hasExactCommands) + && CommandCount * (long)sizeof(NativeCanvasCommand) <= 16 * 1024 * 1024; + if (_hasExactCommands) + { + var start = prefix?.CommandCount ?? 0; + var appended = new ReadOnlySpan(view->CanvasCommands + layer.CommandOffset + start, + checked((CommandCount - start) * sizeof(NativeCanvasCommand))).ToArray(); + _commandChunks = prefix is null ? [appended] : [..prefix._commandChunks, appended]; + } + else _commandChunks = []; + _strings = new string[checked((int)layer.StringCount)]; + for (var index = 0; index < _strings.Length; ++index) + _strings[index] = DomStringAt(view, layer.StringOffset + (uint)index); + } + + internal bool Matches(NativeSceneView* view, in NativeCanvasLayer layer, float scale, ulong revision) + { + if (_scale != scale || _fontVersion != NativeTextShaping.FontRegistrationVersion + || layer.CommandCount < CommandCount || layer.StringCount < _strings.Length) return false; + // The engine verified these prefixes against this exact base + // revision. Older runtimes omit the hint and use byte comparisons. + if ((layer.Flags & LayerUnchangedPrefix) != 0 && view->Header.BaseRevision == revision) return true; + if (!_hasExactCommands) return false; + var current = (byte*)(view->CanvasCommands + layer.CommandOffset); + foreach (var chunk in _commandChunks) + { + if (!chunk.AsSpan().SequenceEqual(new ReadOnlySpan(current, chunk.Length))) return false; + current += chunk.Length; + } + for (var index = 0; index < _strings.Length; ++index) + if (_strings[index] != DomStringAt(view, layer.StringOffset + (uint)index)) return false; + return true; + } + + public void Dispose() => Path.Dispose(); + } +} diff --git a/src/WebScene.Backend.Avalonia/NativeCanvasCheckpoint.cs b/src/WebScene.Backend.Avalonia/NativeCanvasCheckpoint.cs new file mode 100644 index 000000000..aa260c9c0 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeCanvasCheckpoint.cs @@ -0,0 +1,295 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using SkiaSharp; + +#if WEBSCENE_UNO +namespace WebScene.Backends.Uno.Native; +#else +namespace WebScene.Backends.Avalonia.Native; +#endif + +internal sealed unsafe partial class NativeCanvasSceneRenderer +{ + internal const uint CanvasCheckpointCommand = 58; + internal const uint CanvasCheckpointInterval = 32768; + private readonly Dictionary _checkpointAttempts = []; + private long _checkpointSubmissions, _maximumCheckpointCommands; + private double _maximumCheckpointMilliseconds; + private long _checkpointDeferredReadbacks = 0, _checkpointFencePolls = 0; + private long _checkpointWorkerReadbacks = 0; + private bool _checkpointApiAvailable = true; + private sealed record PendingCheckpoint(uint NodeId, ulong Generation, uint Count, Task Encoding); + private PendingCheckpoint? _pendingCheckpoint; +#if !WEBSCENE_UNO + private sealed record PendingReadback(uint NodeId, ulong Generation, uint Count, + SKImage Image, RasterCheckpoint State, GRContext Context, NativeCanvasCheckpointFence? Fence) : IDisposable + { + internal NativeCanvasCheckpointTransfer? Transfer { get; set; } + internal bool IsReady() => Transfer?.IsReady() ?? Fence!.IsReady(); + public void Dispose() { try { Transfer?.Dispose(); Fence?.Dispose(); } finally { Image.Dispose(); } } + } + private PendingReadback? _pendingReadback; +#endif + + internal sealed class RasterCheckpoint + { + public int Version { get; set; } = 1; + public string Png { get; set; } = ""; + public List Path { get; set; } = []; + public int FillType { get; set; } + public float[] Matrix { get; set; } = []; + public CanvasState State { get; set; } + } + + internal static byte[] EncodeCheckpoint(SKImage image, CanvasReplaySnapshot snapshot) + => EncodeCheckpoint(image, CaptureCheckpointState(snapshot)); + + private static byte[] EncodeCheckpoint(SKImage image, RasterCheckpoint checkpoint) + { + using var png = image.Encode(SKEncodedImageFormat.Png, 100); + if (png is null) throw new InvalidOperationException("Canvas checkpoint encoding failed."); + checkpoint.Png = Convert.ToBase64String(png.ToArray()); + return JsonSerializer.SerializeToUtf8Bytes(checkpoint, CanvasCheckpointJsonContext.Default.RasterCheckpoint); + } + + private static RasterCheckpoint CaptureCheckpointState(CanvasReplaySnapshot snapshot) + { + var m = snapshot.Matrix; + return new RasterCheckpoint + { + Path = EncodePath(snapshot.Path), + FillType = (int)snapshot.Path.FillType, State = snapshot.State, + Matrix = [m.ScaleX,m.SkewX,m.TransX,m.SkewY,m.ScaleY,m.TransY,m.Persp0,m.Persp1,m.Persp2] + }; + } + + private static List EncodePath(SKPath path) + { + var result = new List(); + using var iterator = path.CreateRawIterator(); + var points = new SKPoint[4]; + for (var verb=iterator.Next(points);verb!=SKPathVerb.Done;verb=iterator.Next(points)) + result.Add([(float)verb,points[0].X,points[0].Y,points[1].X,points[1].Y, + points[2].X,points[2].Y,points[3].X,points[3].Y,verb==SKPathVerb.Conic ? iterator.ConicWeight() : 0]); + return result; + } + + internal byte[] EncodeRetainedCanvasCheckpoint(uint nodeId) + { + var layer = s_layers[nodeId]; + var state = layer.ReplaySnapshot ?? throw new InvalidOperationException("Canvas state cannot be checkpointed."); + if (layer.CheckpointImage is { } image) return EncodeCheckpoint(image,state); + using var surface = SKSurface.Create(new SKImageInfo((int)layer.BitmapWidth,(int)layer.BitmapHeight, + SKColorType.Rgba8888,SKAlphaType.Premul)); + surface.Canvas.Clear(SKColors.Transparent); + surface.Canvas.DrawPicture(layer.Picture); + using var raster = surface.Snapshot(); + return EncodeCheckpoint(raster,state); + } + + // Called under the owning GPU lease. Checkpoint readback is periodic + // recovery storage, never the presenter path or a per-frame pixel transfer. + internal void CheckpointCanvasHistory(IntPtr engine +#if !WEBSCENE_UNO + , global::Avalonia.Skia.ISkiaSharpApiLease? lease = null +#endif + ) + { + if (!UseIncrementalCanvasBacking || !_checkpointApiAvailable) return; + foreach (var layer in s_layers.Values) + _maximumCheckpointCommands = Math.Max(_maximumCheckpointCommands,layer.CommandCount); +#if !WEBSCENE_UNO + if (_pendingReadback is not null) return; +#endif + if (_pendingCheckpoint is { } pending) + { + if (!pending.Encoding.IsCompleted) return; + _pendingCheckpoint = null; + if (pending.Encoding.IsCompletedSuccessfully) + { + var payload = pending.Encoding.Result; + try + { + // Native access stays on the composition owner. The encoder + // never borrows an engine handle or a GPU object. + if (NativeWebSceneApi.SubmitCanvasCheckpoint(engine,pending.NodeId,pending.Generation, + pending.Count,payload,(nuint)payload.Length) != 0) _checkpointSubmissions++; + } + catch (EntryPointNotFoundException) { _checkpointApiAvailable=false; return; } + } + else { _ = pending.Encoding.Exception; } + } + foreach (var layer in s_layers.Values) + { + if (layer.CommandCount < CanvasCheckpointInterval || layer.ReplaySnapshot is not { } state + || layer.CheckpointImage is not { } image || layer.GpuContext is not { IsAbandoned: false }) continue; + if (_checkpointAttempts.TryGetValue(layer.NodeId, out var attempt) + && attempt.Generation == layer.Generation + && layer.CommandCount - Math.Min(layer.CommandCount,attempt.Count) < CanvasCheckpointInterval) continue; + var started=System.Diagnostics.Stopwatch.GetTimestamp(); + try + { + var capturedState = CaptureCheckpointState(state); +#if !WEBSCENE_UNO + if (lease is not null && Environment.GetEnvironmentVariable("WEBSCENE_ASYNC_CANVAS_READBACK") == "1" + && NativeCanvasCheckpointTransfer.Create(image, lease) is { } transfer) + { + _pendingReadback = new(layer.NodeId, layer.Generation, layer.CommandCount, + image, capturedState, layer.GpuContext, null) { Transfer = transfer }; + layer.CheckpointImage = null; + _checkpointAttempts[layer.NodeId] = (layer.Generation, layer.CommandCount); + break; + } + if (lease is not null && NativeCanvasCheckpointFence.Create(lease) is { } fence) + { + // Transfer ownership: the retained picture keeps its own native + // image reference, while this immutable snapshot survives rebasing. + _pendingReadback = new(layer.NodeId, layer.Generation, layer.CommandCount, + image, capturedState, layer.GpuContext, fence); + layer.CheckpointImage = null; + _checkpointAttempts[layer.NodeId] = (layer.Generation,layer.CommandCount); + break; + } +#endif + var raster = image.ToRasterImage(true); + if (raster is null) continue; + var encoding = StartCheckpointEncoding(raster, capturedState); + _pendingCheckpoint = new(layer.NodeId,layer.Generation,layer.CommandCount,encoding); + _checkpointAttempts[layer.NodeId] = (layer.Generation,layer.CommandCount); + break; + } + finally + { + _maximumCheckpointMilliseconds=Math.Max(_maximumCheckpointMilliseconds, + System.Diagnostics.Stopwatch.GetElapsedTime(started).TotalMilliseconds); + TraceCheckpointStage("queue", started); + } + } + foreach (var id in _checkpointAttempts.Keys.Where(id => !s_layers.ContainsKey(id)).ToArray()) + _checkpointAttempts.Remove(id); + } + + +#if !WEBSCENE_UNO + internal void PollCanvasCheckpointReadback(global::Avalonia.Skia.ISkiaSharpApiLease lease) + { + if (_pendingReadback is { } readback) + { + if (readback.Context.IsAbandoned || !ReferenceEquals(lease?.GrContext, readback.Context) + || !s_layers.TryGetValue(readback.NodeId, out var retained) + || retained.Generation != readback.Generation) + { + _pendingReadback = null; + readback.Dispose(); + } + else + { + _checkpointFencePolls++; + if (!readback.IsReady()) return; + _pendingReadback = null; + var started = System.Diagnostics.Stopwatch.GetTimestamp(); + using (readback) + { + if (readback.Transfer is { } transfer) + { + readback.Transfer = null; // Worker now owns the buffer, including on failure/reset. + var task = Task.Run(() => + { + using (transfer) + { + var copyStarted = System.Diagnostics.Stopwatch.GetTimestamp(); + using var raster = transfer.ReadOnWorker(); + TraceCheckpointStage("worker-copy", copyStarted); + return EncodeCheckpoint(raster, readback.State); + } + }); + ObserveCheckpointTask(task); + _pendingCheckpoint = new(readback.NodeId, readback.Generation, readback.Count, task); + _checkpointWorkerReadbacks++; + _checkpointDeferredReadbacks++; + } + else + { + var raster = readback.Image.ToRasterImage(true); + if (raster is not null) + { + _checkpointDeferredReadbacks++; + _pendingCheckpoint = new(readback.NodeId, readback.Generation, readback.Count, + StartCheckpointEncoding(raster, readback.State)); + } + } + } + _maximumCheckpointMilliseconds = Math.Max(_maximumCheckpointMilliseconds, + System.Diagnostics.Stopwatch.GetElapsedTime(started).TotalMilliseconds); + TraceCheckpointStage("complete", started); + } + } + } +#endif + + private static void TraceCheckpointStage(string stage, long started) + { + if (Environment.GetEnvironmentVariable("WEBSCENE_TRACE_CANVAS_CHECKPOINTS") != "1") return; + var timing = new System.Text.Json.Nodes.JsonObject + { + ["stage"] = stage, ["started"] = started, + ["ended"] = System.Diagnostics.Stopwatch.GetTimestamp(), + ["frequency"] = System.Diagnostics.Stopwatch.Frequency + }; + Console.WriteLine("Canvas checkpoint timing: " + timing.ToJsonString()); + } + + private static Task StartCheckpointEncoding(SKImage raster, RasterCheckpoint state) + { + var task = Task.Run(() => { using (raster) return EncodeCheckpoint(raster, state); }); + ObserveCheckpointTask(task); + return task; + } + + private static void ObserveCheckpointTask(Task task) + { + _ = task.ContinueWith(static failed => { _ = failed.Exception; }, CancellationToken.None, + TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + + private static CanvasState ReplayCheckpoint(SKCanvas canvas, SKPath path, string resource, + uint width, uint height) + { + var checkpoint = JsonSerializer.Deserialize(resource, CanvasCheckpointJsonContext.Default.RasterCheckpoint) + ?? throw new InvalidOperationException("Invalid canvas checkpoint."); + if (checkpoint.Version != 1 || checkpoint.Matrix.Length != 9) + throw new InvalidOperationException("Unsupported canvas checkpoint."); + using var image = SKImage.FromEncodedData(Convert.FromBase64String(checkpoint.Png)); + if (image is null || image.Width != width || image.Height != height) + throw new InvalidOperationException("Canvas checkpoint dimensions differ."); + canvas.ResetMatrix(); + using (var paint = new SKPaint { BlendMode = SKBlendMode.Src }) canvas.DrawImage(image,0,0,paint); + var m = checkpoint.Matrix; + canvas.SetMatrix(new SKMatrix { ScaleX=m[0],SkewX=m[1],TransX=m[2],SkewY=m[3],ScaleY=m[4], + TransY=m[5],Persp0=m[6],Persp1=m[7],Persp2=m[8] }); + path.Reset(); + foreach (var segment in checkpoint.Path) + { + if(segment.Length!=10)throw new InvalidOperationException("Invalid checkpoint path."); + switch((SKPathVerb)segment[0]) + { + case SKPathVerb.Move:path.MoveTo(segment[1],segment[2]);break; + case SKPathVerb.Line:path.LineTo(segment[3],segment[4]);break; + case SKPathVerb.Quad:path.QuadTo(segment[3],segment[4],segment[5],segment[6]);break; + case SKPathVerb.Conic:path.ConicTo(segment[3],segment[4],segment[5],segment[6],segment[9]);break; + case SKPathVerb.Cubic:path.CubicTo(segment[3],segment[4],segment[5],segment[6],segment[7],segment[8]);break; + case SKPathVerb.Close:path.Close();break; + default:throw new InvalidOperationException("Invalid checkpoint path verb."); + } + } + path.FillType=(SKPathFillType)checkpoint.FillType; + return checkpoint.State; + } +} + +[JsonSourceGenerationOptions(IncludeFields = true)] +[JsonSerializable(typeof(NativeCanvasSceneRenderer.RasterCheckpoint))] +internal partial class CanvasCheckpointJsonContext : JsonSerializerContext +{ +} diff --git a/src/WebScene.Backend.Avalonia/NativeCanvasCheckpointFence.cs b/src/WebScene.Backend.Avalonia/NativeCanvasCheckpointFence.cs new file mode 100644 index 000000000..9d81817e7 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeCanvasCheckpointFence.cs @@ -0,0 +1,58 @@ +using System.Runtime.InteropServices; +using Avalonia.OpenGL.Egl; +using Avalonia.Skia; + +namespace WebScene.Backends.Avalonia.Native; + +// A zero-timeout poll never waits for the GPU on the composition thread. +internal sealed class NativeCanvasCheckpointFence : IDisposable +{ + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate IntPtr Fence(uint condition, uint flags); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate uint Wait(IntPtr sync, uint flags, ulong timeout); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void Delete(IntPtr sync); + private readonly EglContext _context; + private readonly Wait _wait; + private readonly Delete _delete; + private IntPtr _sync; + private NativeCanvasCheckpointFence(EglContext context, Wait wait, Delete delete, IntPtr sync) + => (_context, _wait, _delete, _sync) = (context, wait, delete, sync); + + internal static NativeCanvasCheckpointFence? Create(ISkiaSharpApiLease lease) + { + if (!OperatingSystem.IsWindows()) return null; + using var platform = lease.TryLeasePlatformGraphicsApi(); + if (platform?.Context is not EglContext context) return null; + return Create(context, lease.GrContext!); + } + + internal static NativeCanvasCheckpointFence? Create(EglContext context, SkiaSharp.GRContext skia) + { + var gl = context.GlInterface; + var create = gl.GetProcAddress("glFenceSync"); + var wait = gl.GetProcAddress("glClientWaitSync"); + var delete = gl.GetProcAddress("glDeleteSync"); + if (create == IntPtr.Zero || wait == IntPtr.Zero || delete == IntPtr.Zero) return null; + skia.Flush(); + var sync = Marshal.GetDelegateForFunctionPointer(create)(0x9117, 0); + if (sync == IntPtr.Zero) return null; + gl.Flush(); + return new(context, Marshal.GetDelegateForFunctionPointer(wait), + Marshal.GetDelegateForFunctionPointer(delete), sync); + } + + internal bool IsReady() + { + using var current = _context.EnsureCurrent(); + var status = _wait(_sync, 0, 0); + if (status == 0x911D) throw new InvalidOperationException("Canvas checkpoint GPU fence failed."); + return status is 0x911A or 0x911C; + } + + public void Dispose() + { + if (_sync == IntPtr.Zero) return; + using var current = _context.EnsureCurrent(); + _delete(_sync); + _sync = IntPtr.Zero; + } +} diff --git a/src/WebScene.Backend.Avalonia/NativeCanvasCheckpointTransfer.cs b/src/WebScene.Backend.Avalonia/NativeCanvasCheckpointTransfer.cs new file mode 100644 index 000000000..68c42d738 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeCanvasCheckpointTransfer.cs @@ -0,0 +1,164 @@ +using System.Runtime.InteropServices; +using Avalonia.OpenGL.Egl; +using Avalonia.Skia; +using SkiaSharp; + +namespace WebScene.Backends.Avalonia.Native; + +// One immutable checkpoint transfer. The pack buffer is never reused until its +// GPU fence completed and the worker copied its contents into independent RAM. +internal sealed unsafe class NativeCanvasCheckpointTransfer : IDisposable +{ + private const uint PackBuffer = 0x88EB, Texture = 0x0DE1, Framebuffer = 0x8D40; + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void Gen(int count, out uint value); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void Delete(int count, ref uint value); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void Bind(uint target, uint value); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void Integer(uint name, out int value); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void Store(uint name, int value); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void Active(uint texture); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void TexImage(uint target, int level, int format, int width, int height, int border, uint pixelFormat, uint type, IntPtr data); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void Attach(uint target, uint attachment, uint textureTarget, uint texture, int level); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate uint Status(uint target); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void Allocate(uint target, nint size, IntPtr data, uint usage); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate void Read(int x, int y, int width, int height, uint format, uint type, IntPtr data); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate IntPtr Map(uint target, nint offset, nint length, uint access); + [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate byte Unmap(uint target); + private readonly EglContext _context; + private readonly Bind _bind; + private readonly Delete _delete; + private readonly Integer _integer; + private readonly Map _map; + private readonly Unmap _unmap; + private readonly int _width, _height; + private uint _buffer; + private NativeCanvasCheckpointFence? _fence; + + private NativeCanvasCheckpointTransfer(EglContext context, int width, int height) + { + _context = context; _width = width; _height = height; + _bind = Get("glBindBuffer"); _delete = Get("glDeleteBuffers"); + _integer = Get("glGetIntegerv"); _map = Get("glMapBufferRange"); + _unmap = Get("glUnmapBuffer"); + } + private T Get(string name) where T : Delegate + { + var address = _context.GlInterface.GetProcAddress(name); + if (address == IntPtr.Zero) throw new NotSupportedException(name); + return Marshal.GetDelegateForFunctionPointer(address); + } + + internal static NativeCanvasCheckpointTransfer? Create(SKImage image, ISkiaSharpApiLease lease) + { + if (!OperatingSystem.IsWindows() || lease.GrContext is null) return null; + using var platform = lease.TryLeasePlatformGraphicsApi(); + if (platform?.Context is not EglContext context || context.Version.Major < 3) return null; + NativeCanvasCheckpointTransfer? result = null; + try + { + result = new(context, image.Width, image.Height); + result.Queue(image, lease); + return result; + } + catch (NotSupportedException) { result?.Dispose(); return null; } + catch { result?.Dispose(); throw; } + } + + private void Queue(SKImage image, ISkiaSharpApiLease lease) + { + var genTextures = Get("glGenTextures"); var deleteTextures = Get("glDeleteTextures"); + var bindTexture = Get("glBindTexture"); var texImage = Get("glTexImage2D"); + var genFrames = Get("glGenFramebuffers"); var deleteFrames = Get("glDeleteFramebuffers"); + var bindFrame = Get("glBindFramebuffer"); var attach = Get("glFramebufferTexture2D"); + var status = Get("glCheckFramebufferStatus"); var allocate = Get("glBufferData"); + var read = Get("glReadPixels"); var store = Get("glPixelStorei"); + lease.GrContext!.Flush(); + _integer(0x8CAA, out var oldRead); _integer(0x8CA6, out var oldDraw); + _integer(0x8069, out var oldTexture); _integer(0x88ED, out var oldPack); + _integer(0x84E0, out var oldActiveTexture); + _integer(0x88EF, out var oldUnpack); + _integer(0x0D05, out var alignment); _integer(0x0D02, out var rowLength); + _integer(0x0D03, out var skipRows); _integer(0x0D04, out var skipPixels); + uint texture = 0, framebuffer = 0; + try + { + _bind(0x88EC, 0); // Null texture data must not address an unpack buffer. + genTextures(1, out texture); bindTexture(Texture, texture); + texImage(Texture, 0, 0x8058, _width, _height, 0, 0x1908, 0x1401, IntPtr.Zero); + genFrames(1, out framebuffer); bindFrame(Framebuffer, framebuffer); + attach(Framebuffer, 0x8CE0, Texture, texture, 0); + if (status(Framebuffer) != 0x8CD5) throw new NotSupportedException("Checkpoint framebuffer incomplete"); + using (var target = new GRBackendRenderTarget(_width, _height, 0, 0, new GRGlFramebufferInfo(framebuffer, 0x8058))) + using (var surface = SKSurface.Create(lease.GrContext, target, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888)) + { + if (surface is null) throw new NotSupportedException("Checkpoint staging surface unavailable"); + lease.GrContext.ResetContext(); + surface.Canvas.Clear(SKColors.Transparent); + using var paint = new SKPaint { BlendMode = SKBlendMode.Src }; + surface.Canvas.DrawImage(image, 0, 0, paint); + lease.GrContext.Flush(); + bindFrame(Framebuffer, framebuffer); + Get("glGenBuffers")(1, out _buffer); _bind(PackBuffer, _buffer); + allocate(PackBuffer, checked(_width * _height * 4), IntPtr.Zero, 0x88E1); + store(0x0D05, 4); store(0x0D02, 0); store(0x0D03, 0); store(0x0D04, 0); + read(0, 0, _width, _height, 0x1908, 0x1401, IntPtr.Zero); + _fence = NativeCanvasCheckpointFence.Create(_context, lease.GrContext) + ?? throw new NotSupportedException("Checkpoint transfer fence unavailable"); + } + } + finally + { + _bind(PackBuffer, (uint)oldPack); _bind(0x88EC, (uint)oldUnpack); + store(0x0D05, alignment); store(0x0D02, rowLength); store(0x0D03, skipRows); store(0x0D04, skipPixels); + bindFrame(0x8CA8, (uint)oldRead); bindFrame(0x8CA9, (uint)oldDraw); + Get("glActiveTexture")((uint)oldActiveTexture); + bindTexture(Texture, (uint)oldTexture); + if (framebuffer != 0) deleteFrames(1, ref framebuffer); + if (texture != 0) deleteTextures(1, ref texture); + lease.GrContext.ResetContext(); + } + } + + internal bool IsReady() => _fence!.IsReady(); + + // Only called once readiness was observed. EnsureCurrent serializes the + // short map/copy/unmap with Avalonia's context use; encoding holds no GL lock. + internal SKImage ReadOnWorker() + { + using var current = _context.EnsureCurrent(); + _integer(0x88ED, out var oldPack); + _bind(PackBuffer, _buffer); + try + { + var address = _map(PackBuffer, 0, checked(_width * _height * 4), 1); + if (address == IntPtr.Zero) throw new InvalidOperationException("Checkpoint buffer map failed"); + using var bitmap = new SKBitmap(new SKImageInfo(_width, _height, SKColorType.Rgba8888, SKAlphaType.Premul)); + try + { + // GL packs bottom row first; the checkpoint bitmap is top-down. + for (var row = 0; row < _height; row++) + Buffer.MemoryCopy((byte*)address + (_height - row - 1) * _width * 4, + (byte*)bitmap.GetPixels() + row * bitmap.RowBytes, bitmap.RowBytes, _width * 4); + } + finally + { + if (_unmap(PackBuffer) == 0) throw new InvalidOperationException("Checkpoint buffer contents became invalid"); + } + return SKImage.FromBitmap(bitmap); + } + finally { _bind(PackBuffer, (uint)oldPack); } + } + + public void Dispose() + { + if (_buffer == 0 && _fence is null) return; + try + { + using var current = _context.EnsureCurrent(); + _fence?.Dispose(); + if (_buffer != 0) _delete(1, ref _buffer); + } + catch (global::Avalonia.Platform.PlatformGraphicsContextLostException) { } + catch (ObjectDisposedException) { } + finally { _fence = null; _buffer = 0; } + } +} diff --git a/src/WebScene.Backend.Avalonia/NativeCanvasSceneRenderer.cs b/src/WebScene.Backend.Avalonia/NativeCanvasSceneRenderer.cs index 83b5f31f0..b70dca875 100644 --- a/src/WebScene.Backend.Avalonia/NativeCanvasSceneRenderer.cs +++ b/src/WebScene.Backend.Avalonia/NativeCanvasSceneRenderer.cs @@ -33,19 +33,44 @@ namespace WebScene.Backends.Uno.Native; namespace WebScene.Backends.Avalonia.Native; #endif -internal sealed unsafe class NativeCanvasSceneRenderer +internal sealed unsafe partial class NativeCanvasSceneRenderer { private const uint CanvasCommandEvenOdd = 1u << 16; private const uint SceneCheckpoint = 1; private const uint SceneDomReplacement = 2; private const uint LayerReplace = 1; private const uint LayerRemove = 2; + private const uint LayerUnchangedPrefix = 4; private const uint OffscreenCanvasLayer = 1u << 31; private readonly Dictionary s_layers = new(); private readonly List s_orderedLayers = []; private readonly List s_viewportLayers = []; private readonly Dictionary s_strings = new(); + private readonly Dictionary _cachedShapers = new(ReferenceEqualityComparer.Instance); + private long _shaperFontVersion; + + private SKShaper GetShaper(SKTypeface typeface) + { + if (_cachedShapers.TryGetValue(typeface, out var shaper)) return shaper; + shaper = new SKShaper(typeface); + // Do not evict a shaper that a local compilation dictionary may still + // be using. Overflow shapers belong to that compilation only. + if (_cachedShapers.Count < 64) _cachedShapers.Add(typeface, shaper); + return shaper; + } + + private void ReleaseShaper(SKShaper shaper) + { + if (!_cachedShapers.TryGetValue(shaper.Typeface, out var cached) || !ReferenceEquals(cached, shaper)) + shaper.Dispose(); + } + + private void ClearShapers() + { + foreach (var shaper in _cachedShapers.Values) shaper.Dispose(); + _cachedShapers.Clear(); + } private readonly Dictionary s_typefaces = new(StringComparer.Ordinal); private readonly Dictionary s_svgPictures = new(StringComparer.Ordinal); @@ -54,6 +79,7 @@ internal sealed unsafe class NativeCanvasSceneRenderer private float _presenterDeviceScaleFactor = 1f; internal float PresenterDeviceScaleFactor => _presenterDeviceScaleFactor; + private List? _orderedGpuPaint; private SKPicture? s_domBackdropPicture; private SKPicture? s_domOverlayPicture; private uint s_domCommandCount; @@ -129,7 +155,15 @@ internal NativeRendererMemoryMetrics ReadMemoryMetrics() s_svgPictures.Count, SharedSvgPictureCache.EntryCount, SharedSvgPictureCache.ReferenceCount, - SharedSvgPictureCache.MemoryHitCount); + SharedSvgPictureCache.MemoryHitCount) + { + CanvasCheckpointSubmissions = _checkpointSubmissions, + MaximumRetainedCanvasCommands = _maximumCheckpointCommands, + MaximumCanvasCheckpointMilliseconds = _maximumCheckpointMilliseconds, + CanvasCheckpointDeferredReadbacks = _checkpointDeferredReadbacks, + CanvasCheckpointFencePolls = _checkpointFencePolls, + CanvasCheckpointWorkerReadbacks = _checkpointWorkerReadbacks + }; } public bool ApplyDiffAndRender(SKCanvas canvas, NativeSceneView* view) @@ -146,10 +180,54 @@ public bool ApplyDiffAndRender(SKCanvas canvas, NativeSceneView* view) return true; } - public bool ApplyDiff(NativeSceneView* view) + internal sealed class PreparedCanvasLayers(ulong revision, float scale, long fontVersion) : IDisposable + { + internal readonly Dictionary Layers = []; + internal bool Matches(ulong candidateRevision, float candidateScale) + => revision == candidateRevision && scale == candidateScale + && fontVersion == NativeTextShaping.FontRegistrationVersion; + public void Dispose() + { + foreach (var layer in Layers.Values) layer.Dispose(); + Layers.Clear(); + } + } + + // Called under the renderer owner's serialization. Only immutable pictures + // are prepared; visible layer dictionaries and GPU bindings are untouched. + // Canvas-to-canvas dependencies retain the existing synchronous path. + internal PreparedCanvasLayers? PrepareCanvasLayers(NativeSceneView* view) + { + if (!NativeSceneViewValidation.IsValid(view) || view->Header.CanvasLayerCount == 0) return null; + var changes = new ReadOnlySpan(view->CanvasLayers, checked((int)view->Header.CanvasLayerCount)); + foreach (var layer in changes) + { + if ((layer.Flags & LayerRemove) != 0) continue; + if ((layer.Flags & LayerReplace) == 0 || !ValidateLayer(view, layer)) return null; + // A resumable prefix has already been checked and cannot contain + // mutable canvas dependencies. Inspect only its appended suffix. + var prefixCount = FindReplayPrefix(view, layer)?.CommandCount ?? 0; + foreach (ref readonly var command in new ReadOnlySpan(view->CanvasCommands + layer.CommandOffset + prefixCount, + checked((int)layer.CommandCount) - prefixCount)) + if (command.Kind == 27) return null; + } + var prepared = new PreparedCanvasLayers(view->Header.Revision, _presenterDeviceScaleFactor, + NativeTextShaping.FontRegistrationVersion); + try + { + foreach (var layer in changes) + if ((layer.Flags & LayerRemove) == 0) prepared.Layers.Add(layer.NodeId, CompileLayer(view, layer)); + return prepared; + } + catch { prepared.Dispose(); throw; } + } + + public bool ApplyDiff(NativeSceneView* view, bool orderedGpuImages = false, PreparedCanvasLayers? prepared = null) { var header = view->Header; var checkpoint = (header.Flags & SceneCheckpoint) != 0; + if (orderedGpuImages && (!ValidateOrderedGpuState(view) || + !ValidateOrderedCanvasPlacements(view, checkpoint))) return false; if (!checkpoint && header.Revision != s_revision && header.BaseRevision != s_revision) @@ -182,10 +260,19 @@ public bool ApplyDiff(NativeSceneView* view) if (shouldApply) { + var fontVersion = NativeTextShaping.FontRegistrationVersion; + if (_shaperFontVersion != fontVersion) + { + ClearShapers(); + _shaperFontVersion = fontVersion; + } _webTypefaceReference ??= _webTypefaces?.Retain(); if ((header.Flags & SceneDomReplacement) != 0) { - var compiledDom = CompileDom(view); + var ordered = orderedGpuImages ? CompileOrderedGpuDom(view) : null; + var compiledDom = orderedGpuImages ? default : CompileDom(view); + DisposeOrderedGpuDom(ordered); + _orderedGpuPaint = ordered; s_domBackdropPicture?.Dispose(); s_domOverlayPicture?.Dispose(); s_domBackdropPicture = compiledDom.Backdrop; @@ -254,7 +341,7 @@ public bool ApplyDiff(NativeSceneView* view) InstallReplacement( view, changes[index], - ref layerOrderChanged); + ref layerOrderChanged, prepared); compiled[index] = 1; remaining--; madeProgress = true; @@ -270,7 +357,7 @@ public bool ApplyDiff(NativeSceneView* view) InstallReplacement( view, changes[index], - ref layerOrderChanged); + ref layerOrderChanged, prepared); compiled[index] = 1; remaining--; break; @@ -294,15 +381,16 @@ public bool ApplyDiff(NativeSceneView* view) return true; } - private static bool LayerDependenciesAreCompiled( + private bool LayerDependenciesAreCompiled( NativeSceneView* view, in NativeCanvasLayer layer, ReadOnlySpan changes, ReadOnlySpan compiled) { + var prefixCount = FindReplayPrefix(view, layer)?.CommandCount ?? 0; var commands = new ReadOnlySpan( - view->CanvasCommands + layer.CommandOffset, - checked((int)layer.CommandCount)); + view->CanvasCommands + layer.CommandOffset + prefixCount, + checked((int)layer.CommandCount) - prefixCount); foreach (ref readonly var command in commands) { if (command.Kind != 27 || command.ResourceId == layer.NodeId) continue; @@ -322,12 +410,17 @@ private static bool LayerDependenciesAreCompiled( private void InstallReplacement( NativeSceneView* view, in NativeCanvasLayer change, - ref bool layerOrderChanged) + ref bool layerOrderChanged, + PreparedCanvasLayers? prepared = null) { - var replacement = CompileLayer(view, change); + var replacement = prepared?.Matches(view->Header.Revision, _presenterDeviceScaleFactor) == true + && prepared.Layers.Remove(change.NodeId, out var compiled) ? compiled : CompileLayer(view, change); var orderChanged = true; if (s_layers.Remove(change.NodeId, out var previous)) { + if (previous.Generation != replacement.Generation) + foreach (var key in s_strings.Keys.Where(key => key.NodeId == previous.NodeId).ToArray()) + s_strings.Remove(key); orderChanged = previous.ZOrder == replacement.ZOrder ? !ReplaceOrderedLayer(previous, replacement) : !RepositionOrderedLayer(previous, replacement); @@ -364,8 +457,15 @@ public void RenderRetained( SKCanvas canvas, float viewportWidth, float viewportHeight, - Func? intersects) + Func? intersects, + Action? drawGpuImage = null, + GRContext? canvasGpuContext = null) { + if (_orderedGpuPaint is not null) + { + RenderOrderedGpuDom(canvas, drawGpuImage, canvasGpuContext); + return; + } if (s_domBackdropPicture is not null && (intersects is null || intersects(new SKRect(0, 0, viewportWidth, viewportHeight)))) { @@ -378,19 +478,7 @@ public void RenderRetained( { continue; } - var save = canvas.Save(); - canvas.ClipRect(new SKRect(layer.X, layer.Y, layer.X + layer.Width, layer.Y + layer.Height)); - if (layer.RequiresIsolation) - { - // Browser canvases are independent transparent bitmaps. A - // destructive operation must affect this canvas only, then the - // result is source-over composited with lower siblings. - canvas.SaveLayer(); - } - canvas.Translate(layer.X, layer.Y); - canvas.Scale(layer.Width / layer.BitmapWidth, layer.Height / layer.BitmapHeight); - canvas.DrawPicture(layer.Picture); - canvas.RestoreToCount(save); + DrawRetainedCanvasLayer(canvas, layer, canvasGpuContext); } if (s_domOverlayPicture is not null && (intersects is null || intersects(new SKRect(0, 0, viewportWidth, viewportHeight)))) @@ -399,6 +487,21 @@ public void RenderRetained( } } + private static void DrawRetainedCanvasLayer(SKCanvas canvas, RetainedLayer layer, GRContext? gpuContext = null) + { + var backing = gpuContext is null ? null : MaterializeCanvasBacking(layer, gpuContext); + var save = canvas.Save(); + try + { + canvas.ClipRect(new SKRect(layer.X, layer.Y, layer.X + layer.Width, layer.Y + layer.Height)); + if (layer.RequiresIsolation && backing is null) canvas.SaveLayer(); + canvas.Translate(layer.X, layer.Y); + canvas.Scale(layer.Width / layer.BitmapWidth, layer.Height / layer.BitmapHeight); + canvas.DrawPicture(backing ?? layer.Picture); + } + finally { canvas.RestoreToCount(save); } + } + internal byte[]? CaptureCanvasPng(uint nodeId) { if (!s_layers.TryGetValue(nodeId, out var layer) @@ -499,7 +602,9 @@ private static bool ValidateLayer(NativeSceneView* view, in NativeCanvasLayer la && layer.StringOffset <= view->StringCount && layer.StringCount <= view->StringCount - layer.StringOffset; - private (SKPicture Backdrop, SKPicture Overlay) CompileDom(NativeSceneView* view) + private (SKPicture Backdrop, SKPicture Overlay) CompileDom(NativeSceneView* view, + int startIndex = 0, int endIndex = -1, bool mergePaintOrder = false, + Dictionary? sharedShapers = null) { using var backdropRecorder = new SKPictureRecorder(); using var overlayRecorder = new SKPictureRecorder(); @@ -509,7 +614,8 @@ private static bool ValidateLayer(NativeSceneView* view, in NativeCanvasLayer la Math.Max(1, view->Header.ViewportWidth), Math.Max(1, view->Header.ViewportHeight)); var backdrop = backdropRecorder.BeginRecording(recordingBounds); - var overlay = overlayRecorder.BeginRecording(recordingBounds); + var overlayRecording = overlayRecorder.BeginRecording(recordingBounds); + var overlay = mergePaintOrder ? backdrop : overlayRecording; var commands = new ReadOnlySpan( view->Commands, checked((int)view->Header.CommandCount)); @@ -522,12 +628,13 @@ private static bool ValidateLayer(NativeSceneView* view, in NativeCanvasLayer la Style = SKPaintStyle.Fill, TextAlign = SKTextAlign.Left }; - var textShapers = new Dictionary(StringComparer.Ordinal); + var textShapers = sharedShapers ?? new Dictionary(StringComparer.Ordinal); try { - for (var commandIndex = 0; commandIndex < commands.Length; commandIndex++) + var end = endIndex < 0 ? commands.Length : endIndex; + for (var commandIndex = startIndex; commandIndex < end; commandIndex++) { - if (BackgroundPaintIsFullyOccludedByLaterRoundedFill( + if (!mergePaintOrder && BackgroundPaintIsFullyOccludedByLaterRoundedFill( commands, commandIndex)) { @@ -710,10 +817,8 @@ private static bool ValidateLayer(NativeSceneView* view, in NativeCanvasLayer la } finally { - foreach (var shaper in textShapers.Values) - { - shaper.Dispose(); - } + if (sharedShapers is null) + foreach (var shaper in textShapers.Values) ReleaseShaper(shaper); } return (backdropRecorder.EndRecording(), overlayRecorder.EndRecording()); } @@ -1781,7 +1886,7 @@ private void DrawDomText( var shaperKey = parts[4] + '\t' + fontWeight.ToString(CultureInfo.InvariantCulture); if (!shapers.TryGetValue(shaperKey, out var shaper)) { - shaper = new SKShaper(typeface); + shaper = GetShaper(typeface); shapers.Add(shaperKey, shaper); } var tabularDigitScale = NativeTextShaping.ResolveTabularDigitScale( @@ -2452,6 +2557,8 @@ private static string DomStringAt(NativeSceneView* view, uint index) private RetainedLayer CompileLayer(NativeSceneView* view, in NativeCanvasLayer layer) { + if (UseIncrementalCanvasBacking) + return CompileIncrementalCanvasLayer(view, layer); var requiresIsolation = RequiresIsolation(view, layer); using var recorder = new SKPictureRecorder(); var canvas = recorder.BeginRecording(new SKRect( @@ -2493,6 +2600,7 @@ private bool RequiresIsolation( // A clear before the first draw is a no-op on the initially // transparent browser canvas and is omitted from the picture. case 24 when hasDrawn: + case CanvasCheckpointCommand: // drawImage(canvas) needs source-bitmap isolation semantics. case 27: // drawImage(SVGImageElement) needs the same crop/composite semantics. @@ -2630,21 +2738,26 @@ or 48 or 49 or 50 or 52 or 53 or 54 } } - private void Replay( + private CanvasReplaySnapshot? Replay( SKCanvas canvas, NativeSceneView* view, in NativeCanvasLayer layer, - bool skipLeadingClears = false) + bool skipLeadingClears = false, + CanvasReplaySnapshot? prefix = null, + bool captureContinuation = false) { - var state = CanvasState.Default; + var state = prefix?.State ?? CanvasState.Default; var states = new Stack(); var textShapers = new Dictionary(StringComparer.Ordinal); - var hasDrawn = false; - using var path = new SKPath(); + using var paints = new ReplayPaints(this); + var hasDrawn = prefix?.HasDrawn ?? false; + using var path = prefix is null ? new SKPath() : new SKPath(prefix.Path); + if (prefix is not null) canvas.SetMatrix(prefix.Matrix); + var canResume = true; var commands = new ReadOnlySpan( view->CanvasCommands + layer.CommandOffset, checked((int)layer.CommandCount)); - foreach (ref readonly var command in commands) + foreach (ref readonly var command in commands[(prefix?.CommandCount ?? 0)..]) { switch (command.Kind) { @@ -2761,6 +2874,7 @@ private void Replay( (float)(command.V1 + command.V3))); break; case 18: + canResume = false; path.FillType = (command.Flags & CanvasCommandEvenOdd) != 0 ? SKPathFillType.EvenOdd : SKPathFillType.Winding; @@ -2783,38 +2897,33 @@ private void Replay( break; } case 20: - using (var stroke = CreatePaint(state, false, SKPaintStyle.Stroke)) - { - canvas.DrawPath(path, stroke); - } + canvas.DrawPath(path, paints.Get(state, false).Paint); hasDrawn = true; break; case 21: - using (var fill = CreatePaint(state, true, SKPaintStyle.Fill)) { path.FillType = (command.Flags & CanvasCommandEvenOdd) != 0 ? SKPathFillType.EvenOdd : SKPathFillType.Winding; - canvas.DrawPath(path, fill); + canvas.DrawPath(path, paints.Get(state, true).Paint); } hasDrawn = true; break; case 22: - using (var fill = CreatePaint(state, true, SKPaintStyle.Fill)) - { - canvas.DrawRect(ToRect(command), fill); - } + canvas.DrawRect(ToRect(command), paints.Get(state, true).Paint); hasDrawn = true; break; case 23: - using (var stroke = CreatePaint(state, false, SKPaintStyle.Stroke)) - { - canvas.DrawRect(ToRect(command), stroke); - } + canvas.DrawRect(ToRect(command), paints.Get(state, false).Paint); hasDrawn = true; break; case 24 when skipLeadingClears && !hasDrawn: break; + case CanvasCheckpointCommand: + state = ReplayCheckpoint(canvas, path, StringAt(view, layer, command.ResourceId), + layer.BitmapWidth, layer.BitmapHeight); + hasDrawn = true; + break; case 24: using (var clear = new SKPaint { BlendMode = SKBlendMode.Clear, Style = SKPaintStyle.Fill }) { @@ -2822,14 +2931,15 @@ private void Replay( } break; case 25: - DrawText(canvas, view, layer, command, state, false, textShapers); + DrawText(canvas, view, layer, command, state, false, textShapers, paints); hasDrawn = true; break; case 26: - DrawText(canvas, view, layer, command, state, true, textShapers); + DrawText(canvas, view, layer, command, state, true, textShapers, paints); hasDrawn = true; break; case 27: + canResume = false; DrawCanvas(canvas, command, state); hasDrawn = true; break; @@ -2845,6 +2955,7 @@ private void Replay( AppendEllipse(path, command); break; case 31: + canResume = false; DrawSvgImage(canvas, view, layer, command, state); hasDrawn = true; break; @@ -2870,8 +2981,13 @@ private void Replay( } foreach (var shaper in textShapers.Values) { - shaper.Dispose(); + ReleaseShaper(shaper); } + return captureContinuation && canResume && states.Count == 0 + && ((layer.Flags & LayerUnchangedPrefix) != 0 || commands.Length * (long)sizeof(NativeCanvasCommand) <= 16 * 1024 * 1024) + && layer.StringCount <= 4096 + ? new CanvasReplaySnapshot(view, layer, state, canvas.TotalMatrix, path, hasDrawn, + _presenterDeviceScaleFactor, prefix) : null; } private void DrawSvgCanvasPath( @@ -3000,27 +3116,20 @@ private void DrawText( in NativeCanvasCommand command, in CanvasState state, bool stroke, - Dictionary shapers) + Dictionary shapers, + ReplayPaints paints) { var text = StringAt(view, layer, command.ResourceId); if (text.Length == 0) return; - using var paint = CreatePaint( - state, - !stroke, - stroke ? SKPaintStyle.Stroke : SKPaintStyle.Fill); - var font = ConfigureFont(paint, state.Font); - paint.TextAlign = state.TextAlign switch - { - "center" => SKTextAlign.Center, - "right" or "end" => SKTextAlign.Right, - _ => SKTextAlign.Left - }; + var prepared = paints.Get(state, !stroke, text: true); + var paint = prepared.Paint; + var font = prepared.Font; var y = (float)command.V1; var metrics = paint.FontMetrics; y += ResolveCanvasTextBaselineOffset(state.TextBaseline, metrics); if (!shapers.TryGetValue(state.Font, out var shaper)) { - shaper = new SKShaper(paint.Typeface); + shaper = GetShaper(paint.Typeface); shapers.Add(state.Font, shaper); } var featureFlags = NativeTextShaping.ResolveFeatureFlags( @@ -3111,6 +3220,48 @@ internal static float ConstrainCanvasTextWidth( : widthScale; } + // Paints are immutable after preparation and live only for one replay. + // Font registration cannot change mid-replay; the next scene resolves it + // again. Array identity makes dash changes safe (at worst a cache miss). + private sealed class ReplayPaints(NativeCanvasSceneRenderer owner) : IDisposable + { + private readonly record struct Key(string Color, double Alpha, bool Fill, + double Width, double Miter, string Cap, string Join, string Composite, + double[]? Dash, double DashOffset, string? Font, string? Align); + private readonly Dictionary _paints = []; + + public (SKPaint Paint, NativeTextShaping.CanvasFontDescription Font) Get( + in CanvasState state, bool fill, bool text = false) + { + var key = new Key(fill ? state.FillStyle : state.StrokeStyle, state.GlobalAlpha, fill, + state.LineWidth, state.MiterLimit, state.LineCap, state.LineJoin, state.Composite, + !fill && state.LineDash.Length > 0 ? state.LineDash : null, state.LineDashOffset, + text ? state.Font : null, text ? state.TextAlign : null); + if (_paints.TryGetValue(key, out var existing)) return existing; + if (_paints.Count >= 128) Dispose(); + var paint = CreatePaint(state, fill, fill ? SKPaintStyle.Fill : SKPaintStyle.Stroke); + try + { + var font = text ? owner.ConfigureFont(paint, state.Font) : default; + if (text) paint.TextAlign = state.TextAlign switch + { + "center" => SKTextAlign.Center, + "right" or "end" => SKTextAlign.Right, + _ => SKTextAlign.Left + }; + _paints.Add(key, (paint, font)); + return (paint, font); + } + catch { paint.Dispose(); throw; } + } + + public void Dispose() + { + foreach (var entry in _paints.Values) entry.Paint.Dispose(); + _paints.Clear(); + } + } + private static SKPaint CreatePaint(in CanvasState state, bool fill, SKPaintStyle style) { var color = ParseColor(fill ? state.FillStyle : state.StrokeStyle); @@ -3451,8 +3602,163 @@ private static SKColor Rgba(uint rgba) (byte)(rgba >> 8), (byte)rgba); + internal long ReusedDomPictureCount { get; private set; } + private sealed record DomPictureInput(byte[] Commands, string[] Resources, + float Width, float Height, float DeviceScale, long FontVersion) + { + public bool Matches(DomPictureInput other) => Width == other.Width && Height == other.Height + && DeviceScale == other.DeviceScale && FontVersion == other.FontVersion + && Commands.AsSpan().SequenceEqual(other.Commands) + && Resources.AsSpan().SequenceEqual(other.Resources); + } + private sealed record OrderedGpuPaint(SceneCommand Command, DomCornerRadii Radii, SKPicture? Picture, + DomPictureInput? Input = null); + + private bool ValidateOrderedCanvasPlacements(NativeSceneView* view, bool checkpoint) + { + if (view->Header.CanvasLayerCount != 0 && view->CanvasLayers == null) return false; + var visible = new HashSet(); + if (!checkpoint) foreach (var layer in s_layers.Values) + if (!layer.IsOffscreen) visible.Add(layer.NodeId); + foreach (var layer in new ReadOnlySpan(view->CanvasLayers, + checked((int)view->Header.CanvasLayerCount))) + { + if ((layer.Flags & LayerRemove) != 0 || (layer.Reserved & OffscreenCanvasLayer) != 0) + visible.Remove(layer.NodeId); + else visible.Add(layer.NodeId); + } + var placed = new HashSet(); + if ((view->Header.Flags & SceneDomReplacement) != 0) + { + foreach (var command in new ReadOnlySpan(view->Commands, + checked((int)view->Header.CommandCount))) + if (command.Kind == 257 && (!visible.Contains(command.NodeId) || !placed.Add(command.NodeId))) + return false; + } + else if (_orderedGpuPaint is not null && !checkpoint) + { + foreach (var entry in _orderedGpuPaint) + if (entry.Command.Kind == 257 && (!visible.Contains(entry.Command.NodeId) || !placed.Add(entry.Command.NodeId))) + return false; + } + return placed.SetEquals(visible); + } + + private static bool ValidateOrderedGpuState(NativeSceneView* view) + { + var stack = new Stack(); + foreach (var command in new ReadOnlySpan(view->Commands, checked((int)view->Header.CommandCount))) + { + if (command.Kind is 12 or 15 or 19 or 30) stack.Push(command.Kind); + else if (command.Kind is 13 or 16 or 20 or 31) + { + if (stack.Count == 0 || stack.Pop() != command.Kind - 1) return false; + } + } + return stack.Count == 0; + } + + private List CompileOrderedGpuDom(NativeSceneView* view) + { + var result = new List(); + var shapers = new Dictionary(StringComparer.Ordinal); + var commands = new ReadOnlySpan(view->Commands, checked((int)view->Header.CommandCount)); + var start = 0; + try + { + for (var index = 0; index <= commands.Length; ++index) + { + if (index != commands.Length && commands[index].Kind is not (12 or 13 or 15 or 16 or 19 or 20 or 30 or 31 or 256 or 257)) + continue; + if (start < index) + { + // Compare exact commands and resolved resources, not string + // IDs or a hash. Include the preceding command because it + // can supply corner radii for the first command in a span. + var input = new DomPictureInput( + MemoryMarshal.AsBytes(commands.Slice(Math.Max(0, start - 1), index - Math.Max(0, start - 1))).ToArray(), + commands.Slice(start, index - start).ToArray() + .Select(command => DomStringAt(view, command.Flags)).ToArray(), + view->Header.ViewportWidth, view->Header.ViewportHeight, + _presenterDeviceScaleFactor, NativeTextShaping.FontRegistrationVersion); + var previous = _orderedGpuPaint is not null && result.Count < _orderedGpuPaint.Count + ? _orderedGpuPaint[result.Count] : null; + if (previous?.Picture is not null && previous.Input?.Matches(input) == true) + { + ReusedDomPictureCount++; + result.Add(previous); + } + else + { + var pictures = CompileDom(view, start, index, mergePaintOrder: true, sharedShapers: shapers); + pictures.Overlay.Dispose(); + result.Add(new(default, default, pictures.Backdrop, input)); + } + } + if (index != commands.Length) + result.Add(new(commands[index], ResolveDomCornerRadii(commands, index), null)); + start = index + 1; + } + return result; + } + catch + { + var previousPictures = _orderedGpuPaint?.Select(entry => entry.Picture).ToHashSet(); + foreach (var entry in result) + if (previousPictures?.Contains(entry.Picture) != true) entry.Picture?.Dispose(); + throw; + } + finally { foreach (var shaper in shapers.Values) ReleaseShaper(shaper); } + } + private void DisposeOrderedGpuDom(List? retained = null) + { + if (_orderedGpuPaint is null) return; + var retainedPictures = retained?.Select(entry => entry.Picture).ToHashSet(); + foreach (var entry in _orderedGpuPaint) + if (retainedPictures?.Contains(entry.Picture) != true) entry.Picture?.Dispose(); + _orderedGpuPaint = null; + } + private void RenderOrderedGpuDom(SKCanvas canvas, Action? drawGpuImage, GRContext? gpuContext) + { + if (drawGpuImage is null) throw new InvalidOperationException("Ordered GPU replay requires an image renderer."); + var save = canvas.Save(); + using var opacity = new SKPaint(); + try + { + foreach (var entry in _orderedGpuPaint!) + { + if (entry.Picture is not null) { canvas.DrawPicture(entry.Picture); continue; } + var command = entry.Command; + switch (command.Kind) + { + case 12: + canvas.Save(); ClipDomRoundedRect(canvas, command, entry.Radii); break; + case 15: ApplyScale(canvas, command); break; + case 19: ApplyRotation(canvas, command); break; + case 30: + opacity.Color = new SKColor(255, 255, 255, (byte)(command.Rgba & 255)); + canvas.SaveLayer(opacity); break; + case 13: case 16: case 20: case 31: + // Never allow an invalid stream to pop the host's state. + if (canvas.SaveCount <= save + 1) throw new InvalidOperationException("Unbalanced GPU scene state."); + canvas.Restore(); break; + case 257: + if (!s_layers.TryGetValue(command.NodeId, out var layer) || layer.IsOffscreen) + throw new InvalidOperationException("Ordered canvas layer is unavailable."); + DrawRetainedCanvasLayer(canvas, layer, gpuContext); + break; + case 256: + drawGpuImage(command.Rgba, new SKRect(command.X, command.Y, + command.X + command.Width, command.Y + command.Height)); break; + } + } + } + finally { canvas.RestoreToCount(save); } + } + internal void Reset() { + DisposeOrderedGpuDom(); s_domBackdropPicture?.Dispose(); s_domBackdropPicture = null; s_domOverlayPicture?.Dispose(); @@ -3468,6 +3774,13 @@ internal void Reset() foreach (var svg in s_svgPictures.Values) svg.Dispose(); s_svgPictures.Clear(); s_strings.Clear(); + _checkpointAttempts.Clear(); +#if !WEBSCENE_UNO + _pendingReadback?.Dispose(); + _pendingReadback = null; +#endif + _pendingCheckpoint = null; // Encoder owns only its independent CPU snapshot. + ClearShapers(); s_revision = 0; s_totalCommandCount = 0; _webTypefaceReference?.Dispose(); @@ -3581,7 +3894,7 @@ public CanvasAffine Multiply(in CanvasAffine value) => (A * x + C * y + E, B * x + D * y + F); } - private sealed record RetainedLayer( + internal sealed record RetainedLayer( uint NodeId, ulong Generation, uint ZOrder, @@ -3597,11 +3910,21 @@ private sealed record RetainedLayer( SKPicture Picture) : IDisposable { public int OrderedIndex { get; set; } = -1; + internal CanvasReplaySnapshot? ReplaySnapshot { get; init; } + internal SKPicture? GpuPicture { get; set; } + internal GRContext? GpuContext { get; set; } + internal bool IsMaterialized { get; set; } + internal SKImage? CheckpointImage { get; set; } + internal CanvasPictureNode[]? CpuHistory { get; init; } - public void Dispose() => Picture.Dispose(); + public void Dispose() + { + ReplaySnapshot?.Dispose(); GpuPicture?.Dispose(); CheckpointImage?.Dispose(); Picture.Dispose(); + if (CpuHistory is not null) foreach (var node in CpuHistory) node.Dispose(); + } } - private struct CanvasState + internal struct CanvasState { public string FillStyle; public string StrokeStyle; diff --git a/src/WebScene.Backend.Avalonia/NativeCompositorFrameClock.cs b/src/WebScene.Backend.Avalonia/NativeCompositorFrameClock.cs new file mode 100644 index 000000000..8fcd0a308 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeCompositorFrameClock.cs @@ -0,0 +1,14 @@ +namespace WebScene.Backends.Avalonia.Native; + +// Diagnostic render timers may supply the display's QPC phase for the duration +// of their synchronous render-loop tick. Other hosts retain their existing clock. +internal static class NativeCompositorFrameClock +{ + [ThreadStatic] internal static long CurrentTimestamp; + internal static long AppliedTimestamps; + internal static long ReadTimestamp() + { + if (CurrentTimestamp != 0) Interlocked.Increment(ref AppliedTimestamps); + return CurrentTimestamp; + } +} diff --git a/src/WebScene.Backend.Avalonia/NativeGpuImageSampling.cs b/src/WebScene.Backend.Avalonia/NativeGpuImageSampling.cs new file mode 100644 index 000000000..3964b6180 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeGpuImageSampling.cs @@ -0,0 +1,35 @@ +using SkiaSharp; + +namespace WebScene.Backends.Avalonia.Native; + +internal static class NativeGpuImageSampling +{ +#if !WEBSCENE_AVALONIA12 + // Immutable shared default: no paint allocation for each video frame. + private static readonly SKPaint LinearPaint = new() { FilterQuality = SKFilterQuality.Low }; +#endif + + internal static void Draw(SKCanvas canvas, SKImage image, SKRect destination, SKPaint? paint = null) + { + // Imported textures have no mip chain. Bilinear sampling smooths Retina + // enlargement without CPU resizing, texture copies or per-frame mipmaps. +#if WEBSCENE_AVALONIA12 + canvas.DrawImage(image, destination, new SKSamplingOptions(SKFilterMode.Linear, SKMipmapMode.None), paint); +#else + if (paint is null) + { + canvas.DrawImage(image, destination, LinearPaint); + return; + } + // The active drawing lease owns the paint on this thread. Preserve its + // opacity/blend settings and restore it after the synchronous draw. + var quality = paint.FilterQuality; + try + { + paint.FilterQuality = SKFilterQuality.Low; + canvas.DrawImage(image, destination, paint); + } + finally { paint.FilterQuality = quality; } +#endif + } +} diff --git a/src/WebScene.Backend.Avalonia/NativeGpuRetirement.cs b/src/WebScene.Backend.Avalonia/NativeGpuRetirement.cs new file mode 100644 index 000000000..c19008156 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeGpuRetirement.cs @@ -0,0 +1,63 @@ +using System.Collections.Concurrent; +using System.Diagnostics; + +namespace WebScene.Backends.Avalonia.Native; + +// Owns detached presenters independently of their removed composition visual. +// Failed retirement stays retained for diagnosis; timeout/loss is not completion. +internal static class NativeGpuRetirement +{ + private sealed class Pending(NativeGpuScenePresenter presenter) + { + internal readonly NativeGpuScenePresenter Presenter = presenter; + internal readonly TaskCompletionSource Completion = new(TaskCreationOptions.RunContinuationsAsynchronously); + } + private static readonly ConcurrentDictionary Owners = new(); + private static long _nextId; + internal static int RetainedCount => Owners.Count; + + internal static Task Start(NativeGpuScenePresenter presenter) + { + ArgumentNullException.ThrowIfNull(presenter); + presenter.BeginShutdown(); + if (presenter.TryDiscardUnprepared()) return Task.CompletedTask; + var pending = new Pending(presenter); + var id = Interlocked.Increment(ref _nextId); + if (!Owners.TryAdd(id, pending)) throw new InvalidOperationException("GPU retirement identity collision."); + try + { + // Stop is delivered on the composition owner. Seal GPU reads here, + // before handing polling to a background task. In particular Metal + // session finalization is not covered by only the GRContext monitor. + presenter.SealForDetachedRetirement(); + if (presenter.TryCompleteWithoutVisual()) + { + Owners.TryRemove(id, out _); + pending.Completion.TrySetResult(); + return pending.Completion.Task; + } + _ = Task.Run(async () => + { + try + { + var started = Stopwatch.GetTimestamp(); + while (!presenter.TryCompleteWithoutVisual()) + { + if (Stopwatch.GetElapsedTime(started) > TimeSpan.FromSeconds(15)) + throw new TimeoutException("GPU retirement did not complete; resources remain retained."); + await Task.Delay(4).ConfigureAwait(false); + } + Owners.TryRemove(id, out _); + pending.Completion.TrySetResult(); + } + catch (Exception error) + { + Trace.TraceError($"GPU retirement {id} failed and retains its resources: {error}"); + pending.Completion.TrySetException(error); + } + }); + } + catch (Exception error) { pending.Completion.TrySetException(error); } + return pending.Completion.Task; + } +} diff --git a/src/WebScene.Backend.Avalonia/NativeGpuSceneImages.cs b/src/WebScene.Backend.Avalonia/NativeGpuSceneImages.cs new file mode 100644 index 000000000..2670589d3 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeGpuSceneImages.cs @@ -0,0 +1,158 @@ +using Avalonia.Skia; +using SkiaSharp; + +namespace WebScene.Backends.Avalonia.Native; + +// Indexed image ownership for one immutable scene version. Retain/Acquire are +// CPU-only; preparation, drawing and retirement require the presenter's lease. +// Keep this owner until TryComplete succeeds. GC cannot certify GPU completion. +internal sealed class NativeGpuSceneImages +{ + private readonly NativeGpuImageLeaseV3?[] _sources; + private readonly INativeRetainedGpuImage?[] _images; + private readonly NativeGpuImageInfoV3[] _metadata; + internal bool IsRetiring { get; private set; } + internal int ImportedCount { get; private set; } + internal int ImageCount => _images.Length; + private NativeGpuSceneImages(int count) + { + _sources = new NativeGpuImageLeaseV3?[count]; + _images = new INativeRetainedGpuImage?[count]; + _metadata = new NativeGpuImageInfoV3[count]; + } + + internal static NativeSceneAcquireStatus Acquire(NativeSceneLeaseV3 scene, out NativeGpuSceneImages? images) + { + ArgumentNullException.ThrowIfNull(scene); + return Capture(checked((int)scene.ImageCount), + (int index, out NativeGpuImageLeaseV3? image) => NativeGpuImageLeaseV3.Acquire(scene, (uint)index, out image), out images); + } + + internal static NativeSceneAcquireStatus Retain(IReadOnlyList sources, + out NativeGpuSceneImages? images) + { + ArgumentNullException.ThrowIfNull(sources); + return Capture(sources.Count, (int index, out NativeGpuImageLeaseV3? image) => sources[index].Retain(out image), out images); + } + + private delegate NativeSceneAcquireStatus CaptureImage(int index, out NativeGpuImageLeaseV3? image); + private static NativeSceneAcquireStatus Capture(int count, CaptureImage capture, out NativeGpuSceneImages? images) + { + images = null; + var candidate = new NativeGpuSceneImages(count); + try + { + for (var index = 0; index < count; ++index) + { + var status = capture(index, out candidate._sources[index]); + if (status != NativeSceneAcquireStatus.Success) return status; + var metadata = candidate._sources[index]!.Describe(); + if (metadata.Format != 2 || metadata.ColorSpace != 1 || + metadata.Alpha is not (1 or 2) || metadata.Orientation is not (1 or 2)) + throw new NotSupportedException("The scene importer requires BGRA8 sRGB with opaque or premultiplied alpha."); + candidate._metadata[index] = metadata; + } + images = candidate; + return NativeSceneAcquireStatus.Success; + } + finally + { + if (images is null) candidate.ReleaseSources(); + } + } + + // Rejecting an unapplied scene needs no graphics context because no import + // or GPU read has started. Imported groups must use Retire/TryComplete. + internal void DiscardUnprepared() + { + if (ImportedCount != 0) throw new InvalidOperationException("Imported scene images require GPU retirement."); + IsRetiring = true; + ReleaseSources(); + } + + private void ReleaseSources() + { + for (var index = 0; index < _sources.Length; ++index) + { + _sources[index]?.Dispose(); + _sources[index] = null; + } + } + + // Prepare every image before replaying the scene, so admission backpressure + // cannot leave only part of its GPU content drawn. Completed imports survive + // a retry and are never imported again for unchanged frames. + internal bool TryPrepare(ISkiaSharpApiLease lease) + { + if (IsRetiring) throw new InvalidOperationException("A retiring scene cannot be prepared."); + for (var index = 0; index < _sources.Length; ++index) + { + if (_images[index] is not null) continue; + var metadata = _metadata[index]; + var origin = metadata.Orientation == 1 ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft; + var alpha = metadata.Alpha == 1 ? SKAlphaType.Opaque : SKAlphaType.Premul; + INativeRetainedGpuImage? image = OperatingSystem.IsWindows() + ? NativeWindowsRetainedGpuImage.Import(_sources[index]!, lease, origin, alpha) + : NativeMetalRetainedGpuImage.Supports(lease) + ? NativeMetalRetainedGpuImage.Import(_sources[index]!, lease, origin, alpha) + : NativeMacOSRetainedGpuImage.Import(_sources[index]!, lease, + metadata.Orientation == 1 ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft, + metadata.Alpha == 1 ? SKAlphaType.Opaque : SKAlphaType.Premul); + if (image is null) return false; + _images[index] = image; + _sources[index]!.Dispose(); _sources[index] = null; + ++ImportedCount; + } + return true; + } + + internal void Draw(ISkiaSharpApiLease lease, uint index, SKRect destination) + { + if (IsRetiring || index >= _images.Length || _images[index] is not { } image) + throw new InvalidOperationException("Scene image is unavailable or retiring."); + image.Draw(lease, destination); + } + + internal void Retire(ISkiaSharpApiLease lease) + { + IsRetiring = true; + ReleaseSources(); + foreach (var image in _images) image?.Retire(lease); + } + + internal bool TryComplete(ISkiaSharpApiLease lease) + { + if (!IsRetiring) throw new InvalidOperationException("Scene retirement has not started."); + var complete = true; + for (var index = 0; index < _images.Length; ++index) + { + if (_images[index] is not { } image) continue; + // Retry a retirement that was interrupted by a host lease failure. + image.Retire(lease); + if (image.TryComplete(lease)) _images[index] = null; + else complete = false; + } + return complete; + } + internal bool TryRetireWithoutVisual() + { + IsRetiring = true; + ReleaseSources(); + var complete = true; + for (var index = 0; index < _images.Length; ++index) + { + if (_images[index] is not { } image) continue; + if (image.TryRetireWithoutVisual()) _images[index] = null; + else complete = false; + } + return complete; + } + + internal void SealForDetachedRetirement() + { + IsRetiring = true; + ReleaseSources(); + foreach (var image in _images) image?.SealForDetachedRetirement(); + } + +} diff --git a/src/WebScene.Backend.Avalonia/NativeGpuSceneInterop.cs b/src/WebScene.Backend.Avalonia/NativeGpuSceneInterop.cs new file mode 100644 index 000000000..1bd73dd81 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeGpuSceneInterop.cs @@ -0,0 +1,381 @@ +using System; +using System.Runtime.InteropServices; + +#if WEBSCENE_UNO +namespace WebScene.Backends.Uno.Native; +#else +namespace WebScene.Backends.Avalonia.Native; +#endif + +internal enum NativeSceneAcquireStatus : uint +{ + Success, Empty, InvalidArgument, UnsupportedVersion, UnsupportedCapabilities, + OutOfMemory, InternalError, Backpressure +} + +[StructLayout(LayoutKind.Sequential)] +internal struct NativeSceneAcquireOptionsV3 +{ + public uint StructSize, SceneVersion; + public ulong ConsumerCapabilities; + public static NativeSceneAcquireOptionsV3 CpuOnly => new() { StructSize = 16, SceneVersion = 3 }; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct NativeSceneViewV3 +{ + public uint StructSize, SceneVersion; + public ulong RequiredCapabilities; + // Borrowed until SceneReleaseV3; never release this CPU view separately. + public IntPtr CpuView, LeaseToken; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct NativeGpuImageInfoV3 +{ + public uint StructSize, Version; + public ulong Canvas, Allocation, AllocationGeneration, ContentSerial; + public ulong ProducerTimeline, ProducerValue; + public uint Width, Height, Format, Alpha, ColorSpace, Orientation; + public static NativeGpuImageInfoV3 Empty => new() { StructSize = 80, Version = 3 }; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct NativeGpuIOSurfaceViewV3 +{ + public uint StructSize, Version; + public IntPtr BorrowedIOSurface; + public ulong AllocationBytes; + public static NativeGpuIOSurfaceViewV3 Empty => new() + { + StructSize = (uint)Marshal.SizeOf(), Version = 3 + }; +} + +public static unsafe partial class NativeWebSceneApi +{ + // These declarations do not opt the existing renderer into GPU scenes. + internal const ulong GpuImageCapability = 1; + internal const uint GpuImagePaintCommand = 256; + internal const ulong OrderedCanvasCapability = 2; + internal const ulong ProducerGpuWaitCapability = 4; + internal const ulong CanvasCheckpointCapability = 8; + [DllImport(LibraryName, EntryPoint = "webscene_engine_submit_canvas_checkpoint_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern byte SubmitCanvasCheckpoint(IntPtr engine, uint nodeId, ulong generation, + uint commandCount, byte[] payload, nuint payloadLength); + internal const uint OrderedCanvasPaintCommand = 257; + + [DllImport(LibraryName, EntryPoint = "webscene_engine_acquire_latest_scene_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern NativeSceneAcquireStatus AcquireLatestSceneV3(IntPtr engine, in NativeSceneAcquireOptionsV3 options, out IntPtr scene); + [DllImport(LibraryName, EntryPoint = "webscene_engine_acquire_next_scene_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern NativeSceneAcquireStatus AcquireNextSceneV3(IntPtr engine, in NativeSceneAcquireOptionsV3 options, out IntPtr scene); + [DllImport(LibraryName, EntryPoint = "webscene_scene_acknowledge_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern byte SceneAcknowledgeV3(IntPtr scene); + [DllImport(LibraryName, EntryPoint = "webscene_scene_release_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern void SceneReleaseV3(IntPtr scene); + [DllImport(LibraryName, EntryPoint = "webscene_scene_gpu_image_count_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern uint SceneGpuImageCountV3(IntPtr scene); + [DllImport(LibraryName, EntryPoint = "webscene_scene_retain_gpu_image_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern NativeSceneAcquireStatus SceneRetainGpuImageV3(IntPtr scene, uint index, out IntPtr image); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_image_retain_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern NativeSceneAcquireStatus GpuImageRetainV3(IntPtr image, out IntPtr retained); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_image_describe_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern byte GpuImageDescribeV3(IntPtr image, ref NativeGpuImageInfoV3 info); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_image_release_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern void GpuImageReleaseV3(IntPtr image); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_image_begin_consumer_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern NativeSceneAcquireStatus GpuImageBeginConsumerV3(IntPtr image, out IntPtr consumer); + // Completion consumes the handle. Call only from a GPU completion path; + // Dispose/finalization of a CPU wrapper is not a GPU completion event. + [DllImport(LibraryName, EntryPoint = "webscene_gpu_image_complete_consumer_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern void GpuImageCompleteConsumerV3(IntPtr consumer); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_image_get_iosurface_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern byte GpuImageGetIOSurfaceV3(IntPtr consumer, ref NativeGpuIOSurfaceViewV3 view); + [DllImport(LibraryName, EntryPoint="webscene_gpu_image_dependency_count_v3", CallingConvention=CallingConvention.Cdecl)] + internal static extern byte GpuImageDependencyCountV3(IntPtr consumer,out uint count); + [DllImport(LibraryName, EntryPoint="webscene_gpu_image_get_metal_event_v3", CallingConvention=CallingConvention.Cdecl)] + internal static extern byte GpuImageGetMetalEventV3(IntPtr consumer,uint index,ref NativeGpuMetalEventViewV3 view); + + +} + +// CPU scene retention only: disposing this handle does not complete GPU work. +internal sealed class NativeSceneLeaseV3 : SafeHandle +{ + private NativeSceneLeaseV3() : base(IntPtr.Zero, ownsHandle: true) { } + public override bool IsInvalid => handle == IntPtr.Zero; + + internal static NativeSceneAcquireStatus Acquire(IntPtr engine, in NativeSceneAcquireOptionsV3 options, + bool ordered, out NativeSceneLeaseV3? lease) + { + lease = null; + // Allocate managed ownership before native acquisition, so an allocation + // failure cannot strand a newly acquired native lease. + var candidate = new NativeSceneLeaseV3(); + try + { + var status = ordered + ? NativeWebSceneApi.AcquireNextSceneV3(engine, in options, out var pointer) + : NativeWebSceneApi.AcquireLatestSceneV3(engine, in options, out pointer); + candidate.SetHandle(pointer); + if (status != NativeSceneAcquireStatus.Success) + { + candidate.Dispose(); + return status; + } + if (candidate.IsInvalid) throw new InvalidOperationException("Native acquisition returned an empty successful lease."); + lease = candidate; + return status; + } + catch + { + candidate.Dispose(); + throw; + } + } + + internal bool Acknowledge() => NativeWebSceneApi.SceneAcknowledgeV3(this) != 0; + internal uint ImageCount => NativeWebSceneApi.SceneGpuImageCountV3(this); + + // All borrowed pointers are valid only during this callback. The SafeHandle + // reference protects the native lease even if another thread calls Dispose. + internal void WithView(Action read) + { + ArgumentNullException.ThrowIfNull(read); + var added = false; + try + { + DangerousAddRef(ref added); + read(Marshal.PtrToStructure(handle)); + } + finally + { + if (added) DangerousRelease(); + } + } + + protected override bool ReleaseHandle() + { + NativeWebSceneApi.SceneReleaseV3(handle); + return true; + } +} + +public static unsafe partial class NativeWebSceneApi +{ + [DllImport(LibraryName, EntryPoint = "webscene_scene_acknowledge_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern byte SceneAcknowledgeV3(NativeSceneLeaseV3 scene); + [DllImport(LibraryName, EntryPoint = "webscene_scene_gpu_image_count_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern uint SceneGpuImageCountV3(NativeSceneLeaseV3 scene); +} + +internal sealed class NativeGpuImageLeaseV3 : SafeHandle +{ + private NativeGpuImageLeaseV3() : base(IntPtr.Zero, ownsHandle: true) { } + public override bool IsInvalid => handle == IntPtr.Zero; + + internal static NativeSceneAcquireStatus Acquire(NativeSceneLeaseV3 scene, uint index, + out NativeGpuImageLeaseV3? image) + { + ArgumentNullException.ThrowIfNull(scene); + image = null; + var candidate = new NativeGpuImageLeaseV3(); + try + { + var status = NativeWebSceneApi.SceneRetainGpuImageV3(scene, index, out var pointer); + return Adopt(candidate, status, pointer, out image); + } + catch { candidate.Dispose(); throw; } + } + + internal NativeSceneAcquireStatus Retain(out NativeGpuImageLeaseV3? retained) + { + retained = null; + var candidate = new NativeGpuImageLeaseV3(); + try + { + var status = NativeWebSceneApi.GpuImageRetainV3(this, out var pointer); + return Adopt(candidate, status, pointer, out retained); + } + catch { candidate.Dispose(); throw; } + } + + private static NativeSceneAcquireStatus Adopt(NativeGpuImageLeaseV3 candidate, + NativeSceneAcquireStatus status, IntPtr pointer, out NativeGpuImageLeaseV3? image) + { + image = null; + candidate.SetHandle(pointer); + if (status != NativeSceneAcquireStatus.Success) + { + candidate.Dispose(); + return status; + } + if (candidate.IsInvalid) throw new InvalidOperationException("Native retain returned an empty successful lease."); + image = candidate; + return status; + } + + internal NativeGpuImageInfoV3 Describe() + { + var info = NativeGpuImageInfoV3.Empty; + if (NativeWebSceneApi.GpuImageDescribeV3(this, ref info) == 0) + throw new InvalidOperationException("Native image metadata is unavailable."); + return info; + } + + protected override bool ReleaseHandle() + { + NativeWebSceneApi.GpuImageReleaseV3(handle); + return true; + } +} + +public static unsafe partial class NativeWebSceneApi +{ + [DllImport(LibraryName, EntryPoint = "webscene_scene_retain_gpu_image_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern NativeSceneAcquireStatus SceneRetainGpuImageV3(NativeSceneLeaseV3 scene, uint index, out IntPtr image); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_image_retain_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern NativeSceneAcquireStatus GpuImageRetainV3(NativeGpuImageLeaseV3 image, out IntPtr retained); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_image_describe_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern byte GpuImageDescribeV3(NativeGpuImageLeaseV3 image, ref NativeGpuImageInfoV3 info); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_image_begin_consumer_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern NativeSceneAcquireStatus GpuImageBeginConsumerV3(NativeGpuImageLeaseV3 image, out IntPtr consumer); +} + + +// Explicit GPU completion ownership: deliberately neither IDisposable nor a +// finalizable SafeHandle. The presenter must retain this wrapper until its GPU +// completion path calls Complete; GC cannot certify that GPU use has ended. +[StructLayout(LayoutKind.Sequential)] +internal struct NativeGpuMetalEventViewV3 +{ + internal uint StructSize, Version; + internal IntPtr BorrowedSharedEvent; + internal ulong SignaledValue; + internal static NativeGpuMetalEventViewV3 Empty => new() { StructSize=(uint)Marshal.SizeOf(), Version=3 }; +} + +internal sealed class NativeGpuImageConsumerV3 +{ + private readonly object _gate = new(); + private IntPtr _handle; + private int _borrows; + private bool _completionRequested; + private NativeGpuImageConsumerV3() { } + + internal static NativeSceneAcquireStatus Acquire(NativeGpuImageLeaseV3 image, + out NativeGpuImageConsumerV3? consumer) + { + ArgumentNullException.ThrowIfNull(image); + consumer = null; + var candidate = new NativeGpuImageConsumerV3(); + var status = NativeWebSceneApi.GpuImageBeginConsumerV3(image, out candidate._handle); + if (status != NativeSceneAcquireStatus.Success) return status; + if (candidate._handle == IntPtr.Zero) + throw new InvalidOperationException("Native consumer acquisition returned an empty handle."); + consumer = candidate; + return status; + } + + // Borrow is synchronous. Importers must take their own required native + // references and preserve this consumer until the associated GPU fence. + internal bool WithIOSurface(Action import) + { + ArgumentNullException.ThrowIfNull(import); + IntPtr pointer; + lock (_gate) + { + if (_completionRequested) throw new InvalidOperationException("GPU consumer already completed."); + _borrows++; + pointer = _handle; + } + try + { + var view = NativeGpuIOSurfaceViewV3.Empty; + try + { + if (NativeWebSceneApi.GpuImageGetIOSurfaceV3(pointer, ref view) == 0) return false; + } + catch (EntryPointNotFoundException) { return false; } // Older v3 runtime lacks this optional hook. + if (view.BorrowedIOSurface == IntPtr.Zero || view.AllocationBytes == 0) + throw new InvalidOperationException("Native IOSurface lookup returned an invalid view."); + import(view); + return true; + } + finally + { + IntPtr retired = IntPtr.Zero; + lock (_gate) + { + _borrows--; + if (_completionRequested && _borrows == 0) { retired = _handle; _handle = IntPtr.Zero; } + } + if (retired != IntPtr.Zero) NativeWebSceneApi.GpuImageCompleteConsumerV3(retired); + } + } + + // Event pointers are borrowed only for this callback. Consumer completion + // requested concurrently or reentrantly waits for this borrow to leave. + internal void WithMetalEvents(Action use) + { + ArgumentNullException.ThrowIfNull(use); + IntPtr pointer; + lock(_gate) { + if(_completionRequested) throw new InvalidOperationException("GPU consumer already completed."); + ++_borrows; pointer=_handle; + } + try { + if(NativeWebSceneApi.GpuImageDependencyCountV3(pointer,out var count)==0) + throw new InvalidOperationException("Producer dependency lookup failed."); + var events=new NativeGpuMetalEventViewV3[checked((int)count)]; + for(uint i=0;i(Func use) + { + IntPtr pointer; + lock (_gate) + { + if (_completionRequested) throw new InvalidOperationException("GPU consumer already completed."); + ++_borrows; pointer = _handle; + } + try { return use(pointer); } + finally + { + IntPtr retired = IntPtr.Zero; + lock (_gate) + { + --_borrows; + if (_completionRequested && _borrows == 0) { retired = _handle; _handle = IntPtr.Zero; } + } + if (retired != IntPtr.Zero) NativeWebSceneApi.GpuImageCompleteConsumerV3(retired); + } + } +} diff --git a/src/WebScene.Backend.Avalonia/NativeGpuScenePresenter.cs b/src/WebScene.Backend.Avalonia/NativeGpuScenePresenter.cs new file mode 100644 index 000000000..72bd95bb2 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeGpuScenePresenter.cs @@ -0,0 +1,188 @@ +using Avalonia.Skia; +using SkiaSharp; + +namespace WebScene.Backends.Avalonia.Native; + +// Serialized by the composition owner. Replacement is CPU-only; all imported +// resources stay here until retirement completes under the host graphics lease. +internal enum NativeGpuSceneApplyResult { Applied, Backpressure, InvalidScene, RejectedDiff, AcknowledgementFailed } + +internal sealed class NativeGpuScenePresenter +{ + private NativeGpuSceneImages? _current; + private bool _hostInspected; + internal bool SupportsProducerGpuWaits { get; private set; } + private readonly NativeGpuSceneImages?[] _retiring = new NativeGpuSceneImages?[2]; + private bool _prepared; + internal bool IsStopping { get; private set; } + internal int ImportedCount { get; private set; } + internal bool HasPendingRetirements => Array.Exists(_retiring, image => image is not null) || (IsStopping && _current is not null); + + // On false, ownership remains with the caller. Never replace a visible + // group's ownership until there is bounded space to retire it safely. + internal bool TryReplace(NativeGpuSceneImages images) + { + ArgumentNullException.ThrowIfNull(images); + if (IsStopping) return false; + if (images.IsRetiring || Array.Exists(_retiring, image => ReferenceEquals(image, images))) + throw new InvalidOperationException("A retiring scene cannot become current."); + if (ReferenceEquals(_current, images)) return true; + if (_current is not null) + { + if (_current.ImportedCount == 0) + { + // An intermediate mailbox scene never borrowed a GPU image. + // Release its CPU leases without consuming a fence-retirement slot. + _current.DiscardUnprepared(); + } + else + { + var slot = Array.FindIndex(_retiring, image => image is null); + if (slot < 0) return false; + _retiring[slot] = _current; + } + } + _current = images; + _prepared = false; + return true; + } + + // Apply under the composition owner's serialization. Image retention is + // completed before mutating the renderer; acknowledge only after both the + // renderer and its indexed image bindings have accepted the same version. + internal unsafe NativeGpuSceneApplyResult ApplyScene(NativeSceneLeaseV3 scene, NativeCanvasSceneRenderer renderer, NativeSceneRenderObserver? observer = null, + NativeCanvasSceneRenderer.PreparedCanvasLayers? prepared = null) + { + ArgumentNullException.ThrowIfNull(scene); + ArgumentNullException.ThrowIfNull(renderer); + if (IsStopping || ((_current?.ImportedCount ?? 0) != 0 && Array.TrueForAll(_retiring, image => image is not null))) + return NativeGpuSceneApplyResult.Backpressure; + var result = NativeGpuSceneApplyResult.InvalidScene; + scene.WithView(view => + { + var supported = NativeWebSceneApi.GpuImageCapability | NativeWebSceneApi.OrderedCanvasCapability | NativeWebSceneApi.CanvasCheckpointCapability + | (SupportsProducerGpuWaits ? NativeWebSceneApi.ProducerGpuWaitCapability : 0UL); + if (view.SceneVersion != 3 || view.StructSize != System.Runtime.InteropServices.Marshal.SizeOf() || + (view.RequiredCapabilities & ~supported) != 0 || !NativeSceneViewValidation.IsValid((NativeSceneView*)view.CpuView)) return; + var status = NativeGpuSceneImages.Acquire(scene, out var images); + observer?.RecordScheduling("apply:images-retained", 0, ((NativeSceneView*)view.CpuView)->Header.Revision, HasPendingRetirements); + if (status == NativeSceneAcquireStatus.Backpressure) { result = NativeGpuSceneApplyResult.Backpressure; return; } + if (status != NativeSceneAcquireStatus.Success || images is null) + throw new InvalidOperationException($"Scene image retention failed: {status}"); + try + { + var cpu = (NativeSceneView*)view.CpuView; + foreach (var command in new ReadOnlySpan(cpu->Commands, checked((int)cpu->Header.CommandCount))) + if (command.Kind == NativeWebSceneApi.GpuImagePaintCommand && + ((view.RequiredCapabilities & NativeWebSceneApi.GpuImageCapability) == 0 || command.Rgba >= images.ImageCount)) return; + if (!renderer.ApplyDiff((NativeSceneView*)view.CpuView, orderedGpuImages: (view.RequiredCapabilities & supported) != 0, prepared)) + { result = NativeGpuSceneApplyResult.RejectedDiff; return; } + observer?.RecordScheduling("apply:cpu-applied", 0, cpu->Header.Revision, HasPendingRetirements); + if (!TryReplace(images)) throw new InvalidOperationException("Scene replacement lost its serialized admission slot."); + images = null; // Presenter now owns the bindings used by this renderer version. + observer?.RecordScheduling("apply:replaced", 0, cpu->Header.Revision, HasPendingRetirements); + result = scene.Acknowledge() ? NativeGpuSceneApplyResult.Applied : NativeGpuSceneApplyResult.AcknowledgementFailed; + } + finally { images?.DiscardUnprepared(); } + }); + return result; + } + + // A visual that never imported an image can stop synchronously. Once an + // import exists, shutdown must retain the visual's graphics retirement path. + internal bool TryDiscardUnprepared() + { + if ((_current?.ImportedCount ?? 0) != 0 || Array.Exists(_retiring, image => (image?.ImportedCount ?? 0) != 0)) return false; + IsStopping = true; + _current?.DiscardUnprepared(); _current = null; + for (var index = 0; index < _retiring.Length; ++index) + { _retiring[index]?.DiscardUnprepared(); _retiring[index] = null; } + _prepared = false; + return true; + } + + private void DrainRetirements(ISkiaSharpApiLease lease) + { + for (var index = 0; index < _retiring.Length; ++index) + { + if (_retiring[index] is not { } image) continue; + image.Retire(lease); + if (image.TryComplete(lease)) _retiring[index] = null; + } + } + + // Recheck retired groups after recording the current frame, while the + // composition owner still holds its graphics lease. Never poll by waiting. + internal void PollRetirementsAfterDraw(ISkiaSharpApiLease lease) + { + if (!IsStopping) DrainRetirements(lease); + } + + // Windows retirement is sealed on the graphics owner during draw. Poll its + // D3D11 completion fences before admitting the next producer frame, so an + // already completed consumer does not occupy a pool slot for another vsync. + // No GL context, new signal or CPU wait is allowed on this path. + internal void PollWindowsRetirementsBeforeFrame() + { + if (IsStopping || !OperatingSystem.IsWindows()) return; + for (var index = 0; index < _retiring.Length; ++index) + if (_retiring[index] is { } image && image.TryRetireWithoutVisual()) + _retiring[index] = null; + } + + internal bool TryPrepare(ISkiaSharpApiLease lease) + { + if (IsStopping) throw new InvalidOperationException("Scene presenter is stopping."); + if (!_hostInspected) + { + SupportsProducerGpuWaits = OperatingSystem.IsWindows() + ? NativeWindowsRetainedGpuImage.Supports(lease) + : NativeMetalRetainedGpuImage.Supports(lease); + _hostInspected = true; + } + DrainRetirements(lease); + if (_current is null) return false; + var before = _current.ImportedCount; + try { return _prepared = _current.TryPrepare(lease); } + finally { ImportedCount += _current.ImportedCount - before; } + } + + internal void Draw(ISkiaSharpApiLease lease, uint index, SKRect destination) + { + if (IsStopping || !_prepared || _current is null) + throw new InvalidOperationException("Scene presenter is not ready to draw."); + _current.Draw(lease, index, destination); + } + + // The host must continue graphics callbacks until TryComplete returns true, + // including when ordinary scene rendering has stopped or is hidden. + internal void BeginShutdown() => IsStopping = true; + internal void SealForDetachedRetirement() + { + if (!IsStopping) throw new InvalidOperationException("Scene presenter has not begun shutdown."); + foreach (var image in _retiring) image?.SealForDetachedRetirement(); + _current?.SealForDetachedRetirement(); + } + internal bool TryComplete(ISkiaSharpApiLease lease) + { + if (!IsStopping) throw new InvalidOperationException("Scene presenter has not begun shutdown."); + DrainRetirements(lease); + if (_current is not null) + { + _current.Retire(lease); + if (_current.TryComplete(lease)) _current = null; + } + return !HasPendingRetirements; + } + // Transfer this presenter exclusively to the retirement worker after stop. + // No replacement, preparation or drawing may run concurrently with it. + internal bool TryCompleteWithoutVisual() + { + if (!IsStopping) throw new InvalidOperationException("Scene presenter has not begun shutdown."); + for (var index = 0; index < _retiring.Length; ++index) + if (_retiring[index] is { } image && image.TryRetireWithoutVisual()) _retiring[index] = null; + if (_current is not null && _current.TryRetireWithoutVisual()) _current = null; + return !HasPendingRetirements; + } + +} diff --git a/src/WebScene.Backend.Avalonia/NativeMacOSGpuConsumerFence.cs b/src/WebScene.Backend.Avalonia/NativeMacOSGpuConsumerFence.cs new file mode 100644 index 000000000..0d107eeac --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeMacOSGpuConsumerFence.cs @@ -0,0 +1,96 @@ +using System; +using System.Runtime.InteropServices; +using Avalonia.Skia; +using Avalonia.OpenGL; + +namespace WebScene.Backends.Avalonia.Native; + +// Owns consumer retirement after the host's final GL use. The host must retain +// and poll this object in its current context; there is no GC-based completion. +internal sealed class NativeMacOSGpuConsumerFence +{ + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate IntPtr FenceSync(uint condition, uint flags); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate uint ClientWaitSync(IntPtr sync, uint flags, ulong timeout); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void DeleteSync(IntPtr sync); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void Flush(); + + private readonly IntPtr _context; + private readonly int _thread = Environment.CurrentManagedThreadId; + private readonly ClientWaitSync _wait; + private readonly DeleteSync _delete; + private readonly NativeGpuImageConsumerV3 _consumer; + private IntPtr _fence; + + private NativeMacOSGpuConsumerFence(IntPtr context, ClientWaitSync wait, DeleteSync delete, + NativeGpuImageConsumerV3 consumer) + { + _context = context; _wait = wait; _delete = delete; _consumer = consumer; + } + + // Resolve from the host GlInterface.GetProcAddress, not global GL symbols + // that might name ANGLE. Ownership transfers only when this call succeeds. + internal static NativeMacOSGpuConsumerFence Create(Func getProcAddress, + NativeGpuImageConsumerV3 consumer) + { + ArgumentNullException.ThrowIfNull(getProcAddress); + ArgumentNullException.ThrowIfNull(consumer); + var context = NativeMacOSGpuImageImport.CurrentContext; + if (context == IntPtr.Zero) throw new InvalidOperationException("A current CGL context is required."); + T Resolve(string name) where T : Delegate + { + var address = getProcAddress(name); + if (address == IntPtr.Zero) throw new NotSupportedException($"Host GL entry point {name} is unavailable."); + return Marshal.GetDelegateForFunctionPointer(address); + } + var insert = Resolve("glFenceSync"); + var wait = Resolve("glClientWaitSync"); + var delete = Resolve("glDeleteSync"); + var flush = Resolve("glFlush"); + var result = new NativeMacOSGpuConsumerFence(context, wait, delete, consumer); + result._fence = insert(0x9117, 0); // GL_SYNC_GPU_COMMANDS_COMPLETE + if (result._fence == IntPtr.Zero) throw new InvalidOperationException("Host GL fence creation failed."); + flush(); + return result; + } + + internal bool TryComplete() => Poll(requireOriginalThread: true); + + // Avalonia can migrate its context between UI and compositor threads. The + // live platform lease supplies host serialization; native context identity + // must still match. Standalone callers retain the strict thread check above. + internal bool TryCompleteInHostLease(ISkiaSharpPlatformGraphicsApiLease lease) + { + ArgumentNullException.ThrowIfNull(lease); + if (lease.Context is not IGlContext) throw new NotSupportedException("A host GL lease is required."); + return Poll(requireOriginalThread: false); + } + + // Caller holds this host context's EnsureCurrent scope. Retirement code + // also holds Avalonia's GRContext monitor before touching Skia resources. + internal bool TryCompleteInSerializedHostContext(IGlContext context) + { + ArgumentNullException.ThrowIfNull(context); + if (context.IsLost) throw new InvalidOperationException("Host graphics context is lost."); + return Poll(requireOriginalThread: false); + } + + private bool Poll(bool requireOriginalThread) + { + if ((requireOriginalThread && _thread != Environment.CurrentManagedThreadId) || + _context != NativeMacOSGpuImageImport.CurrentContext) + throw new InvalidOperationException("GPU fence polling requires its owning thread and CGL context."); + if (_fence == IntPtr.Zero) return true; + var status = _wait(_fence, 0, 0); // Zero timeout: never wait for the device on the CPU. + if (status == 0x911B) return false; // GL_TIMEOUT_EXPIRED + if (status != 0x911A && status != 0x911C) // ALREADY_SIGNALED / CONDITION_SATISFIED + throw new InvalidOperationException("Host GL fence polling failed; consumer remains retained."); + _delete(_fence); + _fence = IntPtr.Zero; + _consumer.Complete(); + return true; + } +} diff --git a/src/WebScene.Backend.Avalonia/NativeMacOSGpuImageImport.cs b/src/WebScene.Backend.Avalonia/NativeMacOSGpuImageImport.cs new file mode 100644 index 000000000..5c83e5493 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeMacOSGpuImageImport.cs @@ -0,0 +1,69 @@ +using System; +using System.Runtime.InteropServices; +using SkiaSharp; + +namespace WebScene.Backends.Avalonia.Native; + +// Import into a rectangle texture already bound by the host's GlInterface. +// The caller owns GL state and must keep the consumer through GPU completion. +// This does not opt the retained renderer into GPU scenes or synchronize writes. +internal static class NativeMacOSGpuImageImport +{ + private const string OpenGL = "/System/Library/Frameworks/OpenGL.framework/OpenGL"; + private const string IOSurface = "/System/Library/Frameworks/IOSurface.framework/IOSurface"; + + internal static IntPtr CurrentContext => OperatingSystem.IsMacOS() ? CGLGetCurrentContext() : IntPtr.Zero; + + internal static bool TryBindCurrentRectangleTexture(NativeGpuImageConsumerV3 consumer) + { + ArgumentNullException.ThrowIfNull(consumer); + if (!OperatingSystem.IsMacOS()) return false; + var context = CGLGetCurrentContext(); + if (context == IntPtr.Zero) return false; + var imported = false; + return consumer.WithIOSurface(view => + { + var width = IOSurfaceGetWidth(view.BorrowedIOSurface); + var height = IOSurfaceGetHeight(view.BorrowedIOSurface); + if (width == 0 || height == 0 || width > int.MaxValue || height > int.MaxValue) return; + // The native pool exposes negotiated BGRA8 only. CGL requires this + // tuple for the packed BGRA IOSurface storage on the tested route. + imported = CGLTexImageIOSurface2D(context, 0x84F5, 0x8058, + (int)width, (int)height, 0x80E1, 0x8367, view.BorrowedIOSurface, 0) == 0; + }) && imported; + } + + // Wrap the texture successfully imported above in the same host GL context. + // Neither this SKImage nor its disposal owns/completes the native consumer. + // Keep both the GL texture and consumer until the last submitted Skia read + // has completed; retained redraw also needs a live image/consumer lease. + internal static SKImage? TryWrapRectangleTexture(NativeGpuImageConsumerV3 consumer, + GRContext context, uint texture, GRSurfaceOrigin origin, SKAlphaType alpha) + { + ArgumentNullException.ThrowIfNull(consumer); + ArgumentNullException.ThrowIfNull(context); + if (CurrentContext == IntPtr.Zero || texture == 0) return null; + SKImage? image = null; + consumer.WithIOSurface(view => + { + var width = IOSurfaceGetWidth(view.BorrowedIOSurface); + var height = IOSurfaceGetHeight(view.BorrowedIOSurface); + if (width == 0 || height == 0 || width > int.MaxValue || height > int.MaxValue) return; + using var backend = new GRBackendTexture((int)width, (int)height, false, + new GRGlTextureInfo(0x84F5, texture, 0x8058)); + // GL exposes RGBA channels despite the IOSurface's BGRA byte storage. + image = SKImage.FromTexture(context, backend, origin, SKColorType.Rgba8888, alpha); + }); + return image; + } + + [DllImport(OpenGL, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr CGLGetCurrentContext(); + [DllImport(OpenGL, CallingConvention = CallingConvention.Cdecl)] + private static extern int CGLTexImageIOSurface2D(IntPtr context, uint target, uint internalFormat, + int width, int height, uint format, uint type, IntPtr surface, uint plane); + [DllImport(IOSurface, CallingConvention = CallingConvention.Cdecl)] + private static extern nuint IOSurfaceGetWidth(IntPtr surface); + [DllImport(IOSurface, CallingConvention = CallingConvention.Cdecl)] + private static extern nuint IOSurfaceGetHeight(IntPtr surface); +} diff --git a/src/WebScene.Backend.Avalonia/NativeMacOSRetainedGpuImage.cs b/src/WebScene.Backend.Avalonia/NativeMacOSRetainedGpuImage.cs new file mode 100644 index 000000000..03e5166dd --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeMacOSRetainedGpuImage.cs @@ -0,0 +1,128 @@ +using System; +using Avalonia.OpenGL; +using Avalonia.Skia; +using SkiaSharp; + +namespace WebScene.Backends.Avalonia.Native; + +// Host-context ownership of one imported image version. The scene cache must +// retain this object through Retire/TryComplete; GC is never GPU completion. +internal sealed class NativeMacOSRetainedGpuImage : INativeRetainedGpuImage +{ + private readonly IGlContext _host; + private readonly GRContext _skia; + private readonly IntPtr _nativeContext = NativeMacOSGpuImageImport.CurrentContext; + private NativeGpuImageConsumerV3? _consumer; + private SKImage? _image; + private NativeMacOSGpuConsumerFence? _fence; + private int _texture; + internal bool IsRetiring { get; private set; } + private NativeMacOSRetainedGpuImage(IGlContext host, GRContext skia) + { _host = host; _skia = skia; } + + // The producer must already have certified readiness. Admission failure + // leaves the scene's retained lease untouched so the caller can retry. + internal static NativeMacOSRetainedGpuImage? Import(NativeGpuImageLeaseV3 image, + ISkiaSharpApiLease lease, GRSurfaceOrigin origin, SKAlphaType alpha) + { + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(lease); + var skia = lease.GrContext ?? throw new NotSupportedException("A GPU Skia context is required."); + using var platform = lease.TryLeasePlatformGraphicsApi() + ?? throw new NotSupportedException("A host graphics API lease is required."); + if (platform.Context is not IGlContext host || NativeMacOSGpuImageImport.CurrentContext == IntPtr.Zero) + throw new NotSupportedException("The macOS IOSurface route requires a current host CGL context."); + var result = new NativeMacOSRetainedGpuImage(host, skia); + var status = NativeGpuImageConsumerV3.Acquire(image, out result._consumer); + if (status == NativeSceneAcquireStatus.Backpressure) return null; + if (status != NativeSceneAcquireStatus.Success) + throw new InvalidOperationException($"GPU image consumer acquisition failed: {status}."); + try + { + result._texture = host.GlInterface.GenTexture(); + if (result._texture == 0) throw new InvalidOperationException("Host texture allocation failed."); + host.GlInterface.BindTexture(0x84F5, result._texture); + if (!NativeMacOSGpuImageImport.TryBindCurrentRectangleTexture(result._consumer!)) + throw new NotSupportedException("Host IOSurface import failed."); + result._image = NativeMacOSGpuImageImport.TryWrapRectangleTexture(result._consumer!, skia, + (uint)result._texture, origin, alpha) + ?? throw new NotSupportedException("Host Ganesh rectangle wrapping failed."); + return result; + } + catch + { + // Import has not issued a draw: no consumer GPU read needs fencing. + result._image?.Dispose(); + if (result._texture != 0) host.GlInterface.DeleteTexture(result._texture); + result._consumer!.Complete(); + throw; + } + } + private void Check(ISkiaSharpApiLease lease) + { + // Avalonia may migrate the same context between its UI and render + // threads. Its active drawing lease serializes access to the GRContext. + if (!ReferenceEquals(lease.GrContext, _skia) || + NativeMacOSGpuImageImport.CurrentContext != _nativeContext) + throw new InvalidOperationException("GPU image use requires its owning leased Skia and CGL contexts."); + } + public void Draw(ISkiaSharpApiLease lease, SKRect destination, SKPaint? paint = null) + { + Check(lease); + if (IsRetiring) throw new InvalidOperationException("A retiring GPU image cannot be drawn again."); + NativeGpuImageSampling.Draw(lease.SkCanvas, _image!, destination, paint); + } + public void Retire(ISkiaSharpApiLease lease) + { + Check(lease); + if (_fence is not null || _consumer is null) return; + IsRetiring = true; + _image?.Dispose(); _image = null; + using var platform = lease.TryLeasePlatformGraphicsApi() + ?? throw new NotSupportedException("Host graphics API lease disappeared during retirement."); + if (!ReferenceEquals(platform.Context, _host)) throw new InvalidOperationException("Host graphics context changed."); + // Entering Avalonia's platform lease flushes Skia's final reads. On fence + // failure retain ownership and allow retry; never fabricate completion. + _fence = NativeMacOSGpuConsumerFence.Create(_host.GlInterface.GetProcAddress, _consumer); + } + public bool TryComplete(ISkiaSharpApiLease lease) + { + Check(lease); + if (!IsRetiring) throw new InvalidOperationException("Retire the GPU image before polling completion."); + if (_consumer is null) return true; + if (_fence is null) return false; + using var platform = lease.TryLeasePlatformGraphicsApi() + ?? throw new NotSupportedException("Host graphics API lease disappeared during completion."); + if (!ReferenceEquals(platform.Context, _host)) throw new InvalidOperationException("Host graphics context changed."); + if (!_fence.TryCompleteInHostLease(platform)) return false; + _consumer = null; + _host.GlInterface.DeleteTexture(_texture); _texture = 0; + return true; + } + // Avalonia 11.3.4 takes the CGL lock via EnsureCurrent before the GRContext + // monitor during drawing. Use the same order after a visual is detached; + // never touch a live drawing lease or wait for GPU completion on the CPU. + public bool TryRetireWithoutVisual() + { + using var current = _host.EnsureCurrent(); + lock (_skia) + { + if (NativeMacOSGpuImageImport.CurrentContext != _nativeContext || _skia.IsAbandoned) + throw new InvalidOperationException("Detached retirement lost its host context."); + IsRetiring = true; + try + { + _image?.Dispose(); _image = null; + if (_consumer is null) return true; + _skia.Flush(); + _fence ??= NativeMacOSGpuConsumerFence.Create(_host.GlInterface.GetProcAddress, _consumer); + if (!_fence.TryCompleteInSerializedHostContext(_host)) return false; + _consumer = null; + _host.GlInterface.DeleteTexture(_texture); _texture = 0; + return true; + } + finally { _skia.ResetContext(); } + } + } + +} diff --git a/src/WebScene.Backend.Avalonia/NativeMetalBackendTexture.cs b/src/WebScene.Backend.Avalonia/NativeMetalBackendTexture.cs new file mode 100644 index 000000000..72a120cb2 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeMetalBackendTexture.cs @@ -0,0 +1,35 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.InteropServices; +using SkiaSharp; + +namespace WebScene.Backends.Avalonia.Native; + +// Pinned SkiaSharp 2.88 native Metal entry point; its generic managed assembly +// omits the Metal constructor. This owns the backend wrapper, not the texture. +internal static unsafe class NativeMetalBackendTexture +{ + [DllImport("libSkiaSharp", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr gr_backendtexture_new_metal(int width, int height, + [MarshalAs(UnmanagedType.I1)] bool mipmapped, IntPtr* textureInfo); + [DllImport("libSkiaSharp", CallingConvention = CallingConvention.Cdecl)] + private static extern void gr_backendtexture_delete(IntPtr texture); + + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(GRBackendTexture))] + internal static GRBackendTexture Create(int width, int height, IntPtr metalTexture) + { + if (!OperatingSystem.IsMacOS()) throw new PlatformNotSupportedException(); + if (width <= 0) throw new ArgumentOutOfRangeException(nameof(width)); + if (height <= 0) throw new ArgumentOutOfRangeException(nameof(height)); + if (metalTexture == IntPtr.Zero) throw new ArgumentException("A retained Metal texture is required.", nameof(metalTexture)); + var constructor = typeof(GRBackendTexture).GetConstructor( + BindingFlags.Instance | BindingFlags.NonPublic, null, + new[] { typeof(IntPtr), typeof(bool) }, null) + ?? throw new MissingMethodException("GRBackendTexture(IntPtr, bool)"); + var handle = gr_backendtexture_new_metal(width, height, false, &metalTexture); + if (handle == IntPtr.Zero) throw new InvalidOperationException("Metal backend texture wrapping failed."); + try { return (GRBackendTexture)constructor.Invoke(new object[] { handle, true }); } + catch { gr_backendtexture_delete(handle); throw; } + } +} diff --git a/src/WebScene.Backend.Avalonia/NativeMetalConsumerFence.cs b/src/WebScene.Backend.Avalonia/NativeMetalConsumerFence.cs new file mode 100644 index 000000000..e2a357b9c --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeMetalConsumerFence.cs @@ -0,0 +1,42 @@ +using System; +using System.Runtime.InteropServices; + +namespace WebScene.Backends.Avalonia.Native; + +// Caller must flush/submit Skia reads before inserting this marker while holding +// the same host queue lease. Own this fence and the image until TryComplete succeeds. +internal sealed class NativeMetalConsumerFence +{ + private IntPtr _command; + private bool _completed; + private NativeMetalConsumerFence(IntPtr command) => _command = command; + + internal static NativeMetalConsumerFence Insert(IntPtr queue) + { + if (!OperatingSystem.IsMacOS()) throw new PlatformNotSupportedException(); + if (queue == IntPtr.Zero) throw new ArgumentException("A leased Metal queue is required.", nameof(queue)); + var command = SendObject(queue, Selector("commandBuffer")); + if (command == IntPtr.Zero) throw new InvalidOperationException("Metal retirement marker allocation failed."); + SendObject(command, Selector("retain")); + SendVoid(command, Selector("commit")); + return new NativeMetalConsumerFence(command); + } + + internal bool TryComplete() + { + if (_completed) return true; + var status = SendUInt(_command, Selector("status")); + if (status == 5) throw new InvalidOperationException("Metal retirement command failed; image ownership must remain retained."); + if (status != 4) return false; // MTLCommandBufferStatusCompleted + SendVoid(_command, Selector("release")); + _command = IntPtr.Zero; + _completed = true; + return true; + } + // No finalizer: abandoning this object must not fabricate consumer completion. + private const string ObjC = "/usr/lib/libobjc.A.dylib"; + [DllImport(ObjC, EntryPoint="sel_registerName")] private static extern IntPtr Selector(string name); + [DllImport(ObjC, EntryPoint="objc_msgSend")] private static extern IntPtr SendObject(IntPtr receiver, IntPtr selector); + [DllImport(ObjC, EntryPoint="objc_msgSend")] private static extern void SendVoid(IntPtr receiver, IntPtr selector); + [DllImport(ObjC, EntryPoint="objc_msgSend")] private static extern nuint SendUInt(IntPtr receiver, IntPtr selector); +} diff --git a/src/WebScene.Backend.Avalonia/NativeMetalIOSurfaceTexture.cs b/src/WebScene.Backend.Avalonia/NativeMetalIOSurfaceTexture.cs new file mode 100644 index 000000000..c49d399f6 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeMetalIOSurfaceTexture.cs @@ -0,0 +1,56 @@ +using System; +using System.Runtime.InteropServices; + +namespace WebScene.Backends.Avalonia.Native; + +// Owns a Metal texture view of an existing IOSurface; does not copy pixels. +// The caller must keep the consumer and this texture until GPU reads complete. +internal sealed class NativeMetalIOSurfaceTexture : IDisposable +{ + internal IntPtr Handle { get; private set; } + internal int Width { get; } + internal int Height { get; } + private NativeMetalIOSurfaceTexture(IntPtr texture, int width, int height) + { Handle = texture; Width = width; Height = height; } + + internal static NativeMetalIOSurfaceTexture Import(IntPtr device, NativeGpuImageConsumerV3 consumer) + { + if (!OperatingSystem.IsMacOS()) throw new PlatformNotSupportedException(); + ArgumentNullException.ThrowIfNull(consumer); + if (device == IntPtr.Zero) throw new ArgumentException("A leased Metal device is required.", nameof(device)); + NativeMetalIOSurfaceTexture? result = null; + consumer.WithIOSurface(view => + { + var width = IOSurfaceGetWidth(view.BorrowedIOSurface); + var height = IOSurfaceGetHeight(view.BorrowedIOSurface); + if (width == 0 || height == 0 || width > int.MaxValue || height > int.MaxValue + || IOSurfaceGetPixelFormat(view.BorrowedIOSurface) != 0x42475241) + throw new NotSupportedException("Metal import requires a nonempty BGRA IOSurface."); + var descriptor = CreateDescriptor(GetClass("MTLTextureDescriptor"), + Selector("texture2DDescriptorWithPixelFormat:width:height:mipmapped:"), + 80, width, height, false); // MTLPixelFormatBGRA8Unorm + SetValue(descriptor, Selector("setUsage:"), 1); // ShaderRead + var texture = CreateTexture(device, Selector("newTextureWithDescriptor:iosurface:plane:"), + descriptor, view.BorrowedIOSurface, 0); + if (texture == IntPtr.Zero) throw new InvalidOperationException("Metal IOSurface import failed."); + result = new NativeMetalIOSurfaceTexture(texture, (int)width, (int)height); + }); + return result ?? throw new NotSupportedException("The native consumer exposes no IOSurface."); + } + public void Dispose() + { + var texture = Handle; Handle = IntPtr.Zero; + if (texture != IntPtr.Zero) Release(texture, Selector("release")); + } + private const string ObjC = "/usr/lib/libobjc.A.dylib"; + private const string Surface = "/System/Library/Frameworks/IOSurface.framework/IOSurface"; + [DllImport(ObjC, EntryPoint="objc_getClass")] private static extern IntPtr GetClass(string name); + [DllImport(ObjC, EntryPoint="sel_registerName")] private static extern IntPtr Selector(string name); + [DllImport(ObjC, EntryPoint="objc_msgSend")] private static extern IntPtr CreateDescriptor(IntPtr receiver, IntPtr selector, ulong format, nuint width, nuint height, [MarshalAs(UnmanagedType.I1)] bool mipmapped); + [DllImport(ObjC, EntryPoint="objc_msgSend")] private static extern IntPtr CreateTexture(IntPtr receiver, IntPtr selector, IntPtr descriptor, IntPtr surface, nuint plane); + [DllImport(ObjC, EntryPoint="objc_msgSend")] private static extern void SetValue(IntPtr receiver, IntPtr selector, ulong value); + [DllImport(ObjC, EntryPoint="objc_msgSend")] private static extern void Release(IntPtr receiver, IntPtr selector); + [DllImport(Surface)] private static extern nuint IOSurfaceGetWidth(IntPtr surface); + [DllImport(Surface)] private static extern nuint IOSurfaceGetHeight(IntPtr surface); + [DllImport(Surface)] private static extern uint IOSurfaceGetPixelFormat(IntPtr surface); +} diff --git a/src/WebScene.Backend.Avalonia/NativeMetalProducerWait.cs b/src/WebScene.Backend.Avalonia/NativeMetalProducerWait.cs new file mode 100644 index 000000000..e5c03dced --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeMetalProducerWait.cs @@ -0,0 +1,27 @@ +using System; +using System.Runtime.InteropServices; +namespace WebScene.Backends.Avalonia.Native; + +internal static class NativeMetalProducerWait +{ + // Caller holds the Metal platform lease. Native consumer owns every borrowed + // event until submission; Metal command encoding retains its dependencies. + internal static void Submit(IntPtr queue, NativeGpuImageConsumerV3 consumer) + { + if(queue==IntPtr.Zero) throw new ArgumentException("A leased Metal queue is required.",nameof(queue)); + consumer.WithMetalEvents(events => { + if(events.Length==0) return; + // WithMetalEvents validates the entire list before invoking us. + var command=SendObject(queue,Selector("commandBuffer")); + if(command==IntPtr.Zero) throw new InvalidOperationException("Metal producer barrier allocation failed."); + foreach(var dependency in events) + EncodeWait(command,Selector("encodeWaitForEvent:value:"),dependency.BorrowedSharedEvent,dependency.SignaledValue); + SendVoid(command,Selector("commit")); + }); + } + private const string ObjC="/usr/lib/libobjc.A.dylib"; + [DllImport(ObjC,EntryPoint="sel_registerName")] private static extern IntPtr Selector(string name); + [DllImport(ObjC,EntryPoint="objc_msgSend")] private static extern IntPtr SendObject(IntPtr receiver,IntPtr selector); + [DllImport(ObjC,EntryPoint="objc_msgSend")] private static extern void EncodeWait(IntPtr receiver,IntPtr selector,IntPtr sharedEvent,ulong value); + [DllImport(ObjC,EntryPoint="objc_msgSend")] private static extern void SendVoid(IntPtr receiver,IntPtr selector); +} diff --git a/src/WebScene.Backend.Avalonia/NativeMetalRetainedGpuImage.cs b/src/WebScene.Backend.Avalonia/NativeMetalRetainedGpuImage.cs new file mode 100644 index 000000000..5ef1d785b --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeMetalRetainedGpuImage.cs @@ -0,0 +1,112 @@ +using System; +using Avalonia.Platform; +using Avalonia.Skia; +using SkiaSharp; +namespace WebScene.Backends.Avalonia.Native; + +// Completion-certified producer route. Early producer admission remains disabled. +internal sealed class NativeMetalRetainedGpuImage : INativeRetainedGpuImage +{ + private readonly IPlatformGraphicsContext _host; + private readonly GRContext _skia; + private readonly IntPtr _queue; + private NativeGpuImageConsumerV3? _consumer; + private NativeMetalIOSurfaceTexture? _texture; + private SKImage? _image; + private NativeMetalConsumerFence? _fence; + private bool _retiring; + private NativeMetalRetainedGpuImage(IPlatformGraphicsContext host, GRContext skia, IntPtr queue) + { _host=host; _skia=skia; _queue=queue; } + internal static bool Supports(ISkiaSharpApiLease lease) + { + using var platform=lease.TryLeasePlatformGraphicsApi(); + return platform?.Context is global::Avalonia.Metal.IMetalDevice; + } + internal static NativeMetalRetainedGpuImage? Import(NativeGpuImageLeaseV3 source, + ISkiaSharpApiLease lease, GRSurfaceOrigin origin, SKAlphaType alpha) + { + var skia=lease.GrContext ?? throw new NotSupportedException("GPU Skia context required"); + NativeMetalRetainedGpuImage result; + using(var platform=lease.TryLeasePlatformGraphicsApi() + ?? throw new NotSupportedException("Metal platform lease required")) + { + var host=platform.Context; + var metal=host as global::Avalonia.Metal.IMetalDevice + ?? throw new NotSupportedException("Metal host required"); + // Avalonia hides these members in its reference assembly. Reflect on + // the known interface type so NativeAOT preserves the accessors. + var type=typeof(global::Avalonia.Metal.IMetalDevice); + var device=(IntPtr)type.GetProperty("Device")!.GetValue(metal)!; + var queue=(IntPtr)type.GetProperty("CommandQueue")!.GetValue(metal)!; + result=new(host,skia,queue); + var status=NativeGpuImageConsumerV3.Acquire(source,out result._consumer); + if(status==NativeSceneAcquireStatus.Backpressure) return null; + if(status!=NativeSceneAcquireStatus.Success) throw new InvalidOperationException($"Metal consumer acquisition failed: {status}"); + try { + result._texture=NativeMetalIOSurfaceTexture.Import(device,result._consumer!); + NativeMetalProducerWait.Submit(queue,result._consumer!); + } + catch { result._texture?.Dispose(); result._consumer!.Complete(); throw; } + } + try { + using var backend=NativeMetalBackendTexture.Create(result._texture.Width,result._texture.Height,result._texture.Handle); + result._image=SKImage.FromTexture(skia,backend,origin,SKColorType.Bgra8888,alpha) + ?? throw new InvalidOperationException("Metal Skia image wrapping failed"); + return result; + } catch { result._texture.Dispose(); result._consumer!.Complete(); throw; } + } + private void Check(ISkiaSharpApiLease lease) + { + if(!ReferenceEquals(lease.GrContext,_skia)) throw new InvalidOperationException("Metal image belongs to another Skia context"); + // The GRContext identifies the owning device/queue. The active Skia lease + // serializes it; opening a platform lease here would flush every draw. + } + public void Draw(ISkiaSharpApiLease lease,SKRect destination,SKPaint? paint=null) + { + Check(lease); + if(_retiring) throw new InvalidOperationException("Retiring image cannot be drawn"); + NativeGpuImageSampling.Draw(lease.SkCanvas, _image!, destination, paint); + } + public void Retire(ISkiaSharpApiLease lease) + { + Check(lease); + _retiring=true; + if(_fence is not null || _consumer is null) return; + _image?.Dispose(); _image=null; + _skia.Flush(true,false); + using var platform=lease.TryLeasePlatformGraphicsApi() + ?? throw new NotSupportedException("Metal retirement requires a host lease"); + _fence=NativeMetalConsumerFence.Insert(_queue); + } + public bool TryComplete(ISkiaSharpApiLease lease) + { + Check(lease); + if(!_retiring) throw new InvalidOperationException("Retirement not started"); + return Complete(); + } + private bool Complete() + { + if(_consumer is null) return true; + if(_fence is null || !_fence.TryComplete()) return false; + _texture!.Dispose(); _texture=null; + _consumer.Complete(); _consumer=null; + return true; + } + public bool TryRetireWithoutVisual() + { + // Once sealed on the composition owner, background retirement only + // observes the command buffer. Never flush a live Skia session there. + if (_fence is not null || _consumer is null) return Complete(); + using var current=_host.EnsureCurrent(); + lock(_skia) + { + if(_skia.IsAbandoned) throw new InvalidOperationException("Metal context abandoned before retirement"); + _retiring=true; + if(_consumer is null) return true; + _image?.Dispose(); _image=null; + _skia.Flush(true,false); + _fence ??= NativeMetalConsumerFence.Insert(_queue); + return Complete(); + } + } +} diff --git a/src/WebScene.Backend.Avalonia/NativeSceneComposition.cs b/src/WebScene.Backend.Avalonia/NativeSceneComposition.cs index 6fe923ae0..9a2a92899 100644 --- a/src/WebScene.Backend.Avalonia/NativeSceneComposition.cs +++ b/src/WebScene.Backend.Avalonia/NativeSceneComposition.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Text.Json; +using System.Text.Json.Nodes; #if !WEBSCENE_UNO using Avalonia; using Avalonia.Controls; @@ -50,6 +51,7 @@ internal enum NativeSceneCompositionMessage internal sealed class NativeScenePublicationMailbox { + internal event Action? Published; private long _published; private long _consumed; @@ -63,7 +65,10 @@ public long PendingCount } public void Publish() - => Interlocked.Increment(ref _published); + { + Interlocked.Increment(ref _published); + Published?.Invoke(); + } public bool TryConsume() { @@ -180,7 +185,12 @@ public readonly record struct NativeResizeSubmissionSample( public readonly record struct NativeSceneRenderSample( long Timestamp, ulong Revision, - ulong ConsumedInputSequence); + ulong ConsumedInputSequence) +{ + // Stopwatch timestamp after the compositor accepts this revision; zero + // when unavailable. Neither timestamp certifies platform presentation. + public long AcceptedTimestamp { get; init; } +} internal static class NativeSceneResizeProjection { @@ -225,6 +235,16 @@ internal readonly record struct NativeSceneDamage( double SummedArea) { public static NativeSceneDamage None => default; + + internal NativeSceneDamage Combine(in NativeSceneDamage following) + { + if (!RequiresRender) return following; + if (!following.RequiresRender) return this; + return new NativeSceneDamage(true, IsFull || following.IsFull, + Bounds.Union(following.Bounds), + RectangleCount + following.RectangleCount, + SummedArea + following.SummedArea); + } } internal static class NativeSceneDamagePolicy @@ -431,6 +451,94 @@ internal sealed unsafe class NativeSceneCompositionHandler : CompositionCustomVisualHandler { private readonly IntPtr _engine; + private bool _stopped; + private readonly object _engineAccessGate = new(); + private bool _engineAccessRevoked; + private readonly bool _asyncCanvasPreparation; + private readonly bool _singleScenePerFrame = + OperatingSystem.IsWindows() + && Environment.GetEnvironmentVariable("WEBSCENE_SINGLE_SCENE_PER_FRAME") == "1"; + private int _canvasPreparationQueued; + private bool _canvasPreparationFailed; + private PreparedCanvasScene? _preparedCanvasScene; + + private sealed class PreparedCanvasScene(NativeSceneLeaseV3 scene, NativeCanvasSceneRenderer.PreparedCanvasLayers layers) : IDisposable + { + private NativeSceneLeaseV3? _scene = scene; + public NativeCanvasSceneRenderer.PreparedCanvasLayers Layers => layers; + public NativeSceneLeaseV3 TakeScene() + { + var result = _scene ?? throw new InvalidOperationException("Prepared scene was already consumed."); + _scene = null; + return result; + } + public void Dispose() { _scene?.Dispose(); _scene = null; layers.Dispose(); } + } + + private void OnCanvasPublication() => QueueCanvasPreparation(fromPublication: true); + + private void QueueCanvasPreparation(bool fromPublication = false) + { + if (!_asyncCanvasPreparation || (!fromPublication && (_canvasPreparationFailed || !_running || _manualFrames + || _preparedCanvasScene is not null || _publicationMailbox.PendingCount == 0)) + || Interlocked.CompareExchange(ref _canvasPreparationQueued, 1, 0) != 0) return; + ThreadPool.QueueUserWorkItem(state => + { + try + { + lock (_engineAccessGate) + { + if (_engineAccessRevoked || _stopped || _canvasPreparationFailed || !_running || _manualFrames + || _hasPendingRenderMetrics + || _preparedCanvasScene is not null || _publicationMailbox.PendingCount == 0) return; + var options = NativeSceneAcquireOptionsV3.CpuOnly; + options.ConsumerCapabilities = NativeWebSceneApi.GpuImageCapability | NativeWebSceneApi.OrderedCanvasCapability | NativeWebSceneApi.CanvasCheckpointCapability + | (_gpuPresenter?.SupportsProducerGpuWaits == true ? NativeWebSceneApi.ProducerGpuWaitCapability : 0UL); + var status = NativeSceneLeaseV3.Acquire(_engine, in options, true, out var scene); + if (status != NativeSceneAcquireStatus.Success || scene is null) return; + NativeCanvasSceneRenderer.PreparedCanvasLayers? layers = null; + try + { + scene.WithView(view => + { + if (view.SceneVersion == 3 && view.StructSize == System.Runtime.InteropServices.Marshal.SizeOf()) + { + var cpu = (NativeSceneView*)view.CpuView; + _renderObserver.RecordScheduling("prepare:start", 0, cpu->Header.Revision, false); + layers = _renderer.PrepareCanvasLayers((NativeSceneView*)view.CpuView); + _renderObserver.RecordScheduling("prepare:end", 0, cpu->Header.Revision, false); + } + }); + if (layers is null) return; + _preparedCanvasScene = new PreparedCanvasScene(scene, layers); + scene = null; layers = null; + } + finally { scene?.Dispose(); layers?.Dispose(); } + } + } + catch (Exception error) + { + lock (_engineAccessGate) _canvasPreparationFailed = true; + Console.Error.WriteLine("Canvas preparation fell back to synchronous compilation: " + error); + } + finally { Volatile.Write(ref _canvasPreparationQueued, 0); } + }); + } + + internal void RevokeEngineAccess() + { + // Join any engine-using callback before the UI destroys the engine. + // No compositor tick or GPU completion is needed to revoke access. + lock (_engineAccessGate) + { + _engineAccessRevoked = true; + _publicationMailbox.Published -= OnCanvasPublication; + _preparedCanvasScene?.Dispose(); _preparedCanvasScene = null; + } + } + private NativeGpuScenePresenter? _gpuPresenter; + private bool _gpuNeedsRender; + internal Task GpuRetirement { get; private set; } = Task.CompletedTask; private readonly NativeCanvasSceneRenderer _renderer = new(); private readonly NativeSceneRenderObserver _renderObserver; private readonly NativePerformanceInstrumentation _performanceInstrumentation; @@ -446,6 +554,7 @@ internal sealed unsafe class NativeSceneCompositionHandler private bool _animationFrameScheduled; private long _liveResizeFrameDeadlineTimestamp; private bool _hasPendingRenderMetrics; + private long _pendingAcceptedTimestamp; private NativeSceneDamage _pendingDamage; private SceneHeader _pendingRenderHeader; private long _pendingDiffApplyTicks; @@ -474,8 +583,12 @@ public NativeSceneCompositionHandler( NativeSceneUiWakeGate uiWakeGate, NativePerformanceInstrumentation performanceInstrumentation, Action scheduleUiWake, - double deviceScaleFactor) + double deviceScaleFactor, + bool enableGpuScenes = false) { + if (enableGpuScenes && !OperatingSystem.IsMacOS() && !OperatingSystem.IsWindows()) + throw new PlatformNotSupportedException("GPU composition requires a supported macOS or Windows graphics host."); + _gpuPresenter = enableGpuScenes ? new NativeGpuScenePresenter() : null; _engine = engine; _renderObserver = renderObserver; _publicationMailbox = publicationMailbox; @@ -483,9 +596,28 @@ public NativeSceneCompositionHandler( _performanceInstrumentation = performanceInstrumentation; _scheduleUiWake = scheduleUiWake; _renderer.SetPresenterDeviceScaleFactor(deviceScaleFactor); + _asyncCanvasPreparation = enableGpuScenes && OperatingSystem.IsWindows() + && Environment.GetEnvironmentVariable("WEBSCENE_ASYNC_CANVAS_PREPARATION") == "1"; + if (_asyncCanvasPreparation) _publicationMailbox.Published += OnCanvasPublication; } public override void OnMessage(object message) + { + lock (_engineAccessGate) + { + if (_engineAccessRevoked && !Equals(message, NativeSceneCompositionMessage.Stop)) + { + if (message is NativeCanvasCaptureRequest canvas) + canvas.TrySetException(new ObjectDisposedException(nameof(NativeSceneCompositionHandler))); + if (message is NativeSceneCaptureRequest scene) + scene.TrySetException(new ObjectDisposedException(nameof(NativeSceneCompositionHandler))); + return; + } + OnMessageCore(message); + } + } + + private void OnMessageCore(object message) { if (message is NativeCanvasCaptureRequest canvasCapture) { @@ -523,6 +655,10 @@ public override void OnMessage(object message) return; } + // A detached handler can still receive messages already queued by the UI. + // Stop is terminal: its engine may be destroyed once this batch applies. + if (_stopped) return; + if (command is NativeSceneCompositionMessage.TextScale1X or NativeSceneCompositionMessage.TextScaleRetina) { @@ -621,10 +757,16 @@ public override void OnMessage(object message) return; } + _stopped = true; + _publicationMailbox.Published -= OnCanvasPublication; + _preparedCanvasScene?.Dispose(); _preparedCanvasScene = null; _running = false; _manualFrames = false; _animationFrameScheduled = false; _uiWakeGate.Complete(); + var retiring = _gpuPresenter; + _gpuPresenter = null; _gpuNeedsRender = false; + if (retiring is not null) GpuRetirement = NativeGpuRetirement.Start(retiring); _renderer.Reset(); _appliedRevision = 0; _viewportWidth = 0; @@ -636,6 +778,15 @@ public override void OnMessage(object message) } public override void OnAnimationFrameUpdate() + { + lock (_engineAccessGate) + { + if (_engineAccessRevoked) return; + OnAnimationFrameUpdateCore(); + } + } + + private void OnAnimationFrameUpdateCore() { _animationFrameScheduled = false; if (!_running) @@ -648,13 +799,17 @@ public override void OnAnimationFrameUpdate() { Interlocked.Increment(ref AnimationFrameCount); } - var frameTimestamp = Stopwatch.GetTimestamp(); + _renderObserver.RecordScheduling("frame", _publicationMailbox.PendingCount, + _appliedRevision, _gpuPresenter?.HasPendingRetirements == true); + var frameTimestamp = NativeCompositorFrameClock.ReadTimestamp(); + if (frameTimestamp == 0) frameTimestamp = Stopwatch.GetTimestamp(); var frameTimestampMilliseconds = frameTimestamp * 1000.0 / Stopwatch.Frequency; + _gpuPresenter?.PollWindowsRetirementsBeforeFrame(); NativeWebSceneApi.EngineObserveCompositorFrame( _engine, frameTimestampMilliseconds); - if (frameTimestamp + if (Stopwatch.GetTimestamp() > Interlocked.Read(ref _liveResizeFrameDeadlineTimestamp)) { var demand = NativeWebSceneApi.EngineRequiresAnimationFrame(_engine); @@ -722,10 +877,28 @@ private void RequestRenderIfNeeded() if (!_hasPendingRenderMetrics && (!TryAcquireNextDiff(out damage) || !damage.RequiresRender)) { - _invalidationGate.Complete(); - return; + if (!_gpuNeedsRender && _gpuPresenter?.HasPendingRetirements != true) + { _invalidationGate.Complete(); return; } + damage = new NativeSceneDamage(true, true, default, 0, 0); } + // Consume at most the second slot already offered by the ordered + // mailbox. Apply both diffs in order, then draw the newest coherent + // scene once. Manual certification retains one scene per boundary. + // Optional single-scene pacing preserves queued intermediate frames, + // trading one refresh of latency for steadier drawing cadence. + if (_gpuPresenter is not null && _running && !_manualFrames + && !_singleScenePerFrame + && _publicationMailbox.PendingCount > 0 + && TryAcquireNextDiff(out var followingDamage)) + { + damage = damage.Combine(followingDamage); + _pendingDamage = damage; + } + + if (_gpuNeedsRender && !damage.RequiresRender) + { damage = new NativeSceneDamage(true, true, default, 0, 0); _pendingDamage = damage; } + if (_performanceInstrumentation.IsEnabled) { Interlocked.Increment(ref InvalidationCallCount); @@ -745,6 +918,7 @@ private void RequestRenderIfNeeded() private bool TryAcquireNextDiff(out NativeSceneDamage damage) { + if (_gpuPresenter is not null) return TryAcquireNextGpuDiff(out damage); damage = NativeSceneDamage.None; var scene = NativeWebSceneApi.EngineAcquireNextScene(_engine); if (scene == IntPtr.Zero) @@ -793,6 +967,7 @@ private bool TryAcquireNextDiff(out NativeSceneDamage damage) { _pendingDamage = damage; _pendingRenderHeader = header; + _pendingAcceptedTimestamp = monitoring ? Stopwatch.GetTimestamp() : 0; if (monitoring) { _pendingDiffApplyTicks += diffApplyTicks; @@ -821,6 +996,66 @@ private bool TryAcquireNextDiff(out NativeSceneDamage damage) return accepted; } + private bool TryAcquireNextGpuDiff(out NativeSceneDamage damage) + { + damage = NativeSceneDamage.None; + using var prepared = _preparedCanvasScene; + _preparedCanvasScene = null; + var options = NativeSceneAcquireOptionsV3.CpuOnly; + options.ConsumerCapabilities = NativeWebSceneApi.GpuImageCapability | NativeWebSceneApi.OrderedCanvasCapability | NativeWebSceneApi.CanvasCheckpointCapability + | (_gpuPresenter?.SupportsProducerGpuWaits == true ? NativeWebSceneApi.ProducerGpuWaitCapability : 0UL); + NativeSceneLeaseV3? scene; + NativeSceneAcquireStatus status; + if (prepared is not null) + { + scene = prepared.TakeScene(); + status = NativeSceneAcquireStatus.Success; + } + else status = NativeSceneLeaseV3.Acquire(_engine, in options, true, out scene); + if (_performanceInstrumentation.IsEnabled) + _renderObserver.RecordScheduling("acquire:" + status, _publicationMailbox.PendingCount, + _appliedRevision, _gpuPresenter?.HasPendingRetirements == true); + if (status is NativeSceneAcquireStatus.Empty or NativeSceneAcquireStatus.Backpressure) return false; + if (status != NativeSceneAcquireStatus.Success || scene is null) throw new InvalidOperationException($"GPU scene acquisition failed: {status}"); + using (scene) + { + var accepted = false; + var nextDamage = NativeSceneDamage.None; + scene.WithView(versioned => + { + var view = (NativeSceneView*)versioned.CpuView; + if (!NativeSceneViewValidation.IsValid(view) || view->Header.Revision <= _appliedRevision) return; + var monitoring = _performanceInstrumentation.IsEnabled; + var started = monitoring ? Stopwatch.GetTimestamp() : 0; + var applied = _gpuPresenter!.ApplyScene(scene, _renderer, monitoring ? _renderObserver : null, prepared?.Layers); + if (monitoring) _renderObserver.RecordScheduling("apply:" + applied, _publicationMailbox.PendingCount, + view->Header.Revision, _gpuPresenter.HasPendingRetirements); + if (applied == NativeGpuSceneApplyResult.Backpressure) return; + _publicationMailbox.TryConsume(); + if (applied != NativeGpuSceneApplyResult.Applied) + { + _publicationMailbox.Reset(); NativeWebSceneApi.EngineRequestSceneCheckpoint(_engine); return; + } + var header = view->Header; + var changed = Math.Abs(_viewportWidth - header.ViewportWidth) > 0.01f || Math.Abs(_viewportHeight - header.ViewportHeight) > 0.01f; + _viewportWidth = header.ViewportWidth; _viewportHeight = header.ViewportHeight; + nextDamage = EvaluateDamage(view, changed); + _appliedRevision = header.Revision; _gpuNeedsRender = true; + _pendingDamage = nextDamage; _pendingRenderHeader = header; _hasPendingRenderMetrics = true; + _pendingAcceptedTimestamp = monitoring ? Stopwatch.GetTimestamp() : 0; + if (monitoring) + { + Interlocked.Increment(ref AppliedDiffCount); + _pendingDiffApplyTicks += Stopwatch.GetTimestamp() - started; + _pendingDiffCanvasCommandCount += view->CanvasCommandCount; + } + accepted = true; + }); + damage = nextDamage; + return accepted; + } + } + private NativeSceneDamage EvaluateDamage( NativeSceneView* view, bool viewportChanged) @@ -871,6 +1106,15 @@ private NativeSceneDamage EvaluateDamage( } public override void OnRender(ImmediateDrawingContext drawingContext) + { + lock (_engineAccessGate) + { + if (_engineAccessRevoked || _stopped) return; + OnRenderCore(drawingContext); + } + } + + private void OnRenderCore(ImmediateDrawingContext drawingContext) { var requestedByWebScene = _invalidationGate.Complete(); var monitoring = _performanceInstrumentation.IsEnabled; @@ -932,6 +1176,10 @@ public override void OnRender(ImmediateDrawingContext drawingContext) } using var lease = feature.Lease(); + // Finish an older checkpoint before submitting this frame's GPU draws. + _renderer.PollCanvasCheckpointReadback(lease); + if (_gpuPresenter is not null && !_gpuPresenter.TryPrepare(lease)) + { _gpuNeedsRender = true; _scheduleUiWake(); return; } var skiaStarted = monitoring ? Stopwatch.GetTimestamp() : 0; var canvas = lease.SkCanvas; var effective = EffectiveSize; @@ -959,7 +1207,11 @@ public override void OnRender(ImmediateDrawingContext drawingContext) canvas, _viewportWidth, _viewportHeight, - null); + null, + _gpuPresenter is null ? null : (index, destination) => _gpuPresenter.Draw(lease, index, destination), + OperatingSystem.IsWindows() ? lease.GrContext : null); + if (_gpuPresenter is not null) _renderer.CheckpointCanvasHistory(_engine, lease); + _gpuNeedsRender = false; if (monitoring) { retainedDrawTicks = Stopwatch.GetTimestamp() - retainedStarted; @@ -969,6 +1221,7 @@ public override void OnRender(ImmediateDrawingContext drawingContext) { canvas.RestoreToCount(save); } + _gpuPresenter?.PollRetirementsAfterDraw(lease); NativePresenterTextDiagnostics.TryCapture( lease.SkSurface, presenterMatrix, @@ -997,7 +1250,7 @@ public override void OnRender(ImmediateDrawingContext drawingContext) _pendingDiffCanvasCommandCount, _renderer.TotalCommandCount); } - _renderObserver.RecordRendered(_pendingRenderHeader); + _renderObserver.RecordRendered(_pendingRenderHeader, _pendingAcceptedTimestamp); if (monitoring && renderStarted - _lastRendererMetricsTimestamp >= Stopwatch.Frequency) @@ -1012,6 +1265,8 @@ public override void OnRender(ImmediateDrawingContext drawingContext) _pendingDiffCanvasCommandCount = 0; } + QueueCanvasPreparation(); + // An ordered producer may publish two diffs before Avalonia processes // their coalesced notification. Active composition drains the second // diff on its next animation frame. During the nested macOS live-resize @@ -1055,7 +1310,9 @@ public override Rect GetRenderBounds() private bool HasPendingPresentation => _hasPendingRenderMetrics - || _publicationMailbox.PendingCount > 0; + || _publicationMailbox.PendingCount > 0 + || _gpuNeedsRender + || _gpuPresenter?.HasPendingRetirements == true; } @@ -1484,46 +1741,36 @@ internal static void TryCapture( var colorSpace = image.ColorSpace; var rasterization = NativeTextShaping.ResolveFontRasterizationProfile( presenterDeviceScaleFactor); - var metadata = new + var metadata = new JsonObject { - CapturedUtc = DateTimeOffset.UtcNow, - RasterizationMode = NativeTextShaping.ActiveFontRasterizationMode.ToString(), - RasterizationOverride = Environment.GetEnvironmentVariable( - NativeTextShaping.RasterizationModeEnvironmentVariable), - Rasterization = new + ["CapturedUtc"] = DateTimeOffset.UtcNow, + ["RasterizationMode"] = NativeTextShaping.ActiveFontRasterizationMode.ToString(), + ["RasterizationOverride"] = Environment.GetEnvironmentVariable(NativeTextShaping.RasterizationModeEnvironmentVariable), + ["Rasterization"] = new JsonObject { - rasterization.Subpixel, - rasterization.BaselineSnap, - Edging = rasterization.Edging.ToString(), - Hinting = rasterization.Hinting.ToString(), - rasterization.LinearMetrics, - rasterization.EmbeddedBitmaps + ["Subpixel"] = rasterization.Subpixel, ["BaselineSnap"] = rasterization.BaselineSnap, + ["Edging"] = rasterization.Edging.ToString(), ["Hinting"] = rasterization.Hinting.ToString(), + ["LinearMetrics"] = rasterization.LinearMetrics, ["EmbeddedBitmaps"] = rasterization.EmbeddedBitmaps }, - PresenterDeviceScaleFactor = presenterDeviceScaleFactor, - EffectiveSize = new { effectiveSize.X, effectiveSize.Y }, - Viewport = new { Width = viewportWidth, Height = viewportHeight }, - ContentScale = new { contentScale.X, contentScale.Y }, - PresenterMatrix = MatrixValues(presenterMatrix), - ContentMatrix = MatrixValues(contentMatrix), - Surface = new + ["PresenterDeviceScaleFactor"] = presenterDeviceScaleFactor, + ["EffectiveSize"] = new JsonObject { ["X"] = effectiveSize.X, ["Y"] = effectiveSize.Y }, + ["Viewport"] = new JsonObject { ["Width"] = viewportWidth, ["Height"] = viewportHeight }, + ["ContentScale"] = new JsonObject { ["X"] = contentScale.X, ["Y"] = contentScale.Y }, + ["PresenterMatrix"] = MatrixValues(presenterMatrix), + ["ContentMatrix"] = MatrixValues(contentMatrix), + ["Surface"] = new JsonObject { - image.Width, - image.Height, - ColorType = image.ColorType.ToString(), - AlphaType = image.AlphaType.ToString(), - IsSrgb = colorSpace?.IsSrgb, - GammaIsCloseToSrgb = colorSpace?.GammaIsCloseToSrgb, - GammaIsLinear = colorSpace?.GammaIsLinear, - PixelGeometry = surface.SurfaceProperties.PixelGeometry.ToString(), - Flags = surface.SurfaceProperties.Flags.ToString(), - Backend = surface.Context?.Backend.ToString() ?? "CPU" + ["Width"] = image.Width, ["Height"] = image.Height, + ["ColorType"] = image.ColorType.ToString(), ["AlphaType"] = image.AlphaType.ToString(), + ["IsSrgb"] = colorSpace?.IsSrgb, ["GammaIsCloseToSrgb"] = colorSpace?.GammaIsCloseToSrgb, + ["GammaIsLinear"] = colorSpace?.GammaIsLinear, + ["PixelGeometry"] = surface.SurfaceProperties.PixelGeometry.ToString(), + ["Flags"] = surface.SurfaceProperties.Flags.ToString(), + ["Backend"] = surface.Context?.Backend.ToString() ?? "CPU" } }; - File.WriteAllText( - Path.Combine(OutputDirectory, "presenter-metadata.json"), - JsonSerializer.Serialize( - metadata, - new JsonSerializerOptions { WriteIndented = true })); + File.WriteAllText(Path.Combine(OutputDirectory, "presenter-metadata.json"), + metadata.ToJsonString(new JsonSerializerOptions { WriteIndented = true })); Console.WriteLine( $"WebScene text presenter diagnostic captured to {OutputDirectory}"); Interlocked.Exchange(ref s_captureState, 2); @@ -1535,18 +1782,12 @@ internal static void TryCapture( Interlocked.Exchange(ref s_captureState, 0); } - static object MatrixValues(SKMatrix matrix) - => new + static JsonObject MatrixValues(SKMatrix matrix) + => new() { - matrix.ScaleX, - matrix.SkewX, - matrix.TransX, - matrix.SkewY, - matrix.ScaleY, - matrix.TransY, - matrix.Persp0, - matrix.Persp1, - matrix.Persp2 + ["ScaleX"] = matrix.ScaleX, ["SkewX"] = matrix.SkewX, ["TransX"] = matrix.TransX, + ["SkewY"] = matrix.SkewY, ["ScaleY"] = matrix.ScaleY, ["TransY"] = matrix.TransY, + ["Persp0"] = matrix.Persp0, ["Persp1"] = matrix.Persp1, ["Persp2"] = matrix.Persp2 }; } } diff --git a/src/WebScene.Backend.Avalonia/NativeSceneInteropTypes.cs b/src/WebScene.Backend.Avalonia/NativeSceneInteropTypes.cs index 7741b224e..709ec5e2d 100644 --- a/src/WebScene.Backend.Avalonia/NativeSceneInteropTypes.cs +++ b/src/WebScene.Backend.Avalonia/NativeSceneInteropTypes.cs @@ -647,6 +647,8 @@ internal struct EngineOptions public IntPtr ResourceLoadV3UserData; public IntPtr StylesheetConsumedCallback; public IntPtr StylesheetConsumedUserData; + public IntPtr WebGpuPolicyCallback; + public IntPtr WebGpuPolicyUserData; } [StructLayout(LayoutKind.Sequential)] diff --git a/src/WebScene.Backend.Avalonia/NativeSceneRenderingTypes.cs b/src/WebScene.Backend.Avalonia/NativeSceneRenderingTypes.cs index dce613e84..f14f27b58 100644 --- a/src/WebScene.Backend.Avalonia/NativeSceneRenderingTypes.cs +++ b/src/WebScene.Backend.Avalonia/NativeSceneRenderingTypes.cs @@ -46,7 +46,15 @@ public readonly record struct NativeRendererMemoryMetrics( int SvgPictureCount, int ProcessSvgPictureCount, int ProcessSvgPictureReferenceCount, - long ProcessSvgPictureMemoryHits); + long ProcessSvgPictureMemoryHits) +{ + public long CanvasCheckpointSubmissions { get; init; } + public long MaximumRetainedCanvasCommands { get; init; } + public double MaximumCanvasCheckpointMilliseconds { get; init; } + public long CanvasCheckpointDeferredReadbacks { get; init; } + public long CanvasCheckpointFencePolls { get; init; } + public long CanvasCheckpointWorkerReadbacks { get; init; } +} internal sealed class SharedSvgPictureLease : IDisposable { diff --git a/src/WebScene.Backend.Avalonia/NativeSceneRuntime.cs b/src/WebScene.Backend.Avalonia/NativeSceneRuntime.cs index f11e652cf..df58e51f2 100644 --- a/src/WebScene.Backend.Avalonia/NativeSceneRuntime.cs +++ b/src/WebScene.Backend.Avalonia/NativeSceneRuntime.cs @@ -89,13 +89,20 @@ internal NativeRendererMemoryMetrics ReadRendererMetrics() } } +public readonly record struct NativeSceneSchedulingSample( + long Timestamp, string Stage, long PendingPublications, ulong Revision, bool PendingRetirements); + internal sealed class NativeSceneRenderObserver { private const uint SceneComponentReady = 4; private readonly object _viewportGate = new(); private readonly List _renderedViewportHeights = []; private readonly Queue _renderedScenes = new(4096); + private readonly Queue _scheduling = new(4096); private readonly Queue _presentations = new(4096); + // Optional draw-only timing avoids enabling the full performance census. + private readonly bool _traceDrawCallbacks = + Environment.GetEnvironmentVariable("WEBSCENE_TRACE_DRAW_CALLBACKS") == "1"; private readonly NativePerformanceInstrumentation _instrumentation; private long _renderedSceneCount; private long _firstRenderedSceneTimestamp; @@ -161,9 +168,24 @@ public long[] Presentations } } + public NativeSceneSchedulingSample[] SchedulingSamples + { + get { lock (_viewportGate) return _scheduling.ToArray(); } + } + + public void RecordScheduling(string stage, long pending, ulong revision, bool retiring) + { + if (!_instrumentation.IsEnabled) return; + lock (_viewportGate) + { + if (_scheduling.Count == 4096) _scheduling.Dequeue(); + _scheduling.Enqueue(new(Stopwatch.GetTimestamp(), stage, pending, revision, retiring)); + } + } + public void RecordPresented() { - if (!_instrumentation.IsEnabled) + if (!_instrumentation.IsEnabled && !_traceDrawCallbacks) { return; } @@ -177,7 +199,7 @@ public void RecordPresented() } } - public void RecordRendered(in SceneHeader header) + public void RecordRendered(in SceneHeader header, long acceptedTimestamp = 0) { var monitoring = _instrumentation.IsEnabled; var needsFirstRenderTimestamp = @@ -210,7 +232,7 @@ public void RecordRendered(in SceneHeader header) _renderedScenes.Enqueue(new NativeSceneRenderSample( timestamp, header.Revision, - header.ConsumedInputSequence)); + header.ConsumedInputSequence) { AcceptedTimestamp = acceptedTimestamp }); if (_renderedViewportHeights.Count == 0 || _renderedViewportHeights[^1] != viewportHeight) { @@ -238,6 +260,8 @@ public static void SetLegacyConsoleCapture(IntPtr engine, bool enabled) private static readonly IntPtr ResourceLoadV2Address = Marshal.GetFunctionPointerForDelegate(ResourceLoadV2); private static readonly ResourceLoadCallbackV3 ResourceLoadV3 = LoadResourceV3; + private static readonly WebGpuPolicyCallback WebGpuPolicy = EvaluateWebGpuPolicy; + private static readonly IntPtr WebGpuPolicyAddress = Marshal.GetFunctionPointerForDelegate(WebGpuPolicy); private static readonly StylesheetConsumedCallback StylesheetConsumed = NotifyStylesheetConsumed; private static readonly IntPtr StylesheetConsumedAddress = Marshal.GetFunctionPointerForDelegate(StylesheetConsumed); @@ -313,7 +337,8 @@ public static IntPtr EngineCreate( Action scenePublished, Action? hostRequestAvailable = null, Action? interopCallbackAvailable = null, - Action? animationFrameRequested = null) + Action? animationFrameRequested = null, + Func? admitWebGpuDocument = null) { ArgumentNullException.ThrowIfNull(resourceLoader); ArgumentNullException.ThrowIfNull(scenePublished); @@ -326,7 +351,8 @@ public static IntPtr EngineCreate( scenePublished, hostRequestAvailable, interopCallbackAvailable, - animationFrameRequested)); + animationFrameRequested, + admitWebGpuDocument)); try { fixed (byte* directory = directoryBytes) @@ -366,7 +392,9 @@ public static IntPtr EngineCreate( ResourceLoadCallbackV3 = ResourceLoadV3Address, ResourceLoadV3UserData = GCHandle.ToIntPtr(bridgeHandle), StylesheetConsumedCallback = StylesheetConsumedAddress, - StylesheetConsumedUserData = GCHandle.ToIntPtr(bridgeHandle) + StylesheetConsumedUserData = GCHandle.ToIntPtr(bridgeHandle), + WebGpuPolicyCallback = admitWebGpuDocument is null ? IntPtr.Zero : WebGpuPolicyAddress, + WebGpuPolicyUserData = admitWebGpuDocument is null ? IntPtr.Zero : GCHandle.ToIntPtr(bridgeHandle) }; var engine = EngineCreateWithOptions(in options); if (engine == IntPtr.Zero) return IntPtr.Zero; @@ -588,6 +616,27 @@ private delegate nuint ResourceLoadCallbackV3( IntPtr destination, nuint destinationCapacity); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate uint WebGpuPolicyCallback(IntPtr userData, IntPtr url, nuint urlLength); + + // Admission selects the platform transport for a trusted secure document. + // The bridge and static delegate stay rooted until native engine destruction. + private static uint EvaluateWebGpuPolicy(IntPtr userData, IntPtr url, nuint urlLength) + { + try + { + var bridge = (ResourceBridge?)GCHandle.FromIntPtr(userData).Target; + return bridge?.AdmitWebGpuDocument( + Marshal.PtrToStringUTF8(url, checked((int)urlLength)) ?? string.Empty) == true + ? (OperatingSystem.IsWindows() ? 2u : 1u) : 0u; + } + catch + { + // Deny on policy failure; exceptions cannot cross reverse P/Invoke. + return 0; + } + } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void StylesheetConsumedCallback( IntPtr userData, IntPtr address, nuint addressLength, IntPtr css, nuint cssLength); @@ -939,8 +988,12 @@ internal sealed class ResourceBridge( Action scenePublished, Action? hostRequestAvailable, Action? interopCallbackAvailable, - Action? animationFrameRequested) : IDisposable + Action? animationFrameRequested, + Func? admitWebGpuDocument = null) : IDisposable { + public bool AdmitWebGpuDocument(string url) + => (OperatingSystem.IsMacOS() || OperatingSystem.IsWindows()) && admitWebGpuDocument?.Invoke(url) == true; + private const int EnvelopeHeaderSize = 2 + sizeof(uint) + sizeof(long) + sizeof(long); [ThreadStatic] private static PendingResourceCopy? _pendingCopy; @@ -1136,7 +1189,7 @@ private static PreparedResource PrepareResource( var responseEntityTagLength = Encoding.UTF8.GetByteCount(responseEntityTag); var contentLength = resource.NotModified ? 0 - : Encoding.UTF8.GetByteCount(resource.Content); + : resource.BinaryContent?.Length ?? Encoding.UTF8.GetByteCount(resource.Content); return new PreparedResource( resource.NotModified, resource.IsCacheable, @@ -1145,8 +1198,8 @@ private static PreparedResource PrepareResource( responseEntityTag, responseEntityTagLength, resource.Content, - default, - false, + resource.BinaryContent.GetValueOrDefault(), + resource.BinaryContent.HasValue, contentLength, checked((nuint)( EnvelopeHeaderSize + responseEntityTagLength + contentLength))); diff --git a/src/WebScene.Backend.Avalonia/NativeSceneSurface.cs b/src/WebScene.Backend.Avalonia/NativeSceneSurface.cs index 99fc8c178..42ab41d97 100644 --- a/src/WebScene.Backend.Avalonia/NativeSceneSurface.cs +++ b/src/WebScene.Backend.Avalonia/NativeSceneSurface.cs @@ -38,6 +38,7 @@ public sealed class NativeSceneSurface : Control, INativeWebSceneRenderDiagnosti { private IntPtr _engine; private readonly bool _useCompositionVisual; + private readonly bool _enableGpuScenes; private readonly bool _submitAnimationFrames; private readonly NativeCanvasSceneRenderer _renderer = new(); private readonly object _rendererGate = new(); @@ -69,12 +70,14 @@ public sealed class NativeSceneSurface : Control, INativeWebSceneRenderDiagnosti private int _compositionProjectionActive; private long _compositionUiWakeCount; private CompositionCustomVisual? _customVisual; + private NativeSceneCompositionHandler? _compositionHandler; public NativeSceneSurface( IntPtr engine, bool useCompositionVisual = false, - bool submitAnimationFrames = true) - : this(engine, useCompositionVisual, submitAnimationFrames, null) + bool submitAnimationFrames = true, + bool enableGpuScenes = false) + : this(engine, useCompositionVisual, submitAnimationFrames, null, enableGpuScenes) { } @@ -82,7 +85,8 @@ internal NativeSceneSurface( IntPtr engine, bool useCompositionVisual, bool submitAnimationFrames, - Func? enqueuePointerInput) + Func? enqueuePointerInput, + bool enableGpuScenes = false) { _performanceInstrumentation = new NativePerformanceInstrumentation(); _renderObserver = new NativeSceneRenderObserver(_performanceInstrumentation); @@ -94,6 +98,9 @@ internal NativeSceneSurface( "WEBSCENE_AVALONIA_DIRECT_DRAW"), "1", StringComparison.Ordinal); + if (enableGpuScenes && ((!OperatingSystem.IsMacOS() && !OperatingSystem.IsWindows()) || !_useCompositionVisual)) + throw new PlatformNotSupportedException("GPU scenes require macOS or Windows composition rendering."); + _enableGpuScenes = enableGpuScenes; _submitAnimationFrames = submitAnimationFrames; Focusable = true; ClipToBounds = true; @@ -162,6 +169,8 @@ public void SetEngine(IntPtr engine) if (_customVisual is not null) { Volatile.Write(ref _compositionProjectionActive, 0); + _compositionHandler?.RevokeEngineAccess(); + _compositionHandler = null; _customVisual.SendHandlerMessage(NativeSceneCompositionMessage.Stop); ElementComposition.SetElementChildVisual(this, null); _customVisual = null; @@ -212,15 +221,16 @@ private void StartProjection() var compositor = ElementComposition.GetElementVisual(this)?.Compositor; if (compositor is not null) { - _customVisual = compositor.CreateCustomVisual( - new NativeSceneCompositionHandler( + _compositionHandler = new NativeSceneCompositionHandler( _engine, _renderObserver, _compositionMailbox, _compositionUiWakeGate, _performanceInstrumentation, ScheduleCompositionUiWake, - TopLevel.GetTopLevel(this)?.RenderScaling ?? 1)); + TopLevel.GetTopLevel(this)?.RenderScaling ?? 1, + enableGpuScenes: _enableGpuScenes); + _customVisual = compositor.CreateCustomVisual(_compositionHandler); _customVisual.Size = new Vector2((float)Bounds.Width, (float)Bounds.Height); ElementComposition.SetElementChildVisual(this, _customVisual); Volatile.Write(ref _compositionProjectionActive, 1); @@ -229,6 +239,8 @@ private void StartProjection() } } + if (_enableGpuScenes) + throw new InvalidOperationException("GPU scene rendering requires an attached compositor."); _frameLoopActive = true; RequestNextFrame(); } @@ -239,6 +251,8 @@ private void OnSurfaceDetached(object? sender, VisualTreeAttachmentEventArgs arg if (_customVisual is not null) { Volatile.Write(ref _compositionProjectionActive, 0); + _compositionHandler?.RevokeEngineAccess(); + _compositionHandler = null; _customVisual.SendHandlerMessage(NativeSceneCompositionMessage.Stop); ElementComposition.SetElementChildVisual(this, null); _customVisual = null; @@ -364,6 +378,8 @@ public int[] RenderedViewportHeights public long[] RenderedSceneTimestamps => _renderObserver.RenderedSceneTimestamps; + public NativeSceneSchedulingSample[] SchedulingSamples => _renderObserver.SchedulingSamples; + public NativeSceneRenderSample[] RenderedScenes => _renderObserver.RenderedScenes; diff --git a/src/WebScene.Backend.Avalonia/NativeTextShaping.cs b/src/WebScene.Backend.Avalonia/NativeTextShaping.cs index 9eb26bcfc..f207909c7 100644 --- a/src/WebScene.Backend.Avalonia/NativeTextShaping.cs +++ b/src/WebScene.Backend.Avalonia/NativeTextShaping.cs @@ -50,6 +50,8 @@ public struct NativeTextMetrics public static class NativeTextShaping { + private static long _fontRegistrationVersion; + internal static long FontRegistrationVersion => Interlocked.Read(ref _fontRegistrationVersion); private static readonly ShapedRunCache ShapedRuns = new(2048, 4 * 1024 * 1024); private static readonly TextBlobCache TextBlobs = new(); // Cache the immutable native glyph container as well as HarfBuzz output. @@ -298,6 +300,7 @@ internal bool Register(string family, ReadOnlySpan data, int? minimumWeigh return true; } _typefaces[normalizedFamily] = [.. existing, new(lease, min, max, faceSlant)]; + Interlocked.Increment(ref _fontRegistrationVersion); return true; } } @@ -358,6 +361,7 @@ private void Release() foreach (var faces in _typefaces.Values) foreach (var face in faces) face.Lease.Dispose(); _typefaces.Clear(); + Interlocked.Increment(ref _fontRegistrationVersion); } } public void Dispose() @@ -406,7 +410,11 @@ public static bool RegisterWebTypeface(string family, ReadOnlySpan data) if (typeface is null) return false; var normalizedFamily = family.Trim().Trim('"', '\''); - if (WebTypefaces.TryAdd(normalizedFamily, typeface)) return true; + if (WebTypefaces.TryAdd(normalizedFamily, typeface)) + { + Interlocked.Increment(ref _fontRegistrationVersion); + return true; + } typeface.Dispose(); return true; diff --git a/src/WebScene.Backend.Avalonia/NativeWebSceneView.cs b/src/WebScene.Backend.Avalonia/NativeWebSceneView.cs index 117769f38..57f467ec7 100644 --- a/src/WebScene.Backend.Avalonia/NativeWebSceneView.cs +++ b/src/WebScene.Backend.Avalonia/NativeWebSceneView.cs @@ -2,6 +2,9 @@ using System.Runtime.CompilerServices; using Avalonia.Controls; using Avalonia.Input; +#if WEBSCENE_AVALONIA12 +using Avalonia.Input.Platform; +#endif using Avalonia.Platform.Storage; using Avalonia.Styling; using Avalonia.Threading; @@ -20,6 +23,7 @@ public sealed partial class NativeWebSceneView : ContentControl, IAsyncDisposabl private static long s_nextContextId; private readonly SemaphoreSlim _lifecycleGate = new(1, 1); private readonly NativeSceneSurface _surface; + private readonly Func? _admitWebGpuDocument; private IntPtr _engine; private long _contextId; private NativeInteropInvoker? _interop; @@ -35,8 +39,20 @@ public NativeWebSceneView() } public NativeWebSceneView(bool useCompositionVisual) + : this(useCompositionVisual, null) { - _surface = new NativeSceneSurface(IntPtr.Zero, useCompositionVisual); + } + + /// + /// Enables the experimental macOS IOSurface WebGPU route when a policy is supplied. + /// The host must provide a CGL/Skia compositor and approve only secure documents. + /// The policy runs on the native runtime worker before each document's scripts. + /// + public NativeWebSceneView(bool useCompositionVisual, Func? admitWebGpuDocument) + { + _admitWebGpuDocument = admitWebGpuDocument; + _surface = new NativeSceneSurface(IntPtr.Zero, useCompositionVisual, + enableGpuScenes: admitWebGpuDocument is not null); Content = _surface; InitializeRuntimeDiagnostics(); ActualThemeVariantChanged += OnActualThemeVariantChanged; @@ -430,7 +446,8 @@ await NativeWebSceneRuntime _surface.OnNativeScenePublished, hostRequestAvailable: OnNativeHostRequestAvailable, interopCallbackAvailable: callbackSignal.Notify, - animationFrameRequested: _surface.OnNativeAnimationFrameRequested); + animationFrameRequested: _surface.OnNativeAnimationFrameRequested, + admitWebGpuDocument: _admitWebGpuDocument); if (engine == IntPtr.Zero) { throw new InvalidOperationException( @@ -572,11 +589,21 @@ private async Task HandleHostRequestAsync(string request) "image/png", StringComparison.OrdinalIgnoreCase)) { +#if WEBSCENE_AVALONIA12 + var item = new DataTransferItem(); + item.Set(DataFormat.CreateBytesPlatformFormat("image/png"), clipboardBytes); + item.Set(DataFormat.CreateBytesPlatformFormat("public.png"), clipboardBytes); + item.Set(DataFormat.CreateBytesPlatformFormat("PNG"), clipboardBytes); + var data = new DataTransfer(); + data.Add(item); + await topLevel.Clipboard.SetDataAsync(data).ConfigureAwait(true); +#else var data = new DataObject(); data.Set("image/png", clipboardBytes); data.Set("public.png", clipboardBytes); data.Set("PNG", clipboardBytes); await topLevel.Clipboard.SetDataObjectAsync(data).ConfigureAwait(true); +#endif } else if (clipboardWrite.ContentType.StartsWith( "text/", @@ -587,9 +614,17 @@ await topLevel.Clipboard.SetTextAsync( } else { +#if WEBSCENE_AVALONIA12 + var item = new DataTransferItem(); + item.Set(DataFormat.CreateBytesPlatformFormat(clipboardWrite.ContentType), clipboardBytes); + var data = new DataTransfer(); + data.Add(item); + await topLevel.Clipboard.SetDataAsync(data).ConfigureAwait(true); +#else var data = new DataObject(); data.Set(clipboardWrite.ContentType, clipboardBytes); await topLevel.Clipboard.SetDataObjectAsync(data).ConfigureAwait(true); +#endif } return; } diff --git a/src/WebScene.Backend.Avalonia/NativeWindowsRetainedGpuImage.cs b/src/WebScene.Backend.Avalonia/NativeWindowsRetainedGpuImage.cs new file mode 100644 index 000000000..61be91ae5 --- /dev/null +++ b/src/WebScene.Backend.Avalonia/NativeWindowsRetainedGpuImage.cs @@ -0,0 +1,160 @@ +using System.Runtime.InteropServices; +using Avalonia.OpenGL.Egl; +using Avalonia.Skia; +using SkiaSharp; + +namespace WebScene.Backends.Avalonia.Native; + +// Imports on the actual ANGLE/D3D11 device behind the active Skia lease. +// Native queue waits order producer writes; a native fence retires final reads. +internal sealed class NativeWindowsRetainedGpuImage : INativeRetainedGpuImage +{ + private readonly EglContext _host; + private readonly GRContext _skia; + private NativeGpuImageConsumerV3? _consumer; + private IntPtr _bridge; + private EglSurface? _surface; + private SKImage? _image; + private int _texture; + private bool _retiring, _sealed; + private NativeWindowsRetainedGpuImage(EglContext host, GRContext skia) { _host = host; _skia = skia; } + + internal static bool Supports(ISkiaSharpApiLease lease) + { + if (!OperatingSystem.IsWindows() || lease.GrContext is null) return false; + using var platform = lease.TryLeasePlatformGraphicsApi(); + return platform?.Context is EglContext context && TryGetDevice(context, out var device) + && NativeWebSceneApi.GpuD3D11SupportedV3(device) == 0; + } + private static bool TryGetDevice(EglContext host, out IntPtr device) + { + device = IntPtr.Zero; + return host.EglInterface.QueryDisplayAttribExt(host.Display.Handle, 0x322C, out var eglDevice) + && host.EglInterface.QueryDeviceAttribExt(eglDevice, 0x33A1, out device) && device != IntPtr.Zero; + } + internal static NativeWindowsRetainedGpuImage? Import(NativeGpuImageLeaseV3 source, + ISkiaSharpApiLease lease, GRSurfaceOrigin origin, SKAlphaType alpha) + { + var skia = lease.GrContext ?? throw new NotSupportedException("Windows GPU composition requires a Skia GPU context."); + using var platform = lease.TryLeasePlatformGraphicsApi() + ?? throw new NotSupportedException("Windows GPU composition requires a platform graphics lease."); + if (platform.Context is not EglContext host || !TryGetDevice(host, out var device)) + throw new NotSupportedException("Windows GPU composition requires an ANGLE D3D11 host."); + var result = new NativeWindowsRetainedGpuImage(host, skia); + var status = NativeGpuImageConsumerV3.Acquire(source, out result._consumer); + if (status == NativeSceneAcquireStatus.Backpressure) return null; + if (status != NativeSceneAcquireStatus.Success) throw new InvalidOperationException($"DXGI consumer acquisition failed: {status}"); + try + { + var metadata = source.Describe(); + IntPtr texture = IntPtr.Zero; + result._consumer!.WithNativeHandle(pointer => + { + Marshal.ThrowExceptionForHR(NativeWebSceneApi.GpuD3D11ImportV3(pointer, device, out result._bridge, out texture)); + return true; + }); + result._surface = host.Display.CreatePBufferFromClientBuffer(0x33A3, texture, new[] + { + 0x3057, checked((int)metadata.Width), 0x3056, checked((int)metadata.Height), + 0x3080, 0x305E, 0x3081, 0x305F, 0x345D, 0x1908, 0x3038 + }); + result._texture = host.GlInterface.GenTexture(); + if (result._texture == 0) throw new InvalidOperationException("ANGLE texture allocation failed."); + host.GlInterface.BindTexture(0x0DE1, result._texture); + if (host.EglInterface.BindTexImage(host.Display.Handle, result._surface.DangerousGetHandle(), 0x3084) == 0) + throw new InvalidOperationException($"ANGLE shared texture binding failed: 0x{host.EglInterface.GetError():X}"); + using var backend = new GRBackendTexture(checked((int)metadata.Width), checked((int)metadata.Height), false, + new GRGlTextureInfo(0x0DE1, (uint)result._texture, 0x8058)); + result._image = SKImage.FromTexture(skia, backend, origin, SKColorType.Rgba8888, alpha) + ?? throw new InvalidOperationException("Skia could not wrap the ANGLE shared image."); + return result; + } + catch + { + result.ReleaseHostObjects(); // No drawing has sampled this image. + if (result._bridge != IntPtr.Zero) NativeWebSceneApi.GpuD3D11DestroyV3(result._bridge); + result._consumer!.Complete(); + throw; + } + } + private void Check(ISkiaSharpApiLease lease) + { + if (!ReferenceEquals(lease.GrContext, _skia) || !_host.IsCurrent || _host.IsLost) + throw new InvalidOperationException("DXGI image requires its owning Skia and ANGLE contexts."); + } + public void Draw(ISkiaSharpApiLease lease, SKRect destination, SKPaint? paint = null) + { + Check(lease); + if (_retiring) throw new InvalidOperationException("Retiring DXGI image cannot be drawn."); + NativeGpuImageSampling.Draw(lease.SkCanvas, _image!, destination, paint); + } + private void ReleaseHostObjects() + { + _image?.Dispose(); _image = null; + if (_texture != 0) { _host.GlInterface.DeleteTexture(_texture); _texture = 0; } + _surface?.Dispose(); _surface = null; + } + public void Retire(ISkiaSharpApiLease lease) + { + Check(lease); _retiring = true; + if (_sealed || _consumer is null) return; + _image?.Dispose(); _image = null; + using var platform = lease.TryLeasePlatformGraphicsApi() + ?? throw new NotSupportedException("DXGI retirement requires a platform lease."); + if (!ReferenceEquals(platform.Context, _host)) throw new InvalidOperationException("ANGLE host changed during retirement."); + // Entering the platform lease submits Skia's final reads. GL deletion + // retains in-flight driver references; the native bridge owns the D3D + // texture until its signal completes, so background polling needs no GL. + ReleaseHostObjects(); + _host.GlInterface.Flush(); + Marshal.ThrowExceptionForHR(NativeWebSceneApi.GpuD3D11SealV3(_bridge)); + _sealed = true; + } + private bool Complete() + { + if (_consumer is null) return true; + if (!_sealed) return false; + var status = NativeWebSceneApi.GpuD3D11PollV3(_bridge); + Marshal.ThrowExceptionForHR(status); + if (status != 0) return false; + NativeWebSceneApi.GpuD3D11DestroyV3(_bridge); _bridge = IntPtr.Zero; + _consumer.Complete(); _consumer = null; + return true; + } + public bool TryComplete(ISkiaSharpApiLease lease) { Check(lease); return Complete(); } + public bool TryRetireWithoutVisual() => Complete(); + public void SealForDetachedRetirement() + { + if (_sealed || _consumer is null) return; + using var current = _host.EnsureCurrent(); + lock (_skia) + { + if (_skia.IsAbandoned || _host.IsLost) throw new InvalidOperationException("DXGI host lost before retirement."); + _retiring = true; + _image?.Dispose(); _image = null; + _skia.Flush(); + try + { + ReleaseHostObjects(); + _host.GlInterface.Flush(); + Marshal.ThrowExceptionForHR(NativeWebSceneApi.GpuD3D11SealV3(_bridge)); + _sealed = true; + } + finally { _skia.ResetContext(); } + } + } +} + +public static unsafe partial class NativeWebSceneApi +{ + [DllImport(LibraryName, EntryPoint = "webscene_gpu_d3d11_supported_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern int GpuD3D11SupportedV3(IntPtr device); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_d3d11_import_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern int GpuD3D11ImportV3(IntPtr consumer, IntPtr device, out IntPtr owner, out IntPtr texture); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_d3d11_seal_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern int GpuD3D11SealV3(IntPtr owner); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_d3d11_poll_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern int GpuD3D11PollV3(IntPtr owner); + [DllImport(LibraryName, EntryPoint = "webscene_gpu_d3d11_destroy_v3", CallingConvention = CallingConvention.Cdecl)] + internal static extern void GpuD3D11DestroyV3(IntPtr owner); +} diff --git a/src/WebScene.Backend.Uno/UnoNativeSceneSurface.cs b/src/WebScene.Backend.Uno/UnoNativeSceneSurface.cs index 09733428a..ca948dc67 100644 --- a/src/WebScene.Backend.Uno/UnoNativeSceneSurface.cs +++ b/src/WebScene.Backend.Uno/UnoNativeSceneSurface.cs @@ -1061,12 +1061,14 @@ public WebSceneTextResource LoadText(in WebSceneResourceRequest request) var uri = new Uri(address); if (uri.IsFile) { + var bytes = request.Kind == WebSceneResourceKind.Data ? File.ReadAllBytes(uri.LocalPath) : null; return new WebSceneTextResource( address, - File.ReadAllText(uri.LocalPath), + bytes is null ? File.ReadAllText(uri.LocalPath) : System.Text.Encoding.UTF8.GetString(bytes), address, null) { + BinaryContent = bytes is null ? (ReadOnlyMemory?)null : new ReadOnlyMemory(bytes), LastModified = File.GetLastWriteTimeUtc(uri.LocalPath), IsCacheable = true }; @@ -1086,7 +1088,10 @@ public WebSceneTextResource LoadText(in WebSceneResourceRequest request) ? System.Text.Encoding.UTF8.GetString( Convert.FromBase64String(payload)) : Uri.UnescapeDataString(payload); - return new WebSceneTextResource(address, dataContent, address, null); + return new WebSceneTextResource(address, dataContent, address, null) + { BinaryContent = request.Kind == WebSceneResourceKind.Data + ? new ReadOnlyMemory(metadata.EndsWith(";base64", StringComparison.OrdinalIgnoreCase) + ? Convert.FromBase64String(payload) : System.Text.Encoding.UTF8.GetBytes(dataContent)) : (ReadOnlyMemory?)null }; } if (uri.Scheme is not ("http" or "https")) { @@ -1163,11 +1168,13 @@ public WebSceneTextResource LoadText(in WebSceneResourceRequest request) inner: null, response.StatusCode); } - var content = request.Kind == WebSceneResourceKind.Image + var binaryBytes = request.Kind == WebSceneResourceKind.Data ? response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult() : null; + var content = binaryBytes is not null ? System.Text.Encoding.UTF8.GetString(binaryBytes) : request.Kind == WebSceneResourceKind.Image ? NativeImageResource.ToMarkup(response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult()) : response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); return new WebSceneTextResource(address, content, address, null) { + BinaryContent = binaryBytes is null ? (ReadOnlyMemory?)null : new ReadOnlyMemory(binaryBytes), EntityTag = responseEntityTag, LastModified = responseLastModified, FreshUntil = cachePolicy.FreshUntil, diff --git a/src/WebScene.Backend.Uno/WebScene.Backend.Uno.csproj b/src/WebScene.Backend.Uno/WebScene.Backend.Uno.csproj index 347c56e55..440d300e4 100644 --- a/src/WebScene.Backend.Uno/WebScene.Backend.Uno.csproj +++ b/src/WebScene.Backend.Uno/WebScene.Backend.Uno.csproj @@ -33,8 +33,11 @@ + + + diff --git a/src/WebScene.Core/HostContracts.cs b/src/WebScene.Core/HostContracts.cs index 64429f4c5..6760dfab4 100644 --- a/src/WebScene.Core/HostContracts.cs +++ b/src/WebScene.Core/HostContracts.cs @@ -154,6 +154,10 @@ public readonly record struct WebSceneTextResource( string DisplayName, string? Directory) { + /// Optional original bytes for binary resources. Native hosts must prefer + /// these over UTF-8 encoding Content when present, including an empty payload. + public ReadOnlyMemory? BinaryContent { get; init; } + public string? EntityTag { get; init; } public DateTimeOffset? LastModified { get; init; } diff --git a/tests/GraphicsCompatibility/README.md b/tests/GraphicsCompatibility/README.md new file mode 100644 index 000000000..3888cccf1 --- /dev/null +++ b/tests/GraphicsCompatibility/README.md @@ -0,0 +1,64 @@ +# Graphics compatibility inputs + +Tracks the reproducible fixture portion of [G01 #23](https://github.com/wieslawsoltes/WebScene/issues/23), under [epic #22](https://github.com/wieslawsoltes/WebScene/issues/22). + +`fixtures/Kestrel-CAD.zip` is the exact user-provided archive, redistributed under its included MIT license. `fixtures/kestrel.json` records its SHA-256, every member's SHA-256 and provenance. Keeping the original archive makes the fixture available to developers and CI without a local Downloads path or mutable external download. Do not edit application code to make compatibility tests pass. + +From any working directory: + +```sh +python3 /path/to/WebScene/tests/GraphicsCompatibility/prepare-kestrel.py +python3 /path/to/WebScene/tests/GraphicsCompatibility/prepare-kestrel.py --destination /new/disposable/directory +``` + +Extraction requires a new directory. Run application tests against disposable extracted copies; never regenerate the committed input from test outputs. Verification checks both the archive and member hashes and the separately distributed license. Fixture documents describe the upstream app and are not implementation instructions for WebScene. + +The archive contains historical screenshots, test reports and build metadata. They are **not** WebScene results or a hardware Chrome baseline. A successful fixture verification proves input integrity only. See `docs/graphics/evidence` for actual partial G01 results. Issue #23 must stay open until all its gates pass; implementation of #24 follows completion of #23. + +## Hardware Chrome reference capture + +Run a headed Chrome on a real GPU, with an available desktop session: + +```sh +node --test tests/GraphicsCompatibility/reference-tests.mjs +node tests/GraphicsCompatibility/capture-chrome-reference.mjs --chrome /path/to/chrome --output artifacts/chrome-reference-new +``` + +The output directory must be new. Chrome uses a disposable profile and a local HTTP server; the harness verifies and extracts the original archive without editing Kestrel's sources. It opens the courtyard, the supplied drawing fixture, and deterministic seeded 10,000/100,000-line project data. Each runs at DPR 1 and 2, in light and dark themes, twice by default. `--case courtyard-dpr1-dark --repeat 2 --frames 30` provides a shorter diagnostic run, not a complete matrix. + +The document viewport is 1920×1080 CSS pixels. The CAD canvas occupies the remaining app area (currently 1446×743 CSS pixels); metadata records its actual bounds and physical size. Do not describe this as a 1920×1080 CAD render target. The app's ordinary UI handlers dismiss command suggestions before captures. Screenshots and explicit GPU/overlay canvas exports happen outside timed interaction; these diagnostic readbacks are not part of WebScene's intended GPU-resident presentation path. + +`reference.json` records browser revision, system GPU identity, non-fallback adapter evidence, fixture and harness hashes, camera state, inputs, errors, retained buffer checks, CPU submission samples, and per-file hashes. Each run saves before/after composite and canvas-layer PNGs plus a compressed Chromium trace. Repeatability compares exact PNG bytes separately for composition, GPU content and overlay; inspect any differences before accepting reference pixels. + +Presentation analysis uses Chromium `PipelineReporter` termination timestamps whose source has been verified to consume platform presentation feedback at the recorded Chrome revision. It does not treat rAF callbacks or CPU submission time as presentation. Unknown Chrome revisions, incomplete traces or missing hardware evidence remain unavailable; verify the new Chromium source contract before extending the analyzer's revision allowlist. Reported frame-state counts describe Chromium reporters and must not be relabelled as Kestrel dropped frames. A `captured` result means evidence acquisition succeeded, not that WebScene compatibility or the epic's performance gates passed. + +### Analyze native Kestrel pan timing + +Capture the probe's stdout/stderr when running `--pan-kestrel --verify-kestrel`, +then run: + +```sh +python3 tests/GraphicsCompatibility/analyze-kestrel-pan.py /path/to/probe.log +python3 -m unittest discover -s tests/GraphicsCompatibility -p test_pan_analysis.py +``` + +The analyzer requires the pan-workload validation marker, matches publication and +rendered revision timestamps, and separates acceptance wait from draw-callback +work. Missing acceptance samples remain unavailable. If input sequence samples +are present it reports progress to a published consumption watermark; coalesced +inputs need not each be drawn. These distributions include the probe's settling +period and never establish physical presentation FPS. Logs rejected by workload +validation must not be used for performance comparisons. + +For viewport alignment, the native GPU document probe accepts `--document-width 792 --document-height 878` (positive integer CSS dimensions). Sidebar timelines record actual viewport/DPR/canvas geometry; verify these against the browser instead of assuming the requested window size or scale was applied. Matching geometry alone is not a matched performance workload. + +After a capture finishes, verify its retained file bytes before copying or archiving: + +```sh +python3 tests/GraphicsCompatibility/verify-reference-archive.py artifacts/chrome-reference-new +python3 -m unittest discover -s tests/GraphicsCompatibility -p test_reference_archive.py +``` + +This rejects incomplete captures, missing harness sources, missing or changed referenced artifacts, and paths outside the archive. It checks archive integrity only; it does not establish full matrix coverage, pixel correctness, hardware qualification or performance. Preserve the complete directory, including its exact harness sources and generated inputs. + +New captures retain all six capture-tool sources, including the fixture preparation script and imported CDP client, under `harness/tests/` with their relative paths preserved. The integrity verifier now requires these helpers. Earlier four-source captures retain their historical verification results but fail this strengthened source-completeness requirement; do not silently relabel those archives as complete. Reproduction still uses the repository and verified original fixture, not the archived scripts as a standalone package. diff --git a/tests/GraphicsCompatibility/analyze-kestrel-pan.py b/tests/GraphicsCompatibility/analyze-kestrel-pan.py new file mode 100644 index 000000000..3452b34cf --- /dev/null +++ b/tests/GraphicsCompatibility/analyze-kestrel-pan.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +"""Analyze a validated probe log without claiming physical presentation timing.""" +import argparse +import json +import math +import statistics +from pathlib import Path + + +def distribution(values): + values = sorted(values) + if not values: + return {"count": 0} + return {"count": len(values), "medianMilliseconds": statistics.median(values), + "p95Milliseconds": values[math.ceil(len(values) * .95) - 1], + "maximumMilliseconds": values[-1]} + + +def analyze(log): + lines = log.splitlines() + if not any(line.startswith("Kestrel pan workload validated (") for line in lines): + raise ValueError("Pan workload was not validated; reject timing comparison") + prefix = "Kestrel pan composition timeline: " + timelines = [json.loads(line[len(prefix):]) for line in lines if line.startswith(prefix)] + if len(timelines) != 1: + raise ValueError("Expected exactly one pan timeline") + timeline = timelines[0] + frequency = timeline["timestampFrequency"] + if not math.isfinite(frequency) or frequency <= 0: + raise ValueError("Invalid timestamp frequency") + publications = {sample["Revision"]: sample for sample in timeline["publications"]} + if len(publications) != len(timeline["publications"]): + raise ValueError("Duplicate published revision") + queue, draw, total = [], [], [] + for sample in timeline["renderedScenes"]: + publication = publications.get(sample["Revision"]) + if publication is None: + continue # Revision may have been published before the measurement. + published, rendered = publication["Timestamp"], sample["Timestamp"] + accepted = sample.get("AcceptedTimestamp", 0) + if rendered < published or accepted and not published <= accepted <= rendered: + raise ValueError("Invalid publication/acceptance/draw timestamp order") + total.append((rendered - published) * 1000 / frequency) + if accepted: + queue.append((accepted - published) * 1000 / frequency) + draw.append((rendered - accepted) * 1000 / frequency) + moves = timeline.get("submittedMoves", []) + if moves and (len(moves) != 80 or any( + a["sequence"] >= b["sequence"] or a["submittedAt"] > b["submittedAt"] + for a, b in zip(moves, moves[1:]))): + raise ValueError("Invalid injected move sequence") + progress = [] + for move in moves: + candidates = [sample["Timestamp"] for sample in timeline["publications"] + if sample["ConsumedInputSequence"] >= move["sequence"] + and sample["Timestamp"] >= move["submittedAt"]] + if candidates: + progress.append((min(candidates) - move["submittedAt"]) * 1000 / frequency) + return {"physicalPresentationVerified": False, + "publicationToAcceptance": distribution(queue), + "acceptanceToDrawCallbackEnd": distribution(draw), + "publicationToDrawCallbackEnd": distribution(total), + "inputToPublishedConsumptionWatermark": distribution(progress), + "unmatchedInputCount": len(moves) - len(progress), + "limitations": ["Draw callback completion is not physical presentation.", + "A consumption watermark does not prove each coalesced move was drawn.", + "Measurement includes settling; no FPS qualification is derived."]} + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("log", type=Path) + args = parser.parse_args() + try: + print(json.dumps(analyze(args.log.read_text()), indent=2)) + except (ValueError, KeyError, TypeError) as error: + parser.exit(1, f"Invalid trace: {error}\n") diff --git a/tests/GraphicsCompatibility/capture-chrome-reference.mjs b/tests/GraphicsCompatibility/capture-chrome-reference.mjs new file mode 100644 index 000000000..11848ef9a --- /dev/null +++ b/tests/GraphicsCompatibility/capture-chrome-reference.mjs @@ -0,0 +1,339 @@ +#!/usr/bin/env node +import { createHash } from "node:crypto"; +import { spawnSync } from "node:child_process"; +import { createServer } from "node:http"; +import { existsSync } from "node:fs"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { gzipSync } from "node:zlib"; +import { startChrome, stopChrome, evaluate, waitFor, delay } from "./chrome-session.mjs"; +import { lineProject, referenceCases } from "./reference-workloads.mjs"; +import { analyzePresentation, distribution } from "./presentation-trace.mjs"; + +const here = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(here, "../.."); +const sha = bytes => createHash("sha256").update(bytes).digest("hex"); + +export function hardwareAssessment(system, app) { + const descriptions = [system.gpu?.auxAttributes?.glRenderer, app.adapter?.description, + ...(system.gpu?.devices ?? []).map(device => device.deviceString)].join(" "); + const confirmed = app.secureContext === true && app.backend === "WebGPU" + && app.adapter?.isFallbackAdapter === false && system.gpu?.featureStatus?.webgpu === "enabled" + && system.gpu?.featureStatus?.gpu_compositing === "enabled" + && system.gpu?.devices?.length > 0 && !/swiftshader|llvmpipe|softpipe|software|warp/i.test(descriptions); + return { status: confirmed ? "confirmed" : "unavailable", hardwareAccelerated: confirmed, + reason: confirmed ? "Non-fallback WebGPU adapter and hardware browser GPU features confirmed" + : "Required hardware/non-fallback evidence is missing or reports software" }; +} + +function options(argv) { + const result = { chrome: process.env.CHROME_BIN, repeat: 2, frames: 180 }; + for (let index = 0; index < argv.length; index += 2) { + const key = argv[index], value = argv[index + 1]; + if (!value) throw new Error(`Missing value for ${key}`); + if (key === "--chrome") result.chrome = value; + else if (key === "--output") result.output = path.resolve(value); + else if (key === "--case") result.case = value; + else if (key === "--repeat") result.repeat = Number(value); + else if (key === "--frames") result.frames = Number(value); + else throw new Error(`Unknown option ${key}`); + } + if (!result.output) throw new Error("--output must name a new evidence directory"); + if (!Number.isInteger(result.repeat) || result.repeat < 1 || result.repeat > 10) throw new Error("Invalid repeat count"); + if (!Number.isInteger(result.frames) || result.frames < 10 || result.frames > 1800) throw new Error("Invalid frame count"); + if (result.case && !referenceCases.some(test => test.id === result.case)) throw new Error("Unknown reference case"); + result.chrome ??= ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + "/Volumes/SSD/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + "/usr/bin/google-chrome", "/usr/bin/chromium", + path.join(process.env.PROGRAMFILES ?? "", "Google/Chrome/Application/chrome.exe")].find(existsSync); + if (!result.chrome || !existsSync(result.chrome)) throw new Error("Set --chrome or CHROME_BIN to a hardware-capable Chrome installation"); + return result; +} + +async function fixtureServer(directory, generated) { + const types = { ".html": "text/html", ".js": "text/javascript", ".css": "text/css", + ".json": "application/json", ".kcad": "application/json", ".png": "image/png" }; + const server = createServer(async (request, response) => { + try { + const pathname = decodeURIComponent(new URL(request.url, "http://localhost").pathname); + if (request.method !== "GET") { response.writeHead(405).end(); return; } + let content = generated.get(pathname); + if (!content) { + const filename = path.resolve(directory, "." + (pathname === "/" ? "/index.html" : pathname)); + if (!filename.startsWith(directory + path.sep)) { response.writeHead(403).end(); return; } + content = await readFile(filename); + } + response.writeHead(200, { "Content-Type": types[path.extname(pathname)] ?? "application/octet-stream", + "Cache-Control": "no-store" }); + response.end(content); + } catch { response.writeHead(404).end(); } + }); + await new Promise((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", resolve); }); + return { server, url: `http://127.0.0.1:${server.address().port}` }; +} + +async function settled(page) { + await evaluate(page, `(async()=>{ + await new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve))); + if(kestrel.renderer.device) await kestrel.renderer.device.queue.onSubmittedWorkDone(); + return true; + })()`); +} + +async function settleUi(page) { + // A neutral title click blurs the command input through the app's normal handlers. + // Merely moving the pointer leaves its startup suggestion menu open on some navigations. + const point = await evaluate(page, "(()=>{const r=document.getElementById('title-name').getBoundingClientRect();return {x:r.x+r.width/2,y:r.y+r.height/2};})()"); + for (const event of [{ type: "mouseMoved", ...point }, + { type: "mousePressed", ...point, button: "left", buttons: 1, clickCount: 1 }, + { type: "mouseReleased", ...point, button: "left", buttons: 0, clickCount: 1 }]) + await page.send("Input.dispatchMouseEvent", event); + await waitFor(page, "document.getElementById('command-suggestions').hidden && document.getElementById('file-menu').hidden"); + await delay(200); // Allow the app's 150 ms blur handler and hover transitions to finish. + await settled(page); +} + +export function validateReferenceUi(state) { + if (!state || state.tool !== null || state.bannerHidden !== true + || state.suggestionsHidden !== true || state.fileMenuHidden !== true) { + throw new Error(`Reference UI is not neutral: ${JSON.stringify(state)}`); + } +} + +async function referenceUiState(page) { + const state = await evaluate(page, `({tool:kestrel.tool?.id ?? null, + bannerHidden:document.getElementById('tool-banner').hidden, + suggestionsHidden:document.getElementById('command-suggestions').hidden, + fileMenuHidden:document.getElementById('file-menu').hidden})`); + validateReferenceUi(state); + return state; +} + +async function snapshot(page, output, name, clip) { + const uiBefore = await referenceUiState(page); + const capture = await page.send("Page.captureScreenshot", { format: "png", fromSurface: true, + captureBeyondViewport: false, clip: { ...clip, scale: 1 } }); + const bytes = Buffer.from(capture.data, "base64"); + await writeFile(path.join(output, name), bytes); + const canvasData = await evaluate(page, "({gpu:kestrel.renderer.canvas.toDataURL('image/png'),overlay:kestrel.renderer.overlay.toDataURL('image/png')})"); + const layers = {}; + for (const [layer, dataUrl] of Object.entries(canvasData)) { + if (!dataUrl.startsWith("data:image/png;base64,")) throw new Error("Canvas PNG serialization failed"); + const layerBytes = Buffer.from(dataUrl.slice("data:image/png;base64,".length), "base64"); + const layerName = name.replace(/\.png$/, `-${layer}.png`); + await writeFile(path.join(output, layerName), layerBytes); + layers[layer] = { file: layerName, sha256: sha(layerBytes), width: layerBytes.readUInt32BE(16), height: layerBytes.readUInt32BE(20) }; + } + const uiAfter = await referenceUiState(page); + return { uiBefore, uiAfter, file: name, sha256: sha(bytes), width: bytes.readUInt32BE(16), height: bytes.readUInt32BE(20), + layers, purpose: "Diagnostic reference capture outside the timed interaction; not a presentation path" }; +} + +async function beginTrace(browser) { + let timer; + const completion = new Promise((resolve, reject) => { + timer = setTimeout(() => reject(new Error("Trace completion timed out")), 120_000); + browser.on("Tracing.tracingComplete", event => { clearTimeout(timer); resolve(event); }); + }); + // Avoid an unhandled rejection if an earlier page command fails; callers still observe rejection. + completion.catch(() => {}); + await browser.send("Tracing.start", { transferMode: "ReturnAsStream", streamFormat: "json", + traceConfig: { recordMode: "recordAsMuchAsPossible", includedCategories: ["benchmark", "cc", "gpu", "viz", + "blink.user_timing", "devtools.timeline", "disabled-by-default-devtools.timeline.frame"] } }); + return { completion, cancel: () => clearTimeout(timer) }; +} + +async function endTrace(browser, tracing, output, filename, revision) { + await browser.send("Tracing.end"); + const result = await tracing.completion; + if (result.dataLossOccurred) throw new Error("Chrome lost trace events"); + if (!result.stream) throw new Error("Chrome returned no trace stream"); + const chunks = []; + try { + while (true) { + const chunk = await browser.send("IO.read", { handle: result.stream, size: 1024 * 1024 }); + chunks.push(Buffer.from(chunk.data, chunk.base64Encoded ? "base64" : "utf8")); + if (chunk.eof) break; + } + } finally { await browser.send("IO.close", { handle: result.stream }); } + const raw = Buffer.concat(chunks); + const presentationTiming = analyzePresentation(JSON.parse(raw.toString("utf8")), revision); + const bytes = gzipSync(raw); + await writeFile(path.join(output, filename), bytes); + return { file: filename, sha256: sha(bytes), dataLossOccurred: false, + presentationTiming }; +} + +async function captureCase(chrome, serverUrl, output, test, repetition) { + const page = chrome.page, errors = []; + page.on("Runtime.exceptionThrown", error => errors.push(error)); + await page.send("Emulation.setDeviceMetricsOverride", { ...test.documentViewport, deviceScaleFactor: test.dpr, mobile: false }); + await page.send("Page.navigate", { url: `${serverUrl}/index.html?reference=${test.id}&repeat=${repetition}` }); + await page.send("Page.bringToFront"); + await waitFor(page, "document.documentElement?.dataset.ready==='true' && !!window.kestrel?.backendReady"); + const projectPath = test.scene.startsWith("lines-") ? `/reference/${test.scene}.kcad` : `/examples/${test.scene}.kcad`; + await evaluate(page, `(async()=>{ + const response=await fetch(${JSON.stringify(projectPath)}); + if(!response.ok) throw new Error('Project fetch failed'); + await kestrel.openFile(new File([await response.text()],${JSON.stringify(test.scene + ".kcad")},{type:'application/json'})); + kestrel.theme=${JSON.stringify(test.theme)}; kestrel.applyTheme(); + kestrel.setView(${JSON.stringify(test.view)}); kestrel.setStyle(${JSON.stringify(test.style)}); + kestrel.settings.grid=true; kestrel.settings.lineweights=false; kestrel.fit(false); + return true; + })()`); + // Let the app's own transient UI expire; do not patch/remove its DOM for stable screenshots. + await waitFor(page, "document.querySelectorAll('#toast-stack .toast').length===0"); + await settleUi(page); + const app = await evaluate(page, `(()=>{ + const r=kestrel.renderer, info=r.adapter?.info, rect=document.getElementById('viewport').getBoundingClientRect(); + return {backend:r.backend,secureContext:isSecureContext,fallbackReason:r.fallbackReason, + adapter:info?{vendor:info.vendor,architecture:info.architecture,device:info.device,description:info.description,isFallbackAdapter:info.isFallbackAdapter}:null, + clip:{x:rect.x,y:rect.y,width:rect.width,height:rect.height}, + documentViewport:{width:innerWidth,height:innerHeight,dpr:devicePixelRatio}, + canvas:{width:r.canvas.width,height:r.canvas.height},stats:{...r.stats}, + entities:kestrel.doc.entities.length,camera:kestrel.camera.serialize(),visibility:document.visibilityState,focused:document.hasFocus()}; + })()`); + const system = await chrome.browser.send("SystemInfo.getInfo"); + const hardware = hardwareAssessment(system, app); + if (!hardware.hardwareAccelerated) return { test, repetition, status: "unavailable", hardware, app, system }; + if (app.documentViewport.width !== test.documentViewport.width || app.documentViewport.height !== test.documentViewport.height + || Math.abs(app.documentViewport.dpr - test.dpr) > 1e-6 || app.visibility !== "visible") throw new Error( + `Viewport/DPR/visibility mismatch: ${JSON.stringify({ actual: app.documentViewport, visibility: app.visibility, + expected: test.documentViewport, dpr: test.dpr })}`); + const prefix = `${test.id}-run${repetition}`; + const before = await snapshot(page, output, `${prefix}-before.png`, app.clip); + const tracing = await beginTrace(chrome.browser); + let trace, samples, input; + try { + // Exercise real browser mouse input once, then restore the camera for deterministic timed pans. + const x = app.clip.x + app.clip.width / 2, y = app.clip.y + app.clip.height / 2; + const events = [{ type: "mouseMoved", x, y }, { type: "mousePressed", x, y, button: "middle", buttons: 4, clickCount: 1 }, + { type: "mouseMoved", x: x + 40, y: y + 20, button: "middle", buttons: 4 }, + { type: "mouseReleased", x: x + 40, y: y + 20, button: "middle", buttons: 0, clickCount: 1 }]; + for (const event of events) await page.send("Input.dispatchMouseEvent", event); + await settled(page); + input = await evaluate(page, "({target:kestrel.camera.target.slice(),camera:kestrel.camera.serialize()})"); + input.events = events; + input.changedCamera = JSON.stringify(input.target) !== JSON.stringify(app.camera.target); + if (!input.changedCamera) throw new Error("Middle-button input did not pan the Kestrel camera"); + await settleUi(page); + await evaluate(page, `kestrel.camera.restore(${JSON.stringify(app.camera)});kestrel.invalidate();true`); + await settled(page); + samples = await evaluate(page, `new Promise(resolve=>{ + const app=kestrel, config=${JSON.stringify(test.interaction)}, samples=[]; + const buffers=Object.fromEntries(Object.entries(app.renderer.buffers).map(([key,value])=>[key,value.buffer])); + let previous=null, moved=0; + performance.mark('webscene-reference-pan-start'); + const step=timestamp=>{ + if(previous!==null) samples.push({rafIntervalMs:timestamp-previous,cpuRenderSubmissionMs:app.renderer.stats.cpuMs}); + if(moved===config.frames) { + performance.mark('webscene-reference-pan-end'); + resolve({samples,finalCamera:app.camera.serialize(),stats:{...app.renderer.stats}, + retainedBuffers:Object.fromEntries(Object.entries(buffers).map(([key,value])=>[key,app.renderer.buffers[key]?.buffer===value])),gpuErrors:app.renderer.gpuErrors.slice()}); + return; + } + app.camera.pan(config.dxCssPixels,config.dyCssPixels);app.invalidate();moved++;previous=timestamp; + requestAnimationFrame(step); + }; requestAnimationFrame(step); + })`); + await settled(page); + await delay(100); // Allow platform presentation feedback for the last submitted update to arrive. + trace = await endTrace(chrome.browser, tracing, output, `${prefix}-trace.json.gz`, chrome.revision); + } finally { tracing.cancel(); } + await settled(page); + const after = await snapshot(page, output, `${prefix}-after.png`, app.clip); + if (samples.gpuErrors.length || errors.length) throw new Error("Kestrel reported browser/GPU errors"); + samples.cpuRenderSubmissionMilliseconds = distribution(samples.samples.map(sample => sample.cpuRenderSubmissionMs)); + samples.rafIntervalMilliseconds = distribution(samples.samples.map(sample => sample.rafIntervalMs)); + return { test, repetition, status: "captured", hardware, app, system, input, before, after, trace, samples, + timingScope: "Application CPU render/submission plus rAF intervals; GPU execution and presentation require separate trace analysis" }; +} + +export async function main(argv) { + const args = options(argv); + await mkdir(path.dirname(args.output), { recursive: true }); + await mkdir(args.output); // Never overwrite evidence, including failed attempts. + const evidence = { schemaVersion: 1, status: "running", capturedAt: new Date().toISOString(), + scope: "Hardware Chrome reference for G01; not WebScene support or full epic qualification", + host: { platform: os.platform(), release: os.release(), architecture: os.arch() }, results: [] }; + let chrome, service; + try { + const fixtureRoot = path.join(args.output, "fixture-input"); + const validation = spawnSync(process.env.PYTHON ?? (process.platform === "win32" ? "python" : "python3"), + [path.join(here, "prepare-kestrel.py"), "--destination", fixtureRoot], { encoding: "utf8" }); + if (validation.status !== 0) throw new Error(validation.stderr || validation.stdout); + evidence.fixture = JSON.parse(await readFile(path.join(here, "fixtures/kestrel.json"), "utf8")); + evidence.repositoryCommit = spawnSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).stdout.trim(); + const harnessArchive = await archiveReferenceHarness(args.output); + evidence.harness = harnessArchive.hashes; + evidence.harnessFiles = harnessArchive.files; + await mkdir(path.join(args.output, "generated-inputs"), { recursive: true }); + const generated = new Map(); + evidence.generatedProjects = {}; + for (const count of [10_000, 100_000]) { + const bytes = Buffer.from(JSON.stringify(lineProject(count))); + generated.set(`/reference/lines-${count}.kcad`, bytes); + const file = `generated-inputs/lines-${count}.kcad`; + await writeFile(path.join(args.output, file), bytes); + evidence.generatedProjects[`lines-${count}`] = { file, sha256: sha(bytes), bytes: bytes.length, count }; + } + service = await fixtureServer(path.join(fixtureRoot, "Kestrel-CAD"), generated); + chrome = await startChrome(args.chrome); + evidence.browser = await chrome.browser.send("Browser.getVersion"); + chrome.revision = evidence.browser.revision; + evidence.launch = { executable: args.chrome, args: chrome.args, headless: false }; + await chrome.page.send("Page.addScriptToEvaluateOnNewDocument", { source: "try { localStorage.clear(); } catch {}" }); + for (const test of referenceCases.filter(test => !args.case || test.id === args.case)) { + for (let repetition = 1; repetition <= args.repeat; ++repetition) { + const configured = { ...test, interaction: { ...test.interaction, frames: args.frames } }; + const result = await captureCase(chrome, service.url, args.output, configured, repetition); + evidence.results.push(result); + await writeFile(path.join(args.output, "reference.json"), JSON.stringify(evidence, null, 2) + "\n"); + console.log(`${test.id} run ${repetition}: ${result.status}`); + if (result.status === "unavailable") break; + } + } + evidence.status = evidence.results.every(result => result.status === "captured") ? "captured" : "unavailable"; + evidence.repeatability = referenceCases.filter(test => !args.case || test.id === args.case).map(test => { + const runs = evidence.results.filter(result => result.test.id === test.id && result.status === "captured"); + return { case: test.id, runs: runs.length, beforeExactPngMatch: runs.length >= 2 && new Set(runs.map(run => run.before.sha256)).size === 1, + afterExactPngMatch: runs.length >= 2 && new Set(runs.map(run => run.after.sha256)).size === 1, + layerMatches: Object.fromEntries(["before", "after"].flatMap(phase => ["gpu", "overlay"].map(layer => + [`${phase}-${layer}`, runs.length >= 2 && new Set(runs.map(run => run[phase].layers[layer].sha256)).size === 1]))) }; + }); + evidence.remainingVerification = ["Presentation timing is unavailable for unverified Chrome revisions or incomplete traces", + "Repeat pixel differences require inspection if hashes differ", + "Other target hardware and WebScene comparison remain separate gates"]; + } catch (error) { + evidence.status = "failed"; evidence.error = error.stack ?? String(error); + console.error(evidence.error); + } finally { + if (chrome) { evidence.chromeStderr = chrome.stderr; await stopChrome(chrome); } + if (service) await new Promise(resolve => service.server.close(resolve)); + await writeFile(path.join(args.output, "reference.json"), JSON.stringify(evidence, null, 2) + "\n"); + } + return evidence.status === "captured" ? 0 : evidence.status === "unavailable" ? 77 : 1; +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + main(process.argv.slice(2)).then(code => { process.exitCode = code; }).catch(error => { console.error(error); process.exitCode = 1; }); +} + +// Persist the exact source bytes used to identify a reference capture, including +// uncommitted harness edits. A repository SHA alone cannot recover those bytes. +export async function archiveReferenceHarness(output, sourceDirectory = here) { + const hashes = {}, files = {}; + await mkdir(path.join(output, "harness"), { recursive: true }); + for (const name of ["capture-chrome-reference.mjs", "chrome-session.mjs", "reference-workloads.mjs", "presentation-trace.mjs", + "prepare-kestrel.py", "../WebPlatformSubset/chrome/cdp-client.mjs"]) { + const bytes = await readFile(path.join(sourceDirectory, name)); + const file = path.posix.join("harness/tests/GraphicsCompatibility", name); + await mkdir(path.dirname(path.join(output, file)), { recursive: true }); + await writeFile(path.join(output, file), bytes); + hashes[name] = sha(bytes); + files[name] = { file, sha256: hashes[name], bytes: bytes.length }; + } + return { hashes, files }; +} diff --git a/tests/GraphicsCompatibility/chrome-session.mjs b/tests/GraphicsCompatibility/chrome-session.mjs new file mode 100644 index 000000000..3d01e6d96 --- /dev/null +++ b/tests/GraphicsCompatibility/chrome-session.mjs @@ -0,0 +1,74 @@ +import { spawn } from "node:child_process"; +import { mkdtemp, rm } from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { CdpClient } from "../WebPlatformSubset/chrome/cdp-client.mjs"; + +export const delay = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds)); + +export async function evaluate(client, expression) { + const response = await client.send("Runtime.evaluate", { expression, awaitPromise: true, returnByValue: true }, 60_000); + if (response.exceptionDetails) throw new Error(response.exceptionDetails.exception?.description ?? response.exceptionDetails.text); + return response.result.value; +} + +export async function startChrome(executable) { + const userDataDirectory = await mkdtemp(path.join(os.tmpdir(), "webscene-kestrel-chrome-")); + const args = ["--disable-background-networking", "--disable-component-update", "--disable-default-apps", + "--disable-extensions", "--disable-sync", "--no-first-run", "--no-default-browser-check", + "--remote-debugging-port=0", "--window-size=1960,1200", `--user-data-dir=${userDataDirectory}`, "about:blank"]; + const child = spawn(executable, args, { stdio: ["ignore", "ignore", "pipe"] }); + const session = { child, userDataDirectory, args, stderr: "", browser: null, page: null }; + try { + const endpoint = await new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error("Chrome DevTools startup timed out")), 30_000); + child.stderr.setEncoding("utf8"); + child.stderr.on("data", chunk => { + session.stderr += chunk; + const match = session.stderr.match(/DevTools listening on (ws:\/\/[^\s]+)/); + if (match) { clearTimeout(timer); resolve(match[1]); } + }); + child.once("error", error => { clearTimeout(timer); reject(error); }); + child.once("exit", code => { clearTimeout(timer); reject(new Error(`Chrome exited: ${code}`)); }); + }); + session.browser = await CdpClient.connect(endpoint); + const url = new URL(endpoint); + let target; + for (let attempt = 0; attempt < 100 && !target; ++attempt) { + const targets = await (await fetch(`http://${url.host}/json/list`)).json(); + target = targets.find(value => value.type === "page" && value.webSocketDebuggerUrl); + if (!target) await delay(50); + } + if (!target) throw new Error("Chrome page target unavailable"); + session.page = await CdpClient.connect(target.webSocketDebuggerUrl); + await session.page.send("Page.enable"); + await session.page.send("Runtime.enable"); + await session.page.send("Page.bringToFront"); + return session; + } catch (error) { + await stopChrome(session); + throw error; + } +} + +export async function stopChrome(session) { + try { await session.browser?.send("Browser.close", {}, 2_000); } + catch { session.child.kill("SIGTERM"); } + session.page?.close(); + session.browser?.close(); + if (session.child.exitCode === null && session.child.signalCode === null) { + await new Promise(resolve => { + const timer = setTimeout(() => { session.child.kill("SIGKILL"); resolve(); }, 2_000); + session.child.once("exit", () => { clearTimeout(timer); resolve(); }); + }); + } + await rm(session.userDataDirectory, { recursive: true, force: true }); +} + +export async function waitFor(client, expression) { + for (let attempt = 0; attempt < 300; ++attempt) { + if (await evaluate(client, expression)) return; + await delay(100); + } + throw new Error(`Timed out waiting for ${expression}`); +} diff --git a/tests/GraphicsCompatibility/fixtures/Kestrel-CAD.zip b/tests/GraphicsCompatibility/fixtures/Kestrel-CAD.zip new file mode 100644 index 000000000..199e4376b Binary files /dev/null and b/tests/GraphicsCompatibility/fixtures/Kestrel-CAD.zip differ diff --git a/tests/GraphicsCompatibility/fixtures/Kestrel-LICENSE.txt b/tests/GraphicsCompatibility/fixtures/Kestrel-LICENSE.txt new file mode 100644 index 000000000..54d8dc79e --- /dev/null +++ b/tests/GraphicsCompatibility/fixtures/Kestrel-LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Kestrel CAD contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tests/GraphicsCompatibility/fixtures/kestrel.json b/tests/GraphicsCompatibility/fixtures/kestrel.json new file mode 100644 index 000000000..fa1ee1450 --- /dev/null +++ b/tests/GraphicsCompatibility/fixtures/kestrel.json @@ -0,0 +1,65 @@ +{ + "schemaVersion": 1, + "archive": "Kestrel-CAD.zip", + "sha256": "1e9a272449923ea1d2a24b4ce7f1a1f424d0a6c9a979ef155e2a67c1424d9f7d", + "license": "MIT", + "licenseFile": "Kestrel-LICENSE.txt", + "provenance": "User-provided Kestrel-CAD.zip; original bytes preserved. No public upstream URL supplied.", + "historicalEvidence": "Archive previews, build-info and tests/results are upstream historical data, not WebScene qualification evidence.", + "files": { + "Kestrel-CAD/.gitignore": "39f5c28d03671d72064ba9108bc8da5b872788d83d778eff7cdb9c80140e84e8", + "Kestrel-CAD/Kestrel-CAD.html": "0549ac0817db91f4df5ff8e6274843a72cec3b91a5aa6e32101e3f2a888c0563", + "Kestrel-CAD/LICENSE": "9a035f4c57fe14149c39dd1b9fcd666e67c3a9870eef51e8aeaff8fcf4c622d0", + "Kestrel-CAD/README.md": "a70e33ee93e994293d193c167c59aee7e52551fe7a091dc735ce54a2b3e8363c", + "Kestrel-CAD/SHA256SUMS": "390e38e19de6c19c5dbfaa1dc8c084405bfaaec6510b7f2bd8e2e36f4f2e4287", + "Kestrel-CAD/VERIFICATION.md": "5c867724bdb24b23874d9456f6deb6cdeef6c565b3830cf185409cfef49324ef", + "Kestrel-CAD/build-info.json": "ee495ccd680959e66b276899865ef8147bf71a482debd8683475487f2c3b496d", + "Kestrel-CAD/examples/courtyard.dxf": "5cacf27957ad08c1cbe7dd88002e7f6f3b755f35ca6a72fc912dff4aa2b05c87", + "Kestrel-CAD/examples/courtyard.kcad": "8473f6d6b83ef9028465b9894e7e6e21d6821ca222eaf5d193fb230fe96538be", + "Kestrel-CAD/examples/fixture.dxf": "d3c670409e14b3b50d4336ec9d322ecfcdbc56ce5c6910c5c42e88aaedde7f59", + "Kestrel-CAD/examples/fixture.kcad": "6dc578e996607ee0258b8039847cdcadacd1cc155e7f3f5712eebf0ac76d94d2", + "Kestrel-CAD/index.html": "f05dd7b7a887dc1eb9388655b5b17fb497987eae5e60b03f4a9144b8b6001303", + "Kestrel-CAD/previews/capture-info.json": "4f4d3685ca9467bdb503f11d4d3651eacd77f612c8f708560a344cd922e6c149", + "Kestrel-CAD/previews/kestrel-2d-dark.png": "461daeb602f6dbb6f76cf0aa7e47d8fee3d520e08c543f2e8eac94b6d72f4664", + "Kestrel-CAD/previews/kestrel-2d-light.png": "9caae4a044c2a3a87e7eec500c39d3e371f47a88ca0f2688821f114ce8e139b7", + "Kestrel-CAD/previews/kestrel-3d-dark.png": "d7f561df1393d1034fc90c8c40938513d64ff925f6ae7a6654ae0fa7d411a794", + "Kestrel-CAD/src/app.js": "24e3b4d8027bce246aad982b01410d0ed3d068c21e059f24e0f4168674aba5d4", + "Kestrel-CAD/src/csg.js": "083c341c3706b7d15dd6a35bde5cf3aca7f66d6be441d8c5672bf4dfba624d8f", + "Kestrel-CAD/src/examples.js": "3a253a200858e00eb5ae55356eaceec471ca3c87e039e51497d7cfb7dcad9b1a", + "Kestrel-CAD/src/exchange.js": "e574d8b761d4ab233f59351fa4ce5ebf4f9f035503b1121ea05ec8d0f3371e7b", + "Kestrel-CAD/src/geometry.js": "dfee94fe15d204f1f9f784f5f08674b12653b9bff94f211a3415f16b8fb8550f", + "Kestrel-CAD/src/io-worker.js": "c45a8a173281dc9628340844b1d1d0eeede1bec604e5083dc323702fdf276210", + "Kestrel-CAD/src/math.js": "63b6d9a251c5416cd24100b887c2db73603b91bccdbdec9d00357d57e5e68847", + "Kestrel-CAD/src/model.js": "ff1b537629166fed88687bee4bcbbbd675e152b81882149b13ebbd2a184bd268", + "Kestrel-CAD/src/renderer.js": "3dd46d236c3ba1a1966660e5e4ced3d4bb42a53fdb1e2a43acc1504e9bf3f982", + "Kestrel-CAD/src/style.css": "b8c9f3935d46e503ca539fe6e893015968d3f27f79f5df074e101902dc1338c3", + "Kestrel-CAD/src/ui.js": "1a3e9418139ed04dcd84e9c10a583e44c70e9ca622927d042a603b176d9cfb72", + "Kestrel-CAD/start.bat": "1b2f9341ebb577c66f7e0a3ed587a97d546654117d58a0f2475f81bb36918281", + "Kestrel-CAD/start.sh": "98e41a32e1172f8eca22319ae5c620cf16fc028027fabd9106709f67686d5d2e", + "Kestrel-CAD/tests/browser.test.py": "9ec357b237aca2e77e580fc3a3d6017109eda320fa44f8756013e07146d40552", + "Kestrel-CAD/tests/core.test.js": "f37e5110011236ef5687aabee1d8ac554aff663cbc782a2c78608442db82f829", + "Kestrel-CAD/tests/dxf_interop.py": "dd424003de96a4c5312e66ab63f62107e29377e4d8723d98cb61af19a40b1fc9", + "Kestrel-CAD/tests/fixtures/analytic-roundtrip.dxf": "0f0006b38d10461f9adc46a29cf1b40f9c14b1b13947acb3b8cd06c9a77901d6", + "Kestrel-CAD/tests/fixtures/external-expected.json": "18fb2fcc034de09de3f96fb8a5087e35f190a3ac1d424869a45b4e3306e864ad", + "Kestrel-CAD/tests/fixtures/external.dxf": "d52ed14bb6fc12de6550ab6273edd4cde6b9182e23441623dc384644767ee843", + "Kestrel-CAD/tests/gpu.test.js": "eb31261838219fa769fbad9d0f7666277bdf1af58e1f662cd1ffb1d4f738ab13", + "Kestrel-CAD/tests/results/browser-export.dxf": "5cacf27957ad08c1cbe7dd88002e7f6f3b755f35ca6a72fc912dff4aa2b05c87", + "Kestrel-CAD/tests/results/browser-export.kcad": "4b086701ceb3b9e21a1d4d0b835e55a00505907d122dba6b5062c50e525a7abc", + "Kestrel-CAD/tests/results/browser-export.png": "4e179044dbff598b684e2dfcc6c2dee9232583feecf2f8a963cb6bfa1297706c", + "Kestrel-CAD/tests/results/browser-export.svg": "6ddae088fa30821bf022d9a6663ca196161890696c645a1f178ce75b3b142ee6", + "Kestrel-CAD/tests/results/browser-results.json": "b465001ff757382191a7d1ca1e70e5bea54ae3318483e04b99b51b4f6596a6d8", + "Kestrel-CAD/tests/results/core-results.json": "3779a30b3bdf1e41b978b25bca8d73a9ccdd62242ef56de98bfd3d8581f9ce64", + "Kestrel-CAD/tests/results/dxf-interop-results.json": "a31b85ec685b09ab431136cca9ea7907b8aa49c9967362c22f713c175df5ae18", + "Kestrel-CAD/tests/results/external-import.json": "c5657d68853779d878273b0b45c8763977684c31a44f95b08b1fb51cd2566940", + "Kestrel-CAD/tests/results/gpu-environment.json": "4970faace8ff84468d3e1afaeec1d61a0e7f8b3ad22faf57cd9879f4b1aef4f5", + "Kestrel-CAD/tests/results/kestrel-2d-dark.png": "5b229229d2bcda1fc1b3c1276fc6e86aee3fc34fea2f4088e8d83f040afb2514", + "Kestrel-CAD/tests/results/kestrel-2d-light.png": "f09392e1901dc708d335dccfa9e31accdab9e8ef224f940675ec4fc83f4528de", + "Kestrel-CAD/tests/results/kestrel-3d-dark.png": "5c764128b0a560f90e455ffc4cec0e2fa3d1902fa897902bce707da754e514f9", + "Kestrel-CAD/tests/results/server-results.json": "f14c8265f1c7a9fc764643728bae2d27aaa21d4a1a4e2152695e28bd8ff2ef64", + "Kestrel-CAD/tests/server.test.py": "04d39242bd8455b581cbad160f1bf99d6f3415ee3f2af41b9e951617113dce2f", + "Kestrel-CAD/tests/webgpu.html": "70c24b80339514772fda94faac1feeccdd3a3ebf21b12c7fd21ca8d57d67c532", + "Kestrel-CAD/tools/build.py": "f0eeb9d213f429b4f4503348bf794bacb2715a35e2b9adcb440f2ffd6d24f269", + "Kestrel-CAD/tools/capture_previews.py": "5b7d9d28883323a5ae532e916e140fed68f9533631708570be17cba902e8eb14", + "Kestrel-CAD/tools/serve.py": "559b13fc63cec38c7cf1527695a82ba1986816ecf441045645981917c5547b3d" + } +} diff --git a/tests/GraphicsCompatibility/prepare-kestrel.py b/tests/GraphicsCompatibility/prepare-kestrel.py new file mode 100644 index 000000000..1e8780b05 --- /dev/null +++ b/tests/GraphicsCompatibility/prepare-kestrel.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Verify the immutable Kestrel input and optionally extract a disposable copy.""" +import argparse +import hashlib +import json +from pathlib import Path, PurePosixPath +import zipfile + + +def prepare(destination=None): + fixtures = Path(__file__).resolve().parent / "fixtures" + manifest = json.loads((fixtures / "kestrel.json").read_text()) + archive = fixtures / manifest["archive"] + if hashlib.sha256(archive.read_bytes()).hexdigest() != manifest["sha256"]: + raise ValueError("Kestrel archive checksum mismatch") + with zipfile.ZipFile(archive) as bundle: + entries = bundle.infolist() + names = [entry.filename for entry in entries] + if len(set(names)) != len(names) or set(names) != set(manifest["files"]): + raise ValueError("Kestrel archive inventory mismatch") + for entry in entries: + path = PurePosixPath(entry.filename) + if path.is_absolute() or ".." in path.parts or "\\" in entry.filename: + raise ValueError("Unsafe archive path") + data = bundle.read(entry) + if hashlib.sha256(data).hexdigest() != manifest["files"][entry.filename]: + raise ValueError(f"Kestrel file checksum mismatch: {entry.filename}") + if bundle.read("Kestrel-CAD/LICENSE") != (fixtures / manifest["licenseFile"]).read_bytes(): + raise ValueError("Kestrel license mismatch") + if destination is not None: + # A fresh destination prevents prior test output or symlinks contaminating the fixture. + destination.mkdir(parents=True, exist_ok=False) + for entry in entries: + output = destination / entry.filename + output.parent.mkdir(parents=True, exist_ok=True) + output.write_bytes(bundle.read(entry)) + print(f"Verified {len(names)} immutable Kestrel files; no GPU qualification implied.") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--destination", type=Path, help="New directory for a disposable extraction") + args = parser.parse_args() + prepare(args.destination) diff --git a/tests/GraphicsCompatibility/presentation-trace.mjs b/tests/GraphicsCompatibility/presentation-trace.mjs new file mode 100644 index 000000000..04110e097 --- /dev/null +++ b/tests/GraphicsCompatibility/presentation-trace.mjs @@ -0,0 +1,62 @@ +// This source contract was inspected at the Chrome revision below. Unknown revisions stay unqualified. +export const supportedChromeRevision = "@529d9a34b491745086b59458f58a5aae8292adaa"; +const sourceRoot = `https://chromium.googlesource.com/chromium/src/+/${supportedChromeRevision.slice(1)}/cc/metrics/`; + +export function distribution(values) { + if (!values.length || values.some(value => !Number.isFinite(value))) return null; + const sorted = [...values].sort((a, b) => a - b); + const percentile = fraction => sorted[Math.max(0, Math.ceil(fraction * sorted.length) - 1)]; + return { count: sorted.length, min: sorted[0], median: percentile(0.5), p95: percentile(0.95), + max: sorted.at(-1), mean: sorted.reduce((sum, value) => sum + value, 0) / sorted.length }; +} + +export function analyzePresentation(trace, revision) { + const unavailable = reason => ({ status: "unavailable", reason }); + if (revision !== supportedChromeRevision) return unavailable("Chrome revision has not had its presentation trace source contract verified"); + const events = trace.traceEvents; + if (!Array.isArray(events)) return unavailable("Missing trace event array"); + const starts = events.filter(event => event.name === "webscene-reference-pan-start" && event.ph === "I"); + const ends = events.filter(event => event.name === "webscene-reference-pan-end" && event.ph === "I"); + if (starts.length !== 1 || ends.length !== 1 || starts[0].pid !== ends[0].pid || ends[0].ts <= starts[0].ts) + return unavailable("Missing or ambiguous interaction markers"); + const start = starts[0], end = ends[0], active = new Map(), frames = [], reporterStates = {}; + let ambiguous = false; + const reporters = events.filter(event => event.name === "PipelineReporter" && event.pid === start.pid) + .sort((a, b) => a.ts - b.ts); + for (const event of reporters) { + // Trace local IDs are strings. Do not use the 64-bit numeric surface/display IDs: JSON loses precision. + const identity = event.id2?.local ?? event.id2?.global; + if (!identity) continue; + if (event.ph === "b") { + if (active.has(identity)) ambiguous = true; + active.set(identity, event); + } else if (event.ph === "e") { + const begin = active.get(identity); + active.delete(identity); + if (!begin || begin.ts < start.ts || begin.ts > end.ts) continue; + const info = begin.args?.frame_reporter; + if (!info?.state || event.ts < begin.ts || !Number.isFinite(event.ts)) { ambiguous = true; continue; } + reporterStates[info.state] = (reporterStates[info.state] ?? 0) + 1; + if (["STATE_PRESENTED_ALL", "STATE_PRESENTED_PARTIAL"].includes(info.state)) { + frames.push({ beginMicroseconds: begin.ts, presentedMicroseconds: event.ts, + sequence: info.frame_sequence, source: info.frame_source, layerTreeHost: info.layer_tree_host_id, + partial: info.state === "STATE_PRESENTED_PARTIAL", missingContent: info.has_missing_content === true }); + } + } + } + if (ambiguous) return unavailable("Ambiguous or malformed PipelineReporter event pairing"); + if ([...active.values()].some(event => event.ts >= start.ts && event.ts <= end.ts)) + return unavailable("Trace ended before all interaction reporters completed"); + const timestamps = [...new Set(frames.map(frame => frame.presentedMicroseconds))].sort((a, b) => a - b); + if (timestamps.length < 2) return unavailable("Fewer than two distinct platform presentation feedback timestamps"); + const intervals = timestamps.slice(1).map((timestamp, index) => (timestamp - timestamps[index]) / 1000); + return { status: "measured", source: "Presented PipelineReporter termination timestamps (platform presentation feedback)", + sourceContract: [sourceRoot + "compositor_frame_reporting_controller.cc", sourceRoot + "compositor_frame_reporter.cc"], + revision, rendererPid: start.pid, interactionStartMicroseconds: start.ts, interactionEndMicroseconds: end.ts, + uniquePresentedFrames: timestamps.length, + framesPerSecond: (timestamps.length - 1) * 1e6 / (timestamps.at(-1) - timestamps[0]), + intervalMilliseconds: distribution(intervals), partialReporters: frames.filter(frame => frame.partial).length, + missingContentReporters: frames.filter(frame => frame.missingContent).length, + reporterStates, frames, + note: "Reporter state counts include compositor bookkeeping; they are not counts of distinct application frames. Presentation feedback is separate from CPU submission and rAF delivery." }; +} diff --git a/tests/GraphicsCompatibility/reference-tests.mjs b/tests/GraphicsCompatibility/reference-tests.mjs new file mode 100644 index 000000000..fd2246a25 --- /dev/null +++ b/tests/GraphicsCompatibility/reference-tests.mjs @@ -0,0 +1,112 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import { lineProject, referenceCases } from "./reference-workloads.mjs"; +import { hardwareAssessment, archiveReferenceHarness, validateReferenceUi } from "./capture-chrome-reference.mjs"; +import { analyzePresentation, supportedChromeRevision } from "./presentation-trace.mjs"; + +test("seeded line input has stable bytes and complete unique entities", () => { + const project = lineProject(10_000); + assert.equal(createHash("sha256").update(JSON.stringify(project)).digest("hex"), + "07ebda76dc757bb365a092f9ed7fb7c7d5c8a6498ceb11760f23f953b05c9099"); + assert.equal(new Set(project.entities.map(entity => entity.id)).size, 10_000); + assert.notDeepEqual(lineProject(2, 1).entities, lineProject(2, 2).entities); + assert.equal(lineProject(100_000).entities.length, 100_000); + assert.throws(() => lineProject(200_001)); + assert.throws(() => lineProject(2, -1)); +}); + +test("matrix includes both DPRs and themes for all four required scenes", () => { + assert.equal(referenceCases.length, 16); + assert.equal(new Set(referenceCases.map(value => value.id)).size, 16); + for (const scene of ["courtyard", "fixture", "lines-10000", "lines-100000"]) + assert.equal(referenceCases.filter(value => value.scene === scene).length, 4); +}); + +test("Chrome hardware evidence rejects missing, fallback and software records", () => { + const system = { gpu: { devices: [{ deviceString: "Synthetic discrete GPU" }], + featureStatus: { webgpu: "enabled", gpu_compositing: "enabled" } } }; + const app = { secureContext: true, backend: "WebGPU", adapter: { isFallbackAdapter: false } }; + assert.equal(hardwareAssessment(system, app).status, "confirmed"); + for (const fallback of [undefined, true, "false", 0]) + assert.equal(hardwareAssessment(system, { ...app, adapter: { isFallbackAdapter: fallback } }).status, "unavailable"); + assert.equal(hardwareAssessment(system, { ...app, backend: "Canvas 2D" }).status, "unavailable"); + assert.equal(hardwareAssessment({}, app).status, "unavailable"); + system.gpu.devices[0].deviceString = "SwiftShader"; + assert.equal(hardwareAssessment(system, app).status, "unavailable"); +}); + +function fixture() { + // Synthetic trace data tests the parser contract, never hardware qualification. + const traceEvents = [ + { name: "webscene-reference-pan-start", ph: "I", ts: 1000, pid: 10 }, + { name: "webscene-reference-pan-end", ph: "I", ts: 60000, pid: 10 } + ]; + const frame = (id, begin, end, state = "STATE_PRESENTED_ALL", pid = 10) => { + traceEvents.push({ name: "PipelineReporter", ph: "b", ts: begin, pid, id2: { local: id }, + args: { frame_reporter: { state, frame_sequence: begin, has_missing_content: false } } }); + traceEvents.push({ name: "PipelineReporter", ph: "e", ts: end, pid, id2: { local: id } }); + }; + frame("0x1", 2000, 20000); + frame("0x2", 18000, 36667); + frame("0x3", 34000, 53334); + frame("0x4", 35000, 53334); // Two reporters may share one physical presentation. + frame("0x5", 50000, 55000, "STATE_DROPPED"); + frame("0x6", 50000, 59000, "STATE_PRESENTED_ALL", 20); // Another renderer must not contaminate cadence. + frame("0x7", 100, 59000); // A pre-interaction reporter is excluded. + return { traceEvents }; +} + +test("presentation cadence uses feedback timestamps, not rAF or submission events", () => { + const result = analyzePresentation(fixture(), supportedChromeRevision); + assert.equal(result.status, "measured"); + assert.equal(result.uniquePresentedFrames, 3); + assert.equal(result.intervalMilliseconds.p95, 16.667); + assert.equal(result.reporterStates.STATE_DROPPED, 1); + assert.ok(Math.abs(result.framesPerSecond - 60) < 0.01); +}); + +test("unknown Chrome, missing markers and malformed pairs remain unavailable", () => { + assert.equal(analyzePresentation(fixture(), "unknown").status, "unavailable"); + const missing = fixture(); + missing.traceEvents.shift(); + assert.equal(analyzePresentation(missing, supportedChromeRevision).status, "unavailable"); + const incomplete = fixture(); + incomplete.traceEvents = incomplete.traceEvents.filter(event => !(event.ph === "e" && event.id2?.local === "0x3")); + assert.equal(analyzePresentation(incomplete, supportedChromeRevision).status, "unavailable"); +}); + +test("reference archive retains exact harness bytes after the source changes", async t => { + const { mkdtemp, mkdir, writeFile, readFile, rm } = await import("node:fs/promises"); + const { tmpdir } = await import("node:os"); + const path = await import("node:path"); + const root = await mkdtemp(path.join(tmpdir(), "webscene-harness-archive-")); + t.after(() => rm(root, { recursive: true, force: true })); + const source = path.join(root, "source"), output = path.join(root, "capture"); + await mkdir(source); + const names = ["capture-chrome-reference.mjs", "chrome-session.mjs", "reference-workloads.mjs", "presentation-trace.mjs", + "prepare-kestrel.py", "../WebPlatformSubset/chrome/cdp-client.mjs"]; + for (const name of names) { + await mkdir(path.dirname(path.join(source, name)), { recursive: true }); + await writeFile(path.join(source, name), `// original ${name}\r\n`); + } + const archive = await archiveReferenceHarness(output, source); + for (const name of names) await writeFile(path.join(source, name), "// changed after capture"); + for (const name of names) { + const stored = await readFile(path.join(output, archive.files[name].file)); + assert.equal(stored.toString(), `// original ${name}\r\n`); + assert.equal(createHash("sha256").update(stored).digest("hex"), archive.hashes[name]); + assert.equal(archive.files[name].sha256, archive.hashes[name]); + assert.equal(archive.files[name].bytes, stored.length); + } +}); + + +test("reference capture rejects active commands and visible transient UI", () => { + const neutral = { tool: null, bannerHidden: true, suggestionsHidden: true, fileMenuHidden: true }; + assert.doesNotThrow(() => validateReferenceUi(neutral)); + assert.throws(() => validateReferenceUi({ ...neutral, tool: "erase" }), /not neutral/); + for (const key of ["bannerHidden", "suggestionsHidden", "fileMenuHidden"]) + assert.throws(() => validateReferenceUi({ ...neutral, [key]: false }), /not neutral/); + assert.throws(() => validateReferenceUi({}), /not neutral/); +}); diff --git a/tests/GraphicsCompatibility/reference-workloads.mjs b/tests/GraphicsCompatibility/reference-workloads.mjs new file mode 100644 index 000000000..90c7b58f3 --- /dev/null +++ b/tests/GraphicsCompatibility/reference-workloads.mjs @@ -0,0 +1,34 @@ +// Synthetic drawing data only. Kestrel application files remain byte-for-byte unchanged. +export const referenceSeed = 0x22c0ffee; + +export function lineProject(count, seed = referenceSeed) { + if (!Number.isInteger(count) || count < 1 || count > 200_000) throw new Error("Invalid line count"); + if (!Number.isInteger(seed) || seed < 0 || seed > 0xffffffff) throw new Error("Invalid uint32 seed"); + let state = seed >>> 0; + const random = () => { + state = (Math.imul(state, 1664525) + 1013904223) >>> 0; + return (state >>> 8) / 0x1000000; + }; + const quantize = value => Math.round(value * 1024) / 1024; + const entities = Array.from({ length: count }, (_, index) => { + const x = quantize((random() - 0.5) * 1000); + const y = quantize((random() - 0.5) * 1000); + const dx = quantize(2 + random() * 18); + const dy = quantize((random() - 0.5) * 40); + return { id: `reference-line-${index}`, type: "LINE", layer: "0", color: "bylayer", + linetype: "ByLayer", points: [[x, y, 0], [quantize(x + dx), quantize(y + dy), 0]] }; + }); + return { format: "kestrel-cad", version: 1, name: `Seeded ${count} lines`, units: "mm", + currentLayer: "0", layers: [{ id: "0", name: "REFERENCE", color: "#59c8d9", visible: true, + locked: false, linetype: "Continuous", lineweight: 0.25 }], entities, camera: null }; +} + +export const referenceCases = ["courtyard", "fixture", "lines-10000", "lines-100000"].flatMap(scene => + [1, 2].flatMap(dpr => ["dark", "light"].map(theme => ({ + id: `${scene}-dpr${dpr}-${theme}`, scene, dpr, theme, + style: scene === "fixture" ? "shaded-edges" : "wireframe", + view: scene === "fixture" ? "iso" : "top", + documentViewport: { width: 1920, height: 1080 }, seed: referenceSeed, + interaction: { kind: "camera-pan", frames: 180, dxCssPixels: 0.5, dyCssPixels: 0.25 } + }))) +); diff --git a/tests/GraphicsCompatibility/test_pan_analysis.py b/tests/GraphicsCompatibility/test_pan_analysis.py new file mode 100644 index 000000000..40df843e8 --- /dev/null +++ b/tests/GraphicsCompatibility/test_pan_analysis.py @@ -0,0 +1,52 @@ +import importlib.util +import json +from pathlib import Path +import unittest + +spec = importlib.util.spec_from_file_location('pan_analysis', Path(__file__).with_name('analyze-kestrel-pan.py')) +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) + + +class PanAnalysisTests(unittest.TestCase): + def timeline(self): + return {'timestampFrequency': 1000, + 'publications': [{'Revision': 7, 'Timestamp': 100, 'ConsumedInputSequence': 3}], + 'renderedScenes': [{'Revision': 7, 'Timestamp': 140, 'AcceptedTimestamp': 130}]} + + def log(self, timeline): + return 'Kestrel pan composition timeline: ' + json.dumps(timeline) + '\nKestrel pan workload validated (physical presentation remains unqualified).' + + def test_separates_queue_and_draw_without_claiming_presentation(self): + result = module.analyze(self.log(self.timeline())) + self.assertEqual(30, result['publicationToAcceptance']['medianMilliseconds']) + self.assertEqual(10, result['acceptanceToDrawCallbackEnd']['medianMilliseconds']) + self.assertEqual(40, result['publicationToDrawCallbackEnd']['medianMilliseconds']) + self.assertFalse(result['physicalPresentationVerified']) + + def test_rejects_unvalidated_workload(self): + with self.assertRaisesRegex(ValueError, 'not validated'): + module.analyze('Kestrel WebGPU startup check passed (interaction qualification remains).') + + def test_rejects_inverted_acceptance_timestamp(self): + timeline = self.timeline() + timeline['renderedScenes'][0]['AcceptedTimestamp'] = 150 + with self.assertRaisesRegex(ValueError, 'timestamp order'): + module.analyze(self.log(timeline)) + + def test_missing_acceptance_is_unavailable_not_zero_latency(self): + timeline = self.timeline() + del timeline['renderedScenes'][0]['AcceptedTimestamp'] + result = module.analyze(self.log(timeline)) + self.assertEqual({'count': 0}, result['publicationToAcceptance']) + self.assertEqual(40, result['publicationToDrawCallbackEnd']['medianMilliseconds']) + + def test_rejects_duplicate_revision(self): + timeline = self.timeline() + timeline['publications'] *= 2 + with self.assertRaisesRegex(ValueError, 'Duplicate'): + module.analyze(self.log(timeline)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/GraphicsCompatibility/test_reference_archive.py b/tests/GraphicsCompatibility/test_reference_archive.py new file mode 100644 index 000000000..b51307c5c --- /dev/null +++ b/tests/GraphicsCompatibility/test_reference_archive.py @@ -0,0 +1,56 @@ +import hashlib +import importlib.util +import json +from pathlib import Path +import tempfile +import unittest + +spec = importlib.util.spec_from_file_location('archive', Path(__file__).with_name('verify-reference-archive.py')) +archive = importlib.util.module_from_spec(spec) +spec.loader.exec_module(archive) + + +class ArchiveTests(unittest.TestCase): + def test_retained_bytes_and_rejections(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + names = ['capture-chrome-reference.mjs', 'chrome-session.mjs', + 'reference-workloads.mjs', 'presentation-trace.mjs', + 'prepare-kestrel.py', '../WebPlatformSubset/chrome/cdp-client.mjs'] + digest = hashlib.sha256(b'source').hexdigest() + for name in names: + target = root / 'harness/tests/GraphicsCompatibility' / name + target.parent.mkdir(parents=True, exist_ok=True) + target.write_bytes(b'source') + data = {'status': 'captured', 'harness': dict.fromkeys(names, digest), + 'harnessFiles': {n: {'file': str(Path('harness/tests/GraphicsCompatibility') / n), 'sha256': digest, 'bytes': 6} for n in names}} + def save(): + (root / 'reference.json').write_text(json.dumps(data)) + save() + self.assertEqual(archive.verify(root)['referencedFiles'], 6) + helper = data['harnessFiles'].pop(names[-1]) + save() + with self.assertRaisesRegex(ValueError, 'harness archive is missing'): + archive.verify(root) + data['harnessFiles'][names[-1]] = helper + save() + target = root / data['harnessFiles'][names[0]]['file'] + target.write_bytes(b'changed') + with self.assertRaisesRegex(ValueError, 'hash mismatch'): + archive.verify(root) + target.unlink() + with self.assertRaises(FileNotFoundError): + archive.verify(root) + target.write_bytes(b'source') + data['harnessFiles'][names[0]]['file'] = '../outside' + save() + with self.assertRaisesRegex(ValueError, 'escapes'): + archive.verify(root) + data['status'] = 'running' + save() + with self.assertRaisesRegex(ValueError, 'incomplete'): + archive.verify(root) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/GraphicsCompatibility/verify-reference-archive.py b/tests/GraphicsCompatibility/verify-reference-archive.py new file mode 100644 index 000000000..9c70e6a4c --- /dev/null +++ b/tests/GraphicsCompatibility/verify-reference-archive.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Verify archived reference bytes, not rendering or performance qualification.""" +import argparse +import hashlib +import json +from pathlib import Path + + +def verify(root): + root = Path(root).resolve() + evidence = json.loads((root / 'reference.json').read_text()) + if evidence.get('status') != 'captured': + raise ValueError('Reference capture is incomplete or unavailable') + required = {'capture-chrome-reference.mjs', 'chrome-session.mjs', + 'reference-workloads.mjs', 'presentation-trace.mjs', + 'prepare-kestrel.py', '../WebPlatformSubset/chrome/cdp-client.mjs'} + if set(evidence.get('harnessFiles', {})) != required: + raise ValueError('Exact harness archive is missing') + checked = set() + + def visit(value): + if isinstance(value, dict): + if 'file' in value and 'sha256' in value: + filename = value['file'] + target = (root / filename).resolve() + if Path(filename).is_absolute() or not target.is_relative_to(root): + raise ValueError(f'Artifact escapes archive: {filename}') + data = target.read_bytes() + if hashlib.sha256(data).hexdigest() != value['sha256']: + raise ValueError(f'Artifact hash mismatch: {filename}') + if 'bytes' in value and len(data) != value['bytes']: + raise ValueError(f'Artifact size mismatch: {filename}') + checked.add(filename) + for child in value.values(): + visit(child) + elif isinstance(value, list): + for child in value: + visit(child) + + visit(evidence) + for name, item in evidence['harnessFiles'].items(): + if evidence.get('harness', {}).get(name) != item['sha256']: + raise ValueError(f'Harness identity mismatch: {name}') + return {'status': 'verified', 'referencedFiles': len(checked), + 'scope': 'Referenced archive bytes only; not matrix completeness, pixels, conformance or performance qualification'} + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('archive', type=Path) + args = parser.parse_args() + try: + print(json.dumps(verify(args.archive), indent=2)) + except (ValueError, OSError, TypeError, KeyError) as error: + parser.exit(1, f'Archive verification failed: {error}\n') diff --git a/tests/WebPlatformSubset/README.md b/tests/WebPlatformSubset/README.md index ac45fee5b..124bc6f05 100644 --- a/tests/WebPlatformSubset/README.md +++ b/tests/WebPlatformSubset/README.md @@ -664,3 +664,19 @@ Against the clean parent, three alternating same-machine runs put the 500-sample median-of-medians at 0.715/0.702 ms (parent/current) and the 30-sample light-DOM selector workload at 32.465/32.648 ms (+0.56%). This is within the established no-meaningful- regression envelope while the feature paths themselves remain pay for what is used. + +The project-owned animation-frame batch candidate covers cancellation, one timestamp per admitted batch, microtask checkpoints, deferred nested RAF and timer ordering. Run it with: + +```sh +dotnet run --project tests/WebPlatformSubset/runner -c Release -- --manifest tests/WebPlatformSubset/webscene-animation-frame-batch-profile.json --selection candidate --native-library /absolute/path/to/libwebscene_native_engine.dylib --output artifacts/wpt-animation-frame-batch +``` + +It is a local candidate, not an upstream WPT or physical-presentation qualification. The native engine regression additionally checks that host evaluation cannot observe a partial admitted batch. + +A harness or contract entry can opt into `"nativeNavigation": true`. That path loads the prepared HTML through the native document parser and the product resource loader, instead of extracting scripts/styles into an `innerHTML` fixture. The temporary prepared file lives beside the source fixture so relative resources retain their directory, and is removed after engine destruction. Other entries retain their existing adapter path. This still uses the runner’s prepared harness scripts; it is not a claim of an unmodified upstream navigation test. + +The inert script/comment/template stylesheet regression uses this path. Reproduce its candidate result with: + +```sh +dotnet run --project tests/WebPlatformSubset/runner -c Release -- --manifest tests/WebPlatformSubset/webscene-native-navigation-profile.json --selection candidate --native-library /absolute/path/to/libwebscene_native_engine.dylib --output artifacts/wpt-native-navigation +``` diff --git a/tests/WebPlatformSubset/contracts/animation-frame-batch-boundary.html b/tests/WebPlatformSubset/contracts/animation-frame-batch-boundary.html new file mode 100644 index 000000000..8861f8557 --- /dev/null +++ b/tests/WebPlatformSubset/contracts/animation-frame-batch-boundary.html @@ -0,0 +1,43 @@ + + +Animation callbacks preserve a rendering opportunity + diff --git a/tests/WebPlatformSubset/contracts/css-border-functional-color-width.html b/tests/WebPlatformSubset/contracts/css-border-functional-color-width.html new file mode 100644 index 000000000..e9edc3362 --- /dev/null +++ b/tests/WebPlatformSubset/contracts/css-border-functional-color-width.html @@ -0,0 +1,15 @@ + +Border shorthand functional colors preserve width + + +
Layer
+ diff --git a/tests/WebPlatformSubset/contracts/css-grid-auto-row-shrink.html b/tests/WebPlatformSubset/contracts/css-grid-auto-row-shrink.html new file mode 100644 index 000000000..1afd05cff --- /dev/null +++ b/tests/WebPlatformSubset/contracts/css-grid-auto-row-shrink.html @@ -0,0 +1,23 @@ + +Auto grid rows respect item minimums when resized + + + +
+
+
+ diff --git a/tests/WebPlatformSubset/contracts/css-grid-flex-track-minimum.html b/tests/WebPlatformSubset/contracts/css-grid-flex-track-minimum.html new file mode 100644 index 000000000..219933f96 --- /dev/null +++ b/tests/WebPlatformSubset/contracts/css-grid-flex-track-minimum.html @@ -0,0 +1,20 @@ + +Flexible grid tracks distribute space without subtracting their own minimum twice + + + diff --git a/tests/WebPlatformSubset/contracts/css-root-theme-interaction-recascade.html b/tests/WebPlatformSubset/contracts/css-root-theme-interaction-recascade.html new file mode 100644 index 000000000..fc7614ec6 --- /dev/null +++ b/tests/WebPlatformSubset/contracts/css-root-theme-interaction-recascade.html @@ -0,0 +1,42 @@ + +Root theme variables survive descendant interaction recascade + + + + + +
+ diff --git a/tests/WebPlatformSubset/contracts/dialog-opening-lifecycle.html b/tests/WebPlatformSubset/contracts/dialog-opening-lifecycle.html new file mode 100644 index 000000000..625bc26bd --- /dev/null +++ b/tests/WebPlatformSubset/contracts/dialog-opening-lifecycle.html @@ -0,0 +1,71 @@ + +Dialog opening mode, cancellation, focus and cleanup + + + + diff --git a/tests/WebPlatformSubset/contracts/html-script-style-text-is-inert.html b/tests/WebPlatformSubset/contracts/html-script-style-text-is-inert.html new file mode 100644 index 000000000..7ef3181b0 --- /dev/null +++ b/tests/WebPlatformSubset/contracts/html-script-style-text-is-inert.html @@ -0,0 +1,17 @@ + +Stylesheet markup inside script text is inert + + + + + + + + diff --git a/tests/WebPlatformSubset/contracts/media-audio-lifecycle.html b/tests/WebPlatformSubset/contracts/media-audio-lifecycle.html new file mode 100644 index 000000000..918dd913b --- /dev/null +++ b/tests/WebPlatformSubset/contracts/media-audio-lifecycle.html @@ -0,0 +1,78 @@ + + + + + + + + + + + diff --git a/tests/WebPlatformSubset/contracts/media-video-lifecycle.html b/tests/WebPlatformSubset/contracts/media-video-lifecycle.html new file mode 100644 index 000000000..f3b08bcba --- /dev/null +++ b/tests/WebPlatformSubset/contracts/media-video-lifecycle.html @@ -0,0 +1,43 @@ + + + diff --git a/tests/WebPlatformSubset/contracts/module-worker-clone.html b/tests/WebPlatformSubset/contracts/module-worker-clone.html new file mode 100644 index 000000000..b33c6c5a0 --- /dev/null +++ b/tests/WebPlatformSubset/contracts/module-worker-clone.html @@ -0,0 +1,44 @@ + +Module workers and structured clone contracts + + diff --git a/tests/WebPlatformSubset/contracts/resize-observer-raf-order.html b/tests/WebPlatformSubset/contracts/resize-observer-raf-order.html new file mode 100644 index 000000000..035694acf --- /dev/null +++ b/tests/WebPlatformSubset/contracts/resize-observer-raf-order.html @@ -0,0 +1,41 @@ + + +ResizeObserver follows the complete RAF batch +
+ diff --git a/tests/WebPlatformSubset/contracts/resources/aureon/module.js b/tests/WebPlatformSubset/contracts/resources/aureon/module.js new file mode 100644 index 000000000..327033a9c --- /dev/null +++ b/tests/WebPlatformSubset/contracts/resources/aureon/module.js @@ -0,0 +1,3 @@ +export let count = 1; +export const url = import.meta.url; +export function increment() { ++count; } diff --git a/tests/WebPlatformSubset/contracts/resources/aureon/worker.js b/tests/WebPlatformSubset/contracts/resources/aureon/worker.js new file mode 100644 index 000000000..c5b7f6f9b --- /dev/null +++ b/tests/WebPlatformSubset/contracts/resources/aureon/worker.js @@ -0,0 +1,6 @@ +import { count } from './module.js'; +self.onmessage = e => { + e.data[0] += count; + postMessage(e.data, [e.data.buffer]); + postMessage({ detached: e.data.byteLength === 0, documentType: typeof document }); +}; diff --git a/tests/WebPlatformSubset/contracts/resources/media/numbered-motion.mp4 b/tests/WebPlatformSubset/contracts/resources/media/numbered-motion.mp4 new file mode 100644 index 000000000..d5482c257 Binary files /dev/null and b/tests/WebPlatformSubset/contracts/resources/media/numbered-motion.mp4 differ diff --git a/tests/WebPlatformSubset/contracts/resources/media/stereo.wav b/tests/WebPlatformSubset/contracts/resources/media/stereo.wav new file mode 100644 index 000000000..c349632e3 Binary files /dev/null and b/tests/WebPlatformSubset/contracts/resources/media/stereo.wav differ diff --git a/tests/WebPlatformSubset/contracts/svg-inline-style.html b/tests/WebPlatformSubset/contracts/svg-inline-style.html new file mode 100644 index 000000000..5a1d1e61d --- /dev/null +++ b/tests/WebPlatformSubset/contracts/svg-inline-style.html @@ -0,0 +1,19 @@ + +SVG inline style and namespace identity + + diff --git a/tests/WebPlatformSubset/runner/EngineAdapters.cs b/tests/WebPlatformSubset/runner/EngineAdapters.cs index a68cd6b31..11dbd7ced 100644 --- a/tests/WebPlatformSubset/runner/EngineAdapters.cs +++ b/tests/WebPlatformSubset/runner/EngineAdapters.cs @@ -63,8 +63,9 @@ internal sealed unsafe class NativeWptEngineEnvironment : IWptEngineEnvironment private ulong _sequence; private double _frameTimestampMs; private bool _loaded; + private string? _navigationDocumentPath; private bool _disposed; - private readonly bool _managedFontEngine; + private readonly bool _managedHostEngine; internal NativeWptEngineEnvironment( RunnerOptions options, @@ -72,7 +73,8 @@ internal NativeWptEngineEnvironment( string upstreamRoot, string documentPath, string html, - string? fontBaseDirectory = null) + string? fontBaseDirectory = null, + bool nativeNavigation = false) { _viewport = viewport; _renderer = new NativeSceneSnapshotRenderer(viewport.DeviceScaleFactor); @@ -88,10 +90,10 @@ internal NativeWptEngineEnvironment( } NativeApi.Configure(libraryPath); - _managedFontEngine = html.Contains("@font-face", StringComparison.OrdinalIgnoreCase); - if (_managedFontEngine) + _managedHostEngine = nativeNavigation || html.Contains("@font-face", StringComparison.OrdinalIgnoreCase); + if (_managedHostEngine) { - // Font contracts must exercise the product stylesheet-consumption, + // Navigation and font contracts use the product resource-loading, // registration and measurement path, not a separate harness font map. NativeWebSceneApi.ConfigureLibraryPath(libraryPath); _engine = NativeWebSceneApi.EngineCreate(0, options.NativeCacheDirectory, @@ -122,7 +124,17 @@ internal NativeWptEngineEnvironment( // screenshot rasterization, including before document scripts. DeltaX = viewport.DeviceScaleFactor }); - LoadPreparedDocument(html, upstreamRoot, documentPath); + if (nativeNavigation) + { + // Preserve parser ordering and raw-text/template semantics. + // Keep relative fixture resources beside the prepared document. + _navigationDocumentPath = Path.Combine(fontBaseDirectory ?? upstreamRoot, + $".webscene-wpt-navigation-{Guid.NewGuid():N}.html"); + File.WriteAllText(_navigationDocumentPath, html); + if (!NativeWebSceneApi.TryLoadUrl(_engine, new Uri(_navigationDocumentPath).AbsoluteUri)) + throw new InvalidOperationException(NativeApi.GetLastError(_engine)); + } + else LoadPreparedDocument(html, upstreamRoot, documentPath); _loaded = true; for (var index = 0; index < 4; index++) SettleFrame(); } @@ -278,9 +290,10 @@ public void Dispose() _interop.Dispose(); if (_engine != IntPtr.Zero) { - if (_managedFontEngine) NativeWebSceneApi.EngineDestroy(_engine); + if (_managedHostEngine) NativeWebSceneApi.EngineDestroy(_engine); else NativeApi.EngineDestroy(_engine); } + if (_navigationDocumentPath is not null) File.Delete(_navigationDocumentPath); } private void LoadPreparedDocument(string html, string upstreamRoot, string documentPath) diff --git a/tests/WebPlatformSubset/runner/ProfileModels.cs b/tests/WebPlatformSubset/runner/ProfileModels.cs index 75be81f09..3b4747e3b 100644 --- a/tests/WebPlatformSubset/runner/ProfileModels.cs +++ b/tests/WebPlatformSubset/runner/ProfileModels.cs @@ -25,6 +25,7 @@ internal sealed class ProfileTest { public required string Path { get; init; } public string Type { get; init; } = "testharness"; + public bool NativeNavigation { get; init; } public string? Reference { get; init; } public List Capabilities { get; init; } = []; public List Evidence { get; init; } = []; diff --git a/tests/WebPlatformSubset/runner/WptSubsetRunner.cs b/tests/WebPlatformSubset/runner/WptSubsetRunner.cs index 47882f85d..3c0e02511 100644 --- a/tests/WebPlatformSubset/runner/WptSubsetRunner.cs +++ b/tests/WebPlatformSubset/runner/WptSubsetRunner.cs @@ -168,7 +168,7 @@ private TestResult RunTestHarness(ProfileTest test) : test.Path.EndsWith(".any.js", StringComparison.OrdinalIgnoreCase) ? PrepareWindowAnyTestHarnessDocument(source, test.Path) : PrepareTestHarnessDocument(source, test.Path); - var state = RunHarnessDocument(html, test.Path); + var state = RunHarnessDocument(html, test.Path, test.NativeNavigation); timer.Stop(); if (!state.Complete) @@ -427,9 +427,9 @@ private TestResult RunVisualTest(ProfileTest test) } } - private HarnessState RunHarnessDocument(string html, string documentPath) + private HarnessState RunHarnessDocument(string html, string documentPath, bool nativeNavigation = false) { - using var environment = CreateEnvironment(html, documentPath); + using var environment = CreateEnvironment(html, documentPath, nativeNavigation); var timer = Stopwatch.StartNew(); HarnessState? latest = null; while (timer.Elapsed < _options.Timeout) @@ -487,7 +487,7 @@ private WptRenderSnapshot RenderDocument(string html, string documentName) throw new TimeoutException($"Reftest document '{documentName}' did not reach readyState=complete."); } - private IWptEngineEnvironment CreateEnvironment(string html, string documentPath) + private IWptEngineEnvironment CreateEnvironment(string html, string documentPath, bool nativeNavigation = false) { return new NativeWptEngineEnvironment( _options, @@ -495,7 +495,7 @@ private IWptEngineEnvironment CreateEnvironment(string html, string documentPath _upstreamRoot, documentPath, html, - Path.GetDirectoryName(TestDocumentPath(documentPath))); + Path.GetDirectoryName(TestDocumentPath(documentPath)), nativeNavigation); } private string PrepareTestHarnessDocument(string html, string path) @@ -785,6 +785,8 @@ private void ValidateManifest() { throw new InvalidDataException($"Unknown test type '{test.Type}' for '{test.Path}'."); } + if (test.NativeNavigation && test.Type is not ("testharness" or "contract")) + throw new InvalidDataException($"Native navigation is only supported for harness/contract documents: '{test.Path}'."); if (test.Type == "reftest" && (string.IsNullOrWhiteSpace(test.Reference) || !File.Exists(TestDocumentPath(test.Reference)))) { diff --git a/tests/WebPlatformSubset/webscene-animation-frame-batch-profile.json b/tests/WebPlatformSubset/webscene-animation-frame-batch-profile.json new file mode 100644 index 000000000..e54afd443 --- /dev/null +++ b/tests/WebPlatformSubset/webscene-animation-frame-batch-profile.json @@ -0,0 +1,37 @@ +{ + "profile": "webscene-animation-frame-batch-1", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "viewport": { + "width": 800, + "height": 600, + "deviceScaleFactor": 2 + }, + "required": [], + "candidate": [ + { + "path": "contracts/animation-frame-batch-boundary.html", + "type": "contract", + "capabilities": [ + "animation-frame-batch-ordering", + "animation-frame-cancellation", + "microtask-checkpoints" + ], + "evidence": [ + "native-engine-large-raf-batch-regression" + ], + "reason": "Candidate regression for complete host rendering opportunities; hardware and upstream qualification remain separate." + }, + { + "path": "contracts/resize-observer-raf-order.html", + "type": "contract", + "capabilities": [ + "resize-observer", + "animation-frame-batch-ordering" + ], + "reason": "Protect browser rendering order while correcting resize redraw starvation; local candidate only." + } + ], + "harnessBlocked": [], + "excluded": [] +} diff --git a/tests/WebPlatformSubset/webscene-aureon-runtime-profile.json b/tests/WebPlatformSubset/webscene-aureon-runtime-profile.json new file mode 100644 index 000000000..169748c0a --- /dev/null +++ b/tests/WebPlatformSubset/webscene-aureon-runtime-profile.json @@ -0,0 +1,37 @@ +{ + "profile": "webscene-aureon-runtime-1", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "viewport": { + "width": 800, + "height": 600, + "deviceScaleFactor": 2 + }, + "required": [], + "candidate": [ + { + "path": "contracts/module-worker-clone.html", + "type": "contract", + "capabilities": [ + "javascript-modules", + "module-workers", + "structured-clone", + "arraybuffer-transfer" + ], + "reason": "Project-owned regression contract; full upstream WPT conformance remains separate.", + "nativeNavigation": true + }, + { + "path": "contracts/svg-inline-style.html", + "type": "contract", + "nativeNavigation": true, + "capabilities": [ + "svg-inline-style", + "svg-interface-identity" + ], + "reason": "Protect original Aureon transform controls when entering path-tracing mode." + } + ], + "harnessBlocked": [], + "excluded": [] +} diff --git a/tests/WebPlatformSubset/webscene-component-profile.json b/tests/WebPlatformSubset/webscene-component-profile.json index 962f79e43..dca0c4910 100644 --- a/tests/WebPlatformSubset/webscene-component-profile.json +++ b/tests/WebPlatformSubset/webscene-component-profile.json @@ -8,6 +8,13 @@ "deviceScaleFactor": 1 }, "required": [ + { + "path": "contracts/dialog-opening-lifecycle.html", + "type": "testharness", + "capabilities": ["dialog-opening", "dialog-modal-focus", "dialog-beforetoggle"], + "evidence": ["kestrel-original-box-command"], + "reason": "Native dialog opening regression; does not assert complete ToggleEvent or popover interoperability." + }, { "path": "contracts/youtube-embed-fallback.html", "type": "testharness", @@ -1064,6 +1071,34 @@ } ], "candidate": [ + { + "path": "contracts/css-grid-flex-track-minimum.html", + "type": "testharness", + "capabilities": ["css-grid", "fractional-track-sizing", "minmax"], + "evidence": ["kestrel-unused-horizontal-grid-space"], + "reason": "Fractional track allocation must include its minimum, freeze constrained tracks and redistribute space. Local regression pending cross-RID qualification." + }, + { + "path": "contracts/css-grid-auto-row-shrink.html", + "type": "testharness", + "capabilities": ["css-grid", "automatic-minimum-size", "dynamic-layout"], + "evidence": ["kestrel-stale-canvas-height-after-resize"], + "reason": "Auto grid rows must shrink to definite available height when item minimums permit; preserve visible content and authored minimums. Local regression pending cross-RID qualification." + }, + { + "path": "contracts/css-border-functional-color-width.html", + "type": "testharness", + "capabilities": ["border-shorthand", "functional-color"], + "evidence": ["kestrel-white-layer-list"], + "reason": "Color-mix percentages must stay inside the color component instead of becoming border widths. Local regression pending cross-RID qualification." + }, + { + "path": "contracts/css-root-theme-interaction-recascade.html", + "type": "testharness", + "capabilities": ["css-custom-properties", "attribute-selector", "focus-pseudo-class"], + "evidence": ["kestrel-white-ui-investigation"], + "reason": "Reduced Kestrel dark/light theme and interaction recascade regression. This local contract does not establish upstream WPT or pixel presentation conformance." + }, { "path": "contracts/scroll-static-absolute-containing-block.html", "type": "testharness", @@ -2064,6 +2099,19 @@ } ], "reason": "Self-verifying reduction for two retained-path replay boundaries: geometry keeps the transform active when each segment is authored even if the transform changes before stroke(), and a full-circle arc appended to a non-empty subpath includes the required connecting line to its start point." + }, + { + "path": "contracts/html-script-style-text-is-inert.html", + "type": "testharness", + "capabilities": [ + "html-raw-text", + "stylesheet-discovery" + ], + "evidence": [ + "kestrel-print-preview-style-leak" + ], + "reason": "Candidate passes through native document navigation, preserving parser raw-text/comment/template semantics. Prepared-markup regex loading cannot qualify this parsing regression.", + "nativeNavigation": true } ], "harnessBlocked": [], diff --git a/tests/WebPlatformSubset/webscene-macos-video-runtime-profile.json b/tests/WebPlatformSubset/webscene-macos-video-runtime-profile.json new file mode 100644 index 000000000..d313dd2a4 --- /dev/null +++ b/tests/WebPlatformSubset/webscene-macos-video-runtime-profile.json @@ -0,0 +1,25 @@ +{ + "profile": "webscene-macos-video-runtime-1", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "viewport": { + "width": 800, + "height": 600, + "deviceScaleFactor": 2 + }, + "required": [ + { + "path": "contracts/media-video-lifecycle.html", + "type": "contract", + "nativeNavigation": true, + "capabilities": [ + "html-media-elements", + "native-macos-video" + ], + "reason": "macOS native H.264 provider contract; unsupported platforms must not run or report video conformance." + } + ], + "candidate": [], + "harnessBlocked": [], + "excluded": [] +} diff --git a/tests/WebPlatformSubset/webscene-media-runtime-profile.json b/tests/WebPlatformSubset/webscene-media-runtime-profile.json new file mode 100644 index 000000000..cb60fc0f1 --- /dev/null +++ b/tests/WebPlatformSubset/webscene-media-runtime-profile.json @@ -0,0 +1,26 @@ +{ + "profile": "webscene-media-runtime-1", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "viewport": { + "width": 800, + "height": 600, + "deviceScaleFactor": 2 + }, + "required": [ + { + "path": "contracts/media-audio-lifecycle.html", + "type": "contract", + "nativeNavigation": true, + "capabilities": [ + "html-media-elements", + "audio-context", + "audio-decoding" + ], + "reason": "Project-owned native media/WPT harness contract; not full standards conformance." + } + ], + "candidate": [], + "harnessBlocked": [], + "excluded": [] +} diff --git a/tests/WebPlatformSubset/webscene-native-navigation-profile.json b/tests/WebPlatformSubset/webscene-native-navigation-profile.json new file mode 100644 index 000000000..f61ce4a62 --- /dev/null +++ b/tests/WebPlatformSubset/webscene-native-navigation-profile.json @@ -0,0 +1,27 @@ +{ + "profile": "native-navigation-style-check", + "wptRevision": "2c705104a295c48053eeddf7fe0170d790a4e853", + "runtime": "v8", + "viewport": { + "width": 800, + "height": 600, + "deviceScaleFactor": 1 + }, + "required": [], + "candidate": [ + { + "path": "contracts/html-script-style-text-is-inert.html", + "type": "testharness", + "capabilities": [ + "html-raw-text", + "stylesheet-discovery" + ], + "evidence": [ + "kestrel-print-preview-style-leak" + ], + "reason": "Candidate passes through native document navigation, preserving parser raw-text/comment/template semantics. Prepared-markup regex loading cannot qualify this parsing regression.", + "nativeNavigation": true + } + ], + "harnessBlocked": [] +} diff --git a/tests/WebScene.Backend.Avalonia.Tests/AvaloniaResourceLoaderTests.cs b/tests/WebScene.Backend.Avalonia.Tests/AvaloniaResourceLoaderTests.cs index 20389fb8f..cdfee6f1b 100644 --- a/tests/WebScene.Backend.Avalonia.Tests/AvaloniaResourceLoaderTests.cs +++ b/tests/WebScene.Backend.Avalonia.Tests/AvaloniaResourceLoaderTests.cs @@ -10,6 +10,21 @@ namespace WebScene.Backend.Avalonia.Tests; public sealed class AvaloniaResourceLoaderTests { + [Fact] + public void DataResourcePreservesOriginalFileBytes() + { + var path = Path.GetTempFileName(); + try + { + byte[] bytes = [0, 255, 128, 195, 40]; + File.WriteAllBytes(path, bytes); + var resource = new AvaloniaResourceLoader().LoadText(new WebSceneResourceRequest(new Uri(path).AbsoluteUri, null, WebSceneResourceKind.Data)); + Assert.True(resource.BinaryContent.HasValue); + Assert.Equal(bytes, resource.BinaryContent.Value.ToArray()); + } + finally { File.Delete(path); } + } + [Fact] public async Task CrossOriginFetchSendsFrameOriginAndReferrer() { diff --git a/tests/WebScene.Backend.Avalonia.Tests/KestrelDragWorkloadValidatorTests.cs b/tests/WebScene.Backend.Avalonia.Tests/KestrelDragWorkloadValidatorTests.cs new file mode 100644 index 000000000..345e90165 --- /dev/null +++ b/tests/WebScene.Backend.Avalonia.Tests/KestrelDragWorkloadValidatorTests.cs @@ -0,0 +1,83 @@ +using System.Text.Json; +using Xunit; + +namespace WebScene.Backend.Avalonia.Tests; + +public sealed class KestrelDragWorkloadValidatorTests +{ + private record Pointer(string type, double x, double y, int button, int buttons); + private static Pointer[] Sidebar() => [ + new("pointerdown", 100, 200, 0, 1), + new("pointermove", 102, 200, -1, 1), + new("pointermove", 180, 200, -1, 1), + new("pointermove", 220, 200, -1, 1), + new("pointerup", 220, 200, 0, 0) + ]; + private static void Validate(Pointer[] events, bool sidebar = true, int errors = 0, bool panning = false) => + KestrelDragWorkloadValidator.Validate(JsonSerializer.Serialize(new { events, errors, panning }), 100, 200, sidebar); + + [Fact] + public void CircularPanValidatesCoalescedCardinalPointsAndRejectsReversedMotion() + { + Pointer[] events = [new("pointerdown", 100, 200, 2, 2), + new("pointermove", 140, 240, -1, 2), new("pointermove", 180, 200, -1, 2), + new("pointermove", 140, 160, -1, 2), new("pointermove", 100, 200, -1, 2), + new("pointerup", 100, 200, 2, 0)]; + var diagnostics = JsonSerializer.Serialize(new { events, errors = 0, panning = false }); + KestrelDragWorkloadValidator.Validate(diagnostics, 100, 200, circular: true); + (events[1], events[3]) = (events[3], events[1]); + diagnostics = JsonSerializer.Serialize(new { events, errors = 0, panning = false }); + Assert.Throws(() => KestrelDragWorkloadValidator.Validate(diagnostics, 100, 200, circular: true)); + } + + [Fact] + public void RepeatedPanRequiresTheExplicitCycleCount() + { + Pointer[] events = [ + new("pointerdown", 100, 200, 2, 2), + new("pointermove", 260, 240, -1, 2), + new("pointermove", 100, 200, -1, 2), + new("pointermove", 260, 240, -1, 2), + new("pointermove", 100, 200, -1, 2), + new("pointerup", 100, 200, 2, 0) + ]; + var diagnostics = JsonSerializer.Serialize(new { events, errors = 0, panning = false }); + Assert.Throws(() => KestrelDragWorkloadValidator.Validate(diagnostics, 100, 200)); + KestrelDragWorkloadValidator.Validate(diagnostics, 100, 200, panCycles: 2); + events[3] = events[3] with { y = 241 }; + diagnostics = JsonSerializer.Serialize(new { events, errors = 0, panning = false }); + Assert.Throws(() => KestrelDragWorkloadValidator.Validate(diagnostics, 100, 200, panCycles: 2)); + } + + [Fact] + public void CoalescedSidebarMovesRemainValid() => Validate(Sidebar()); + + [Theory] + [InlineData(181, 200, 1)] // Outside submitted two-pixel steps. + [InlineData(180, 201, 1)] // External vertical movement. + [InlineData(180, 200, 2)] // Wrong held button. + [InlineData(100, 200, 1)] // Reversed movement. + public void ContaminatedSidebarMovesAreRejected(double x, double y, int buttons) + { + var events = Sidebar(); + events[2] = new("pointermove", x, y, -1, buttons); + Assert.Throws(() => Validate(events)); + } + + [Fact] + public void MissingReleaseIsRejected() => + Assert.Throws(() => Validate(Sidebar()[..^1])); + + [Fact] + public void ApplicationErrorIsRejected() => + Assert.Throws(() => Validate(Sidebar(), errors: 1)); + + [Fact] + public void PanCanTravelOutAndBackWithCoalescing() => Validate([ + new("pointerdown", 100, 200, 2, 2), + new("pointermove", 104, 201, -1, 2), + new("pointermove", 260, 240, -1, 2), + new("pointermove", 100, 200, -1, 2), + new("pointerup", 100, 200, 2, 0) + ], sidebar: false); +} diff --git a/tests/WebScene.Backend.Avalonia.Tests/NativeCanvasBackingTests.cs b/tests/WebScene.Backend.Avalonia.Tests/NativeCanvasBackingTests.cs new file mode 100644 index 000000000..27d8c8692 --- /dev/null +++ b/tests/WebScene.Backend.Avalonia.Tests/NativeCanvasBackingTests.cs @@ -0,0 +1,140 @@ +using SkiaSharp; +using WebScene.Backends.Avalonia.Native; +using Xunit; + +namespace WebScene.Backend.Avalonia.Tests; + +[Collection("Native web-font cache")] +public sealed unsafe class NativeCanvasBackingTests +{ + private static void Apply(NativeCanvasSceneRenderer renderer, NativeCanvasCommand[] commands, + ulong revision, ulong generation = 1, byte[]? checkpoint = null) + { + checkpoint ??= []; + fixed (NativeCanvasCommand* data = commands) + fixed (byte* bytes = checkpoint) + { + var resource = new NativeSceneString { ByteLength = (uint)checkpoint.Length }; + var layer = new NativeCanvasLayer { NodeId = 7, Flags = 1, Generation = generation, + CommandCount = (uint)commands.Length, StringCount = checkpoint.Length == 0 ? 0u : 1u, + Width = 20, Height = 10, BitmapWidth = 20, BitmapHeight = 10 }; + var scene = new NativeSceneView { StructSize = (uint)sizeof(NativeSceneView), AbiVersion = 2, + CanvasLayers = &layer, CanvasCommands = data, CanvasCommandCount = (uint)commands.Length, + Strings = &resource, StringBytes = bytes, StringCount = layer.StringCount, StringByteCount = (uint)checkpoint.Length, + Header = new SceneHeader { Revision = revision, BaseRevision = revision - 1, + Flags = revision == 1 ? 1u : 0u, CanvasLayerCount = 1, ViewportWidth = 20, ViewportHeight = 10 } }; + Assert.True(renderer.ApplyDiff(&scene)); + } + } + + [Fact] + public void RasterCheckpointPreservesFractionalClearPathTransformAndExport() + { + var bounded = new NativeCanvasSceneRenderer { UseIncrementalCanvasBacking = true }; + var full = new NativeCanvasSceneRenderer { UseIncrementalCanvasBacking = false }; + var original = new List { + new() { Kind=22,V0=19,V2=1,V3=10 }, new() { Kind=6,V0=.25,V1=.5 }, + new() { Kind=11,V0=1,V1=1 }, new() { Kind=12,V0=10,V1=1 } + }; + try + { + Apply(bounded,original.ToArray(),1); + Apply(full,original.ToArray(),1); + var checkpoint=bounded.EncodeRetainedCanvasCheckpoint(7); + var continued = new List { new() { Kind=58 } }; + for (ulong revision=2;revision<14;revision++) + { + NativeCanvasCommand[] tail = [new() { Kind=24,V2=19.25,V3=10 }, + new() { Kind=12,V0=revision%17,V1=1 }, new() { Kind=20 }]; + continued.AddRange(tail); + original.AddRange(tail);Apply(full,original.ToArray(),revision); + Apply(bounded,continued.ToArray(),revision,2,checkpoint); + var expected=Pixels(full);var actual=Pixels(bounded); + for(var pixel=0;pixel { new() { Kind = 11, V0 = 1, V1 = 1 } }; + try + { + for (ulong revision = 1; revision <= 270; ++revision) + { + commands.Add(new() { Kind = 24, V2 = 19.5, V3 = 10 }); + commands.Add(new() { Kind = 12, V0 = revision % 16 + 1, V1 = revision % 8 + 1 }); + commands.Add(new() { Kind = 20 }); + commands.Add(new() { Kind = 1 }); + commands.Add(new() { Kind = 6, V0 = 0.25, V1 = 0.5 }); + commands.Add(new() { Kind = 22, V0 = 19, V2 = 1, V3 = 10 }); + commands.Add(new() { Kind = 2 }); + var data = commands.ToArray(); + Apply(cached, data, revision); Apply(full, data, revision); + if (revision is 1 or 2 or 128 or 257 or 270) Assert.Equal(Pixels(full), Pixels(cached)); + } + Assert.True(cached.ResumedCanvasCompilations > 260); + Assert.Equal(full.CaptureCanvasPng(7), cached.CaptureCanvasPng(7)); + } + finally { cached.Reset(); full.Reset(); } + } + + [Fact] + public void ChangedPrefixesGenerationAndScaleInvalidateContinuation() + { + var renderer = new NativeCanvasSceneRenderer { UseIncrementalCanvasBacking = true }; + NativeCanvasCommand[] commands = [new() { Kind = 22, V2 = 4, V3 = 10 }]; + try + { + Apply(renderer, commands, 1); + commands[0].V2 = 12; + Apply(renderer, commands, 2); + Assert.Equal(0, renderer.ResumedCanvasCompilations); + Assert.Equal(SKColors.Black, Pixels(renderer)[8]); + Apply(renderer, commands, 3, generation: 2); + Assert.Equal(0, renderer.ResumedCanvasCompilations); + renderer.SetPresenterDeviceScaleFactor(1.75); + Apply(renderer, commands, 4, generation: 2); + Assert.Equal(0, renderer.ResumedCanvasCompilations); + Apply(renderer, commands, 5, generation: 2); + Assert.Equal(1, renderer.ResumedCanvasCompilations); + } + finally { renderer.Reset(); } + } + + [Theory] + [InlineData(1u)] // Active save stack cannot be reconstructed from paint state alone. + [InlineData(18u)] // A clip path requires complete replay, even if later restored. + [InlineData(27u)] // Another canvas can change without changing these commands. + [InlineData(31u)] // External image dependencies are not immutable command resources. + public void StateAndImageDependenciesUseFullReplay(uint unsupported) + { + var renderer = new NativeCanvasSceneRenderer { UseIncrementalCanvasBacking = true }; + NativeCanvasCommand[] commands = [new() { Kind = unsupported }]; + try + { + Apply(renderer, commands, 1); Apply(renderer, commands, 2); + Assert.Equal(0, renderer.ResumedCanvasCompilations); + } + finally { renderer.Reset(); } + } +} diff --git a/tests/WebScene.Backend.Avalonia.Tests/NativeGpuImageSamplingTests.cs b/tests/WebScene.Backend.Avalonia.Tests/NativeGpuImageSamplingTests.cs new file mode 100644 index 000000000..6246d25f2 --- /dev/null +++ b/tests/WebScene.Backend.Avalonia.Tests/NativeGpuImageSamplingTests.cs @@ -0,0 +1,42 @@ +using SkiaSharp; +using WebScene.Backends.Avalonia.Native; +using Xunit; + +namespace WebScene.Backend.Avalonia.Tests; + +public sealed class NativeGpuImageSamplingTests +{ + [Theory] + [InlineData(8, 1f)] + [InlineData(8, 2f)] + [InlineData(3, 1f)] + public void ScaledTextureInterpolatesInsteadOfRepeatingNearestPixels(int width, float scale) + { + using var source = new SKBitmap(2, 2); + source.Erase(SKColors.Black); + source.SetPixel(1, 0, SKColors.White); + source.SetPixel(1, 1, SKColors.White); + using var image = SKImage.FromBitmap(source); + using var result = new SKBitmap((int)(width * scale), (int)(width * scale)); + using var canvas = new SKCanvas(result); + canvas.Scale(scale); + NativeGpuImageSampling.Draw(canvas, image, new SKRect(0, 0, width, width)); + var middle = result.GetPixel((int)(width * scale / 2), 1).Red; + Assert.InRange((int)middle, 32, 223); + } + + [Fact] + public void SamplingPreservesCallerOpacity() + { + using var source = new SKBitmap(2, 2); + source.Erase(SKColors.White); + using var image = SKImage.FromBitmap(source); + using var result = new SKBitmap(8, 8); + using var canvas = new SKCanvas(result); + canvas.Clear(SKColors.Transparent); + using var paint = new SKPaint { Color = new SKColor(255, 255, 255, 128) }; + NativeGpuImageSampling.Draw(canvas, image, new SKRect(0, 0, 8, 8), paint); + Assert.InRange((int)result.GetPixel(4, 4).Alpha, 127, 129); + Assert.Equal((byte)128, paint.Color.Alpha); + } +} diff --git a/tests/WebScene.Backend.Avalonia.Tests/NativeGpuSceneInteropTests.cs b/tests/WebScene.Backend.Avalonia.Tests/NativeGpuSceneInteropTests.cs new file mode 100644 index 000000000..9cf5f70ce --- /dev/null +++ b/tests/WebScene.Backend.Avalonia.Tests/NativeGpuSceneInteropTests.cs @@ -0,0 +1,486 @@ +using System.Runtime.InteropServices; +using WebScene.Backends.Avalonia.Native; +using WebScene.Backends.Avalonia; +using Xunit; +using SkiaSharp; + +namespace WebScene.Backend.Avalonia.Tests; + +public sealed class NativeGpuSceneInteropTests +{ + private sealed class NativeRuntimeFactAttribute : FactAttribute + { + public NativeRuntimeFactAttribute() + { + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY"))) + Skip = "Set WEBSCENE_TEST_NATIVE_LIBRARY to verify native GPU scene ABI integration."; + } + } + + private sealed class IOSurfaceFixtureFactAttribute : FactAttribute + { + public IOSurfaceFixtureFactAttribute() + { + if (!OperatingSystem.IsMacOS() || + string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")) || + string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSCENE_TEST_GPU_FIXTURE_LIBRARY"))) + Skip = "Requires macOS native runtime and IOSurface fixture libraries."; + } + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate byte CreateIOSurface(out NativeGpuImageLeaseV3 image); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate byte IOSurfaceAlive(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void EndCgl(); + + [IOSurfaceFixtureFact] + public void NativeIOSurfaceImportsIntoCurrentCglRectangleTexture() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + var library = NativeLibrary.Load(Environment.GetEnvironmentVariable("WEBSCENE_TEST_GPU_FIXTURE_LIBRARY")!); + var create = Marshal.GetDelegateForFunctionPointer( + NativeLibrary.GetExport(library, "webscene_test_create_dawn_iosurface")); + var begin = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_begin_cgl")); + var bound = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_cgl_image_bound")); + var copy = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_cgl_copy")); + var alive = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_iosurface_alive")); + var pixels = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_cgl_pixels")); + var end = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_end_cgl")); + Assert.Equal(1, create(out var image)); + Assert.Equal(NativeSceneAcquireStatus.Success, NativeGpuImageConsumerV3.Acquire(image, out var consumer)); + image.Dispose(); + Assert.NotNull(consumer); + var completed = false; + try + { + Assert.Equal(1, begin()); + Assert.True(NativeMacOSGpuImageImport.TryBindCurrentRectangleTexture(consumer)); + Assert.Equal(1, bound()); + Assert.Equal(1, copy()); + Assert.Equal(1, alive()); + var openGl = NativeLibrary.Load("/System/Library/Frameworks/OpenGL.framework/OpenGL"); + var fence = NativeMacOSGpuConsumerFence.Create(name => NativeLibrary.GetExport(openGl, name), consumer); + Exception? wrongThreadError = null; + var wrongThread = new Thread(() => + { + try { fence.TryComplete(); } + catch (Exception error) { wrongThreadError = error; } + }); + wrongThread.Start(); + wrongThread.Join(); + Assert.IsType(wrongThreadError); + var deadline = DateTime.UtcNow.AddSeconds(5); + while (!(completed = fence.TryComplete()) && DateTime.UtcNow < deadline) Thread.Sleep(1); + Assert.True(completed); + Assert.Equal(0, alive()); // Native source owner retires only after its GPU read. + Assert.Equal(1, pixels()); // Read destination only, after fence completion. + Assert.True(fence.TryComplete()); // Retired polling is idempotent. + Assert.Throws(consumer.Complete); + } + finally + { + end(); // Delete GL references before completing the native image consumer. + if (!completed) consumer.Complete(); // Fixture cleanup drained GL before release. + } + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void GetGlInteger(uint name, out int value); + + [IOSurfaceFixtureFact] + public void DawnIOSurfaceComposesDirectlyInPinnedSkiaGanesh() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + var library = NativeLibrary.Load(Environment.GetEnvironmentVariable("WEBSCENE_TEST_GPU_FIXTURE_LIBRARY")!); + var create = Marshal.GetDelegateForFunctionPointer( + NativeLibrary.GetExport(library, "webscene_test_create_dawn_iosurface")); + var begin = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_begin_cgl")); + var end = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_end_cgl")); + var alive = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_iosurface_alive")); + Assert.Equal(1, create(out var image)); + Assert.Equal(NativeSceneAcquireStatus.Success, NativeGpuImageConsumerV3.Acquire(image, out var consumer)); + image.Dispose(); + Assert.NotNull(consumer); + var completed = false; + try + { + Assert.Equal(1, begin()); + Assert.True(NativeMacOSGpuImageImport.TryBindCurrentRectangleTexture(consumer)); + var openGl = NativeLibrary.Load("/System/Library/Frameworks/OpenGL.framework/OpenGL"); + IntPtr Resolve(string name) => NativeLibrary.TryGetExport(openGl, name, out var address) ? address : IntPtr.Zero; + var getInteger = Marshal.GetDelegateForFunctionPointer(Resolve("glGetIntegerv")); + getInteger(0x84F6, out var texture); // GL_TEXTURE_BINDING_RECTANGLE + Assert.NotEqual(0, texture); + using var gl = GRGlInterface.CreateOpenGl(Resolve); + Assert.NotNull(gl); + using var context = GRContext.CreateGl(gl); + Assert.NotNull(context); + using var target = SKSurface.Create(context, false, + new SKImageInfo(24, 8, SKColorType.Rgba8888, SKAlphaType.Premul)); + Assert.NotNull(target); + target.Canvas.Clear(SKColors.Blue); + using (var source = NativeMacOSGpuImageImport.TryWrapRectangleTexture(consumer, + context, (uint)texture, GRSurfaceOrigin.TopLeft, SKAlphaType.Premul)) + { + Assert.NotNull(source); + Assert.True(source.IsTextureBacked); + using var paint = new SKPaint { Color = new SKColor(255, 255, 255, 128) }; + target.Canvas.Save(); + target.Canvas.ClipRect(new SKRect(3, 2, 20, 6)); + target.Canvas.DrawImage(source, 1, 1, paint); + target.Canvas.Restore(); + } + context.Flush(submit: true, synchronous: false); // Submit Skia's reads before inserting the host completion fence. + var fence = NativeMacOSGpuConsumerFence.Create(Resolve, consumer); + var deadline = DateTime.UtcNow.AddSeconds(5); + while (!(completed = fence.TryComplete()) && DateTime.UtcNow < deadline) Thread.Sleep(1); + Assert.True(completed); + Assert.Equal(0, alive()); + // Diagnostic destination readback only, after all source GPU reads have retired. + using var pixels = new SKBitmap(new SKImageInfo(24, 8, SKColorType.Rgba8888, SKAlphaType.Premul)); + Assert.True(target.ReadPixels(pixels.Info, pixels.GetPixels(), pixels.RowBytes, 0, 0)); + for (var y = 0; y < 8; ++y) + for (var x = 0; x < 24; ++x) + { + var color = pixels.GetPixel(x, y); + var painted = x >= 3 && x < 18 && y >= 2 && y < 5; + Assert.InRange((int)color.Red, painted ? 25 : 0, painted ? 27 : 0); + Assert.InRange((int)color.Green, painted ? 50 : 0, painted ? 52 : 0); + Assert.InRange((int)color.Blue, painted ? 203 : 255, painted ? 205 : 255); + Assert.Equal(255, color.Alpha); + } + } + finally + { + end(); // Diagnostic failure cleanup drains GPU work before releasing the provider. + if (!completed) consumer.Complete(); + } + } + + [IOSurfaceFixtureFact] + public void IOSurfaceImportDefersConcurrentCompletionUntilBorrowReturns() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + // Keep fixture code loaded: native provider vtables may outlive this method + // if a test fails. Unloading code before releasing a provider is unsafe. + var library = NativeLibrary.Load(Environment.GetEnvironmentVariable("WEBSCENE_TEST_GPU_FIXTURE_LIBRARY")!); + var create = Marshal.GetDelegateForFunctionPointer( + NativeLibrary.GetExport(library, "webscene_test_create_iosurface")); + var alive = Marshal.GetDelegateForFunctionPointer( + NativeLibrary.GetExport(library, "webscene_test_iosurface_alive")); + Assert.Equal(1, create(out var image)); + Assert.Equal(NativeSceneAcquireStatus.Success, NativeGpuImageConsumerV3.Acquire(image, out var consumer)); + image.Dispose(); + Assert.NotNull(consumer); + Assert.True(consumer.WithIOSurface(view => + { + Assert.NotEqual(IntPtr.Zero, view.BorrowedIOSurface); + Assert.True(view.AllocationBytes >= 17 * 4 * 4); + Task.Run(consumer.Complete).GetAwaiter().GetResult(); + Assert.Equal(1, alive()); // Completion must not free the borrowed native object. + Assert.Throws(() => consumer.WithIOSurface(_ => { })); + })); + Assert.Equal(0, alive()); + Assert.Throws(consumer.Complete); + + Assert.Equal(1, create(out image)); + Assert.Equal(NativeSceneAcquireStatus.Success, NativeGpuImageConsumerV3.Acquire(image, out var retry)); + image.Dispose(); + Assert.NotNull(retry); + Assert.Throws(() => retry.WithIOSurface(_ => throw new ApplicationException("Import failed"))); + Assert.Equal(1, alive()); // An importer exception cannot certify GPU completion. + Assert.True(retry.WithIOSurface(_ => { })); + retry.Complete(); + Assert.Equal(0, alive()); + } + + [IOSurfaceFixtureFact] + public void MetalDependencyBorrowDefersCompletionAndSurvivesCallbackFailure() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + // Keep fixture code loaded: native provider vtables may outlive this method + // if a test fails. Unloading code before releasing a provider is unsafe. + var library = NativeLibrary.Load(Environment.GetEnvironmentVariable("WEBSCENE_TEST_GPU_FIXTURE_LIBRARY")!); + var create = Marshal.GetDelegateForFunctionPointer( + NativeLibrary.GetExport(library, "webscene_test_create_iosurface")); + var alive = Marshal.GetDelegateForFunctionPointer( + NativeLibrary.GetExport(library, "webscene_test_iosurface_alive")); + Assert.Equal(1, create(out var image)); + Assert.Equal(NativeSceneAcquireStatus.Success, NativeGpuImageConsumerV3.Acquire(image, out var consumer)); + image.Dispose(); + Assert.NotNull(consumer); + consumer.WithMetalEvents(events => + { + Assert.Empty(events); // Completion-certified fixture has no producer dependencies. + Task.Run(consumer.Complete).GetAwaiter().GetResult(); + Assert.Equal(1, alive()); // Completion must not free the borrowed native object. + Assert.Throws(() => consumer.WithMetalEvents(_ => { })); + }); + Assert.Equal(0, alive()); + Assert.Throws(consumer.Complete); + + Assert.Equal(1, create(out image)); + Assert.Equal(NativeSceneAcquireStatus.Success, NativeGpuImageConsumerV3.Acquire(image, out var retry)); + image.Dispose(); + Assert.NotNull(retry); + Assert.Throws(() => retry.WithMetalEvents(_ => throw new ApplicationException("Import failed"))); + Assert.Equal(1, alive()); // An importer exception cannot certify GPU completion. + retry.WithMetalEvents(_ => { }); + retry.Complete(); + Assert.Equal(0, alive()); + } + + [Fact] + public void LayoutMatchesNativeSceneAndImageAbi() + { + Assert.Equal(16, Marshal.SizeOf()); + Assert.Equal(16 + 2 * IntPtr.Size, Marshal.SizeOf()); + Assert.Equal(80, Marshal.SizeOf()); + Assert.Equal(24, Marshal.SizeOf()); + Assert.Equal(8, Marshal.OffsetOf(nameof(NativeGpuIOSurfaceViewV3.BorrowedIOSurface)).ToInt32()); + Assert.Equal(16, Marshal.OffsetOf(nameof(NativeGpuIOSurfaceViewV3.AllocationBytes)).ToInt32()); + Assert.Equal(8, Marshal.OffsetOf(nameof(NativeGpuImageInfoV3.Canvas)).ToInt32()); + Assert.Equal(56, Marshal.OffsetOf(nameof(NativeGpuImageInfoV3.Width)).ToInt32()); + Assert.Equal(0UL, NativeSceneAcquireOptionsV3.CpuOnly.ConsumerCapabilities); + } + + [NativeRuntimeFact] + public void IOSurfaceLookupRejectsAbsentConsumerAcrossNativeBoundary() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + var view = NativeGpuIOSurfaceViewV3.Empty; + view.BorrowedIOSurface = new IntPtr(123); + view.AllocationBytes = 456; + Assert.Equal(0, NativeWebSceneApi.GpuImageGetIOSurfaceV3(IntPtr.Zero, ref view)); + Assert.Equal(IntPtr.Zero, view.BorrowedIOSurface); + Assert.Equal(0UL, view.AllocationBytes); + } + + [IOSurfaceFixtureFact] + public void UnappliedSceneImagesReleaseWithoutAGraphicsContext() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + var library = NativeLibrary.Load(Environment.GetEnvironmentVariable("WEBSCENE_TEST_GPU_FIXTURE_LIBRARY")!); + var create = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_create_iosurface")); + var alive = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_iosurface_alive")); + Assert.Equal(1, create(out var source)); + Assert.Equal(NativeSceneAcquireStatus.Success, NativeGpuSceneImages.Retain(new[] { source }, out var images)); + source.Dispose(); + Assert.Equal(1, alive()); + images!.DiscardUnprepared(); + images.DiscardUnprepared(); + Assert.Equal(0, alive()); + Assert.Throws(() => new NativeGpuScenePresenter().TryReplace(images)); + } + + [IOSurfaceFixtureFact] + public void UndrawnSceneReplacementDoesNotConsumeGpuRetirementSlots() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + var library = NativeLibrary.Load(Environment.GetEnvironmentVariable("WEBSCENE_TEST_GPU_FIXTURE_LIBRARY")!); + var create = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_create_iosurface")); + var alive = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_iosurface_alive")); + Assert.Equal(1, create(out var source)); + var groups = new List(); + var presenter = new NativeGpuScenePresenter(); + try + { + for (var i = 0; i < 4; ++i) + { + Assert.Equal(NativeSceneAcquireStatus.Success, + NativeGpuSceneImages.Retain(new[] { source }, out var group)); + groups.Add(group!); + } + source.Dispose(); + Assert.True(presenter.TryReplace(groups[0])); + Assert.True(presenter.TryReplace(groups[1])); + Assert.True(presenter.TryReplace(groups[2])); + Assert.True(presenter.TryReplace(groups[3])); + Assert.All(groups.Take(3), group => Assert.True(group.IsRetiring)); + Assert.False(presenter.HasPendingRetirements); + Assert.Equal(1, alive()); + Assert.True(presenter.TryDiscardUnprepared()); + Assert.Equal(0, alive()); + } + finally + { + presenter.TryDiscardUnprepared(); + foreach (var group in groups) group.DiscardUnprepared(); + source.Dispose(); + } + } + + [IOSurfaceFixtureFact] + public void SceneImageCaptureRollsBackEarlierRetainsWhenALaterImageIsDisposed() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + var library = NativeLibrary.Load(Environment.GetEnvironmentVariable("WEBSCENE_TEST_GPU_FIXTURE_LIBRARY")!); + var create = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_create_iosurface")); + var alive = Marshal.GetDelegateForFunctionPointer(NativeLibrary.GetExport(library, "webscene_test_iosurface_alive")); + Assert.Equal(1, create(out var image)); + try + { + Assert.Equal(NativeSceneAcquireStatus.Success, image.Retain(out var disposed)); + disposed!.Dispose(); + Assert.Throws(() => NativeGpuSceneImages.Retain(new[] { image, disposed }, out _)); + } + finally { image.Dispose(); } + Assert.Equal(0, alive()); + } + + [IOSurfaceFixtureFact] + public void DocumentAdmissionCallbackCrossesManagedNativeBoundary() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + foreach (var throwFromPolicy in new[] { false, true }) + { + using var called = new ManualResetEventSlim(); + string? observedUrl = null; + var callbackThread = 0; + var callerThread = Environment.CurrentManagedThreadId; + var engine = NativeWebSceneApi.EngineCreate(0, null, new AvaloniaResourceLoader(), _ => { }, + admitWebGpuDocument: url => + { + observedUrl = url; + callbackThread = Environment.CurrentManagedThreadId; + called.Set(); + if (throwFromPolicy) throw new InvalidOperationException("Test policy failure"); + return true; + }); + try + { + Assert.NotEqual(IntPtr.Zero, engine); + Assert.True(called.Wait(TimeSpan.FromSeconds(5)), "Native runtime did not evaluate managed admission"); + Assert.Equal("about:blank", observedUrl); + Assert.NotEqual(callerThread, callbackThread); + } + finally + { + NativeWebSceneApi.EngineDestroy(engine); + } + } + } + + [NativeRuntimeFact] + public void VersionedAcquisitionCrossesManagedNativeBoundary() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + var options = NativeSceneAcquireOptionsV3.CpuOnly; + Assert.Equal(NativeSceneAcquireStatus.InvalidArgument, + NativeWebSceneApi.AcquireLatestSceneV3(IntPtr.Zero, in options, out var scene)); + Assert.Equal(IntPtr.Zero, scene); + var engine = NativeWebSceneApi.EngineCreate(0, null, new AvaloniaResourceLoader(), _ => { }); + try + { + options.SceneVersion = 99; + Assert.Equal(NativeSceneAcquireStatus.UnsupportedVersion, + NativeWebSceneApi.AcquireNextSceneV3(engine, in options, out scene)); + Assert.Equal(IntPtr.Zero, scene); + options = NativeSceneAcquireOptionsV3.CpuOnly; + var deadline = DateTime.UtcNow.AddSeconds(5); + NativeSceneAcquireStatus status; + do + { + status = NativeWebSceneApi.AcquireNextSceneV3(engine, in options, out scene); + if (status == NativeSceneAcquireStatus.Empty) Thread.Sleep(1); + } while (status == NativeSceneAcquireStatus.Empty && DateTime.UtcNow < deadline); + Assert.Equal(NativeSceneAcquireStatus.Success, status); + var view = Marshal.PtrToStructure(scene); + Assert.Equal(3U, view.SceneVersion); + Assert.NotEqual(IntPtr.Zero, view.CpuView); + Assert.Equal(0U, NativeWebSceneApi.SceneGpuImageCountV3(scene)); + Assert.Equal(NativeSceneAcquireStatus.InvalidArgument, + NativeWebSceneApi.SceneRetainGpuImageV3(scene, 0, out var image)); + Assert.Equal(IntPtr.Zero, image); + Assert.Equal(1, NativeWebSceneApi.SceneAcknowledgeV3(scene)); + NativeWebSceneApi.EngineDestroy(engine); engine = IntPtr.Zero; + Assert.Equal(view.CpuView, Marshal.PtrToStructure(scene).CpuView); + } + finally + { + if (scene != IntPtr.Zero) NativeWebSceneApi.SceneReleaseV3(scene); + if (engine != IntPtr.Zero) NativeWebSceneApi.EngineDestroy(engine); + } + } + [NativeRuntimeFact] + public void ScenePresenterAppliesAndAcknowledgesNativeVersionedScene() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + var options = NativeSceneAcquireOptionsV3.CpuOnly; + options.ConsumerCapabilities = NativeWebSceneApi.GpuImageCapability | NativeWebSceneApi.OrderedCanvasCapability; + var engine = NativeWebSceneApi.EngineCreate(0, null, new AvaloniaResourceLoader(), _ => { }); + NativeSceneLeaseV3? scene = null; + var renderer = new NativeCanvasSceneRenderer(); + var presenter = new NativeGpuScenePresenter(); + try + { + var deadline = DateTime.UtcNow.AddSeconds(5); + NativeSceneAcquireStatus status; + do + { + status = NativeSceneLeaseV3.Acquire(engine, in options, true, out scene); + if (status == NativeSceneAcquireStatus.Empty) Thread.Sleep(1); + } while (status == NativeSceneAcquireStatus.Empty && DateTime.UtcNow < deadline); + Assert.Equal(NativeSceneAcquireStatus.Success, status); + Assert.NotNull(scene); + Assert.Equal(NativeGpuSceneApplyResult.Applied, presenter.ApplyScene(scene, renderer)); + Assert.True(presenter.TryDiscardUnprepared()); + Assert.Equal(NativeGpuSceneApplyResult.Backpressure, presenter.ApplyScene(scene, renderer)); + } + finally + { + presenter.TryDiscardUnprepared(); + renderer.Reset(); scene?.Dispose(); + NativeWebSceneApi.EngineDestroy(engine); + } + } + + [NativeRuntimeFact] + public void SafeSceneLeaseProtectsBorrowedViewDuringConcurrentDispose() + { + NativeWebSceneApi.ConfigureLibraryPath(Environment.GetEnvironmentVariable("WEBSCENE_TEST_NATIVE_LIBRARY")!); + var options = NativeSceneAcquireOptionsV3.CpuOnly; + Assert.Equal(NativeSceneAcquireStatus.InvalidArgument, + NativeSceneLeaseV3.Acquire(IntPtr.Zero, in options, true, out var absent)); + Assert.Null(absent); + var engine = NativeWebSceneApi.EngineCreate(0, null, new AvaloniaResourceLoader(), _ => { }); + NativeSceneLeaseV3? lease = null; + try + { + var deadline = DateTime.UtcNow.AddSeconds(5); + NativeSceneAcquireStatus status; + do + { + status = NativeSceneLeaseV3.Acquire(engine, in options, true, out lease); + if (status == NativeSceneAcquireStatus.Empty) Thread.Sleep(1); + } while (status == NativeSceneAcquireStatus.Empty && DateTime.UtcNow < deadline); + Assert.Equal(NativeSceneAcquireStatus.Success, status); + Assert.NotNull(lease); + Assert.Equal(0U, lease.ImageCount); + Assert.Equal(NativeSceneAcquireStatus.InvalidArgument, + NativeGpuImageLeaseV3.Acquire(lease, 0, out var absentImage)); + Assert.Null(absentImage); + Assert.True(lease.Acknowledge()); + NativeWebSceneApi.EngineDestroy(engine); engine = IntPtr.Zero; + lease.WithView(view => + { + Task.Run(lease.Dispose).GetAwaiter().GetResult(); + // This read happens after Dispose on another thread. WithView + // still owns a SafeHandle reference until the callback returns. + Assert.Equal(2, Marshal.ReadInt32(view.CpuView, sizeof(uint))); + }); + lease.Dispose(); // Idempotent: never double-release the native lease. + Assert.Throws(() => lease.WithView(_ => { })); + Assert.Throws(() => NativeGpuImageLeaseV3.Acquire(lease, 0, out _)); + } + finally + { + lease?.Dispose(); + if (engine != IntPtr.Zero) NativeWebSceneApi.EngineDestroy(engine); + } + } + +} diff --git a/tests/WebScene.Backend.Avalonia.Tests/NativeOrderedGpuPaintTests.cs b/tests/WebScene.Backend.Avalonia.Tests/NativeOrderedGpuPaintTests.cs new file mode 100644 index 000000000..8d083f0f8 --- /dev/null +++ b/tests/WebScene.Backend.Avalonia.Tests/NativeOrderedGpuPaintTests.cs @@ -0,0 +1,335 @@ +using SkiaSharp; +using WebScene.Backends.Avalonia.Native; +using Xunit; + +namespace WebScene.Backend.Avalonia.Tests; + +[Collection("Native web-font cache")] +public sealed unsafe class NativeOrderedGpuPaintTests +{ + [Fact] + public void PreparingCanvasReplacementDoesNotChangeVisiblePixelsAndCommitTransfersOwnership() + { + var renderer = new NativeCanvasSceneRenderer(); + var draw = new NativeCanvasCommand { Kind = 22, V2 = 4, V3 = 10 }; + var layer = new NativeCanvasLayer { NodeId = 7, Flags = 1, CommandCount = 1, + Width = 20, Height = 10, BitmapWidth = 20, BitmapHeight = 10, Generation = 1 }; + var scene = new NativeSceneView { StructSize = (uint)sizeof(NativeSceneView), AbiVersion = 2, + CanvasLayers = &layer, CanvasCommands = &draw, CanvasCommandCount = 1, + Header = new SceneHeader { Revision = 1, Flags = 1, CanvasLayerCount = 1, ViewportWidth = 20, ViewportHeight = 10 } }; + using var bitmap = new SKBitmap(20, 10); + using var canvas = new SKCanvas(bitmap); + void Render() { canvas.Clear(SKColors.White); renderer.RenderRetained(canvas, 20, 10, null); } + try + { + Assert.True(renderer.ApplyDiff(&scene)); + draw.V2 = 12; layer.Generation = 2; + scene.Header.Revision = 2; scene.Header.BaseRevision = 1; scene.Header.Flags = 0; + using (var abandoned = renderer.PrepareCanvasLayers(&scene)) + { + Assert.NotNull(abandoned); + Render(); + Assert.Equal(SKColors.Black, bitmap.GetPixel(2, 5)); + Assert.Equal(SKColors.White, bitmap.GetPixel(8, 5)); + } + Render(); + Assert.Equal(SKColors.White, bitmap.GetPixel(8, 5)); + using (var prepared = renderer.PrepareCanvasLayers(&scene)) + Assert.True(renderer.ApplyDiff(&scene, prepared: prepared)); + Render(); + Assert.Equal(SKColors.Black, bitmap.GetPixel(8, 5)); + Assert.Equal(SKColors.White, bitmap.GetPixel(15, 5)); + } + finally { renderer.Reset(); } + } + + [Fact] + public void StalePreparedCanvasFallsBackAndCanvasDependenciesStaySynchronous() + { + var renderer = new NativeCanvasSceneRenderer(); + var draw = new NativeCanvasCommand { Kind = 22, V2 = 4, V3 = 10 }; + var layer = new NativeCanvasLayer { NodeId = 7, Flags = 1, CommandCount = 1, + Width = 20, Height = 10, BitmapWidth = 20, BitmapHeight = 10, Generation = 1 }; + var scene = new NativeSceneView { StructSize = (uint)sizeof(NativeSceneView), AbiVersion = 2, + CanvasLayers = &layer, CanvasCommands = &draw, CanvasCommandCount = 1, + Header = new SceneHeader { Revision = 1, Flags = 1, CanvasLayerCount = 1, ViewportWidth = 20, ViewportHeight = 10 } }; + try + { + using var prepared = renderer.PrepareCanvasLayers(&scene); + Assert.NotNull(prepared); + draw.V2 = 12; scene.Header.Revision = 2; + Assert.True(renderer.ApplyDiff(&scene, prepared: prepared)); + using var bitmap = new SKBitmap(20, 10); + using var canvas = new SKCanvas(bitmap); + canvas.Clear(SKColors.White); + renderer.RenderRetained(canvas, 20, 10, null); + Assert.Equal(SKColors.Black, bitmap.GetPixel(8, 5)); + draw.Kind = 27; + Assert.Null(renderer.PrepareCanvasLayers(&scene)); + } + finally { renderer.Reset(); } + } + + [Fact] + public void ReplacementReusesUnchangedPicturesAndInvalidatesChangedCommands() + { + var renderer = new NativeCanvasSceneRenderer(); + var command = new SceneCommand { Kind = 1, Width = 20, Height = 10, Rgba = 0xff0000ff }; + var scene = new NativeSceneView { Commands = &command, + Header = new SceneHeader { Revision = 1, Flags = 3, CommandCount = 1, ViewportWidth = 20, ViewportHeight = 10 } }; + using var pixels = new SKBitmap(20, 10); + using var canvas = new SKCanvas(pixels); + try + { + Assert.True(renderer.ApplyDiff(&scene, true)); + for (ulong revision = 2; revision <= 4; revision++) + { + scene.Header.BaseRevision = revision - 1; + scene.Header.Revision = revision; + scene.Header.Flags = 2; + Assert.True(renderer.ApplyDiff(&scene, true)); + renderer.RenderRetained(canvas, 20, 10, null, (_, _) => throw new InvalidOperationException("Unexpected image")); + Assert.Equal(SKColors.Red, pixels.GetPixel(5, 5)); + } + Assert.Equal(3, renderer.ReusedDomPictureCount); + command.Rgba = 0x0000ffff; + scene.Header.BaseRevision = 4; + scene.Header.Revision = 5; + Assert.True(renderer.ApplyDiff(&scene, true)); + renderer.RenderRetained(canvas, 20, 10, null, (_, _) => throw new InvalidOperationException("Unexpected image")); + Assert.Equal(SKColors.Blue, pixels.GetPixel(5, 5)); + Assert.Equal(3, renderer.ReusedDomPictureCount); + } + finally { renderer.Reset(); } + } + + [Fact] + public void PictureReuseChecksResourceContentsAndNewWebFonts() + { + using var registry = new NativeTextShaping.WebTypefaceRegistry(); + var renderer = new NativeCanvasSceneRenderer(); + renderer.SetWebTypefaceRegistry(registry); + var bytes = System.Text.Encoding.UTF8.GetBytes("20\t24\t400\tleft\tPictureCacheFixture\tauto\tAAA"); + fixed (byte* data = bytes) + { + var resource = new NativeSceneString { ByteLength = (uint)bytes.Length }; + var command = new SceneCommand { Kind = 3, X = 2, Y = 2, Width = 120, Height = 28, Rgba = 0x000000ff }; + var scene = new NativeSceneView { Commands = &command, Strings = &resource, + StringCount = 1, StringBytes = data, StringByteCount = (uint)bytes.Length, + Header = new SceneHeader { Revision = 1, Flags = 3, CommandCount = 1, ViewportWidth = 128, ViewportHeight = 40 } }; + using var bitmap = new SKBitmap(128, 40); + using var canvas = new SKCanvas(bitmap); + try + { + Assert.True(renderer.ApplyDiff(&scene, true)); + canvas.Clear(SKColors.White); + renderer.RenderRetained(canvas, 128, 40, null, (_, _) => { }); + var original = bitmap.Pixels; + bytes[^1] = bytes[^2] = bytes[^3] = (byte)'W'; + scene.Header.Flags = 2; scene.Header.BaseRevision = 1; scene.Header.Revision = 2; + Assert.True(renderer.ApplyDiff(&scene, true)); + canvas.Clear(SKColors.White); + renderer.RenderRetained(canvas, 128, 40, null, (_, _) => { }); + Assert.False(original.SequenceEqual(bitmap.Pixels)); + Assert.Equal(0, renderer.ReusedDomPictureCount); + + var root = new DirectoryInfo(AppContext.BaseDirectory); + while (root is not null && !File.Exists(Path.Combine(root.FullName, "tests/Fonts/Roboto/Roboto-400.ttf"))) + root = root.Parent; + Assert.NotNull(root); + Assert.True(registry.Register("PictureCacheFixture", + File.ReadAllBytes(Path.Combine(root.FullName, "tests/Fonts/Roboto/Roboto-400.ttf")))); + original = bitmap.Pixels; + scene.Header.BaseRevision = 2; scene.Header.Revision = 3; + Assert.True(renderer.ApplyDiff(&scene, true)); + canvas.Clear(SKColors.White); + renderer.RenderRetained(canvas, 128, 40, null, (_, _) => { }); + Assert.False(original.SequenceEqual(bitmap.Pixels)); + Assert.Equal(0, renderer.ReusedDomPictureCount); + } + finally { renderer.Reset(); } + } + } + + [Fact] + public void OrderedReplayPreservesInterleavingAndUpdatesImagesWithoutRecompilingDom() + { + var renderer = new NativeCanvasSceneRenderer(); + var commands = stackalloc SceneCommand[] { + new() { Kind = 1, Width = 20, Height = 10, Rgba = 0x0000ffff }, + new() { Kind = 12, X = 2, Width = 16, Height = 10 }, + new() { Kind = 30, Rgba = 128 }, + new() { Kind = 256, Width = 12, Height = 10, Rgba = 0 }, + new() { Kind = 31 }, new() { Kind = 13 }, + new() { Kind = 9, X = 6, Width = 4, Height = 10, Rgba = 0xffff00ff }, + new() { Kind = 256, X = 14, Width = 6, Height = 10, Rgba = 1 } + }; + var scene = new NativeSceneView { Commands = commands, + Header = new SceneHeader { Revision = 1, Flags = 3, CommandCount = 8, ViewportWidth = 20, ViewportHeight = 10 } }; + try + { + Assert.True(renderer.ApplyDiff(&scene, orderedGpuImages: true)); + using var pixels = new SKBitmap(20, 10); + using var canvas = new SKCanvas(pixels); + foreach (var foreground in new[] { SKColors.Green, SKColors.White }) + { + var originalSaveCount = canvas.SaveCount; + renderer.RenderRetained(canvas, 20, 10, null, (slot, rect) => + { + using var paint = new SKPaint { Color = slot == 0 ? SKColors.Red : foreground }; + canvas.DrawRect(rect, paint); + }); + Assert.Equal(originalSaveCount, canvas.SaveCount); + Assert.Equal(SKColors.Blue, pixels.GetPixel(0, 5)); + var blended = pixels.GetPixel(3, 5); + Assert.InRange((int)blended.Red, 127, 129); + Assert.InRange((int)blended.Blue, 126, 128); + Assert.Equal(SKColors.Yellow, pixels.GetPixel(7, 5)); + Assert.Equal(foreground, pixels.GetPixel(16, 5)); + } + } + finally { renderer.Reset(); } + } + + [Fact] + public void OrderedGpuTransformAndFailedDrawRestoreHostState() + { + var renderer = new NativeCanvasSceneRenderer(); + var commands = stackalloc SceneCommand[] { + new() { Kind = 15, Width = 2, Height = 2 }, + new() { Kind = 256, X = 2, Y = 2, Width = 3, Height = 3 }, + new() { Kind = 16 } + }; + var scene = new NativeSceneView { Commands = commands, + Header = new SceneHeader { Revision = 1, Flags = 3, CommandCount = 3, ViewportWidth = 20, ViewportHeight = 20 } }; + try + { + Assert.True(renderer.ApplyDiff(&scene, orderedGpuImages: true)); + using var bitmap = new SKBitmap(20, 20); + using var canvas = new SKCanvas(bitmap); + canvas.Clear(SKColors.Blue); + var save = canvas.SaveCount; + var matrix = canvas.TotalMatrix; + renderer.RenderRetained(canvas, 20, 20, null, (_, rect) => + { + using var paint = new SKPaint { Color = SKColors.Red }; + canvas.DrawRect(rect, paint); + }); + Assert.Equal(SKColors.Red, bitmap.GetPixel(5, 5)); + Assert.Equal(SKColors.Blue, bitmap.GetPixel(2, 2)); + Assert.Throws(() => renderer.RenderRetained(canvas, 20, 20, null, + (_, _) => throw new ApplicationException("Image unavailable"))); + Assert.Equal(save, canvas.SaveCount); + Assert.Equal(matrix, canvas.TotalMatrix); + commands[2].Kind = 13; // A clip pop cannot close the scale scope. + Assert.False(renderer.ApplyDiff(&scene, orderedGpuImages: true)); + } + finally { renderer.Reset(); } + } + + [Fact] + public void CanvasLayerSlotsInterleaveWithGpuAndKeepIncrementalPlacement() + { + var renderer = new NativeCanvasSceneRenderer(); + var commands = stackalloc SceneCommand[] { + new() { Kind = 256, Width = 20, Height = 10, Rgba = 0 }, + new() { Kind = 257, NodeId = 7 }, + new() { Kind = 256, X = 8, Width = 4, Height = 10, Rgba = 1 }, + new() { Kind = 9, X = 14, Width = 3, Height = 10, Rgba = 0xffff00ff } + }; + var canvasCommand = new NativeCanvasCommand { Kind = 22, V2 = 8, V3 = 10 }; + var layer = new NativeCanvasLayer { NodeId = 7, Flags = 1, CommandCount = 1, + X = 4, Width = 8, Height = 10, BitmapWidth = 8, BitmapHeight = 10, Generation = 1 }; + var scene = new NativeSceneView { Commands = commands, CanvasLayers = &layer, + CanvasCommands = &canvasCommand, CanvasCommandCount = 1, + Header = new SceneHeader { Revision = 1, Flags = 3, CommandCount = 4, + CanvasLayerCount = 1, ViewportWidth = 20, ViewportHeight = 10 } }; + try + { + Assert.True(renderer.ApplyDiff(&scene, orderedGpuImages: true)); + using var bitmap = new SKBitmap(20, 10); + using var canvas = new SKCanvas(bitmap); + void Draw() => renderer.RenderRetained(canvas, 20, 10, null, (slot, rect) => + { + using var paint = new SKPaint { Color = slot == 0 ? SKColors.Red : SKColors.Green }; + canvas.DrawRect(rect, paint); + }); + Draw(); + Assert.Equal(SKColors.Red, bitmap.GetPixel(1, 5)); + Assert.Equal(SKColors.Black, bitmap.GetPixel(5, 5)); + Assert.Equal(SKColors.Green, bitmap.GetPixel(9, 5)); + Assert.Equal(SKColors.Yellow, bitmap.GetPixel(15, 5)); + // Change only the layer layout; retain the compiled DOM/GPU paint slots. + layer.X = 2; layer.Width = 4; layer.Generation = 2; + scene.Header.Flags = 0; scene.Header.Revision = 2; scene.Header.BaseRevision = 1; + scene.Header.CommandCount = 0; + Assert.True(renderer.ApplyDiff(&scene, orderedGpuImages: true)); + Draw(); + Assert.Equal(SKColors.Black, bitmap.GetPixel(3, 5)); + Assert.Equal(SKColors.Red, bitmap.GetPixel(7, 5)); + // Removing the layer while leaving a live marker is rejected atomically. + layer.Flags = 2; scene.Header.Revision = 3; scene.Header.BaseRevision = 2; + Assert.False(renderer.ApplyDiff(&scene, orderedGpuImages: true)); + Draw(); + Assert.Equal(SKColors.Black, bitmap.GetPixel(3, 5)); + } + finally { renderer.Reset(); } + } + + [Fact] + public void TextAcrossGpuBoundaryMatchesUnsegmentedReplayAfterCompilation() + { + var bytes = System.Text.Encoding.UTF8.GetBytes("16\t20\t400\tleft\tsans-serif\tauto\tKestrel 0123"); + fixed (byte* data = bytes) + { + var resource = new NativeSceneString { ByteLength = (uint)bytes.Length }; + var commands = stackalloc SceneCommand[] { + new() { Kind = 3, X = 2, Y = 2, Width = 120, Height = 24, Rgba = 0x000000ff }, + new() { Kind = 256 }, // Separates pictures without painting pixels. + new() { Kind = 3, X = 2, Y = 30, Width = 120, Height = 24, Rgba = 0x000000ff } + }; + var scene = new NativeSceneView { Commands = commands, Strings = &resource, + StringCount = 1, StringBytes = data, StringByteCount = (uint)bytes.Length, + Header = new SceneHeader { Revision = 1, Flags = 3, CommandCount = 3, + ViewportWidth = 128, ViewportHeight = 60 } }; + using var segmented = new SKBitmap(128, 60); + using var reference = new SKBitmap(128, 60); + var renderer = new NativeCanvasSceneRenderer(); + try + { + Assert.True(renderer.ApplyDiff(&scene, orderedGpuImages: true)); + // All compilation shapers have been disposed before replay. + using (var canvas = new SKCanvas(segmented)) + { + canvas.Clear(SKColors.White); + renderer.RenderRetained(canvas, 128, 60, null, (_, _) => { }); + } + commands[1].Kind = 0; + scene.Header.Revision = 2; + Assert.True(renderer.ApplyDiff(&scene, orderedGpuImages: true)); + using (var canvas = new SKCanvas(reference)) + { + canvas.Clear(SKColors.White); + renderer.RenderRetained(canvas, 128, 60, null, (_, _) => { }); + } + var ink = 0; + for (var y = 0; y < 60; ++y) + for (var x = 0; x < 128; ++x) + { + Assert.Equal(reference.GetPixel(x, y), segmented.GetPixel(x, y)); + if (segmented.GetPixel(x, y) != SKColors.White) ++ink; + } + Assert.True(ink > 100, "Both comparisons must contain rendered glyphs."); + } + finally { renderer.Reset(); } + } + } + + [Fact] + public void OrderedModeRejectsUnplacedLegacyCanvasLayers() + { + var renderer = new NativeCanvasSceneRenderer(); + var scene = new NativeSceneView { Header = new SceneHeader { Flags = 3, CanvasLayerCount = 1 } }; + Assert.False(renderer.ApplyDiff(&scene, orderedGpuImages: true)); + } +} diff --git a/tests/WebScene.Backend.Avalonia.Tests/NativePerformanceInstrumentationTests.cs b/tests/WebScene.Backend.Avalonia.Tests/NativePerformanceInstrumentationTests.cs index b65323411..97a65fa7b 100644 --- a/tests/WebScene.Backend.Avalonia.Tests/NativePerformanceInstrumentationTests.cs +++ b/tests/WebScene.Backend.Avalonia.Tests/NativePerformanceInstrumentationTests.cs @@ -16,12 +16,14 @@ public void DisabledInstrumentationRetainsOnlyFunctionalRenderState() ViewportHeight = 480 }; + observer.RecordScheduling("frame", 2, 7, false); observer.RecordPresented(); observer.RecordRendered(header); Assert.False(instrumentation.IsEnabled); Assert.Equal(1, observer.RenderedSceneCount); Assert.NotEqual(0, observer.FirstRenderedSceneTimestamp); + Assert.Empty(observer.SchedulingSamples); Assert.Empty(observer.Presentations); Assert.Empty(observer.RenderedScenes); Assert.Empty(observer.RenderedViewportHeights); @@ -43,6 +45,12 @@ public void EnablingInstrumentationCapturesBoundedPresenterDetails() observer.RecordPresented(); observer.RecordRendered(header); + for (var i = 0; i < 4100; ++i) + observer.RecordScheduling("frame", i % 3, (ulong)i, false); + Assert.Equal(4096, observer.SchedulingSamples.Length); + Assert.Equal(4UL, observer.SchedulingSamples[0].Revision); + Assert.Equal(4099UL, observer.SchedulingSamples[^1].Revision); + Assert.True(instrumentation.IsEnabled); Assert.Single(observer.Presentations); var rendered = Assert.Single(observer.RenderedScenes); diff --git a/tests/WebScene.Backend.Avalonia.Tests/NativeResourceBridgeTests.cs b/tests/WebScene.Backend.Avalonia.Tests/NativeResourceBridgeTests.cs index 7ad44d6b9..ba4dafa7c 100644 --- a/tests/WebScene.Backend.Avalonia.Tests/NativeResourceBridgeTests.cs +++ b/tests/WebScene.Backend.Avalonia.Tests/NativeResourceBridgeTests.cs @@ -9,6 +9,29 @@ namespace WebScene.Backend.Avalonia.Tests; public sealed unsafe class NativeResourceBridgeTests { + [Theory] + [InlineData(false)] + [InlineData(true)] + public void BinaryResourceBytesSurviveEnvelopeWithoutTextConversion(bool empty) + { + byte[] payload = empty ? [] : [0, 255, 128, 195, 40, 65]; + using var bridge = CreateBridge(new BinaryLoader(payload)); + var required = bridge.Copy(4, "https://example.test/media.wav", null, 0, IntPtr.Zero, 0); + var destination = NativeMemory.Alloc(required); + try + { + Assert.Equal(required, bridge.Copy(4, "https://example.test/media.wav", null, 0, (IntPtr)destination, required)); + Assert.Equal(payload, new ReadOnlySpan((byte*)destination + 22, (int)required - 22).ToArray()); + } + finally { NativeMemory.Free(destination); } + } + private sealed class BinaryLoader(byte[] bytes) : IWebSceneResourceLoader + { + public WebSceneTextResource LoadText(in WebSceneResourceRequest request) + => new(request.Specifier, "This textual value must not replace binary bytes", request.Specifier, null) + { BinaryContent = bytes }; + } + [Theory] [InlineData("http", 503)] [InlineData("timeout", 0)] diff --git a/tests/WebScene.Backend.Avalonia.Tests/NativeSceneCaptureTests.cs b/tests/WebScene.Backend.Avalonia.Tests/NativeSceneCaptureTests.cs index bc0d820e1..dd69f4a31 100644 --- a/tests/WebScene.Backend.Avalonia.Tests/NativeSceneCaptureTests.cs +++ b/tests/WebScene.Backend.Avalonia.Tests/NativeSceneCaptureTests.cs @@ -277,6 +277,25 @@ public unsafe void SvgBlobImageDrawsIntoDetachedExportCanvas() } } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void StoppedCompositionIgnoresQueuedEngineMessages(bool revokeBeforeStop) + { + var instrumentation = new NativePerformanceInstrumentation(); + var handler = new NativeSceneCompositionHandler( + IntPtr.Zero, new NativeSceneRenderObserver(instrumentation), + new NativeScenePublicationMailbox(), new NativeSceneUiWakeGate(), + instrumentation, static () => throw new InvalidOperationException("Stopped handler woke the UI"), 1); + if (revokeBeforeStop) handler.RevokeEngineAccess(); + else handler.OnMessage(NativeSceneCompositionMessage.Stop); + // No native engine or attached compositor exists: any resumed work is invalid. + foreach (var message in Enum.GetValues()) + handler.OnMessage(message); + handler.OnAnimationFrameUpdate(); + handler.OnRender(null!); // A late render must return before using engine/context. + } + [Fact] public async Task CompositionCaptureCompletesFromRetainedRendererWithoutDrivingSceneLane() { diff --git a/tests/WebScene.Backend.Avalonia.Tests/NativeSceneDamagePolicyTests.cs b/tests/WebScene.Backend.Avalonia.Tests/NativeSceneDamagePolicyTests.cs index 8a2a028c6..abfd5cb0f 100644 --- a/tests/WebScene.Backend.Avalonia.Tests/NativeSceneDamagePolicyTests.cs +++ b/tests/WebScene.Backend.Avalonia.Tests/NativeSceneDamagePolicyTests.cs @@ -6,6 +6,40 @@ namespace WebScene.Backend.Avalonia.Tests; public sealed class NativeSceneDamagePolicyTests { + [Fact] + public void CoalescedScenesInvalidateBothSeparatedChanges() + { + var first = new NativeSceneDamage(true, false, new Rect(10, 20, 30, 40), 1, 1200); + var following = new NativeSceneDamage(true, false, new Rect(200, 100, 20, 10), 1, 200); + var combined = first.Combine(following); + Assert.True(combined.RequiresRender); + Assert.False(combined.IsFull); + Assert.Equal(new Rect(10, 20, 210, 90), combined.Bounds); + Assert.Equal(2, combined.RectangleCount); + Assert.Equal(1400, combined.SummedArea); + } + + [Fact] + public void UnchangedFollowingSceneCannotEraseEarlierDamage() + { + var changed = new NativeSceneDamage(true, false, new Rect(80, 90, 10, 20), 1, 200); + Assert.Equal(changed, changed.Combine(NativeSceneDamage.None)); + Assert.Equal(changed, NativeSceneDamage.None.Combine(changed)); + Assert.Equal(NativeSceneDamage.None, NativeSceneDamage.None.Combine(NativeSceneDamage.None)); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void FullDamageSurvivesEitherSceneOrder(bool fullFirst) + { + var full = new NativeSceneDamage(true, true, default, 0, 0); + var local = new NativeSceneDamage(true, false, new Rect(80, 90, 10, 20), 1, 200); + var combined = fullFirst ? full.Combine(local) : local.Combine(full); + Assert.True(combined.RequiresRender); + Assert.True(combined.IsFull); + } + private const uint SceneCheckpoint = 1; private const uint SceneDomReplacement = 2; diff --git a/tests/WebScene.Backend.Avalonia.Tests/WebScene.Backend.Avalonia.Tests.csproj b/tests/WebScene.Backend.Avalonia.Tests/WebScene.Backend.Avalonia.Tests.csproj index f33d36e78..86629f33b 100644 --- a/tests/WebScene.Backend.Avalonia.Tests/WebScene.Backend.Avalonia.Tests.csproj +++ b/tests/WebScene.Backend.Avalonia.Tests/WebScene.Backend.Avalonia.Tests.csproj @@ -7,6 +7,7 @@ true + diff --git a/tools/webidl-v8-bindings/README.md b/tools/webidl-v8-bindings/README.md index 6eb8efaee..c20bb6947 100644 --- a/tools/webidl-v8-bindings/README.md +++ b/tools/webidl-v8-bindings/README.md @@ -18,3 +18,7 @@ normal native build does not require Node, npm, a network connection, `@webref/i Generation dependencies are pinned by `package-lock.json`: `@webref/idl` 3.82.1 is MIT licensed and `webidl2` 24.5.0 uses the W3C software/document license. They are development tools and are not linked, copied, or packaged with the WebScene runtime. + +The generate/check scripts also produce and verify the native WebGPU feature-name +catalog from the SHA256-pinned `webgpu.idl` contract. Its explicit Dawn enum mapping +excludes native extensions; updating the IDL pin requires reviewing that mapping. diff --git a/tools/webidl-v8-bindings/dom-exposure.json b/tools/webidl-v8-bindings/dom-exposure.json index 088601bed..40e4e2e33 100644 --- a/tools/webidl-v8-bindings/dom-exposure.json +++ b/tools/webidl-v8-bindings/dom-exposure.json @@ -218,6 +218,17 @@ ], "constants": [] }, + { + "name": "SVGElement", + "template": "svg_element_template", + "constructor": "illegal_dom_constructor", + "parent": "Element", + "attributes": [ + { "name": "style", "getter": "get_style" } + ], + "methods": [], + "constants": [] + }, { "name": "HTMLElement", "template": "html_element_template", @@ -226,6 +237,7 @@ "attributes": [ { "name": "style", "getter": "get_style" }, { "name": "dataset", "getter": "get_dataset" }, + { "name": "inert", "getter": "get_inert", "setter": "set_inert" }, { "name": "innerText", "getter": "get_inner_text", "setter": "set_text_content" }, { "name": "content", "getter": "get_template_content" }, { "name": "clientWidth", "getter": "get_client_width" }, @@ -305,6 +317,23 @@ "methods": [], "constants": [] }, + { + "name": "HTMLDialogElement", + "template": "html_dialog_element_template", + "constructor": "illegal_dom_constructor", + "parent": "HTMLElement", + "attributes": [ + { "name": "open", "getter": "get_dialog_open", "setter": "set_dialog_open" }, + { "name": "returnValue", "getter": "get_dialog_return_value", "setter": "set_dialog_return_value" } + ], + "methods": [ + { "name": "show", "callback": "dialog_show", "length": 0 }, + { "name": "showModal", "callback": "dialog_show_modal", "length": 0 }, + { "name": "close", "callback": "dialog_close", "length": 0 }, + { "name": "requestClose", "callback": "dialog_request_close", "length": 0 } + ], + "constants": [] + }, { "name": "HTMLFormElement", "template": "html_form_element_template", @@ -373,7 +402,6 @@ "HTMLImageElement": "HTMLElement", "HTMLInputElement": "HTMLElement", "HTMLParagraphElement": "HTMLElement", - "SVGElement": "Element", "Window": "EventTarget" } } diff --git a/tools/webidl-v8-bindings/generate-webgpu-features.mjs b/tools/webidl-v8-bindings/generate-webgpu-features.mjs new file mode 100644 index 000000000..1b003c8aa --- /dev/null +++ b/tools/webidl-v8-bindings/generate-webgpu-features.mjs @@ -0,0 +1,52 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; +import crypto from 'node:crypto'; +import * as webidl from 'webidl2'; +const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'../..'); +const contract=JSON.parse(fs.readFileSync(path.join(root,'docs/graphics/webgpu-v8-contract.json'),'utf8')); +const source=fs.readFileSync(new URL('./node_modules/@webref/idl/webgpu.idl',import.meta.url),'utf8'); +if(crypto.createHash('sha256').update(source).digest('hex')!==contract.sha256) throw new Error('WebGPU IDL pin mismatch'); +const names=webidl.parse(source).find(x=>x.type==='enum'&&x.name==='GPUFeatureName').values.map(x=>x.value); +// Explicit spellings keep acronym changes and new IDL values reviewable. +const native=['CoreFeaturesAndLimits','DepthClipControl','Depth32FloatStencil8','TextureCompressionBC','TextureCompressionBCSliced3D','TextureCompressionETC2','TextureCompressionASTC','TextureCompressionASTCSliced3D','TimestampQuery','IndirectFirstInstance','ShaderF16','RG11B10UfloatRenderable','BGRA8UnormStorage','Float32Filterable','Float32Blendable','ClipDistances','DualSourceBlending','Subgroups','TextureFormatsTier1','TextureFormatsTier2','PrimitiveIndex','TextureComponentSwizzle','SubgroupSizeControl']; +if(names.length!==native.length) throw new Error('GPUFeatureName mapping requires review'); +const output=`// Generated by tools/webidl-v8-bindings/generate-webgpu-features.mjs. +// @webref/idl ${contract.version}; webgpu.idl SHA256 ${contract.sha256} +#pragma once +#include +#include +#include +#include +#include +#include + +namespace webscene::graphics { +struct webgpu_feature_name { std::string_view name; wgpu::FeatureName native; }; +inline constexpr std::array webgpu_feature_names{{ +${names.map((name,i)=>` {"${name}",wgpu::FeatureName::${native[i]}},`).join('\n')} +}}; +inline std::optional webgpu_feature_from_name(std::string_view name) { + for (const auto& feature:webgpu_feature_names) if (feature.name==name) return feature.native; + return std::nullopt; +} +inline std::optional webgpu_feature_to_name(wgpu::FeatureName native) { + for (const auto& feature:webgpu_feature_names) if (feature.native==native) return feature.name; + return std::nullopt; +} +// Both Adapter and Device implement HasFeature. Device features are the enabled +// subset, not the adapter's full capabilities. Never enumerate native extensions +// into the browser surface, even when the host enables them for shared images. +template std::vector webgpu_supported_feature_names(const Source& source) { + if (!source) throw std::invalid_argument("WebGPU feature source is null"); + std::vector result; + for (const auto& feature:webgpu_feature_names) + if (source.HasFeature(feature.native)) result.push_back(feature.name); + return result; +} +} // namespace webscene::graphics +`; +const destination=path.join(root,'experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_feature_names.h'); +if(process.argv.includes('--check')) { + if(!fs.existsSync(destination)||fs.readFileSync(destination,'utf8')!==output) throw new Error('WebGPU feature catalog is stale'); +} else fs.writeFileSync(destination,output); diff --git a/tools/webidl-v8-bindings/generate-webgpu-limits.mjs b/tools/webidl-v8-bindings/generate-webgpu-limits.mjs new file mode 100644 index 000000000..e24a7b077 --- /dev/null +++ b/tools/webidl-v8-bindings/generate-webgpu-limits.mjs @@ -0,0 +1,59 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; +import crypto from 'node:crypto'; +import * as webidl from 'webidl2'; +const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'../..'); +const contract=JSON.parse(fs.readFileSync(path.join(root,'docs/graphics/webgpu-v8-contract.json'),'utf8')); +const source=fs.readFileSync(new URL('./node_modules/@webref/idl/webgpu.idl',import.meta.url),'utf8'); +if(crypto.createHash('sha256').update(source).digest('hex')!==contract.sha256) throw new Error('WebGPU IDL pin mismatch'); +const members=webidl.parse(source).find(x=>x.type==='interface'&&x.name==='GPUSupportedLimits').members; +for(const member of members) if(member.type!=='attribute'||!member.readonly||!['unsigned long','unsigned long long'].includes(member.idlType.idlType)) throw new Error('Supported limit mapping requires review'); +const compatibility=new Set(["maxStorageBuffersInVertexStage","maxStorageBuffersInFragmentStage","maxStorageTexturesInVertexStage","maxStorageTexturesInFragmentStage"]); +const output=`// Generated by tools/webidl-v8-bindings/generate-webgpu-limits.mjs. +// @webref/idl ${contract.version}; webgpu.idl SHA256 ${contract.sha256} +#pragma once +#include +#include +#include +#include +#include +#include +namespace webscene::graphics { +struct webgpu_limit_name { + std::u16string_view name; + std::variant member; + uint64_t read(const wgpu::Limits& limits,const wgpu::CompatibilityModeLimits& compatibility) const { + return std::visit([&](auto field) { + if constexpr (std::is_same_v) return uint64_t(compatibility.*field); + else return uint64_t(limits.*field); + },member); + } + // Reject values that cannot be represented, including Dawn's undefined + // sentinel. Never let a requested integer silently become an omitted limit. + bool write(wgpu::Limits& limits,wgpu::CompatibilityModeLimits& compatibility,uint64_t value) const { + return std::visit([&](auto field) { + if constexpr (std::is_same_v) { + if (value>=UINT32_MAX) return false; + compatibility.*field=static_cast(value); return true; + } else { + using T=std::remove_reference_t; + if (value>=std::numeric_limits::max()) return false; + limits.*field=static_cast(value); return true; + } + },member); + } +}; +inline const std::array webgpu_limit_names{{ +${members.map(x=>` {u"${x.name}",&wgpu::${compatibility.has(x.name)?"CompatibilityModeLimits":"Limits"}::${x.name}},`).join('\n')} +}}; +inline const webgpu_limit_name* webgpu_limit_from_name(std::u16string_view name) { + for (const auto& limit:webgpu_limit_names) if (limit.name==name) return &limit; + return nullptr; +} +} // namespace webscene::graphics +`; +const destination=path.join(root,'experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_limit_names.h'); +if(process.argv.includes('--check')) { + if(!fs.existsSync(destination)||fs.readFileSync(destination,'utf8')!==output) throw new Error('WebGPU limit catalog is stale'); +} else fs.writeFileSync(destination,output); diff --git a/tools/webidl-v8-bindings/generate-webgpu-render-enums.mjs b/tools/webidl-v8-bindings/generate-webgpu-render-enums.mjs new file mode 100644 index 000000000..561fef2c8 --- /dev/null +++ b/tools/webidl-v8-bindings/generate-webgpu-render-enums.mjs @@ -0,0 +1,37 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; +import crypto from 'node:crypto'; +import * as webidl from 'webidl2'; +const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'../..'); +const contract=JSON.parse(fs.readFileSync(path.join(root,'docs/graphics/webgpu-v8-contract.json'),'utf8')); +const source=fs.readFileSync(new URL('./node_modules/@webref/idl/webgpu.idl',import.meta.url),'utf8'); +if(crypto.createHash('sha256').update(source).digest('hex')!==contract.sha256)throw new Error('WebGPU IDL pin mismatch'); +const idl=webidl.parse(source); +// Explicit reviewed native spellings; never expose Dawn-only enum values. +const mappings=JSON.parse(fs.readFileSync(new URL('./webgpu-render-enums.json',import.meta.url),'utf8')); +let output=`// Generated by tools/webidl-v8-bindings/generate-webgpu-render-enums.mjs. +// @webref/idl ${contract.version}; SHA256 ${contract.sha256} +#pragma once +#include +#include +#include +#include +namespace webscene::graphics { +template struct webgpu_enum_names; +`; +for(const [name,mapping] of Object.entries(mappings)) { + const values=idl.find(x=>x.type==='enum'&&x.name==='GPU'+name).values.map(x=>x.value); + if(JSON.stringify(values)!==JSON.stringify(Object.keys(mapping)))throw new Error(`${name} mapping requires review`); + output+=`template<> struct webgpu_enum_names { + static inline constexpr std::array,${values.length}> values{{ +${values.map(value=>` {"${value}",wgpu::${name}::${mapping[value]}},`).join('\n')} + }}; +}; +`; +} +output+='} // namespace webscene::graphics\n'; +const destination=path.join(root,'experiments/WebScene.NativeEngine.Probe/native/graphics/webgpu_render_enums.h'); +if(process.argv.includes('--check')) { + if(!fs.existsSync(destination)||fs.readFileSync(destination,'utf8')!==output)throw new Error('WebGPU render enum catalog is stale'); +}else fs.writeFileSync(destination,output); diff --git a/tools/webidl-v8-bindings/generate.mjs b/tools/webidl-v8-bindings/generate.mjs index 1254d7d68..dce355ad7 100644 --- a/tools/webidl-v8-bindings/generate.mjs +++ b/tools/webidl-v8-bindings/generate.mjs @@ -113,9 +113,15 @@ line("static std::optional generated_standalone_event_target_id("); line(" v8::Isolate* isolate,"); line(" v8::Local receiver)"); line("{"); +line(" if (receiver.IsEmpty()) return std::nullopt;"); +line(" v8::Local identity;"); +line(" auto key = v8::Private::ForApi(isolate, js_string(isolate, \"WebScene.EventTarget.identity\"));"); +line(" if (receiver->GetPrivate(isolate->GetCurrentContext(), key).ToLocal(&identity) && identity->IsUint32())"); +line(" return identity.As()->Value();"); line(" if (receiver.IsEmpty() || receiver->InternalFieldCount() < 2) {"); line(" return std::nullopt;"); line(" }"); +line(" if (!receiver->GetInternalField(1)->IsValue()) return std::nullopt;"); line(" auto value = receiver->GetInternalField(1).As();"); line(" if (!value->IsUint32()) return std::nullopt;"); line(" return value->Uint32Value(isolate->GetCurrentContext()).FromMaybe(0U);"); @@ -180,6 +186,9 @@ for (const value of manifest.interfaces) { line(` auto ${local} = v8::FunctionTemplate::New(isolate, ${value.constructor});`); line(` ${local}->SetClassName(js_string(isolate, \"${value.name}\"));`); line(` ${local}->SetInterfaceName(js_string(isolate, \"${value.name}\"));`); + line(` ${local}->PrototypeTemplate()->Set(`); + line(` v8::Symbol::GetToStringTag(isolate), js_string(isolate, "${value.name}"),`); + line(` static_cast(v8::ReadOnly | v8::DontEnum));`); if (value.parent) { line(` ${local}->Inherit(generated_${safe(value.parent)}_template);`); } diff --git a/tools/webidl-v8-bindings/package.json b/tools/webidl-v8-bindings/package.json index 86def9b04..48c7d7df4 100644 --- a/tools/webidl-v8-bindings/package.json +++ b/tools/webidl-v8-bindings/package.json @@ -3,8 +3,8 @@ "private": true, "type": "module", "scripts": { - "generate": "node generate.mjs", - "check": "node generate.mjs --check" + "generate": "node generate.mjs && node generate-webgpu-features.mjs && node generate-webgpu-limits.mjs && node generate-webgpu-render-enums.mjs", + "check": "node generate.mjs --check && node generate-webgpu-features.mjs --check && node generate-webgpu-limits.mjs --check && node generate-webgpu-render-enums.mjs --check" }, "dependencies": { "@webref/idl": "3.82.1", diff --git a/tools/webidl-v8-bindings/webgpu-render-enums.json b/tools/webidl-v8-bindings/webgpu-render-enums.json new file mode 100644 index 000000000..76bc83c70 --- /dev/null +++ b/tools/webidl-v8-bindings/webgpu-render-enums.json @@ -0,0 +1,279 @@ +{ + "PrimitiveTopology": { + "point-list": "PointList", + "line-list": "LineList", + "line-strip": "LineStrip", + "triangle-list": "TriangleList", + "triangle-strip": "TriangleStrip" + }, + "IndexFormat": { + "uint16": "Uint16", + "uint32": "Uint32" + }, + "FrontFace": { + "ccw": "CCW", + "cw": "CW" + }, + "CullMode": { + "none": "None", + "front": "Front", + "back": "Back" + }, + "BlendFactor": { + "zero": "Zero", + "one": "One", + "src": "Src", + "one-minus-src": "OneMinusSrc", + "src-alpha": "SrcAlpha", + "one-minus-src-alpha": "OneMinusSrcAlpha", + "dst": "Dst", + "one-minus-dst": "OneMinusDst", + "dst-alpha": "DstAlpha", + "one-minus-dst-alpha": "OneMinusDstAlpha", + "src-alpha-saturated": "SrcAlphaSaturated", + "constant": "Constant", + "one-minus-constant": "OneMinusConstant", + "src1": "Src1", + "one-minus-src1": "OneMinusSrc1", + "src1-alpha": "Src1Alpha", + "one-minus-src1-alpha": "OneMinusSrc1Alpha" + }, + "BlendOperation": { + "add": "Add", + "subtract": "Subtract", + "reverse-subtract": "ReverseSubtract", + "min": "Min", + "max": "Max" + }, + "StencilOperation": { + "keep": "Keep", + "zero": "Zero", + "replace": "Replace", + "invert": "Invert", + "increment-clamp": "IncrementClamp", + "decrement-clamp": "DecrementClamp", + "increment-wrap": "IncrementWrap", + "decrement-wrap": "DecrementWrap" + }, + "CompareFunction": { + "never": "Never", + "less": "Less", + "equal": "Equal", + "less-equal": "LessEqual", + "greater": "Greater", + "not-equal": "NotEqual", + "greater-equal": "GreaterEqual", + "always": "Always" + }, + "TextureFormat": { + "r8unorm": "R8Unorm", + "r8snorm": "R8Snorm", + "r8uint": "R8Uint", + "r8sint": "R8Sint", + "r16unorm": "R16Unorm", + "r16snorm": "R16Snorm", + "r16uint": "R16Uint", + "r16sint": "R16Sint", + "r16float": "R16Float", + "rg8unorm": "RG8Unorm", + "rg8snorm": "RG8Snorm", + "rg8uint": "RG8Uint", + "rg8sint": "RG8Sint", + "r32uint": "R32Uint", + "r32sint": "R32Sint", + "r32float": "R32Float", + "rg16unorm": "RG16Unorm", + "rg16snorm": "RG16Snorm", + "rg16uint": "RG16Uint", + "rg16sint": "RG16Sint", + "rg16float": "RG16Float", + "rgba8unorm": "RGBA8Unorm", + "rgba8unorm-srgb": "RGBA8UnormSrgb", + "rgba8snorm": "RGBA8Snorm", + "rgba8uint": "RGBA8Uint", + "rgba8sint": "RGBA8Sint", + "bgra8unorm": "BGRA8Unorm", + "bgra8unorm-srgb": "BGRA8UnormSrgb", + "rgb9e5ufloat": "RGB9E5Ufloat", + "rgb10a2uint": "RGB10A2Uint", + "rgb10a2unorm": "RGB10A2Unorm", + "rg11b10ufloat": "RG11B10Ufloat", + "rg32uint": "RG32Uint", + "rg32sint": "RG32Sint", + "rg32float": "RG32Float", + "rgba16unorm": "RGBA16Unorm", + "rgba16snorm": "RGBA16Snorm", + "rgba16uint": "RGBA16Uint", + "rgba16sint": "RGBA16Sint", + "rgba16float": "RGBA16Float", + "rgba32uint": "RGBA32Uint", + "rgba32sint": "RGBA32Sint", + "rgba32float": "RGBA32Float", + "stencil8": "Stencil8", + "depth16unorm": "Depth16Unorm", + "depth24plus": "Depth24Plus", + "depth24plus-stencil8": "Depth24PlusStencil8", + "depth32float": "Depth32Float", + "depth32float-stencil8": "Depth32FloatStencil8", + "bc1-rgba-unorm": "BC1RGBAUnorm", + "bc1-rgba-unorm-srgb": "BC1RGBAUnormSrgb", + "bc2-rgba-unorm": "BC2RGBAUnorm", + "bc2-rgba-unorm-srgb": "BC2RGBAUnormSrgb", + "bc3-rgba-unorm": "BC3RGBAUnorm", + "bc3-rgba-unorm-srgb": "BC3RGBAUnormSrgb", + "bc4-r-unorm": "BC4RUnorm", + "bc4-r-snorm": "BC4RSnorm", + "bc5-rg-unorm": "BC5RGUnorm", + "bc5-rg-snorm": "BC5RGSnorm", + "bc6h-rgb-ufloat": "BC6HRGBUfloat", + "bc6h-rgb-float": "BC6HRGBFloat", + "bc7-rgba-unorm": "BC7RGBAUnorm", + "bc7-rgba-unorm-srgb": "BC7RGBAUnormSrgb", + "etc2-rgb8unorm": "ETC2RGB8Unorm", + "etc2-rgb8unorm-srgb": "ETC2RGB8UnormSrgb", + "etc2-rgb8a1unorm": "ETC2RGB8A1Unorm", + "etc2-rgb8a1unorm-srgb": "ETC2RGB8A1UnormSrgb", + "etc2-rgba8unorm": "ETC2RGBA8Unorm", + "etc2-rgba8unorm-srgb": "ETC2RGBA8UnormSrgb", + "eac-r11unorm": "EACR11Unorm", + "eac-r11snorm": "EACR11Snorm", + "eac-rg11unorm": "EACRG11Unorm", + "eac-rg11snorm": "EACRG11Snorm", + "astc-4x4-unorm": "ASTC4x4Unorm", + "astc-4x4-unorm-srgb": "ASTC4x4UnormSrgb", + "astc-5x4-unorm": "ASTC5x4Unorm", + "astc-5x4-unorm-srgb": "ASTC5x4UnormSrgb", + "astc-5x5-unorm": "ASTC5x5Unorm", + "astc-5x5-unorm-srgb": "ASTC5x5UnormSrgb", + "astc-6x5-unorm": "ASTC6x5Unorm", + "astc-6x5-unorm-srgb": "ASTC6x5UnormSrgb", + "astc-6x6-unorm": "ASTC6x6Unorm", + "astc-6x6-unorm-srgb": "ASTC6x6UnormSrgb", + "astc-8x5-unorm": "ASTC8x5Unorm", + "astc-8x5-unorm-srgb": "ASTC8x5UnormSrgb", + "astc-8x6-unorm": "ASTC8x6Unorm", + "astc-8x6-unorm-srgb": "ASTC8x6UnormSrgb", + "astc-8x8-unorm": "ASTC8x8Unorm", + "astc-8x8-unorm-srgb": "ASTC8x8UnormSrgb", + "astc-10x5-unorm": "ASTC10x5Unorm", + "astc-10x5-unorm-srgb": "ASTC10x5UnormSrgb", + "astc-10x6-unorm": "ASTC10x6Unorm", + "astc-10x6-unorm-srgb": "ASTC10x6UnormSrgb", + "astc-10x8-unorm": "ASTC10x8Unorm", + "astc-10x8-unorm-srgb": "ASTC10x8UnormSrgb", + "astc-10x10-unorm": "ASTC10x10Unorm", + "astc-10x10-unorm-srgb": "ASTC10x10UnormSrgb", + "astc-12x10-unorm": "ASTC12x10Unorm", + "astc-12x10-unorm-srgb": "ASTC12x10UnormSrgb", + "astc-12x12-unorm": "ASTC12x12Unorm", + "astc-12x12-unorm-srgb": "ASTC12x12UnormSrgb" + }, + "VertexFormat": { + "uint8": "Uint8", + "uint8x2": "Uint8x2", + "uint8x4": "Uint8x4", + "sint8": "Sint8", + "sint8x2": "Sint8x2", + "sint8x4": "Sint8x4", + "unorm8": "Unorm8", + "unorm8x2": "Unorm8x2", + "unorm8x4": "Unorm8x4", + "snorm8": "Snorm8", + "snorm8x2": "Snorm8x2", + "snorm8x4": "Snorm8x4", + "uint16": "Uint16", + "uint16x2": "Uint16x2", + "uint16x4": "Uint16x4", + "sint16": "Sint16", + "sint16x2": "Sint16x2", + "sint16x4": "Sint16x4", + "unorm16": "Unorm16", + "unorm16x2": "Unorm16x2", + "unorm16x4": "Unorm16x4", + "snorm16": "Snorm16", + "snorm16x2": "Snorm16x2", + "snorm16x4": "Snorm16x4", + "float16": "Float16", + "float16x2": "Float16x2", + "float16x4": "Float16x4", + "float32": "Float32", + "float32x2": "Float32x2", + "float32x3": "Float32x3", + "float32x4": "Float32x4", + "uint32": "Uint32", + "uint32x2": "Uint32x2", + "uint32x3": "Uint32x3", + "uint32x4": "Uint32x4", + "sint32": "Sint32", + "sint32x2": "Sint32x2", + "sint32x3": "Sint32x3", + "sint32x4": "Sint32x4", + "unorm10-10-10-2": "Unorm10_10_10_2", + "unorm8x4-bgra": "Unorm8x4BGRA" + }, + "VertexStepMode": { + "vertex": "Vertex", + "instance": "Instance" + }, + "TextureDimension": { + "1d": "e1D", + "2d": "e2D", + "3d": "e3D" + }, + "TextureViewDimension": { + "1d": "e1D", + "2d": "e2D", + "2d-array": "e2DArray", + "cube": "Cube", + "cube-array": "CubeArray", + "3d": "e3D" + }, + "TextureAspect": { + "all": "All", + "stencil-only": "StencilOnly", + "depth-only": "DepthOnly" + }, + "LoadOp": { + "load": "Load", + "clear": "Clear" + }, + "StoreOp": { + "store": "Store", + "discard": "Discard" + }, + "BufferBindingType": { + "uniform": "Uniform", + "storage": "Storage", + "read-only-storage": "ReadOnlyStorage" + }, + "SamplerBindingType": { + "filtering": "Filtering", + "non-filtering": "NonFiltering", + "comparison": "Comparison" + }, + "TextureSampleType": { + "float": "Float", + "unfilterable-float": "UnfilterableFloat", + "depth": "Depth", + "sint": "Sint", + "uint": "Uint" + }, + "StorageTextureAccess": { + "write-only": "WriteOnly", + "read-only": "ReadOnly", + "read-write": "ReadWrite" + }, + "AddressMode": { + "clamp-to-edge": "ClampToEdge", + "repeat": "Repeat", + "mirror-repeat": "MirrorRepeat" + }, + "FilterMode": { + "nearest": "Nearest", + "linear": "Linear" + }, + "MipmapFilterMode": { + "nearest": "Nearest", + "linear": "Linear" + } +}