From 1c7bd06251e5536c8f782a6f77009e1b6b75240b Mon Sep 17 00:00:00 2001 From: ImmutableJeffrey Date: Sun, 10 May 2026 11:27:27 +1000 Subject: [PATCH 1/3] ci(audience): replace cartesian playmode matrix with explicit cell entries The previous matrix combined a cartesian product (unity x target x backend) with partial-key include entries that attached changeset and runner. Verified on runs 25604434621 (PR 754) and 25613048568 (PR 765) that the partial-include merge silently fails: matrix.runner ends up unset on every combo, runs-on resolves to empty, and the playmode job spawns zero jobs. Win/Mac PlayMode coverage has therefore been missing from every PR stacked on this branch. Replace with 12 fully-attributed explicit include entries, one per cell, plus the existing pull_request exclude that drops the 2022.3.62f2 cells on PR runs. This is a verbose but bulletproof shape: every key is set on every combo, no partial-key merge required. --- .../workflows/test-audience-sample-app.yml | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test-audience-sample-app.yml b/.github/workflows/test-audience-sample-app.yml index 452aab38..0d88bfc2 100644 --- a/.github/workflows/test-audience-sample-app.yml +++ b/.github/workflows/test-audience-sample-app.yml @@ -46,20 +46,26 @@ jobs: strategy: fail-fast: false matrix: - unity: ['2021.3.45f2', '6000.4.0f1', '2022.3.62f2'] - target: [StandaloneWindows64, StandaloneOSX] - backend: [IL2CPP, Mono2x] + # Explicit per-cell entries instead of cartesian + partial-key + # include. The cartesian + partial-include shape used here + # previously had the include entries fail to merge runner and + # changeset onto the generated combos, so every cell had no + # runner and the playmode job spawned zero jobs (verified on + # runs 25604434621 and 25613048568). 12 explicit entries below + # is the bulletproof shape. include: - - unity: '2021.3.45f2' - changeset: 88f88f591b2e - - unity: '6000.4.0f1' - changeset: 8cf496087c8f - - unity: '2022.3.62f2' - changeset: 7670c08855a9 - - target: StandaloneWindows64 - runner: [self-hosted, Windows, X64] - - target: StandaloneOSX - runner: [self-hosted, macOS, ARM64] + - { target: StandaloneWindows64, backend: IL2CPP, unity: '2021.3.45f2', changeset: 88f88f591b2e, runner: [self-hosted, Windows, X64] } + - { target: StandaloneWindows64, backend: Mono2x, unity: '2021.3.45f2', changeset: 88f88f591b2e, runner: [self-hosted, Windows, X64] } + - { target: StandaloneOSX, backend: IL2CPP, unity: '2021.3.45f2', changeset: 88f88f591b2e, runner: [self-hosted, macOS, ARM64] } + - { target: StandaloneOSX, backend: Mono2x, unity: '2021.3.45f2', changeset: 88f88f591b2e, runner: [self-hosted, macOS, ARM64] } + - { target: StandaloneWindows64, backend: IL2CPP, unity: '6000.4.0f1', changeset: 8cf496087c8f, runner: [self-hosted, Windows, X64] } + - { target: StandaloneWindows64, backend: Mono2x, unity: '6000.4.0f1', changeset: 8cf496087c8f, runner: [self-hosted, Windows, X64] } + - { target: StandaloneOSX, backend: IL2CPP, unity: '6000.4.0f1', changeset: 8cf496087c8f, runner: [self-hosted, macOS, ARM64] } + - { target: StandaloneOSX, backend: Mono2x, unity: '6000.4.0f1', changeset: 8cf496087c8f, runner: [self-hosted, macOS, ARM64] } + - { target: StandaloneWindows64, backend: IL2CPP, unity: '2022.3.62f2', changeset: 7670c08855a9, runner: [self-hosted, Windows, X64] } + - { target: StandaloneWindows64, backend: Mono2x, unity: '2022.3.62f2', changeset: 7670c08855a9, runner: [self-hosted, Windows, X64] } + - { target: StandaloneOSX, backend: IL2CPP, unity: '2022.3.62f2', changeset: 7670c08855a9, runner: [self-hosted, macOS, ARM64] } + - { target: StandaloneOSX, backend: Mono2x, unity: '2022.3.62f2', changeset: 7670c08855a9, runner: [self-hosted, macOS, ARM64] } exclude: ${{ fromJSON(github.event_name == 'pull_request' && '[{"unity":"2022.3.62f2"}]' || '[]') }} runs-on: ${{ matrix.runner }} # Healthy cells finish in ~10 min. 30 min covers cold caches + From 415390256b498f8d46fc231a0697a7d48c478788 Mon Sep 17 00:00:00 2001 From: ImmutableJeffrey Date: Sun, 10 May 2026 11:28:17 +1000 Subject: [PATCH 2/3] debug(audience): add T1/T2/T3 timing markers in HttpTransport.SendBatchAsync Temporary diagnostic for the Unity 6 Linux PlayMode regression. Tests on Unity 6 Mono Linux take ~33 sec each vs ~1.8 sec on Unity 2021.3 Mono Linux for the same suite, same llvmpipe, same network endpoint. T1 logs request start, T2 logs response received (continuation back on the main thread), T3 logs batch deletion done. Comparing T1->T2 vs T2->T3 gaps across Unity versions disambiguates whether the stall is inside HttpClient.SendAsync (network/socket/TLS regression in Unity 6 Mono on Linux) or in the awaiter continuation (SyncContext dispatch regression on Unity 6 Linux player loop). To revert: drop this commit. --- .../Audience/Runtime/Transport/HttpTransport.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Packages/Audience/Runtime/Transport/HttpTransport.cs b/src/Packages/Audience/Runtime/Transport/HttpTransport.cs index 6ac6e535..19e3585c 100644 --- a/src/Packages/Audience/Runtime/Transport/HttpTransport.cs +++ b/src/Packages/Audience/Runtime/Transport/HttpTransport.cs @@ -95,8 +95,23 @@ internal async Task SendBatchAsync(CancellationToken ct = default) request.Content = new StringContent(payload, Encoding.UTF8, "application/json"); #endif + // Temporary diagnostic markers to measure where SendBatchAsync + // spends time on Unity 6 Linux PlayMode cells (where each test + // sits at the 30 sec WaitForLogEntry budget on Mono Linux but + // finishes in 1-2 sec on Unity 2021.3 Linux). T1 is request + // start, T2 is response received (continuation alive), T3 is + // batch deletion + state reset done. Comparing T1->T2 vs + // T2->T3 gaps across Unity versions disambiguates whether the + // stall is in HttpClient.SendAsync (network/socket) or in the + // continuation back onto Unity's main thread (SyncContext). + var __t1 = System.Diagnostics.Stopwatch.StartNew(); + Log.Debug($"[HttpTransport][T1] SendAsync start url={_url} count={batch.Count}"); + using var response = await _client.SendAsync(request, ct).ConfigureAwait(false); + var __t2Ms = __t1.Elapsed.TotalMilliseconds; + Log.Debug($"[HttpTransport][T2] SendAsync done t1->t2={__t2Ms:F1}ms status={(int)response.StatusCode}"); + var statusCode = (int)response.StatusCode; if (statusCode >= 200 && statusCode < 300) @@ -113,6 +128,7 @@ internal async Task SendBatchAsync(CancellationToken ct = default) NotifyError(AudienceErrorCode.ValidationRejected, $"Batch partially rejected: {rejected} of {batch.Count} events dropped"); } + Log.Debug($"[HttpTransport][T3] batch handled t1->t3={__t1.Elapsed.TotalMilliseconds:F1}ms (t2->t3={(__t1.Elapsed.TotalMilliseconds - __t2Ms):F1}ms)"); } else if (statusCode == 429) { From 58613edc276e7a520fdcfd47b9cc3a95a671b972 Mon Sep 17 00:00:00 2001 From: ImmutableJeffrey Date: Sun, 10 May 2026 12:08:09 +1000 Subject: [PATCH 3/3] Revert "test(audience-sample): hide log pane on Linux PlayMode to skip llvmpipe rasterisation" This reverts commit f2f7037b1dd1eb18ea38f999698ac503a805abc0. --- .../Tests/Runtime/LinuxLogPaneSuppression.cs | 68 ------------------- .../Runtime/LinuxLogPaneSuppression.cs.meta | 11 --- 2 files changed, 79 deletions(-) delete mode 100644 examples/audience/Assets/SampleApp/Tests/Runtime/LinuxLogPaneSuppression.cs delete mode 100644 examples/audience/Assets/SampleApp/Tests/Runtime/LinuxLogPaneSuppression.cs.meta diff --git a/examples/audience/Assets/SampleApp/Tests/Runtime/LinuxLogPaneSuppression.cs b/examples/audience/Assets/SampleApp/Tests/Runtime/LinuxLogPaneSuppression.cs deleted file mode 100644 index 0a8ffff3..00000000 --- a/examples/audience/Assets/SampleApp/Tests/Runtime/LinuxLogPaneSuppression.cs +++ /dev/null @@ -1,68 +0,0 @@ -#nullable enable - -#if UNITY_STANDALONE_LINUX -using NUnit.Framework; -using UnityEngine; -using UnityEngine.SceneManagement; -using UnityEngine.UIElements; - -namespace Immutable.Audience.Samples.SampleApp.Tests -{ - // Linux PlayMode test optimisation: hide the SampleApp log pane so UI - // Toolkit does not generate triangles for its rows during test runs. - // - // The player profile captured on PR 765 showed Render Thread spending - // roughly 4.5 seconds per frame in Gfx.PresentFrame self time on Unity - // 6 Linux cells, with ~2920 batches and ~7520 triangles per frame. - // Camera.Render is 2 ms, UI.RenderOverlays 1.45 ms; the rest is - // llvmpipe rasterising the deferred command buffer at present time. - // The bulk of those triangles come from the log pane, which - // accumulates one row per logged event over the course of a session. - // - // display:none keeps elements in the visual tree (so VisualElement.Q - // and contentContainer.Children() still find them) but skips layout - // and render entirely. Tests assert on log entries via userData on - // each row, which is reference-based, not layout-based, so the - // assertions stay correct. - // - // Engages only on StandaloneLinux64 builds (gated by the #if). Mac - // and Windows PlayMode runs are unaffected. - [SetUpFixture] - public sealed class LinuxLogPaneSuppression - { - [OneTimeSetUp] - public void RegisterSceneHook() - { - SceneManager.sceneLoaded += HideLogPane; - } - - [OneTimeTearDown] - public void DeregisterSceneHook() - { - SceneManager.sceneLoaded -= HideLogPane; - } - - // Re-fires for every scene load. The SampleApp's UIDocument runs - // its UI initialisation in Awake, so by the time sceneLoaded - // fires the log ScrollView is in the tree and ready to be - // styled. The hook is idempotent across loads. - private static void HideLogPane(Scene scene, LoadSceneMode mode) - { - var sample = Object.FindFirstObjectByType(FindObjectsInactive.Include); - if (sample == null) return; - - var doc = sample.GetComponent(); - if (doc == null) return; - - var root = doc.rootVisualElement; - if (root == null) return; - - var log = root.Q(SampleAppUi.LogScrollView); - if (log == null) return; - - log.style.display = new StyleEnum(DisplayStyle.None); - Debug.Log("[LinuxLogPaneSuppression] log pane hidden for Linux PlayMode test run."); - } - } -} -#endif diff --git a/examples/audience/Assets/SampleApp/Tests/Runtime/LinuxLogPaneSuppression.cs.meta b/examples/audience/Assets/SampleApp/Tests/Runtime/LinuxLogPaneSuppression.cs.meta deleted file mode 100644 index 375feaff..00000000 --- a/examples/audience/Assets/SampleApp/Tests/Runtime/LinuxLogPaneSuppression.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3a7e9d4b8c1f5a6e2b8d9c0e1f2a3b4c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: