diff --git a/src/AcDream.App/Rendering/Wb/ObjectMeshManager.cs b/src/AcDream.App/Rendering/Wb/ObjectMeshManager.cs
index be98a1c2..ec92af48 100644
--- a/src/AcDream.App/Rendering/Wb/ObjectMeshManager.cs
+++ b/src/AcDream.App/Rendering/Wb/ObjectMeshManager.cs
@@ -807,7 +807,13 @@ namespace AcDream.App.Rendering.Wb {
atlasList.Add(atlasManager);
}
- textureIndex = atlasManager.AddTexture(batch.Key, batch.TextureData, batch.UploadPixelFormat, batch.UploadPixelType);
+ // MP1a: AcDream.Content is Silk.NET-free — the extraction records
+ // carry Content-owned UploadPixelFormat/UploadPixelType enums whose
+ // underlying values are the GL ABI constants (numerically identical
+ // to Silk.NET.OpenGL.PixelFormat/PixelType), so this lifted nullable
+ // cast is value- and null-preserving.
+ textureIndex = atlasManager.AddTexture(batch.Key, batch.TextureData,
+ (PixelFormat?)batch.UploadPixelFormat, (PixelType?)batch.UploadPixelType);
if (_useModernRendering) {
ibo = GlobalBuffer!.IBO;
diff --git a/src/AcDream.Content/AcDream.Content.csproj b/src/AcDream.Content/AcDream.Content.csproj
index 1abed36e..e54e275e 100644
--- a/src/AcDream.Content/AcDream.Content.csproj
+++ b/src/AcDream.Content/AcDream.Content.csproj
@@ -12,14 +12,12 @@
exact versions (mirror AcDream.App.csproj — keep in lockstep). -->
-
-
+
diff --git a/src/AcDream.Content/MeshExtractor.cs b/src/AcDream.Content/MeshExtractor.cs
index 66b4d373..fa5f1d46 100644
--- a/src/AcDream.Content/MeshExtractor.cs
+++ b/src/AcDream.Content/MeshExtractor.cs
@@ -15,8 +15,6 @@ using System.Linq;
using System.Numerics;
using System.Threading;
using CullMode = DatReaderWriter.Enums.CullMode;
-using PixelFormat = Silk.NET.OpenGL.PixelFormat;
-using PixelType = Silk.NET.OpenGL.PixelType;
using BoundingBox = Chorizite.Core.Lib.BoundingBox;
namespace AcDream.Content;
@@ -50,8 +48,12 @@ public sealed class MeshExtractor {
/// immediately after each Prepare* call and
/// enqueues the results itself. No behavior change — same objects reach
/// the same queue, just via an explicit hand-off instead of a shared field.
+ /// ConcurrentQueue because one MeshExtractor is shared by up to
+ /// MaxParallelLoads (4) decode workers — the original enqueued to the
+ /// thread-safe _stagedMeshData, so the hand-off buffer must be
+ /// thread-safe too.
///
- private readonly List _sideStaged = new();
+ private readonly ConcurrentQueue _sideStaged = new();
public MeshExtractor(IDatReaderWriter dats, ILogger logger) {
_dats = dats;
@@ -64,9 +66,10 @@ public sealed class MeshExtractor {
/// .
///
public List DrainSideStaged() {
- if (_sideStaged.Count == 0) return new List();
- var result = new List(_sideStaged);
- _sideStaged.Clear();
+ var result = new List();
+ while (_sideStaged.TryDequeue(out var item)) {
+ result.Add(item);
+ }
return result;
}
@@ -173,13 +176,13 @@ public sealed class MeshExtractor {
if (emitter.HwGfxObjId.DataId != 0) {
var meshData = PrepareMeshData(emitter.HwGfxObjId.DataId, false, ct);
if (meshData != null) {
- _sideStaged.Add(meshData);
+ _sideStaged.Enqueue(meshData);
}
}
if (emitter.GfxObjId.DataId != 0 && emitter.GfxObjId.DataId != emitter.HwGfxObjId.DataId) {
var meshData = PrepareMeshData(emitter.GfxObjId.DataId, false, ct);
if (meshData != null) {
- _sideStaged.Add(meshData);
+ _sideStaged.Enqueue(meshData);
}
}
}
@@ -368,8 +371,8 @@ public sealed class MeshExtractor {
int texWidth, texHeight;
byte[] textureData;
TextureFormat textureFormat;
- PixelFormat? uploadPixelFormat = null;
- PixelType? uploadPixelType = null;
+ UploadPixelFormat? uploadPixelFormat = null;
+ UploadPixelType? uploadPixelType = null;
bool isSolid = poly.Stippling.HasFlag(StipplingType.NoPos) || surface.Type.HasFlag(SurfaceType.Base1Solid);
bool isClipMap = surface.Type.HasFlag(SurfaceType.Base1ClipMap);
uint paletteId = 0;
@@ -382,7 +385,7 @@ public sealed class MeshExtractor {
texWidth = texHeight = 32;
textureData = TextureHelpers.CreateSolidColorTexture(surface.ColorValue, texWidth, texHeight);
textureFormat = TextureFormat.RGBA8;
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
}
else if (_dats.Portal.TryGet(surface.OrigTextureId, out var surfaceTexture)) {
var renderSurfaceId = surfaceTexture.Textures.First();
@@ -403,7 +406,7 @@ public sealed class MeshExtractor {
if (TextureHelpers.IsCompressedFormat(renderSurface.Format)) {
isDxt3or5 = renderSurface.Format == DatReaderWriter.Enums.PixelFormat.PFID_DXT3 || renderSurface.Format == DatReaderWriter.Enums.PixelFormat.PFID_DXT5;
textureFormat = TextureFormat.RGBA8;
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
if (_decodedTextureCache.TryGetValue(renderSurfaceId, out textureData!)) {
// use cached data
@@ -446,36 +449,36 @@ public sealed class MeshExtractor {
case DatReaderWriter.Enums.PixelFormat.PFID_A8R8G8B8:
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillA8R8G8B8(renderSurface.SourceData, textureData.AsSpan(), texWidth, texHeight);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_R8G8B8:
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillR8G8B8(renderSurface.SourceData, textureData.AsSpan(), texWidth, texHeight);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_INDEX16:
if (!_dats.Portal.TryGet(renderSurface.DefaultPaletteId, out var paletteData))
throw new Exception($"Unable to load Palette: 0x{renderSurface.DefaultPaletteId:X8}");
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillIndex16(renderSurface.SourceData, paletteData, textureData.AsSpan(), texWidth, texHeight, isClipMap);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_P8:
if (!_dats.Portal.TryGet(renderSurface.DefaultPaletteId, out var p8PaletteData))
throw new Exception($"Unable to load Palette: 0x{renderSurface.DefaultPaletteId:X8}");
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillP8(renderSurface.SourceData, p8PaletteData, textureData.AsSpan(), texWidth, texHeight, isClipMap);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_R5G6B5:
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillR5G6B5(renderSurface.SourceData, textureData.AsSpan(), texWidth, texHeight);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_A4R4G4B4:
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillA4R4G4B4(renderSurface.SourceData, textureData.AsSpan(), texWidth, texHeight);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_A8:
case DatReaderWriter.Enums.PixelFormat.PFID_CUSTOM_LSCAPE_ALPHA:
@@ -486,7 +489,7 @@ public sealed class MeshExtractor {
else {
TextureHelpers.FillA8(renderSurface.SourceData, textureData.AsSpan(), texWidth, texHeight);
}
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
default:
throw new NotSupportedException($"Unsupported surface format: {renderSurface.Format}");
@@ -722,8 +725,8 @@ public sealed class MeshExtractor {
int texWidth, texHeight;
byte[] textureData;
TextureFormat textureFormat;
- PixelFormat? uploadPixelFormat = null;
- PixelType? uploadPixelType = null;
+ UploadPixelFormat? uploadPixelFormat = null;
+ UploadPixelType? uploadPixelType = null;
bool isSolid = poly.Stippling.HasFlag(StipplingType.NoPos) || surface.Type.HasFlag(SurfaceType.Base1Solid);
bool isClipMap = surface.Type.HasFlag(SurfaceType.Base1ClipMap);
uint paletteId = 0;
@@ -736,7 +739,7 @@ public sealed class MeshExtractor {
texWidth = texHeight = 32;
textureData = TextureHelpers.CreateSolidColorTexture(surface.ColorValue, texWidth, texHeight);
textureFormat = TextureFormat.RGBA8;
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
}
else if (_dats.Portal.TryGet(surface.OrigTextureId, out var surfaceTexture)) {
var renderSurfaceId = surfaceTexture.Textures.First();
@@ -757,13 +760,13 @@ public sealed class MeshExtractor {
if (_decodedTextureCache.TryGetValue(renderSurfaceId, out var cachedData)) {
textureData = cachedData;
textureFormat = TextureFormat.RGBA8;
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
}
else {
if (TextureHelpers.IsCompressedFormat(renderSurface.Format)) {
isDxt3or5 = renderSurface.Format == DatReaderWriter.Enums.PixelFormat.PFID_DXT3 || renderSurface.Format == DatReaderWriter.Enums.PixelFormat.PFID_DXT5;
textureFormat = TextureFormat.RGBA8;
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
textureData = new byte[texWidth * texHeight * 4];
CompressionFormat compressionFormat = renderSurface.Format switch {
@@ -784,34 +787,34 @@ public sealed class MeshExtractor {
case DatReaderWriter.Enums.PixelFormat.PFID_A8R8G8B8:
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillA8R8G8B8(renderSurface.SourceData, textureData.AsSpan(), texWidth, texHeight);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_R8G8B8:
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillR8G8B8(renderSurface.SourceData, textureData.AsSpan(), texWidth, texHeight);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_INDEX16:
if (!_dats.Portal.TryGet(renderSurface.DefaultPaletteId, out var paletteData)) return;
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillIndex16(renderSurface.SourceData, paletteData, textureData.AsSpan(), texWidth, texHeight, isClipMap);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_P8:
if (!_dats.Portal.TryGet(renderSurface.DefaultPaletteId, out var p8PaletteData)) return;
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillP8(renderSurface.SourceData, p8PaletteData, textureData.AsSpan(), texWidth, texHeight, isClipMap);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_R5G6B5:
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillR5G6B5(renderSurface.SourceData, textureData.AsSpan(), texWidth, texHeight);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_A4R4G4B4:
textureData = new byte[texWidth * texHeight * 4];
TextureHelpers.FillA4R4G4B4(renderSurface.SourceData, textureData.AsSpan(), texWidth, texHeight);
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
case DatReaderWriter.Enums.PixelFormat.PFID_A8:
case DatReaderWriter.Enums.PixelFormat.PFID_CUSTOM_LSCAPE_ALPHA:
@@ -822,7 +825,7 @@ public sealed class MeshExtractor {
else {
TextureHelpers.FillA8(renderSurface.SourceData, textureData.AsSpan(), texWidth, texHeight);
}
- uploadPixelFormat = PixelFormat.Rgba;
+ uploadPixelFormat = UploadPixelFormat.Rgba;
break;
default: return;
}
@@ -1144,8 +1147,8 @@ public sealed class MeshExtractor {
},
TextureIndex = 0,
TextureData = transparentTexture,
- UploadPixelFormat = PixelFormat.Rgba,
- UploadPixelType = PixelType.UnsignedByte,
+ UploadPixelFormat = UploadPixelFormat.Rgba,
+ UploadPixelType = UploadPixelType.UnsignedByte,
CullMode = CullMode.None
}
},
@@ -1161,8 +1164,8 @@ public sealed class MeshExtractor {
IsSolid = true
},
TextureData = transparentTexture,
- UploadPixelFormat = PixelFormat.Rgba,
- UploadPixelType = PixelType.UnsignedByte,
+ UploadPixelFormat = UploadPixelFormat.Rgba,
+ UploadPixelType = UploadPixelType.UnsignedByte,
CullMode = CullMode.None,
IsTransparent = false // Render in opaque pass but transparent
}
diff --git a/src/AcDream.Content/ObjectMeshData.cs b/src/AcDream.Content/ObjectMeshData.cs
index 8bba1e0c..49089475 100644
--- a/src/AcDream.Content/ObjectMeshData.cs
+++ b/src/AcDream.Content/ObjectMeshData.cs
@@ -7,8 +7,6 @@ using System.Collections.Generic;
using System.Numerics;
using System.Runtime.InteropServices;
using BoundingBox = Chorizite.Core.Lib.BoundingBox;
-using PixelFormat = Silk.NET.OpenGL.PixelFormat;
-using PixelType = Silk.NET.OpenGL.PixelType;
namespace AcDream.Content;
@@ -104,8 +102,8 @@ public class MeshBatchData {
public TextureKey TextureKey { get; set; }
public int TextureIndex { get; set; }
public byte[] TextureData { get; set; } = Array.Empty();
- public PixelFormat? UploadPixelFormat { get; set; }
- public PixelType? UploadPixelType { get; set; }
+ public UploadPixelFormat? UploadPixelFormat { get; set; }
+ public UploadPixelType? UploadPixelType { get; set; }
public DatReaderWriter.Enums.CullMode CullMode { get; set; }
}
@@ -115,8 +113,8 @@ public class MeshBatchData {
public class TextureBatchData {
public TextureKey Key { get; set; }
public byte[] TextureData { get; set; } = Array.Empty();
- public PixelFormat? UploadPixelFormat { get; set; }
- public PixelType? UploadPixelType { get; set; }
+ public UploadPixelFormat? UploadPixelFormat { get; set; }
+ public UploadPixelType? UploadPixelType { get; set; }
public List Indices { get; set; } = new();
public DatReaderWriter.Enums.CullMode CullMode { get; set; }
public bool IsTransparent { get; set; }
diff --git a/src/AcDream.Content/UploadFormats.cs b/src/AcDream.Content/UploadFormats.cs
new file mode 100644
index 00000000..b0f63612
--- /dev/null
+++ b/src/AcDream.Content/UploadFormats.cs
@@ -0,0 +1,34 @@
+namespace AcDream.Content;
+
+// MP1a follow-up (2026-07-05): Content-owned upload-format hint enums.
+// AcDream.Content must stay Silk.NET-free (the MP1b bake tool must not ship
+// GL binaries), so the moved MeshBatchData/TextureBatchData records carry
+// these instead of Silk.NET.OpenGL.PixelFormat/PixelType. The underlying
+// values are the OpenGL ABI constants, kept numerically identical to the
+// corresponding Silk.NET.OpenGL members (verified against Silk.NET.OpenGL
+// 2.23.0) so the App-boundary cast
+// `(Silk.NET.OpenGL.PixelFormat?)batch.UploadPixelFormat` is
+// value-preserving. GL enum constants are a stable specification ABI —
+// they cannot drift between Silk.NET versions.
+//
+// Members are ONLY the values the extraction code actually assigns
+// (see MeshExtractor.cs). Add new members with their GL constant if a
+// future decode path needs them — never renumber.
+
+///
+/// GL pixel-format upload hint computed at extraction time.
+/// Numeric values = OpenGL GL_* constants (= Silk.NET.OpenGL.PixelFormat).
+///
+public enum UploadPixelFormat {
+ /// GL_RGBA.
+ Rgba = 0x1908,
+}
+
+///
+/// GL pixel-type upload hint computed at extraction time.
+/// Numeric values = OpenGL GL_* constants (= Silk.NET.OpenGL.PixelType).
+///
+public enum UploadPixelType {
+ /// GL_UNSIGNED_BYTE.
+ UnsignedByte = 0x1401,
+}