Use asset pipeline for blocking asset loading, remove old texture loader

This commit is contained in:
Simon Lübeß
2024-06-03 00:46:23 +02:00
parent b2806d9460
commit 108b31399a
11 changed files with 134 additions and 643 deletions
+2
View File
@@ -10,3 +10,5 @@ GlitchyEditor/MonoDebugger.log
# Ignore files in build-directory of ScriptCore # Ignore files in build-directory of ScriptCore
GlitchyEditor/Resources/Scripts/* GlitchyEditor/Resources/Scripts/*
.cache
@@ -1,14 +1,23 @@
{ {
AssetLoader = "EditorTextureAssetLoader", AssetLoader = null,
Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){ Config = null,
Importer = "TextureImporter",
ImporterConfig = (GlitchyEditor.Assets.Importers.TextureImporterConfig){
_isSrgb = false
},
Processor = "TextureProcessor",
ProcessorConfig = (GlitchyEditor.Assets.Importers.TextureProcessorConfig){
_generateMipMaps = .No,
_samplerStateDescription = { _samplerStateDescription = {
MinFilter = .Linear, MinFilter = .Linear,
MagFilter = .Linear, MagFilter = .Linear,
MipFilter = .Linear, MipFilter = .Linear,
FilterMode = .Default,
ComparisonFunction = .Never, ComparisonFunction = .Never,
AddressModeU = .Clamp, AddressModeU = .Clamp,
AddressModeV = .Clamp, AddressModeV = .Clamp,
AddressModeW = .Clamp, AddressModeW = .Clamp,
MipLODBias = 0,
MipMinLOD = -3.40282347e+38, MipMinLOD = -3.40282347e+38,
MipMaxLOD = 3.40282347e+38, MipMaxLOD = 3.40282347e+38,
MaxAnisotropy = 1, MaxAnisotropy = 1,
@@ -20,5 +29,9 @@
} }
} }
}, },
AssetHandle = 16828117378770367285 Exporter = "TextureExporter",
ExporterConfig = {
_compression = .LZ4
},
AssetHandle = 17972970543152031508
} }
+9 -6
View File
@@ -149,14 +149,17 @@ class AssetFile
var assetLoader = _contentManager.GetDefaultAssetLoader(fileExtension); var assetLoader = _contentManager.GetDefaultAssetLoader(fileExtension);
// We don't have a loader -> we don't need a config // We don't have a loader -> we don't need a config
if (assetLoader == null) if (assetLoader == null && assetPipeline case .Err)
return; return;
_assetConfig.AssetLoader = new String(); if (assetLoader != null)
assetLoader.GetType().GetName(_assetConfig.AssetLoader); {
_assetConfig.AssetLoader = new String();
_assetConfig.Config = assetLoader?.GetDefaultConfig(); assetLoader.GetType().GetName(_assetConfig.AssetLoader);
_assetConfig.Config?.[Friend]_changed = true;
_assetConfig.Config = assetLoader?.GetDefaultConfig();
_assetConfig.Config?.[Friend]_changed = true;
}
_assetConfig.Importer = new String(); _assetConfig.Importer = new String();
assetPipeline?.Importer?.GetType()?.GetName(_assetConfig.Importer); assetPipeline?.Importer?.GetType()?.GetName(_assetConfig.Importer);
+47 -16
View File
@@ -35,11 +35,13 @@ class AssetCache
/// Current format version of loose asset file (.laf) file reader and writer. /// Current format version of loose asset file (.laf) file reader and writer.
public const uint16 FormatVersion = 1; public const uint16 FormatVersion = 1;
private append String _directory = .();// ~ delete:append _; private append String _projectCacheDirectory = .();// ~ delete:append _;
private append String _globalCacheDirectory = .();// ~ delete:append _;
private append Dictionary<AssetHandle, CachedAsset> _assets = .();// ~ delete:append _; private append Dictionary<AssetHandle, CachedAsset> _assets = .();// ~ delete:append _;
public StringView CacheDirectory => _directory; public StringView ProjectCacheDirectory => _projectCacheDirectory;
public StringView GlobalCacheDirectory => _globalCacheDirectory;
private bool _cacheLoaded; private bool _cacheLoaded;
@@ -60,11 +62,18 @@ class AssetCache
_cacheLoaded = false; _cacheLoaded = false;
ClearDictionaryAndDeleteValues!(_assets); ClearDictionaryAndDeleteValues!(_assets);
} }
/// Sets the directory in which the processed assets are cached.
public void SetGlobalCacheDirectory(StringView directory)
{
_globalCacheDirectory.Set(directory);
ReloadCache();
}
/// Sets the directory in which the processed assets are cached. /// Sets the directory in which the processed assets are cached.
public void SetDirectory(StringView directory) public void SetProjectCacheDirectory(StringView directory)
{ {
_directory.Set(directory); _projectCacheDirectory.Set(directory);
ReloadCache(); ReloadCache();
} }
@@ -73,13 +82,24 @@ class AssetCache
{ {
ClearCache(); ClearCache();
if (!Directory.Exists(_directory)) LoadCache(_globalCacheDirectory);
{ LoadCache(_projectCacheDirectory);
Log.EngineLogger.Info($"Asset cache directory doesn't exist, creating directory \"{_directory}\"...");
if (Directory.CreateDirectory(_directory) case .Err(let error)) _cacheLoaded = true;
}
private void LoadCache(StringView directory)
{
if (directory.IsWhiteSpace)
return;
if (!Directory.Exists(directory))
{
Log.EngineLogger.Info($"Asset cache directory doesn't exist, creating directory \"{directory}\"...");
if (Directory.CreateDirectory(directory) case .Err(let error))
{ {
Log.EngineLogger.Critical($"Failed to create cache directory \"{_directory}\". Reason: {error}."); Log.EngineLogger.Critical($"Failed to create cache directory \"{directory}\". Reason: {error}.");
Log.EngineLogger.Critical($"The engine will not function properly without the asset cache directory. Save your project and restart the engine."); Log.EngineLogger.Critical($"The engine will not function properly without the asset cache directory. Save your project and restart the engine.");
} }
@@ -87,9 +107,9 @@ class AssetCache
// or we failed and can't do anything anyway. // or we failed and can't do anything anyway.
return; return;
} }
String filePath = scope .(); String filePath = scope .();
for (FileFindEntry file in Directory.EnumerateFiles(_directory)) for (FileFindEntry file in Directory.EnumerateFiles(directory))
{ {
filePath.Clear(); filePath.Clear();
file.GetFilePath(filePath); file.GetFilePath(filePath);
@@ -108,8 +128,6 @@ class AssetCache
_assets.Add(cachedAsset.Handle, cachedAsset); _assets.Add(cachedAsset.Handle, cachedAsset);
} }
_cacheLoaded = true;
} }
private Result<void> ReadAssetFile(StringView filePath, CachedAsset cachedAsset) private Result<void> ReadAssetFile(StringView filePath, CachedAsset cachedAsset)
@@ -170,9 +188,17 @@ class AssetCache
return null; return null;
} }
private void GetCacheFilePath(AssetHandle handle, String outFilePath) private void GetProjectCacheFilePath(AssetHandle handle, String outFilePath)
{ {
outFilePath.Append(_directory); outFilePath.Append(_projectCacheDirectory);
outFilePath.Append(Path.DirectorySeparatorChar);
outFilePath.Append(handle);
outFilePath.Append(CachedAsset.CacheFileExtension);
}
private void GetGlobalCacheFilePath(AssetHandle handle, String outFilePath)
{
outFilePath.Append(_globalCacheDirectory);
outFilePath.Append(Path.DirectorySeparatorChar); outFilePath.Append(Path.DirectorySeparatorChar);
outFilePath.Append(handle); outFilePath.Append(handle);
outFilePath.Append(CachedAsset.CacheFileExtension); outFilePath.Append(CachedAsset.CacheFileExtension);
@@ -187,7 +213,12 @@ class AssetCache
asset = new CachedAsset(); asset = new CachedAsset();
asset.Handle = assetInfo.Handle; asset.Handle = assetInfo.Handle;
asset.FilePath = new String(128); asset.FilePath = new String(128);
GetCacheFilePath(asset.Handle, asset.FilePath);
if (assetInfo.AssetIdentifier.IsResource)
GetGlobalCacheFilePath(asset.Handle, asset.FilePath);
else
GetProjectCacheFilePath(asset.Handle, asset.FilePath);
_assets[asset.Handle] = asset; _assets[asset.Handle] = asset;
} }
+5 -2
View File
@@ -18,9 +18,12 @@ class AssetConverter
_contentManager = contentManager; _contentManager = contentManager;
} }
public void QueueForProcessing(AssetFile assetFile) public void QueueForProcessing(AssetFile assetFile, bool isBlocking = false)
{ {
_queue.Add(assetFile); if (!isBlocking)
_queue.Add(assetFile);
else
Process(assetFile);
} }
public void Update() public void Update()
+2 -2
View File
@@ -375,12 +375,12 @@ class AssetHierarchy
if (assetNode.IsInSubtree(_resourcesDirectoryNode)) if (assetNode.IsInSubtree(_resourcesDirectoryNode))
{ {
Path.GetRelativePath(assetNode->Path, _resourcesDirectoryNode->Path, identifier); Path.GetRelativePath(assetNode->Path, _resourcesDirectoryNode->Path, identifier);
identifier.Insert(0, "Resources/"); identifier.Insert(0, AssetIdentifier.ResourcesPrefix);
} }
else if (assetNode.IsInSubtree(_assetsDirectoryNode)) else if (assetNode.IsInSubtree(_assetsDirectoryNode))
{ {
Path.GetRelativePath(assetNode->Path, _assetsDirectoryNode->Path, identifier); Path.GetRelativePath(assetNode->Path, _assetsDirectoryNode->Path, identifier);
identifier.Insert(0, "Assets/"); identifier.Insert(0, AssetIdentifier.AssetsPrefix);
} }
delete assetNode->Identifier; delete assetNode->Identifier;
@@ -1,596 +0,0 @@
using System;
using System.Collections;
using Bon;
using System.IO;
using GlitchyEngine;
using GlitchyEngine.Content;
using GlitchyEngine.Renderer;
using GlitchyEngine.Math;
using DirectXTK;
using ImGui;
using GlitchyEditor.Assets.Importers;
using System.Diagnostics;
namespace GlitchyEditor.Assets;
class TextureAssetPropertiesEditor : AssetPropertiesEditor
{
EditorTextureAssetLoaderConfig _textureConfig;
public this(AssetFile asset) : base(asset)
{
_textureConfig = asset.AssetConfig.Config as EditorTextureAssetLoaderConfig;
}
static char8*[3] _filterFuncNames = char8*[]("Point", "Linear", "Anisotropic");
public override void ShowEditor()
{
return;
if (_textureConfig == null)
return;
bool generateMips = _textureConfig.GenerateMipMaps;
if (ImGui.Checkbox("Generate Mip Maps", &generateMips))
_textureConfig.GenerateMipMaps = generateMips;
bool isSrgb = _textureConfig.IsSRGB;
if (ImGui.Checkbox("Is sRGB", &isSrgb))
_textureConfig.IsSRGB = isSrgb;
SamplerStateDescription samplerStateDescription = _textureConfig.SamplerStateDescription;
void ShowFilterCombo(String label, ref FilterFunction filterFunction)
{
int32 selectedFilter = filterFunction.Underlying;
if (ImGui.Combo(label, &selectedFilter, &_filterFuncNames, 3))
filterFunction = (.)selectedFilter;
}
ImGui.Separator();
ImGui.TextUnformatted("Texture Filtering:");
ImGui.Separator();
ImGui.EnumCombo("Min Filter", ref samplerStateDescription.MinFilter);
ImGui.AttachTooltip("""
Sampling method used for minification.
If set to "Anisotropic" all Filters are set to "Anisotropic" internally.
""");
ImGui.EnumCombo("Mag Filter", ref samplerStateDescription.MagFilter);
ImGui.AttachTooltip("""
Sampling method used for magnification.
If set to "Anisotropic" all Filters are set to "Anisotropic" internally.
""");
ImGui.EnumCombo("Mip Map Filter", ref samplerStateDescription.MipFilter);
ImGui.AttachTooltip("""
Method used for mip-level sampling.
If set to "Anisotropic" all Filters are set to "Anisotropic" internally.
""");
if (samplerStateDescription.MagFilter == .Anisotropic ||
samplerStateDescription.MinFilter == .Anisotropic ||
samplerStateDescription.MipFilter == .Anisotropic)
{
ImGui.SliderScalar("Anisotropy Level", ref samplerStateDescription.MaxAnisotropy, 1, 16);
}
ImGui.NewLine();
ImGui.EnumCombo("Filter Mode", ref samplerStateDescription.FilterMode);
ImGui.AttachTooltip("Filtering method to use when sampling a texture.");
if (samplerStateDescription.FilterMode == .Comparison)
{
ImGui.EnumCombo("Comparison Function", ref samplerStateDescription.ComparisonFunction);
ImGui.AttachTooltip("""
The function that is used to compare the sampled data against the existing sampled data.
Only applies if Filter Mode is set to FilterMode.Comparison.
""");
}
ImGui.Separator();
ImGui.TextUnformatted("Wrapping");
ImGui.Separator();
ImGui.EnumCombo("Wrap Mode U", ref samplerStateDescription.AddressModeU);
ImGui.AttachTooltip("Method to use for resolving a u texture coordinate that is outside the 0 to 1 range.");
ImGui.EnumCombo("Wrap Mode V", ref samplerStateDescription.AddressModeV);
ImGui.AttachTooltip("Method to use for resolving a v texture coordinate that is outside the 0 to 1 range.");
ImGui.EnumCombo("Wrap Mode W", ref samplerStateDescription.AddressModeW);
ImGui.AttachTooltip("Method to use for resolving a w texture coordinate that is outside the 0 to 1 range.");
if (samplerStateDescription.AddressModeU == .Border ||
samplerStateDescription.AddressModeV == .Border ||
samplerStateDescription.AddressModeW == .Border)
{
ImGui.ColorEdit4("Border Color", ref samplerStateDescription.BorderColor);
}
ImGui.Separator();
ImGui.TextUnformatted("Mip Maps");
ImGui.Separator();
ImGui.DragFloat("Mip LOD Bias", &samplerStateDescription.MipLODBias, 0.1f);
ImGui.AttachTooltip("""
Offset from the calculated mipmap level.
For example, if the GPU calculates that a texture should be sampled at mipmap level 3 and "Mip LOD Bias" is 2, then the texture will be sampled at mipmap level 5.
""");
ImGui.DragFloat("Min Mip LOD", &samplerStateDescription.MipMinLOD);
ImGui.AttachTooltip("Lower end of the mipmap range to clamp access to, where 0 is the largest and most detailed mipmap level and any level higher than that is less detailed.");
ImGui.DragFloat("Max LOD Bias", &samplerStateDescription.MipMaxLOD);
ImGui.AttachTooltip("""
Upper end of the mipmap range to clamp access to, where 0 is the largest and most detailed mipmap level and any level higher than that is less detailed.
This value must be greater than or equal to "Min Mip LOD". To have no upper limit on LOD set this to a large value.
""");
_textureConfig.SamplerStateDescription = samplerStateDescription;
}
public static AssetPropertiesEditor Factory(AssetFile assetFile)
{
return new TextureAssetPropertiesEditor(assetFile);
}
}
[BonTarget, BonPolyRegister]
class EditorTextureAssetLoaderConfig : AssetLoaderConfig
{
[BonInclude]
private bool _generateMipMaps;
[BonInclude]
private bool _isSrgb;
[BonInclude]
private SamplerStateDescription _samplerStateDescription = .();
public bool GenerateMipMaps
{
get => _generateMipMaps;
set => SetIfChanged(ref _generateMipMaps, value);
}
public bool IsSRGB
{
get => _isSrgb;
set => SetIfChanged(ref _isSrgb, value);
}
public SamplerStateDescription SamplerStateDescription
{
get => _samplerStateDescription;
set => SetIfChanged(ref _samplerStateDescription, value);
}
}
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader
{
private static readonly List<StringView> _fileExtensions = new .(){".png", ".dds"} ~ delete _; // ".jpg", ".bmp"
public static List<StringView> FileExtensions => _fileExtensions;
public AssetLoaderConfig GetDefaultConfig()
{
return new EditorTextureAssetLoaderConfig();
}
public Asset LoadAsset(Stream data, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager)
{
var config;
if (config == null)
{
config = GetDefaultConfig();
defer:: delete config;
}
Log.EngineLogger.AssertDebug(config is EditorTextureAssetLoaderConfig, "config has wrong type.");
return LoadTexture(data, (EditorTextureAssetLoaderConfig)config);
}
const String PngMagicWord = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
const String DdsMagicWord = "DDS ";
enum TextureType
{
Unknown,
DDS,
PNG
}
private static TextureType GetTextureType(Stream data)
{
int64 position = data.Position;
var readResult = data.Read<char8[8]>();
data.Position = position;
char8[8] magicWord;
if (readResult case .Ok(out magicWord))
{
StringView strView = .(&magicWord, magicWord.Count);
if (strView.StartsWith(PngMagicWord))
{
return .PNG;
}
else if (strView.StartsWith(DdsMagicWord))
{
return .DDS;
}
else
{
Runtime.FatalError("Unknown image format.");
}
}
return .Unknown;
}
/** \brief Loads the texture from the specified path.
* @param path The path of the texture to load.
* @param texture The reference to the pointer that will hold the texture.
* @returns true if the texture was loaded successfully; false otherwise.
*/
protected static bool LoadDdsResourcePlatform(Stream stream)
{
Debug.Profiler.ProfileResourceFunction!();
uint8[] ddsData = new:ScopedAlloc! uint8[stream.Length];
var result = stream.TryRead(ddsData);
if (result case .Err(let err))
{
Log.EngineLogger.Error($"Failed to read texture data from stream. Error: {err}");
}
DirectX.D3D11.ID3D11Texture2D* texture = null;
DirectX.D3D11.ID3D11ShaderResourceView* view = null;
DirectX.Common.HResult loadResult = DDSTextureLoader.CreateDDSTextureFromMemory(GlitchyEngine.Platform.DX11.NativeDevice,
ddsData.Ptr, (uint)ddsData.Count, (.)&texture, &view);
view.Release();
return true;
}
private static Texture LoadTexture(Stream data, EditorTextureAssetLoaderConfig config)
{
Debug.Profiler.ProfileResourceFunction!();
List<LoadedSurface> surfaces = scope .();
LoadedTextureInfo textureInfo;
// Make sure we clean up the pixel data
defer { delete textureInfo.PixelData; }
switch(GetTextureType(data))
{
case .DDS:
var v = data.Position;
//LoadDdsResourcePlatform(data);
data.Position = v;
Result<void> result = LoadDds(data, config, surfaces, out textureInfo);
if (result case .Err)
return null;
case .PNG:
Result<void> result = LoadPng(data, config, surfaces, out textureInfo);
if (result case .Err)
return null;
case .Unknown:
Log.EngineLogger.Error("Unknown texture format.");
return null;
}
// Make the format SRGB or non-SRGB
if (config.IsSRGB)
textureInfo.PixelFormat = textureInfo.PixelFormat.GetSRGB();
else
textureInfo.PixelFormat = textureInfo.PixelFormat.GetNonSRGB();
Texture texture = CreateTexture(surfaces, textureInfo);
if (texture != null)
{
SetSampler(texture, config);
texture.[Friend]Complete = true;
}
return texture;
}
private static Texture CreateTexture(List<LoadedSurface> surfaces, LoadedTextureInfo textureInfo)
{
TextureSliceData[] slices = scope TextureSliceData[surfaces.Count];
for (int i < slices.Count)
{
slices[i] = .(surfaces[i].Data.Ptr, surfaces[i].Pitch, surfaces[i].SlicePitch);
}
//slices[0].SlicePitch = (.)(surfaces[0].Pitch * textureInfo.Height);
switch (textureInfo.Dimension)
{
//case .Texture1D:
case .Texture2D:
if (textureInfo.IsCubeMap)
{
Texture2DDesc staging = .();
staging.Width = (.)textureInfo.Width;
staging.Height = (.)textureInfo.Height;
staging.MipLevels = (.)textureInfo.MipMapCount;
staging.ArraySize = (.)textureInfo.ArraySize;
staging.Format = (.)textureInfo.PixelFormat;
// TODO: allow enabling read/write
staging.CpuAccess = .None;
staging.Usage = .Default;
TextureCube cubeTexture = new TextureCube(staging);
//cubeTexture.SetData();
for (let surface in surfaces)
{
cubeTexture.SetData(surface.Data.Ptr, surface.Pitch / staging.Width, (TextureCubeFace)surface.CubeFace, (.)surface.ArrayIndex, (.)surface.MipLevel);
}
return cubeTexture;
}
else
{
Texture2DDesc staging = .();
staging.Width = (.)textureInfo.Width;
staging.Height = (.)textureInfo.Height;
staging.MipLevels = (.)textureInfo.MipMapCount;
staging.ArraySize = (.)textureInfo.ArraySize;
staging.Format = (.)textureInfo.PixelFormat;
// TODO: allow enabling read/write
staging.CpuAccess = .None;
staging.Usage = .Immutable;
Texture2D stagingTexture = new Texture2D(staging);
stagingTexture.SetData(slices);
/*for (let surface in surfaces)
{
stagingTexture.[Friend]PlatformSetData(surface.Data.Ptr, surface.Pitch / staging.Width, 0, 0, staging.Width, staging.Height, (.)surface.ArrayIndex, (.)surface.MipLevel, .Write);
}*/
return stagingTexture;
}
default:
Runtime.NotImplemented();
}
return null;
}
private static Result<void> LoadPng(Stream data, EditorTextureAssetLoaderConfig config, List<LoadedSurface> surfaces, out LoadedTextureInfo textureInfo)
{
Debug.Profiler.ProfileResourceFunction!();
textureInfo = .();
uint8[] pngData = new:ScopedAlloc! uint8[data.Length];
var result = data.TryRead(pngData);
if (result case .Err(let err))
{
Log.EngineLogger.Error($"Failed to read data from stream. Texture: Error: {err}");
return .Err;
}
uint8* rawData = null;
defer
{
if (rawData != null)
LodePng.LodePng.Free(rawData);
}
uint32 width = 0, height = 0;
{
Debug.Profiler.ProfileResourceScope!("LodePng.LodePng.Decode32");
uint32 errorCode = LodePng.LodePng.Decode32(&rawData, &width, &height, pngData.Ptr, (.)pngData.Count);
if (errorCode != 0)
{
Log.EngineLogger.Error($"Failed to decode PNG file {errorCode}.");
return .Err;
}
}
uint8[] pixelData = new uint8[4 * width * height];
Internal.MemCpy(pixelData.Ptr, rawData, pixelData.Count);
LoadedSurface surface = .();
surface.Data = Span<uint8>(pixelData);
surface.Pitch = 4 * width;
surface.SlicePitch = 0;
surface.ArrayIndex = 0;
surface.MipLevel = 0;
surfaces.Add(surface);
//Texture2DDesc desc = .(width, height, config.IsSRGB ? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm, 1, 1, .Immutable);
//Texture2D texture = new Texture2D(desc);
//texture.SetData<Color>((.)rawData);
// TODO: Generate mip maps
textureInfo.PixelData = pixelData;
textureInfo.Width = width;
textureInfo.Height = height;
textureInfo.Depth = 1;
textureInfo.ArraySize = 1;
textureInfo.MipMapCount = 1;
textureInfo.Dimension = .Texture2D;
textureInfo.IsCubeMap = false;
textureInfo.PixelFormat = config.IsSRGB ? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm;
return .Ok;
//return texture;
}
private static Result<void> LoadDds(Stream data, EditorTextureAssetLoaderConfig config, List<LoadedSurface> surfaces, out LoadedTextureInfo textureInfo)
{
var result = DdsImporter.LoadDds(data, config.IsSRGB, surfaces, out textureInfo);
if (result case .Err)
return .Err;
return .Ok;
}
private static void SetSampler(Texture texture, EditorTextureAssetLoaderConfig config)
{
using (SamplerState samplerState = SamplerStateManager.GetSampler(config.SamplerStateDescription))
{
texture.SamplerState = samplerState;
}
}
private static Texture2D _placeholder2D;
private static TextureCube _placeholderCube;
private static Texture2D _error2D;
private static TextureCube _errorCube;
public Asset GetPlaceholderAsset(Type assetType)
{
switch (assetType)
{
case typeof(TextureCube):
if (_placeholderCube == null)
{
// TODO: immutable
Texture2DDesc desc = .(1, 1, .R8G8B8A8_UNorm, 1, 1, .Default, .None);
_placeholderCube = new TextureCube(desc);
_placeholderCube.SamplerState = SamplerStateManager.PointWrap;
Color color = Color.Cyan;
_placeholderCube.SetData<Color>(&color, .PositiveX);
_placeholderCube.SetData<Color>(&color, .NegativeX);
_placeholderCube.SetData<Color>(&color, .PositiveY);
_placeholderCube.SetData<Color>(&color, .NegativeY);
_placeholderCube.SetData<Color>(&color, .PositiveZ);
_placeholderCube.SetData<Color>(&color, .NegativeZ);
Content.ManageAsset(_placeholderCube);
_placeholderCube.ReleaseRef();
_placeholderCube.[Friend]Complete = false;
}
return _placeholderCube;
case typeof(Texture2D):
fallthrough;
default:
if (_placeholder2D == null)
{
Texture2DDesc desc = .(1, 1, .R8G8B8A8_UNorm, 1, 1, .Immutable, .None);
_placeholder2D = new Texture2D(desc);
_placeholder2D.SamplerState = SamplerStateManager.PointWrap;
Color color = Color.Cyan;
_placeholder2D.SetData<Color>(&color);
Content.ManageAsset(_placeholder2D);
_placeholder2D.ReleaseRef();
_placeholder2D.[Friend]Complete = false;
}
return _placeholder2D;
}
}
public Asset GetErrorAsset(Type assetType)
{
switch (assetType)
{
case typeof(TextureCube):
if (_placeholderCube == null)
{
// TODO: immutable
Texture2DDesc desc = .(2, 2, .R8G8B8A8_UNorm, 1, 1, .Default, .None);
_errorCube = new TextureCube(desc);
_errorCube.SamplerState = SamplerStateManager.PointWrap;
Color[4] color = .(Color.HotPink, Color.Black, Color.Black, Color.HotPink);
_errorCube.SetData<Color>(&color, .PositiveX);
_errorCube.SetData<Color>(&color, .NegativeX);
_errorCube.SetData<Color>(&color, .PositiveY);
_errorCube.SetData<Color>(&color, .NegativeY);
_errorCube.SetData<Color>(&color, .PositiveZ);
_errorCube.SetData<Color>(&color, .NegativeZ);
Content.ManageAsset(_errorCube);
_errorCube.ReleaseRef();
_errorCube.[Friend]Complete = false;
}
return _placeholderCube;
case typeof(Texture2D):
fallthrough;
default:
if (_error2D == null)
{
Texture2DDesc desc = .(2, 2, .R8G8B8A8_UNorm, 1, 1, .Immutable, .None);
_error2D = new Texture2D(desc);
_error2D.SamplerState = SamplerStateManager.PointWrap;
Color[4] color = .(Color.HotPink, Color.Black, Color.Black, Color.HotPink);
_error2D.SetData<Color>(&color);
Content.ManageAsset(_error2D);
_error2D.ReleaseRef();
_placeholder2D.[Friend]Complete = true;
}
return _error2D;
}
}
}
+4 -6
View File
@@ -21,9 +21,6 @@ namespace GlitchyEditor
protected override IContentManager InitContentManager() protected override IContentManager InitContentManager()
{ {
_contentManager = new EditorContentManager(); _contentManager = new EditorContentManager();
_contentManager.RegisterAssetLoader<EditorTextureAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<EditorTextureAssetLoader>(".png", ".dds");
_contentManager.SetAssetPropertiesEditor<EditorTextureAssetLoader>(=> TextureAssetPropertiesEditor.Factory);
_contentManager.RegisterAssetLoader<ModelAssetLoader>(); _contentManager.RegisterAssetLoader<ModelAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<ModelAssetLoader>(".glb", ".gltf"); _contentManager.SetAsDefaultAssetLoader<ModelAssetLoader>(".glb", ".gltf");
@@ -41,9 +38,10 @@ namespace GlitchyEditor
_contentManager.RegisterAssetProcessor<TextureProcessor>(); _contentManager.RegisterAssetProcessor<TextureProcessor>();
_contentManager.RegisterAssetExporter<TextureExporter>(); _contentManager.RegisterAssetExporter<TextureExporter>();
_contentManager.ConfigureDefaultProcessing<TextureImporter, TextureProcessor, TextureExporter>(".png"); _contentManager.ConfigureDefaultProcessing<TextureImporter, TextureProcessor, TextureExporter>(".png", ".dds");
_contentManager.SetResourcesDirectory("./Resources"); _contentManager.SetGlobalAssetCacheDirectory(".cache");
_contentManager.SetResourcesDirectory("Resources");
return _contentManager; return _contentManager;
} }
+39 -11
View File
@@ -94,10 +94,15 @@ class EditorContentManager : IContentManager
_assetHierarchy.SetAssetsDirectory(_assetsDirectory); _assetHierarchy.SetAssetsDirectory(_assetsDirectory);
} }
public void SetGlobalAssetCacheDirectory(StringView fileName)
{
_assetCache.SetGlobalCacheDirectory(fileName);
}
public void SetAssetCacheDirectory(StringView fileName) public void SetAssetCacheDirectory(StringView fileName)
{ {
_assetCache.SetDirectory(fileName); _assetCache.SetProjectCacheDirectory(fileName);
} }
public void Update() public void Update()
@@ -494,14 +499,11 @@ class EditorContentManager : IContentManager
AssetNode assetNode = resultNode->Value; AssetNode assetNode = resultNode->Value;
AssetFile file = assetNode.AssetFile; AssetFile file = assetNode.AssetFile;
CachedAsset cacheEntry = _assetCache.GetCacheEntry(handle);
Asset loadedAsset; Asset loadedAsset;
// TODO: Remove this check once we no longer need the old stuff // TODO: Remove this check once we no longer need the old stuff
if (cacheEntry != null) if (file.UseNewAssetPipeline)
{ {
// TODO: Load asset with new loaders
loadedAsset = LoadFromCache(handle, blocking); loadedAsset = LoadFromCache(handle, blocking);
} }
else else
@@ -522,7 +524,7 @@ class EditorContentManager : IContentManager
} }
// TODO: Support lazy loading for all asset types // TODO: Support lazy loading for all asset types
if (!(assetLoader is EditorTextureAssetLoader) || blocking) if (true || blocking)
{ {
Stream stream = OpenStream(filePath, true); Stream stream = OpenStream(filePath, true);
@@ -948,22 +950,48 @@ class EditorContentManager : IContentManager
{ {
CachedAsset asset = _assetCache.GetCacheEntry(handle); CachedAsset asset = _assetCache.GetCacheEntry(handle);
if (asset == null)
{
Result<TreeNode<AssetNode>> assetNodeResult = AssetHierarchy.GetNodeFromAssetHandle(handle);
if (assetNodeResult case .Ok(let assetNode))
{
_assetConverter.QueueForProcessing(assetNode->AssetFile, isBlocking);
}
if (isBlocking)
{
asset = _assetCache.GetCacheEntry(handle);
Log.EngineLogger.Assert(asset != null, "Failed to load asset.");
}
else
{
// TODO: Return placeholder!
return null;
}
}
Result<Stream> streamResult = _assetCache.OpenStream(asset); Result<Stream> streamResult = _assetCache.OpenStream(asset);
// TODO: Actually return a placeholder, but they probably need rework too...
if (streamResult case .Err) if (streamResult case .Err)
{
// Return error
return null; return null;
}
Stream dataStream = streamResult.Value; Stream dataStream = streamResult.Value;
defer { delete dataStream; } defer { delete dataStream; }
IProcessedAssetLoader loader = GetLoader(asset.AssetType); IProcessedAssetLoader loader = GetLoader(asset.AssetType);
switch (loader.Load(dataStream)) if (loader.Load(dataStream) case .Ok(let loadedAsset))
{ {
case .Ok(let loadedAsset): return loadedAsset;
return loadedAsset; }
case .Err: else
{
// Return error
return null; return null;
} }
} }
+1 -1
View File
@@ -764,8 +764,8 @@ namespace GlitchyEditor
_editor.CurrentProject = _currentProject; _editor.CurrentProject = _currentProject;
String appAssemblyPath = scope String(); String appAssemblyPath = scope String();
_contentManager.SetAssetDirectory(_currentProject.AssetsFolder);
_contentManager.SetAssetCacheDirectory(_currentProject.GetScopedPath!(".cache")); _contentManager.SetAssetCacheDirectory(_currentProject.GetScopedPath!(".cache"));
_contentManager.SetAssetDirectory(_currentProject.AssetsFolder);
_currentProject.PathInProject(appAssemblyPath, scope $"bin/{_currentProject.Name}.dll"); _currentProject.PathInProject(appAssemblyPath, scope $"bin/{_currentProject.Name}.dll");
@@ -4,14 +4,21 @@ namespace GlitchyEngine.Content;
public class AssetIdentifier public class AssetIdentifier
{ {
public const String ResourcesPrefix = "Resources/";
public const String AssetsPrefix = "Assets/";
private String _fullIdentifier ~ delete:append _; private String _fullIdentifier ~ delete:append _;
private bool _isResource;
private int _subassetSeperator; private int _subassetSeperator;
public StringView FullIdentifier => _fullIdentifier; public StringView FullIdentifier => _fullIdentifier;
public StringView AssetIdentifier => _subassetSeperator > 0 ? StringView(_fullIdentifier, 0, _subassetSeperator) : _fullIdentifier; public StringView AssetIdentifier => _subassetSeperator > 0 ? StringView(_fullIdentifier, 0, _subassetSeperator) : _fullIdentifier;
public StringView SubassetIdentifier => _subassetSeperator > 0 ? StringView(_fullIdentifier, _subassetSeperator + 1) : .(); public StringView SubassetIdentifier => _subassetSeperator > 0 ? StringView(_fullIdentifier, _subassetSeperator + 1) : .();
public bool IsResource => _isResource;
[AllowAppend] [AllowAppend]
public this(StringView assetIdentifier) public this(StringView assetIdentifier)
{ {
@@ -21,6 +28,8 @@ public class AssetIdentifier
Fixup(_fullIdentifier); Fixup(_fullIdentifier);
_subassetSeperator = _fullIdentifier.IndexOf(':', 0); _subassetSeperator = _fullIdentifier.IndexOf(':', 0);
_isResource = _fullIdentifier.StartsWith(ResourcesPrefix);
} }
[AllowAppend] [AllowAppend]