Read provider entity SoA views in Unity
This commit is contained in:
parent
ed0efa6589
commit
afd8c7156a
3 changed files with 153 additions and 0 deletions
|
|
@ -0,0 +1,102 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Linq;
|
||||
using GameCult.Eve.Surface;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GameCult.Eve.UnityScene
|
||||
{
|
||||
public sealed class EveUnityEntitySoaView : IDisposable
|
||||
{
|
||||
private readonly Dictionary<string, MemoryMappedViewAccessor> _buffers =
|
||||
new Dictionary<string, MemoryMappedViewAccessor>(StringComparer.Ordinal);
|
||||
private readonly Dictionary<string, EveEntitySoaColumn> _columns;
|
||||
|
||||
private EveUnityEntitySoaView(EveEntitySoaViewDocument document)
|
||||
{
|
||||
Document = document;
|
||||
_columns = document.Columns.ToDictionary(column => column.Semantic, StringComparer.Ordinal);
|
||||
foreach (var buffer in document.Buffers)
|
||||
{
|
||||
if (!string.Equals(buffer.Backend, "memory_mapped_file", StringComparison.Ordinal))
|
||||
throw new NotSupportedException($"EveUnity does not support entity SoA backend '{buffer.Backend}'.");
|
||||
var mapped = MemoryMappedFile.OpenExisting(buffer.Location, MemoryMappedFileRights.Read);
|
||||
_buffers.Add(buffer.BufferId, mapped.CreateViewAccessor(
|
||||
buffer.ByteOffset,
|
||||
buffer.ByteLength,
|
||||
MemoryMappedFileAccess.Read));
|
||||
mapped.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public EveEntitySoaViewDocument Document { get; }
|
||||
public long Generation => Document.Generation;
|
||||
public IReadOnlyList<EveEntityRenderGroup> RenderGroups => Document.RenderGroups;
|
||||
|
||||
public static EveUnityEntitySoaView Open(EveEntitySoaViewDocument document)
|
||||
{
|
||||
if (document == null) throw new ArgumentNullException(nameof(document));
|
||||
if (!string.Equals(document.Schema, EveEntitySoaViewDocument.SchemaId, StringComparison.Ordinal))
|
||||
throw new ArgumentException($"Expected {EveEntitySoaViewDocument.SchemaId}, got '{document.Schema}'.", nameof(document));
|
||||
return new EveUnityEntitySoaView(document);
|
||||
}
|
||||
|
||||
public bool TryReadVector3(string semantic, int index, out Vector3 value)
|
||||
{
|
||||
value = default;
|
||||
if (!TryColumn(semantic, index, "float32", 12, out var column, out var accessor)) return false;
|
||||
var offset = column.ByteOffset + (long)index * column.ElementStride;
|
||||
value = new Vector3(accessor.ReadSingle(offset), accessor.ReadSingle(offset + 4), accessor.ReadSingle(offset + 8));
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryReadFloat(string semantic, int index, out float value)
|
||||
{
|
||||
value = default;
|
||||
if (!TryColumn(semantic, index, "float32", 4, out var column, out var accessor)) return false;
|
||||
value = accessor.ReadSingle(column.ByteOffset + (long)index * column.ElementStride);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryReadInt32(string semantic, int index, out int value)
|
||||
{
|
||||
value = default;
|
||||
if (!TryColumn(semantic, index, "int32", 4, out var column, out var accessor)) return false;
|
||||
value = accessor.ReadInt32(column.ByteOffset + (long)index * column.ElementStride);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryReadByte(string semantic, int index, out byte value)
|
||||
{
|
||||
value = default;
|
||||
if (!TryColumn(semantic, index, "uint8", 1, out var column, out var accessor)) return false;
|
||||
value = accessor.ReadByte(column.ByteOffset + (long)index * column.ElementStride);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var accessor in _buffers.Values) accessor.Dispose();
|
||||
_buffers.Clear();
|
||||
}
|
||||
|
||||
private bool TryColumn(
|
||||
string semantic,
|
||||
int index,
|
||||
string scalarType,
|
||||
int minimumStride,
|
||||
out EveEntitySoaColumn column,
|
||||
out MemoryMappedViewAccessor accessor)
|
||||
{
|
||||
column = null!;
|
||||
accessor = null!;
|
||||
if (index < 0 || !_columns.TryGetValue(semantic ?? "", out column)) return false;
|
||||
if (index >= column.ElementCount || column.ElementStride < minimumStride) return false;
|
||||
if (!string.Equals(column.ScalarType, scalarType, StringComparison.Ordinal)) return false;
|
||||
return _buffers.TryGetValue(column.BufferId, out accessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 14693135dfca4740b967148a449cf182
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using GameCult.Eve.Surface;
|
||||
using GameCult.Mesh;
|
||||
using NUnit.Framework;
|
||||
|
|
@ -11,6 +12,45 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
{
|
||||
public sealed class EveUnitySceneSurfaceLowererTests
|
||||
{
|
||||
[Test]
|
||||
public void EntitySoaViewReadsGenericSemanticColumnsFromAdvertisedBackend()
|
||||
{
|
||||
var location = $"eve-unity-soa-{Guid.NewGuid():N}";
|
||||
using var mapped = MemoryMappedFile.CreateNew(location, 32);
|
||||
using (var writer = mapped.CreateViewAccessor())
|
||||
{
|
||||
writer.Write(0, 4f);
|
||||
writer.Write(4, 5f);
|
||||
writer.Write(8, 6f);
|
||||
writer.Write(12, 7f);
|
||||
writer.Write(16, 8f);
|
||||
writer.Write(20, 9f);
|
||||
writer.Write(24, 41);
|
||||
writer.Write(28, 42);
|
||||
}
|
||||
var document = new EveEntitySoaViewDocument
|
||||
{
|
||||
ProviderId = "provider",
|
||||
ViewId = "pilot",
|
||||
Generation = 7,
|
||||
Backend = "memory_mapped_file",
|
||||
Buffers = new[] { new EveEntitySoaBuffer { BufferId = "hot", Backend = "memory_mapped_file", Location = location, ByteLength = 32, Generation = 7 } },
|
||||
Columns = new[]
|
||||
{
|
||||
new EveEntitySoaColumn { ColumnId = "position", Semantic = "transform.position", BufferId = "hot", ScalarType = "float32", ElementStride = 12, ElementCount = 2 },
|
||||
new EveEntitySoaColumn { ColumnId = "entity-index", Semantic = "entity.index", BufferId = "hot", ScalarType = "int32", ByteOffset = 24, ElementStride = 4, ElementCount = 2 }
|
||||
}
|
||||
};
|
||||
|
||||
using var view = EveUnityEntitySoaView.Open(document);
|
||||
|
||||
Assert.That(view.TryReadVector3("transform.position", 1, out var position), Is.True);
|
||||
Assert.That(position, Is.EqualTo(new Vector3(7f, 8f, 9f)));
|
||||
Assert.That(view.TryReadInt32("entity.index", 0, out var entityIndex), Is.True);
|
||||
Assert.That(entityIndex, Is.EqualTo(41));
|
||||
Assert.That(view.Generation, Is.EqualTo(7));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LowerCarriesProviderAdvertisedWorldBoundary()
|
||||
{
|
||||
|
|
|
|||
Reference in a new issue