Closed KageKirin closed 2 years ago
Addendum: full disclosure, the whole idea is to have a ScriptableObject
to hold the buffer data , and a custom Editor pane to display it in the Inspector pane. Since serialization is fast, it won't be a too big performance bottleneck even if running serialization/deserialization 10x/second (editor refresh rate is 10 FPS).
ScriptableObject
using System;
using System.Linq;
using System.Collections.Generic;
using FlatSharp;
using FlatSharp.Runtime;
using UnityEngine;
using UnityEditor;
#nullable enable
namespace FlatBuffersTest
{
[Serializable]
[CreateAssetMenu(
fileName = "PersonScriptableObject",
menuName = "Test Stuff/Person (SO)",
order = 10
)]
public class PersonScriptableObject : ScriptableObject, ISerializationCallbackReceiver
{
[SerializeField]
[HideInInspector]
private byte[] buffer;
public Person? person;
public void OnBeforeSerialize()
{
if (person != null)
{
int maxBytesNeeded = FlatBufferSerializer.Default.GetMaxSize(person);
buffer = new byte[maxBytesNeeded];
int bytesWritten = FlatBufferSerializer.Default.Serialize(person, buffer);
}
}
public void OnAfterDeserialize()
{
if (buffer != null && buffer.Length != 0)
{
person = FlatBufferSerializer.Default.Parse<Person>(buffer);
}
}
}
}
Editor
using System;
using System.Linq;
using System.Collections.Generic;
using FlatSharp;
using FlatSharp.Runtime;
using FlatSharp.Attributes;
using UnityEngine;
using UnityEditor;
#nullable enable
namespace FlatBuffersTest
{
public class PersonScriptableObjectEditor : Editor
{
public override void OnInspectorGUI()
{
base.DrawDefaultInspector();
PersonScriptableObject personObject = (PersonScriptableObject)target;
if (personObject == null)
return;
if (personObject.person == null)
{
// show button
if (GUILayout.Button("create Person"))
{
personObject.person = new Person();
Repaint();
}
return;
}
personObject.person.Id = EditorGUILayout.IntField("ID", personObject.person.Id);
personObject.person.Name = EditorGUILayout.TextField("Name", personObject.person.Name);
}
}
}
Hello -- Thank you for the detailed report. Unfortunately, I am not a game developer and have no experience with Unity. I know there are periodically Unity issues opened in this repo, but most people seem to get FlatSharp working with a little trial and error.
While I'm not familiar with ScriptableObject
, I can tell from your report that using FlatBufferSerializer
will not work with Unity. The reason for this is that FlatBufferSerializer
uses reflection and Roslyn to generate, compile, and load a DLL at runtime. You can find more details about usage modes here. But that sort of thing won't work with Unity, and I try to discourage people from using it even with Runtime mode at this point.
So, what you need to do is add FlatSharp annotations to your existing schema. Full annotations are documented here. The short version is that you need to tell the FlatSharp compiler that you want a Serializer generated for the Person
table. Then you can access the serializer with Person.Serializer
static property.
Hope that helps. By the way -- I really welcome contributions. If you could suggest detail to add to the wiki that could help other Unity users or publish some Unity-specific packages, I would really appreciate it!
Hi, thanks for your answer and for pointing me to the core of the issue: I forgot adding the flatsharp attribute annotations in my schema.
So, with said annotations, this gives the following schema:
namespace FlatBuffersTest;
attribute "fs_serializer";
attribute "fs_vector";
enum Color : ubyte { Red = 1, Green, Blue }
table Person (fs_serializer:"greedymutable") {
Id:int;
Name:string;
Children:[Person] (fs_vector:"IList");
FavoriteColor:Color = Blue;
Position:Location;
}
struct Location {
Latitude:float;
Longitude:float;
}
root_type Person;
which yields the following generated C# code
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by the FlatSharp FBS to C# compiler (source hash: 6.3.1.IxNPzqpQwK5z5aoARs7nt/c9G78pPMsiPtzr6Wa2PJs=)
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using FlatSharp;
using FlatSharp.Attributes;
using FlatSharp.Internal;
#pragma warning disable 0618
#nullable enable annotations
namespace FlatSharp.Compiler.Generated
{
internal static class CloneHelpers_900f6359359347f59c851f6e8435b98f
{
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::FlatBuffersTest.Color Clone(global::FlatBuffersTest.Color item)
{
checked
{
return item;
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::System.Byte Clone(global::System.Byte item)
{
checked
{
return item;
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::FlatBuffersTest.Location? Clone(global::FlatBuffersTest.Location? item)
{
checked
{
return item is null ? null : new FlatBuffersTest.Location(item);
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::System.Single Clone(global::System.Single item)
{
checked
{
return item;
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::FlatBuffersTest.Person? Clone(global::FlatBuffersTest.Person? item)
{
checked
{
return item is null ? null : new FlatBuffersTest.Person(item);
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::System.Int32 Clone(global::System.Int32 item)
{
checked
{
return item;
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::System.String? Clone(global::System.String? item)
{
checked
{
return item;
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::System.Collections.Generic.IList<FlatBuffersTest.Person>? Clone(global::System.Collections.Generic.IList<FlatBuffersTest.Person>? item)
{
checked
{
return VectorCloneHelpers.Clone<FlatBuffersTest.Person>(item, FlatSharp.Compiler.Generated.CloneHelpers_900f6359359347f59c851f6e8435b98f.Clone);
}
}
}
}
namespace FlatBuffersTest
{
[FlatBufferEnum(typeof(byte))]
public enum Color : byte
{
Red = 1,
Green = 2,
Blue = 3,
}
}
namespace FlatBuffersTest
{
[FlatBufferStruct]
[System.Runtime.CompilerServices.CompilerGenerated]
public partial class Location
{
#pragma warning disable CS8618
public Location()
{
checked
{
this.OnInitialized(null);
}
}
#pragma warning restore CS8618
#pragma warning disable CS8618
protected Location(FlatBufferDeserializationContext context)
{
checked
{
}
}
#pragma warning restore CS8618
public Location(Location source)
{
checked
{
this.Latitude = FlatSharp.Compiler.Generated.CloneHelpers_900f6359359347f59c851f6e8435b98f.Clone(source.Latitude);
this.Longitude = FlatSharp.Compiler.Generated.CloneHelpers_900f6359359347f59c851f6e8435b98f.Clone(source.Longitude);
this.OnInitialized(null);
}
}
partial void OnInitialized(FlatBufferDeserializationContext? context);
protected void OnFlatSharpDeserialized(FlatBufferDeserializationContext? context) => this.OnInitialized(context);
[FlatBufferItemAttribute(0)]
public virtual float Latitude { get; set; }
[FlatBufferItemAttribute(1)]
public virtual float Longitude { get; set; }
}
}
namespace FlatBuffersTest
{
[FlatBufferTable()]
[System.Runtime.CompilerServices.CompilerGenerated]
public partial class Person
: object
, IFlatBufferSerializable<FlatBuffersTest.Person>
{
#pragma warning disable CS8618
public Person()
{
checked
{
this.Id = default!;
this.Name = default!;
this.Children = default!;
this.FavoriteColor = (FlatBuffersTest.Color)3;
this.Position = default!;
this.OnInitialized(null);
}
}
#pragma warning restore CS8618
#pragma warning disable CS8618
protected Person(FlatBufferDeserializationContext context)
{
checked
{
}
}
#pragma warning restore CS8618
public Person(Person source)
{
checked
{
this.Id = FlatSharp.Compiler.Generated.CloneHelpers_900f6359359347f59c851f6e8435b98f.Clone(source.Id);
this.Name = FlatSharp.Compiler.Generated.CloneHelpers_900f6359359347f59c851f6e8435b98f.Clone(source.Name);
this.Children = FlatSharp.Compiler.Generated.CloneHelpers_900f6359359347f59c851f6e8435b98f.Clone(source.Children);
this.FavoriteColor = FlatSharp.Compiler.Generated.CloneHelpers_900f6359359347f59c851f6e8435b98f.Clone(source.FavoriteColor);
this.Position = FlatSharp.Compiler.Generated.CloneHelpers_900f6359359347f59c851f6e8435b98f.Clone(source.Position);
this.OnInitialized(null);
}
}
partial void OnInitialized(FlatBufferDeserializationContext? context);
protected void OnFlatSharpDeserialized(FlatBufferDeserializationContext? context) => this.OnInitialized(context);
[FlatBufferItemAttribute(0)]
public virtual int Id { get; set; }
[FlatBufferItemAttribute(1)]
public virtual string? Name { get; set; }
[FlatBufferItemAttribute(2)]
public virtual IList<FlatBuffersTest.Person>? Children { get; set; }
[FlatBufferItemAttribute(3, DefaultValue = (FlatBuffersTest.Color)3)]
public virtual FlatBuffersTest.Color FavoriteColor { get; set; }
[FlatBufferItemAttribute(4)]
public virtual FlatBuffersTest.Location? Position { get; set; }
public static ISerializer<FlatBuffersTest.Person> Serializer { get; } = new GeneratedSerializer().AsISerializer();
ISerializer IFlatBufferSerializable.Serializer => Serializer;
ISerializer<FlatBuffersTest.Person> IFlatBufferSerializable<FlatBuffersTest.Person>.Serializer => Serializer;
#region Serializer for FlatBuffersTest.Person
[FlatSharpGeneratedSerializerAttribute(FlatBufferDeserializationOption.GreedyMutable)]
private sealed class GeneratedSerializer : IGeneratedSerializer<global::FlatBuffersTest.Person>
{
// Method generated to help AOT compilers make good decisions about generics.
public void __AotHelper()
{
checked
{
this.Write<ISpanWriter>(default!, new byte[10], default!, default!, default!);
this.Write<SpanWriter>(default!, new byte[10], default!, default!, default!);
this.Parse<IInputBuffer>(default!, default);
this.Parse<IInputBuffer2>(default!, default);
this.Parse<MemoryInputBuffer>(default!, default);
this.Parse<ReadOnlyMemoryInputBuffer>(default!, default);
this.Parse<ArrayInputBuffer>(default!, default);
this.Parse<ArraySegmentInputBuffer>(default!, default);
throw new InvalidOperationException("__AotHelper is not intended to be invoked");
}
}
public GeneratedSerializer()
{
checked
{
string? runtimeVersion = System.Reflection.CustomAttributeExtensions.GetCustomAttribute<System.Reflection.AssemblyFileVersionAttribute>(typeof(SpanWriter).Assembly)?.Version;
string compilerVersion = "6.3.1";
if (runtimeVersion != compilerVersion)
{
throw new InvalidOperationException($"FlatSharp runtime version didn't match compiler version. Ensure all FlatSharp NuGet packages use the same version. Runtime = '{runtimeVersion}', Compiler = '{compilerVersion}'.");
}
if (string.IsNullOrEmpty(runtimeVersion))
{
throw new InvalidOperationException($"Unable to find FlatSharp.Runtime version. Ensure all FlatSharp NuGet packages use the same version. Runtime = '{runtimeVersion}', Compiler = '{compilerVersion}'.");
}
}
}
public FlatSharp.FlatBufferDeserializationOption DeserializationOption => FlatSharp.FlatBufferDeserializationOption.GreedyMutable;
public void Write<TSpanWriter>(TSpanWriter writer, Span<byte> target, global::FlatBuffersTest.Person root, int offset, SerializationContext context)
where TSpanWriter : ISpanWriter
{
checked
{
WriteInlineValueOf_70e4f98aa3a64f6e89ee8fff709f79c0(writer, target, root, offset, context);
}
}
public int GetMaxSize(global::FlatBuffersTest.Person root)
{
checked
{
return GetMaxSizeOf_70e4f98aa3a64f6e89ee8fff709f79c0(root);
}
}
public global::FlatBuffersTest.Person Parse<TInputBuffer>(TInputBuffer buffer, in global::FlatSharp.Internal.GeneratedSerializerParseArguments args)
where TInputBuffer : IInputBuffer
{
checked
{
return Read_70e4f98aa3a64f6e89ee8fff709f79c0(buffer, args.Offset, args.DepthLimit);
}
}
private static int GetMaxSizeOf_70e4f98aa3a64f6e89ee8fff709f79c0(global::FlatBuffersTest.Person value)
{
checked
{
int runningSum = 40 + 15;
var index1Value = value.Name;
if (!(index1Value is null))
{
runningSum += GetMaxSizeOf_9fae28dc18494d9d8ce26f3a38f4a5a1(index1Value);
}
var index2Value = value.Children;
if (!(index2Value is null))
{
runningSum += GetMaxSizeOf_75b89f1f0bb34630b0eede644353c404(index2Value);
}
return runningSum;
}
}
private static global::FlatBuffersTest.Person Read_70e4f98aa3a64f6e89ee8fff709f79c0<TInputBuffer>(
TInputBuffer buffer,
int offset,
short remainingDepth
) where TInputBuffer : IInputBuffer
{
checked
{
--remainingDepth;
global::FlatSharp.Internal.SerializationHelpers.EnsureDepthLimit(remainingDepth);
return tableReader_e03866d22f6642e194bc6e8beee45c19<TInputBuffer>.GetOrCreate(buffer, offset + buffer.ReadUOffset(offset), remainingDepth);
}
}
private sealed class tableReader_e03866d22f6642e194bc6e8beee45c19<TInputBuffer>
: global::FlatBuffersTest.Person
, global::FlatSharp.IFlatBufferDeserializedObject
where TInputBuffer : IInputBuffer
{
private static readonly global::FlatSharp.FlatBufferDeserializationContext __CtorContext
= new global::FlatSharp.FlatBufferDeserializationContext(global::FlatSharp.FlatBufferDeserializationOption.GreedyMutable);
private System.Int32 __index0Value;
private System.String? __index1Value;
private System.Collections.Generic.IList<FlatBuffersTest.Person>? __index2Value;
private FlatBuffersTest.Color __index3Value;
private FlatBuffersTest.Location? __index4Value;
public static tableReader_e03866d22f6642e194bc6e8beee45c19<TInputBuffer> GetOrCreate(TInputBuffer buffer, int offset, short remainingDepth)
{
checked
{
var item = new tableReader_e03866d22f6642e194bc6e8beee45c19<TInputBuffer>(buffer, offset, remainingDepth);
return item;
}
}
private tableReader_e03866d22f6642e194bc6e8beee45c19(TInputBuffer buffer, int offset, short remainingDepth) : base(__CtorContext)
{
checked
{
VTable8.Create<TInputBuffer>(buffer, offset, out var vtable);
this.__index0Value = ReadIndex0Value(buffer, offset, vtable, remainingDepth);
this.__index1Value = ReadIndex1Value(buffer, offset, vtable, remainingDepth);
this.__index2Value = ReadIndex2Value(buffer, offset, vtable, remainingDepth);
this.__index3Value = ReadIndex3Value(buffer, offset, vtable, remainingDepth);
this.__index4Value = ReadIndex4Value(buffer, offset, vtable, remainingDepth);
base.OnFlatSharpDeserialized(__CtorContext);
}
}
global::System.Type global::FlatSharp.IFlatBufferDeserializedObject.TableOrStructType => typeof(FlatBuffersTest.Person);
global::FlatSharp.FlatBufferDeserializationContext global::FlatSharp.IFlatBufferDeserializedObject.DeserializationContext => __CtorContext;
global::FlatSharp.IInputBuffer? global::FlatSharp.IFlatBufferDeserializedObject.InputBuffer => null;
bool global::FlatSharp.IFlatBufferDeserializedObject.CanSerializeWithMemoryCopy => false;
public override System.Int32 Id
{
get
{
checked
{
return this.__index0Value;
}
}
set
{
checked
{
this.__index0Value = value;
}
}
}
public override System.String? Name
{
get
{
checked
{
return this.__index1Value;
}
}
set
{
checked
{
this.__index1Value = value;
}
}
}
public override System.Collections.Generic.IList<FlatBuffersTest.Person>? Children
{
get
{
checked
{
return this.__index2Value;
}
}
set
{
checked
{
this.__index2Value = value;
}
}
}
public override FlatBuffersTest.Color FavoriteColor
{
get
{
checked
{
return this.__index3Value;
}
}
set
{
checked
{
this.__index3Value = value;
}
}
}
public override FlatBuffersTest.Location? Position
{
get
{
checked
{
return this.__index4Value;
}
}
set
{
checked
{
this.__index4Value = value;
}
}
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static System.Int32 ReadIndex0Value(
TInputBuffer buffer,
int offset,
VTable8 vtable,
short remainingDepth)
{
checked
{
int absoluteLocation;
{
int relativeOffset = vtable.OffsetOf(buffer, 0);
if (relativeOffset == 0)
{
return default(System.Int32);
}
absoluteLocation = offset + relativeOffset;
}
return Read_abcaf9800ed64c59ac5d21900c30636a(buffer, absoluteLocation, remainingDepth);
}
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static System.String? ReadIndex1Value(
TInputBuffer buffer,
int offset,
VTable8 vtable,
short remainingDepth)
{
checked
{
int absoluteLocation;
{
int relativeOffset = vtable.OffsetOf(buffer, 1);
if (relativeOffset == 0)
{
return null;
}
absoluteLocation = offset + relativeOffset;
}
return Read_9fae28dc18494d9d8ce26f3a38f4a5a1(buffer, absoluteLocation, remainingDepth);
}
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static System.Collections.Generic.IList<FlatBuffersTest.Person>? ReadIndex2Value(
TInputBuffer buffer,
int offset,
VTable8 vtable,
short remainingDepth)
{
checked
{
int absoluteLocation;
{
int relativeOffset = vtable.OffsetOf(buffer, 2);
if (relativeOffset == 0)
{
return null;
}
absoluteLocation = offset + relativeOffset;
}
return Read_75b89f1f0bb34630b0eede644353c404(buffer, absoluteLocation, remainingDepth, tableMetadata_bb10e9d63954422f906e83ca23c84385.Children);
}
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static FlatBuffersTest.Color ReadIndex3Value(
TInputBuffer buffer,
int offset,
VTable8 vtable,
short remainingDepth)
{
checked
{
int absoluteLocation;
{
int relativeOffset = vtable.OffsetOf(buffer, 3);
if (relativeOffset == 0)
{
return (FlatBuffersTest.Color)(System.Byte)(3);
}
absoluteLocation = offset + relativeOffset;
}
return Read_95a3bf49b47c4da1afe8f5ec820895f7(buffer, absoluteLocation, remainingDepth);
}
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static FlatBuffersTest.Location? ReadIndex4Value(
TInputBuffer buffer,
int offset,
VTable8 vtable,
short remainingDepth)
{
checked
{
int absoluteLocation;
{
int relativeOffset = vtable.OffsetOf(buffer, 4);
if (relativeOffset == 0)
{
return null;
}
absoluteLocation = offset + relativeOffset;
}
return Read_af2ad7e12e0d40ae9db2e80ee3a39307(buffer, absoluteLocation, remainingDepth);
}
}
}
private static void WriteInlineValueOf_70e4f98aa3a64f6e89ee8fff709f79c0<TSpanWriter>(
TSpanWriter spanWriter,
Span<byte> span,
global::FlatBuffersTest.Person value,
int offset
, SerializationContext context
) where TSpanWriter : ISpanWriter
{
checked
{
int tableStart = context.AllocateSpace(37, sizeof(int));
spanWriter.WriteUOffset(span, offset, tableStart);
int currentOffset = tableStart + sizeof(int); // skip past vtable soffset_t.
int vtableLength = 4;
Span<byte> vtable = stackalloc byte[14];
var index0Value = value.Id;
var index1Value = value.Name;
var index2Value = value.Children;
var index3Value = value.FavoriteColor;
var index4Value = value.Position;
var index2Offset = tableStart;
if (!(index2Value is null))
{
currentOffset += SerializationHelpers.GetAlignmentError(currentOffset, 4);
index2Offset = currentOffset;
currentOffset += 4;
if (10 > vtableLength)
{
vtableLength = 10;
}
}
spanWriter.WriteUShort(vtable, (ushort)(index2Offset - tableStart), 8);
var index1Offset = tableStart;
if (!(index1Value is null))
{
currentOffset += SerializationHelpers.GetAlignmentError(currentOffset, 4);
index1Offset = currentOffset;
currentOffset += 4;
if (8 > vtableLength)
{
vtableLength = 8;
}
}
spanWriter.WriteUShort(vtable, (ushort)(index1Offset - tableStart), 6);
var index0Offset = tableStart;
if (index0Value != default(System.Int32))
{
currentOffset += SerializationHelpers.GetAlignmentError(currentOffset, 4);
index0Offset = currentOffset;
currentOffset += 4;
WriteInlineValueOf_abcaf9800ed64c59ac5d21900c30636a(spanWriter, span, index0Value, index0Offset);
if (6 > vtableLength)
{
vtableLength = 6;
}
}
spanWriter.WriteUShort(vtable, (ushort)(index0Offset - tableStart), 4);
var index4Offset = tableStart;
if (!(index4Value is null))
{
currentOffset += SerializationHelpers.GetAlignmentError(currentOffset, 4);
index4Offset = currentOffset;
currentOffset += 8;
WriteInlineValueOf_af2ad7e12e0d40ae9db2e80ee3a39307(spanWriter, span, index4Value!, index4Offset);
vtableLength = 14;
}
spanWriter.WriteUShort(vtable, (ushort)(index4Offset - tableStart), 12);
var index3Offset = tableStart;
if (index3Value != (FlatBuffersTest.Color)(System.Byte)(3))
{
currentOffset += SerializationHelpers.GetAlignmentError(currentOffset, 1);
index3Offset = currentOffset;
currentOffset += 1;
WriteInlineValueOf_95a3bf49b47c4da1afe8f5ec820895f7(spanWriter, span, index3Value, index3Offset);
if (12 > vtableLength)
{
vtableLength = 12;
}
}
spanWriter.WriteUShort(vtable, (ushort)(index3Offset - tableStart), 10);
int tableLength = currentOffset - tableStart;
context.Offset -= 37 - tableLength;
spanWriter.WriteUShort(vtable, (ushort)vtableLength, 0);
spanWriter.WriteUShort(vtable, (ushort)tableLength, sizeof(ushort));
int vtablePosition = context.FinishVTable(span, vtable.Slice(0, vtableLength));
spanWriter.WriteInt(span, tableStart - vtablePosition, tableStart);
if (index2Offset != tableStart)
{
WriteInlineValueOf_75b89f1f0bb34630b0eede644353c404(spanWriter, span, index2Value!, index2Offset, context);
}
if (index1Offset != tableStart)
{
WriteInlineValueOf_9fae28dc18494d9d8ce26f3a38f4a5a1(spanWriter, span, index1Value!, index1Offset, context, tableMetadata_bb10e9d63954422f906e83ca23c84385.Name);
}
}
}
private static class tableMetadata_bb10e9d63954422f906e83ca23c84385
{
public static readonly TableFieldContext Id = new TableFieldContext(
"FlatBuffersTest.Person.Id",
false,
false);
public static readonly TableFieldContext Name = new TableFieldContext(
"FlatBuffersTest.Person.Name",
false,
false);
public static readonly TableFieldContext Children = new TableFieldContext(
"FlatBuffersTest.Person.Children",
false,
false);
public static readonly TableFieldContext FavoriteColor = new TableFieldContext(
"FlatBuffersTest.Person.FavoriteColor",
false,
false);
public static readonly TableFieldContext Position = new TableFieldContext(
"FlatBuffersTest.Person.Position",
false,
false);
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static int GetMaxSizeOf_abcaf9800ed64c59ac5d21900c30636a(global::System.Int32 value)
{
checked
{
return 7;
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static global::System.Int32 Read_abcaf9800ed64c59ac5d21900c30636a<TInputBuffer>(
TInputBuffer buffer,
int offset,
short remainingDepth
) where TInputBuffer : IInputBuffer
{
checked
{
--remainingDepth;
global::FlatSharp.Internal.SerializationHelpers.EnsureDepthLimit(remainingDepth);
return buffer.ReadInt(offset);
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static void WriteInlineValueOf_abcaf9800ed64c59ac5d21900c30636a<TSpanWriter>(
TSpanWriter spanWriter,
Span<byte> span,
global::System.Int32 value,
int offset
) where TSpanWriter : ISpanWriter
{
checked
{
spanWriter.WriteInt(span, value, offset);
}
}
private static int GetMaxSizeOf_9fae28dc18494d9d8ce26f3a38f4a5a1(global::System.String value)
{
checked
{
return SerializationHelpers.GetMaxSize(value);
}
}
private static global::System.String Read_9fae28dc18494d9d8ce26f3a38f4a5a1<TInputBuffer>(
TInputBuffer buffer,
int offset,
short remainingDepth
) where TInputBuffer : IInputBuffer
{
checked
{
--remainingDepth;
global::FlatSharp.Internal.SerializationHelpers.EnsureDepthLimit(remainingDepth);
return buffer.ReadString(offset);
}
}
private static void WriteInlineValueOf_9fae28dc18494d9d8ce26f3a38f4a5a1<TSpanWriter>(
TSpanWriter spanWriter,
Span<byte> span,
global::System.String value,
int offset
, SerializationContext context
, TableFieldContext fieldContext) where TSpanWriter : ISpanWriter
{
checked
{
spanWriter.WriteString(
span,
value,
offset,
context);
}
}
private static int GetMaxSizeOf_75b89f1f0bb34630b0eede644353c404(global::System.Collections.Generic.IList<FlatBuffersTest.Person> value)
{
checked
{
int count = value.Count;
int runningSum = 14;
if (value is FlatBuffersTest.Person[] array)
{
for (int i = 0; i < array.Length; i = unchecked(i + 1))
{
var current = array[i];
SerializationHelpers.EnsureNonNull(current);
runningSum += GetMaxSizeOf_70e4f98aa3a64f6e89ee8fff709f79c0(current);
}
}
else if (value is List<FlatBuffersTest.Person> realList)
{
int i;
for (i = 0; i < count; i = unchecked(i + 1))
{
var current = realList[i];
SerializationHelpers.EnsureNonNull(current);
runningSum += GetMaxSizeOf_70e4f98aa3a64f6e89ee8fff709f79c0(current);
}
}
else
{
int i;
for (i = 0; i < count; i = unchecked(i + 1))
{
var current = value[i];
SerializationHelpers.EnsureNonNull(current);
runningSum += GetMaxSizeOf_70e4f98aa3a64f6e89ee8fff709f79c0(current);
}
}
return runningSum;
}
}
private static global::System.Collections.Generic.IList<FlatBuffersTest.Person> Read_75b89f1f0bb34630b0eede644353c404<TInputBuffer>(
TInputBuffer buffer,
int offset,
short remainingDepth
, TableFieldContext fieldContext) where TInputBuffer : IInputBuffer
{
checked
{
--remainingDepth;
global::FlatSharp.Internal.SerializationHelpers.EnsureDepthLimit(remainingDepth);
return (new FlatBufferVector_954898d4a19447b9844e968e5748f7ad<TInputBuffer>(
buffer,
offset + buffer.ReadUOffset(offset),
4,
remainingDepth,
fieldContext)).FlatBufferVectorToList();
}
}
public sealed class FlatBufferVector_954898d4a19447b9844e968e5748f7ad<TInputBuffer> : FlatBufferVector<global::FlatBuffersTest.Person, TInputBuffer>
where TInputBuffer : IInputBuffer
{
public FlatBufferVector_954898d4a19447b9844e968e5748f7ad(
TInputBuffer memory,
int offset,
int itemSize,
short remainingDepth,
TableFieldContext fieldContext) : base(memory, offset, itemSize, remainingDepth, fieldContext)
{
checked
{
}
}
protected override void ParseItem(
TInputBuffer memory,
int offset,
short remainingDepth,
TableFieldContext fieldContext,
out global::FlatBuffersTest.Person item)
{
checked
{
item = Read_70e4f98aa3a64f6e89ee8fff709f79c0(memory, offset, remainingDepth);
}
}
protected override void WriteThrough(global::FlatBuffersTest.Person item, Span<byte> data)
{
checked
{
throw new NotSupportedException("write through is not available for this type");
}
}
}
private static void WriteInlineValueOf_75b89f1f0bb34630b0eede644353c404<TSpanWriter>(
TSpanWriter spanWriter,
Span<byte> span,
global::System.Collections.Generic.IList<FlatBuffersTest.Person> value,
int offset
, SerializationContext context
) where TSpanWriter : ISpanWriter
{
checked
{
int count = value.Count;
int vectorOffset = context.AllocateVector(4, count, 4);
spanWriter.WriteUOffset(span, offset, vectorOffset);
spanWriter.WriteInt(span, count, vectorOffset);
vectorOffset += sizeof(int);
if (value is FlatBuffersTest.Person[] array)
{
for (int i = 0; i < array.Length; i = unchecked(i + 1))
{
var current = array[i];
SerializationHelpers.EnsureNonNull(current);
WriteInlineValueOf_70e4f98aa3a64f6e89ee8fff709f79c0(spanWriter, span, current, vectorOffset, context);
vectorOffset += 4;
}
}
else if (value is List<FlatBuffersTest.Person> realList)
{
int i;
for (i = 0; i < count; i = unchecked(i + 1))
{
var current = realList[i];
SerializationHelpers.EnsureNonNull(current);
WriteInlineValueOf_70e4f98aa3a64f6e89ee8fff709f79c0(spanWriter, span, current, vectorOffset, context);
vectorOffset += 4;
}
}
else
{
int i;
for (i = 0; i < count; i = unchecked(i + 1))
{
var current = value[i];
SerializationHelpers.EnsureNonNull(current);
WriteInlineValueOf_70e4f98aa3a64f6e89ee8fff709f79c0(spanWriter, span, current, vectorOffset, context);
vectorOffset += 4;
}
}
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static int GetMaxSizeOf_95a3bf49b47c4da1afe8f5ec820895f7(global::FlatBuffersTest.Color value)
{
checked
{
return GetMaxSizeOf_2a5c716f3f5d4e4b9d9ce52648731830((global::System.Byte)value);
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static global::FlatBuffersTest.Color Read_95a3bf49b47c4da1afe8f5ec820895f7<TInputBuffer>(
TInputBuffer buffer,
int offset,
short remainingDepth
) where TInputBuffer : IInputBuffer
{
checked
{
--remainingDepth;
global::FlatSharp.Internal.SerializationHelpers.EnsureDepthLimit(remainingDepth);
return (FlatBuffersTest.Color)Read_2a5c716f3f5d4e4b9d9ce52648731830(buffer, offset, remainingDepth);
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static void WriteInlineValueOf_95a3bf49b47c4da1afe8f5ec820895f7<TSpanWriter>(
TSpanWriter spanWriter,
Span<byte> span,
global::FlatBuffersTest.Color value,
int offset
) where TSpanWriter : ISpanWriter
{
checked
{
WriteInlineValueOf_2a5c716f3f5d4e4b9d9ce52648731830(spanWriter, span, (global::System.Byte)value, offset);
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static int GetMaxSizeOf_af2ad7e12e0d40ae9db2e80ee3a39307(global::FlatBuffersTest.Location value)
{
checked
{
return 11;
}
}
private static global::FlatBuffersTest.Location Read_af2ad7e12e0d40ae9db2e80ee3a39307<TInputBuffer>(
TInputBuffer buffer,
int offset,
short remainingDepth
) where TInputBuffer : IInputBuffer
{
checked
{
--remainingDepth;
global::FlatSharp.Internal.SerializationHelpers.EnsureDepthLimit(remainingDepth);
return structReader_03e35aa35ff942ca938cdcb8d7fd0cdf<TInputBuffer>.GetOrCreate(buffer, offset, remainingDepth);
}
}
private sealed class structReader_03e35aa35ff942ca938cdcb8d7fd0cdf<TInputBuffer>
: global::FlatBuffersTest.Location
, global::FlatSharp.IFlatBufferDeserializedObject
where TInputBuffer : IInputBuffer
{
private static readonly global::FlatSharp.FlatBufferDeserializationContext __CtorContext
= new global::FlatSharp.FlatBufferDeserializationContext(global::FlatSharp.FlatBufferDeserializationOption.GreedyMutable);
private System.Single __index0Value;
private System.Single __index1Value;
public static structReader_03e35aa35ff942ca938cdcb8d7fd0cdf<TInputBuffer> GetOrCreate(TInputBuffer buffer, int offset, short remainingDepth)
{
checked
{
var item = new structReader_03e35aa35ff942ca938cdcb8d7fd0cdf<TInputBuffer>(buffer, offset, remainingDepth);
return item;
}
}
private structReader_03e35aa35ff942ca938cdcb8d7fd0cdf(TInputBuffer buffer, int offset, short remainingDepth) : base(__CtorContext)
{
checked
{
this.__index0Value = ReadIndex0Value(buffer, offset, default, remainingDepth);
this.__index1Value = ReadIndex1Value(buffer, offset, default, remainingDepth);
base.OnFlatSharpDeserialized(__CtorContext);
}
}
global::System.Type global::FlatSharp.IFlatBufferDeserializedObject.TableOrStructType => typeof(FlatBuffersTest.Location);
global::FlatSharp.FlatBufferDeserializationContext global::FlatSharp.IFlatBufferDeserializedObject.DeserializationContext => __CtorContext;
global::FlatSharp.IInputBuffer? global::FlatSharp.IFlatBufferDeserializedObject.InputBuffer => null;
bool global::FlatSharp.IFlatBufferDeserializedObject.CanSerializeWithMemoryCopy => false;
public override System.Single Latitude
{
get
{
checked
{
return this.__index0Value;
}
}
set
{
checked
{
this.__index0Value = value;
}
}
}
public override System.Single Longitude
{
get
{
checked
{
return this.__index1Value;
}
}
set
{
checked
{
this.__index1Value = value;
}
}
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static System.Single ReadIndex0Value(
TInputBuffer buffer,
int offset,
VTable4 vtable,
short remainingDepth)
{
checked
{
return Read_5dd3d85932534c8e9ef0be81d70126f4(buffer, offset + 0, remainingDepth);
}
}
[System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static System.Single ReadIndex1Value(
TInputBuffer buffer,
int offset,
VTable4 vtable,
short remainingDepth)
{
checked
{
return Read_5dd3d85932534c8e9ef0be81d70126f4(buffer, offset + 4, remainingDepth);
}
}
}
private static void WriteInlineValueOf_af2ad7e12e0d40ae9db2e80ee3a39307<TSpanWriter>(
TSpanWriter spanWriter,
Span<byte> span,
global::FlatBuffersTest.Location value,
int offset
) where TSpanWriter : ISpanWriter
{
checked
{
Span<byte> scopedSpan = span.Slice(offset, 8);
if (value is null)
{
scopedSpan.Clear();
return;
}
WriteInlineValueOf_5dd3d85932534c8e9ef0be81d70126f4(spanWriter, scopedSpan, value.Latitude, 0);
WriteInlineValueOf_5dd3d85932534c8e9ef0be81d70126f4(spanWriter, scopedSpan, value.Longitude, 4);
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static int GetMaxSizeOf_2a5c716f3f5d4e4b9d9ce52648731830(global::System.Byte value)
{
checked
{
return 1;
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static global::System.Byte Read_2a5c716f3f5d4e4b9d9ce52648731830<TInputBuffer>(
TInputBuffer buffer,
int offset,
short remainingDepth
) where TInputBuffer : IInputBuffer
{
checked
{
--remainingDepth;
global::FlatSharp.Internal.SerializationHelpers.EnsureDepthLimit(remainingDepth);
return buffer.ReadByte(offset);
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static void WriteInlineValueOf_2a5c716f3f5d4e4b9d9ce52648731830<TSpanWriter>(
TSpanWriter spanWriter,
Span<byte> span,
global::System.Byte value,
int offset
) where TSpanWriter : ISpanWriter
{
checked
{
spanWriter.WriteByte(span, value, offset);
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static int GetMaxSizeOf_5dd3d85932534c8e9ef0be81d70126f4(global::System.Single value)
{
checked
{
return 7;
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static global::System.Single Read_5dd3d85932534c8e9ef0be81d70126f4<TInputBuffer>(
TInputBuffer buffer,
int offset,
short remainingDepth
) where TInputBuffer : IInputBuffer
{
checked
{
--remainingDepth;
global::FlatSharp.Internal.SerializationHelpers.EnsureDepthLimit(remainingDepth);
return buffer.ReadFloat(offset);
}
}
[global::System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private static void WriteInlineValueOf_5dd3d85932534c8e9ef0be81d70126f4<TSpanWriter>(
TSpanWriter spanWriter,
Span<byte> span,
global::System.Single value,
int offset
) where TSpanWriter : ISpanWriter
{
checked
{
spanWriter.WriteFloat(span, value, offset);
}
}
}
#endregion
}
}
This one contains the serializer code, which allows then to serialize/deserialize at runtime, like this example.
using System;
using System.Linq;
using System.Collections.Generic;
using FlatSharp;
using FlatSharp.Runtime;
using FlatSharp.Compiler.Generated;
using UnityEngine;
using UnityEditor;
#nullable enable
namespace FlatBuffersTest
{
[Serializable]
[CreateAssetMenu(
fileName = "PersonScriptableObject",
menuName = "Test Stuff/Person (SO)",
order = 10
)]
public class PersonScriptableObject : ScriptableObject, ISerializationCallbackReceiver
{
[SerializeField]
[HideInInspector]
private byte[]? buffer = null;
public Person person;
PersonScriptableObject()
{
person = new Person()
{
Id = 42,
Name = "Albert Nonymous",
Position = new Location() { Latitude = 42.0f, Longitude = 48.0f },
};
}
public void OnBeforeSerialize()
{
if (person != null)
{
int maxBytesNeeded = Person.Serializer.GetMaxSize(person);
buffer = new byte[maxBytesNeeded];
int bytesWritten = Person.Serializer.Write(buffer, person);
}
}
public void OnAfterDeserialize()
{
if (buffer != null && buffer.Length != 0)
{
person = Person.Serializer.Parse<Person>(buffer);
}
}
}
}
Focus on the UnityEngine.ISerializationCallbackReveiver
callback methods below:
public void OnBeforeSerialize()
{
if (person != null)
{
int maxBytesNeeded = Person.Serializer.GetMaxSize(person);
buffer = new byte[maxBytesNeeded];
int bytesWritten = Person.Serializer.Write(buffer, person);
}
}
public void OnAfterDeserialize()
{
if (buffer != null && buffer.Length != 0)
{
person = Person.Serializer.Parse<Person>(buffer);
}
}
So far, this works pretty well. I'm going to whip up some documentation that answer my initial questions.
Cheers.
One thing you might want to play around with is the serialization mode. Perhaps this is just your example code, but Greedy
/GreedyMutable
might not do entirely what you expect given that you've used FlatBuffers before. They won't do anything bad, but for someone who has FlatBuffers experience, Lazy
is the option that most closely mirrors the Google FlatBuffer API. You can find more documentation about deserialzation modes here.
Closing this due to inactivity. Please reopen if there is anything else you need help with!
Hi,
I'm a long-time (C++) Flatbuffers user, but have recently considered using FlatSharp with some Unity projects, mostly b/c of the C# integration and performance advantage.
And I'm currently stuck with some example code, which is why I'd like to get some more information about using FlatSharp with Unity. (I'll come to the stuck point below, please bear with me).
First of all, I'd like the next 3 points to be documented in the Wiki:
FlatSharp.Runtime
since pullingFlatSharp
was adding tons of other dependencies (Roslyn), often with duplicate DLLs. (not a fault of this project, though)If getting FlatSharp to run with Unity requires a couple of defines to be set, or other special build options, what are they?
Now, the point where I'm stuck: I have imported
FlatSharp.Runtime
, created the schema below and generated the C# source.Schema:
Code:
Given the above, how do I serialize/deserialize the data? I'm using the following, but Unity cannot find the
FlatBufferSerializer
Unity error
Thanks for your time reading this and hopefully some pointers to solve this issue.