forked from stride3d/stride
-
Notifications
You must be signed in to change notification settings - Fork 1
/
TextureContentSerializer.cs
198 lines (173 loc) · 9.69 KB
/
TextureContentSerializer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
using System;
using Stride.Core;
using Stride.Core.Serialization;
using Stride.Core.Serialization.Contents;
using Stride.Core.Streaming;
namespace Stride.Graphics.Data
{
internal class TextureContentSerializer : ContentSerializerBase<Texture>
{
/// <inheritdoc/>
public override void Serialize(ContentSerializerContext context, SerializationStream stream, Texture texture)
{
Serialize(context.Mode, stream, texture, context.AllowContentStreaming);
}
internal static void Serialize(ArchiveMode mode, SerializationStream stream, Texture texture, bool allowContentStreaming)
{
if (mode == ArchiveMode.Deserialize)
{
var services = stream.Context.Tags.Get(ServiceRegistry.ServiceRegistryKey);
var graphicsDeviceService = services.GetSafeServiceAs<IGraphicsDeviceService>();
var texturesStreamingProvider = services.GetService<ITexturesStreamingProvider>();
var isStreamable = stream.ReadBoolean();
if (!isStreamable)
{
texturesStreamingProvider?.UnregisterTexture(texture);
// TODO: Error handling?
using (var textureData = Image.Load(stream.UnderlyingStream))
{
if (texture.GraphicsDevice != null)
texture.OnDestroyed(); //Allows fast reloading todo review maybe?
texture.AttachToGraphicsDevice(graphicsDeviceService.GraphicsDevice);
texture.InitializeFrom(textureData.Description, new TextureViewDescription(), textureData.ToDataBox());
// Setup reload callback (reload from asset manager)
var contentSerializerContext = stream.Context.Get(ContentSerializerContext.ContentSerializerContextProperty);
if (contentSerializerContext != null)
{
texture.Reload = static (graphicsResource, services) =>
{
var assetManager = services.GetService<ContentManager>();
assetManager.TryGetAssetUrl(graphicsResource, out var url);
var textureDataReloaded = assetManager.Load<object>(url);
if (textureDataReloaded is Image image)
{
((Texture)graphicsResource).Recreate(image.ToDataBox());
assetManager.Unload(textureDataReloaded);
}
else if (textureDataReloaded is Texture)
{
((Texture)graphicsResource).Recreate();
assetManager.Unload(textureDataReloaded);
}
};
}
}
}
else
{
if (texture.GraphicsDevice != null)
texture.OnDestroyed();
texture.AttachToGraphicsDevice(graphicsDeviceService.GraphicsDevice);
texture.Reload = null;
// Read image header
var imageDescription = new ImageDescription();
ImageHelper.ImageDescriptionSerializer.Serialize(ref imageDescription, ArchiveMode.Deserialize, stream);
// Read content storage header
ContentStorageHeader storageHeader;
ContentStorageHeader.Read(stream, out storageHeader);
// Check if streaming service is available
if (texturesStreamingProvider != null)
{
// Load initial texture (with limited number of mipmaps)
if (storageHeader.InitialImage)
{
using (var textureData = Image.Load(stream.UnderlyingStream))
{
if (texture.GraphicsDevice != null)
texture.OnDestroyed(); //Allows fast reloading todo review maybe?
texture.InitializeFrom(textureData.Description, new TextureViewDescription(), textureData.ToDataBox());
}
}
if (allowContentStreaming)
{
// Register texture for streaming
texturesStreamingProvider.RegisterTexture(texture, ref imageDescription, ref storageHeader);
// Note: here we don't load texture data and don't allocate GPU memory
}
else
{
// Request texture loading (should be fully loaded)
texturesStreamingProvider.FullyLoadTexture(texture, ref imageDescription, ref storageHeader);
}
}
else
{
// Load initial texture and discard it (we are going to load the full chunk texture right after)
if (storageHeader.InitialImage)
{
using (var textureData = Image.Load(stream.UnderlyingStream))
{
}
}
// Deserialize whole texture without streaming feature
var contentSerializerContext = stream.Context.Get(ContentSerializerContext.ContentSerializerContextProperty);
DeserializeTexture(contentSerializerContext.ContentManager, texture, ref imageDescription, ref storageHeader);
}
}
}
else
{
var textureData = texture.GetSerializationData();
if (textureData == null)
throw new InvalidOperationException("Trying to serialize a Texture without CPU info.");
textureData.Write(stream);
}
}
public override object Construct(ContentSerializerContext context)
{
return new Texture();
}
private static void DeserializeTexture(ContentManager contentManager, Texture texture, ref ImageDescription imageDescription, ref ContentStorageHeader storageHeader)
{
using (var content = new ContentStreamingService())
{
// Get content storage container
var storage = content.GetStorage(ref storageHeader);
if (storage == null)
throw new ContentStreamingException("Missing content storage.");
storage.LockChunks();
// Cache data
var fileProvider = contentManager.FileProvider;
var format = imageDescription.Format;
bool isBlockCompressed =
(format >= PixelFormat.BC1_Typeless && format <= PixelFormat.BC5_SNorm) ||
(format >= PixelFormat.BC6H_Typeless && format <= PixelFormat.BC7_UNorm_SRgb);
var dataBoxes = new DataBox[imageDescription.MipLevels * imageDescription.ArraySize];
int dataBoxIndex = 0;
// Get data boxes data
for (int arrayIndex = 0; arrayIndex < imageDescription.ArraySize; arrayIndex++)
{
for (int mipIndex = 0; mipIndex < imageDescription.MipLevels; mipIndex++)
{
int mipWidth = imageDescription.Width >> mipIndex;
int mipHeight = imageDescription.Height >> mipIndex;
if (isBlockCompressed && ((mipWidth % 4) != 0 || (mipHeight % 4) != 0))
{
mipWidth = unchecked((int)(((uint)(mipWidth + 3)) & ~3U));
mipHeight = unchecked((int)(((uint)(mipHeight + 3)) & ~3U));
}
int rowPitch, slicePitch;
int widthPacked;
int heightPacked;
Image.ComputePitch(format, mipWidth, mipHeight, out rowPitch, out slicePitch, out widthPacked, out heightPacked);
var chunk = storage.GetChunk(mipIndex);
if (chunk == null || chunk.Size != slicePitch * imageDescription.ArraySize)
throw new ContentStreamingException("Data chunk is missing or has invalid size.", storage);
var data = chunk.GetData(fileProvider);
if (!chunk.IsLoaded)
throw new ContentStreamingException("Data chunk is not loaded.", storage);
dataBoxes[dataBoxIndex].DataPointer = data + slicePitch * arrayIndex;
dataBoxes[dataBoxIndex].RowPitch = rowPitch;
dataBoxes[dataBoxIndex].SlicePitch = slicePitch;
dataBoxIndex++;
}
}
// Initialize texture
texture.InitializeFrom(imageDescription, new TextureViewDescription(), dataBoxes);
storage.UnlockChunks();
}
}
}
}