feat(eventbus): add platform runtimes and benchmarks

Add the Entities NativeQueue adapter, standalone .NET runtime and source generator, reproducible smoke coverage, and Unity benchmark assets for EventBus 2.0.
This commit is contained in:
2026-08-26 01:17:39 +08:00
parent ad5a7b68a3
commit 724e0bc8d8
32 changed files with 1813 additions and 0 deletions
@@ -0,0 +1,14 @@
# ShrinkEventBus Entities
`com.cneicy.shrink-eventbus-entities` 是可选 ECS/Burst 适配包。它不创建第二套事件总线,而是让 Burst Job 把 `unmanaged IShrinkEvent` 写入 `NativeQueue<T>`,随后由主线程或 ECS playback system 发布到指定的 `IShrinkEventBus`
```csharp
using var queue = new ShrinkEcsEventQueue<MyEcsEvent>();
var writer = queue.Writer;
// Burst Job 中:writer.Post(in eventData);
// Playback 阶段:
queue.Playback(worldBus);
```
限制:Burst 端只负责事实事件生产,不能直接执行托管 handler、UniTask、取消或结果裁决。
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 02173172caac46a6bb3e586a95c6f8e1
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3c58d5bbaaf44a3dba31fe63ab32fd22
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,53 @@
#nullable enable
using System;
using ShrinkEventBus;
using Unity.Collections;
namespace ShrinkEventBus.Entities
{
/// <summary>Burst-safe producer queue; playback enters the regular Bus instance.</summary>
public sealed class ShrinkEcsEventQueue<TEvent> : IDisposable where TEvent : unmanaged, IShrinkEvent
{
private NativeQueue<TEvent> _queue;
public ShrinkEcsEventQueue(Allocator allocator = Allocator.Persistent)
{
_queue = new NativeQueue<TEvent>(allocator);
Writer = new ShrinkEcsEventWriter<TEvent>(_queue.AsParallelWriter());
}
public ShrinkEcsEventWriter<TEvent> Writer { get; }
public int Playback(IShrinkEventBus bus)
{
if (bus == null)
throw new ArgumentNullException(nameof(bus));
var count = 0;
while (_queue.TryDequeue(out var eventData))
{
bus.Post(in eventData);
count++;
}
return count;
}
public void Dispose()
{
if (_queue.IsCreated)
_queue.Dispose();
}
}
public readonly struct ShrinkEcsEventWriter<TEvent> where TEvent : unmanaged, IShrinkEvent
{
private readonly NativeQueue<TEvent>.ParallelWriter _writer;
internal ShrinkEcsEventWriter(NativeQueue<TEvent>.ParallelWriter writer)
{
_writer = writer;
}
public void Post(in TEvent eventData) => _writer.Enqueue(eventData);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 05a2db7ffefb4841bdcd63d8fd3ac6fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,13 @@
{
"name": "ShrinkEventBus.Entities.Runtime",
"rootNamespace": "ShrinkEventBus.Entities",
"references": [
"ShrinkEventBus.Runtime",
"Unity.Collections",
"Unity.Entities"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"autoReferenced": false
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9e31f017584a4a04992bb6fa7538cb2f
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 28ac62d48f4f4ba296c67b9b5985d39c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,76 @@
#nullable enable
using System;
using NUnit.Framework;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
namespace ShrinkEventBus.Entities.Tests
{
internal struct EcsPingEvent : IShrinkEvent
{
public int Value;
}
internal sealed class EcsPingSubscriber : IShrinkGeneratedSubscriber
{
public int Sum { get; private set; }
public IDisposable AttachGenerated(IShrinkBusResolver resolver, ShrinkBusKey? defaultBus = null)
{
return ShrinkGeneratedBinding.Subscribe<EcsPingEvent>(resolver, defaultBus,
string.Empty, this, OnPing, ShrinkEventPriority.Normal, 0, false);
}
private void OnPing(EcsPingEvent value) => Sum += value.Value;
}
[BurstCompile]
internal struct PostPingJob : IJob
{
public ShrinkEcsEventWriter<EcsPingEvent> Writer;
public void Execute()
{
Writer.Post(new EcsPingEvent { Value = 11 });
}
}
public sealed class ShrinkEcsEventQueueTests
{
[Test]
public void NativeWriterPlaybackUsesSameBusChannel()
{
using var host = new ShrinkEventBusHost();
var bus = host.CreateBus(ShrinkBusKey.World("tests"), ShrinkBusOptions.Inline());
var subscriber = new EcsPingSubscriber();
using var binding = host.Attach(subscriber, ShrinkBusKey.World("tests"));
using var queue = new ShrinkEcsEventQueue<EcsPingEvent>(Allocator.TempJob);
queue.Writer.Post(new EcsPingEvent { Value = 2 });
queue.Writer.Post(new EcsPingEvent { Value = 5 });
var played = queue.Playback(bus);
Assert.AreEqual(2, played);
Assert.AreEqual(7, subscriber.Sum);
}
[Test]
public void BurstJobWritesThenPlaybackUsesManagedBus()
{
using var host = new ShrinkEventBusHost();
var bus = host.CreateBus(ShrinkBusKey.World("burst-tests"), ShrinkBusOptions.Inline());
var subscriber = new EcsPingSubscriber();
using var binding = host.Attach(subscriber, ShrinkBusKey.World("burst-tests"));
using var queue = new ShrinkEcsEventQueue<EcsPingEvent>(Allocator.TempJob);
var job = new PostPingJob { Writer = queue.Writer };
job.Schedule().Complete();
var played = queue.Playback(bus);
Assert.AreEqual(1, played);
Assert.AreEqual(11, subscriber.Sum);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d3be15e44ff9405e947ecaaac7905527
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,20 @@
{
"name": "ShrinkEventBus.Entities.Tests",
"rootNamespace": "ShrinkEventBus.Entities.Tests",
"references": [
"ShrinkEventBus.Entities.Runtime",
"ShrinkEventBus.Runtime",
"Unity.Burst",
"Unity.Collections",
"Unity.Jobs",
"UniTask",
"UnityEngine.TestRunner",
"UnityEditor.TestRunner"
],
"includePlatforms": ["Editor"],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"autoReferenced": false,
"defineConstraints": ["UNITY_INCLUDE_TESTS"]
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9ef47a51870047fda3659747535d685a
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,16 @@
{
"name": "com.cneicy.shrink-eventbus-entities",
"version": "0.1.0",
"displayName": "ShrinkEventBus Entities",
"description": "Burst-safe NativeQueue writer and playback adapter for ShrinkEventBus.",
"unity": "2022.3",
"dependencies": {
"com.cneicy.shrink-eventbus": "2.0.0",
"com.unity.entities": "1.0.11"
},
"keywords": ["eventbus", "entities", "burst", "ecs"],
"author": {
"name": "cneicy",
"url": "https://github.com/cneicy"
}
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 908d154d9ac34f55817ba92f073b3aa8
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: