Optimize prepared asset package v2

This commit is contained in:
Erik 2026-08-27 20:09:09 +02:00
parent d123c4b67c
commit 0dd966f3a0
28 changed files with 1277 additions and 106 deletions

View file

@ -36,34 +36,63 @@ namespace AcDream.Content.Pak;
public static class ObjectMeshDataSerializer {
public static void Write(ObjectMeshData data, Stream stream) {
using var bw = new BinaryWriter(stream, System.Text.Encoding.UTF8, leaveOpen: true);
WriteObjectMeshData(bw, data);
WriteObjectMeshData(bw, data, externalTextures: null);
}
public static void WriteExternalTextures(
ObjectMeshData data,
Stream stream,
Func<TextureKey, byte[], ulong> registerTexture) {
ArgumentNullException.ThrowIfNull(registerTexture);
using var bw = new BinaryWriter(
stream,
System.Text.Encoding.UTF8,
leaveOpen: true);
WriteObjectMeshData(bw, data, registerTexture);
}
/// <summary>Deserializes directly over <paramref name="bytes"/> — no defensive copy (PakReader's single-pass read reuses its CRC buffer here).</summary>
public static ObjectMeshData Read(byte[] bytes) {
using var ms = new MemoryStream(bytes, writable: false);
using var br = new BinaryReader(ms, System.Text.Encoding.UTF8, leaveOpen: true);
return ReadObjectMeshData(br);
return ReadObjectMeshData(br, externalTextures: null);
}
public static ObjectMeshData Read(ReadOnlySpan<byte> bytes) => Read(bytes.ToArray());
public static ObjectMeshData ReadExternalTextures(
byte[] bytes,
Func<ulong, byte[]> resolveTexture) {
ArgumentNullException.ThrowIfNull(resolveTexture);
using var ms = new MemoryStream(bytes, writable: false);
using var br = new BinaryReader(
ms,
System.Text.Encoding.UTF8,
leaveOpen: true);
return ReadObjectMeshData(br, resolveTexture);
}
// ---- ObjectMeshData -----------------------------------------------------
private static void WriteObjectMeshData(BinaryWriter w, ObjectMeshData data) {
private static void WriteObjectMeshData(
BinaryWriter w,
ObjectMeshData data,
Func<TextureKey, byte[], ulong>? externalTextures) {
w.Write(data.ObjectId);
w.Write(data.IsSetup);
WriteVertexArray(w, data.Vertices);
w.Write(data.Batches.Count);
foreach (var batch in data.Batches) WriteMeshBatchData(w, batch);
foreach (var batch in data.Batches)
WriteMeshBatchData(w, batch, externalTextures);
w.Write(data.UploadAttempts);
// EnvCellGeometry: recursive nested block.
w.Write(data.EnvCellGeometry is not null);
if (data.EnvCellGeometry is not null) WriteObjectMeshData(w, data.EnvCellGeometry);
if (data.EnvCellGeometry is not null)
WriteObjectMeshData(w, data.EnvCellGeometry, externalTextures);
w.Write(data.SetupParts.Count);
foreach (var (gfxObjId, transform) in data.SetupParts) {
@ -74,7 +103,7 @@ public static class ObjectMeshDataSerializer {
w.Write(data.ParticleEmitters.Count);
foreach (var emitter in data.ParticleEmitters) WriteStagedEmitter(w, emitter);
WriteTextureBatches(w, data.TextureBatches);
WriteTextureBatches(w, data.TextureBatches, externalTextures);
WriteBoundingBox(w, data.BoundingBox);
WriteVector3(w, data.SortCenter);
@ -86,7 +115,9 @@ public static class ObjectMeshDataSerializer {
WriteVector3Array(w, data.EdgeLines);
}
private static ObjectMeshData ReadObjectMeshData(BinaryReader r) {
private static ObjectMeshData ReadObjectMeshData(
BinaryReader r,
Func<ulong, byte[]>? externalTextures) {
var data = new ObjectMeshData {
ObjectId = r.ReadUInt64(),
IsSetup = r.ReadBoolean(),
@ -96,13 +127,16 @@ public static class ObjectMeshDataSerializer {
int batchCount = r.ReadInt32();
var batches = new List<MeshBatchData>(batchCount);
for (int i = 0; i < batchCount; i++) batches.Add(ReadMeshBatchData(r));
for (int i = 0; i < batchCount; i++)
batches.Add(ReadMeshBatchData(r, externalTextures));
data.Batches = batches;
data.UploadAttempts = r.ReadInt32();
bool hasEnvCellGeometry = r.ReadBoolean();
data.EnvCellGeometry = hasEnvCellGeometry ? ReadObjectMeshData(r) : null;
data.EnvCellGeometry = hasEnvCellGeometry
? ReadObjectMeshData(r, externalTextures)
: null;
int setupPartCount = r.ReadInt32();
var setupParts = new List<(ulong GfxObjId, Matrix4x4 Transform)>(setupPartCount);
@ -118,7 +152,7 @@ public static class ObjectMeshDataSerializer {
for (int i = 0; i < emitterCount; i++) emitters.Add(ReadStagedEmitter(r));
data.ParticleEmitters = emitters;
data.TextureBatches = ReadTextureBatches(r);
data.TextureBatches = ReadTextureBatches(r, externalTextures);
data.BoundingBox = ReadBoundingBox(r);
data.SortCenter = ReadVector3(r);
@ -134,24 +168,37 @@ public static class ObjectMeshDataSerializer {
// ---- MeshBatchData -------------------------------------------------------
private static void WriteMeshBatchData(BinaryWriter w, MeshBatchData batch) {
private static void WriteMeshBatchData(
BinaryWriter w,
MeshBatchData batch,
Func<TextureKey, byte[], ulong>? externalTextures) {
WriteUInt16Array(w, batch.Indices);
WriteTextureFormatTuple(w, batch.TextureFormat);
WriteTextureKey(w, batch.TextureKey);
w.Write(batch.TextureIndex);
WriteByteArray(w, batch.TextureData);
WriteTexturePayload(
w,
batch.TextureKey,
batch.TextureData,
externalTextures);
WriteNullableInt32Enum(w, batch.UploadPixelFormat.HasValue, batch.UploadPixelFormat is { } upf ? (int)upf : 0);
WriteNullableInt32Enum(w, batch.UploadPixelType.HasValue, batch.UploadPixelType is { } upt ? (int)upt : 0);
w.Write((int)batch.CullMode);
}
private static MeshBatchData ReadMeshBatchData(BinaryReader r) {
private static MeshBatchData ReadMeshBatchData(
BinaryReader r,
Func<ulong, byte[]>? externalTextures) {
ushort[] indices = ReadUInt16Array(r);
(int Width, int Height, TextureFormat Format) format =
ReadTextureFormatTuple(r);
TextureKey key = ReadTextureKey(r);
var batch = new MeshBatchData {
Indices = ReadUInt16Array(r),
TextureFormat = ReadTextureFormatTuple(r),
TextureKey = ReadTextureKey(r),
Indices = indices,
TextureFormat = format,
TextureKey = key,
TextureIndex = r.ReadInt32(),
TextureData = ReadByteArray(r),
TextureData = ReadTexturePayload(r, externalTextures),
};
batch.UploadPixelFormat = ReadNullableInt32Enum(r, v => (UploadPixelFormat)v);
batch.UploadPixelType = ReadNullableInt32Enum(r, v => (UploadPixelType)v);
@ -161,9 +208,12 @@ public static class ObjectMeshDataSerializer {
// ---- TextureBatchData / TextureBatches dictionary -------------------------
private static void WriteTextureBatchData(BinaryWriter w, TextureBatchData batch) {
private static void WriteTextureBatchData(
BinaryWriter w,
TextureBatchData batch,
Func<TextureKey, byte[], ulong>? externalTextures) {
WriteTextureKey(w, batch.Key);
WriteByteArray(w, batch.TextureData);
WriteTexturePayload(w, batch.Key, batch.TextureData, externalTextures);
WriteNullableInt32Enum(w, batch.UploadPixelFormat.HasValue, batch.UploadPixelFormat is { } upf ? (int)upf : 0);
WriteNullableInt32Enum(w, batch.UploadPixelType.HasValue, batch.UploadPixelType is { } upt ? (int)upt : 0);
WriteUInt16List(w, batch.Indices);
@ -174,10 +224,13 @@ public static class ObjectMeshDataSerializer {
w.Write(batch.HasWrappingUVs);
}
private static TextureBatchData ReadTextureBatchData(BinaryReader r) {
private static TextureBatchData ReadTextureBatchData(
BinaryReader r,
Func<ulong, byte[]>? externalTextures) {
TextureKey key = ReadTextureKey(r);
var batch = new TextureBatchData {
Key = ReadTextureKey(r),
TextureData = ReadByteArray(r),
Key = key,
TextureData = ReadTexturePayload(r, externalTextures),
};
batch.UploadPixelFormat = ReadNullableInt32Enum(r, v => (UploadPixelFormat)v);
batch.UploadPixelType = ReadNullableInt32Enum(r, v => (UploadPixelType)v);
@ -198,7 +251,8 @@ public static class ObjectMeshDataSerializer {
/// </summary>
private static void WriteTextureBatches(
BinaryWriter w,
Dictionary<(int Width, int Height, TextureFormat Format), List<TextureBatchData>> batches) {
Dictionary<(int Width, int Height, TextureFormat Format), List<TextureBatchData>> batches,
Func<TextureKey, byte[], ulong>? externalTextures) {
var sortedKeys = batches.Keys
.OrderBy(k => k.Width)
.ThenBy(k => k.Height)
@ -213,11 +267,14 @@ public static class ObjectMeshDataSerializer {
var list = batches[key];
w.Write(list.Count);
foreach (var item in list) WriteTextureBatchData(w, item);
foreach (var item in list)
WriteTextureBatchData(w, item, externalTextures);
}
}
private static Dictionary<(int Width, int Height, TextureFormat Format), List<TextureBatchData>> ReadTextureBatches(BinaryReader r) {
private static Dictionary<(int Width, int Height, TextureFormat Format), List<TextureBatchData>> ReadTextureBatches(
BinaryReader r,
Func<ulong, byte[]>? externalTextures) {
int groupCount = r.ReadInt32();
var result = new Dictionary<(int Width, int Height, TextureFormat Format), List<TextureBatchData>>(groupCount);
for (int i = 0; i < groupCount; i++) {
@ -227,13 +284,32 @@ public static class ObjectMeshDataSerializer {
int listCount = r.ReadInt32();
var list = new List<TextureBatchData>(listCount);
for (int j = 0; j < listCount; j++) list.Add(ReadTextureBatchData(r));
for (int j = 0; j < listCount; j++)
list.Add(ReadTextureBatchData(r, externalTextures));
result[(width, height, format)] = list;
}
return result;
}
private static void WriteTexturePayload(
BinaryWriter writer,
TextureKey key,
byte[] bytes,
Func<TextureKey, byte[], ulong>? externalTextures) {
if (externalTextures is null)
WriteByteArray(writer, bytes);
else
writer.Write(externalTextures(key, bytes));
}
private static byte[] ReadTexturePayload(
BinaryReader reader,
Func<ulong, byte[]>? externalTextures) =>
externalTextures is null
? ReadByteArray(reader)
: externalTextures(reader.ReadUInt64());
// ---- StagedEmitter / ParticleEmitter (DBObj) ------------------------------
private static void WriteStagedEmitter(BinaryWriter w, StagedEmitter emitter) {