Start of asset handles

This commit is contained in:
Simon Lübeß
2023-03-13 20:35:30 +01:00
parent ad14c9f6ed
commit c22d122921
10 changed files with 370 additions and 60 deletions
+64 -19
View File
@@ -81,7 +81,9 @@ class EditorContentManager : IContentManager
//private append List<String> _identifiers = .() ~ _.ClearAndDeleteItems();
private append Dictionary<StringView, Asset> _loadedAssets = .(); // Check if all resources are unloaded
private append Dictionary<StringView, AssetHandle> _handles = .(); // TODO: Check if all resources are unloaded
private append Dictionary<AssetHandle, Asset> _handleToAsset = .();
private append AssetHierarchy _assetHierarchy = .(this);
@@ -99,6 +101,8 @@ class EditorContentManager : IContentManager
private void OnFileContentChanged(AssetNode assetNode)
{
// TODO: update for AssetHandles
// TODO: Subassets break reloading because we can't find them when we only receive the file that changed...
// Asset isn't loaded so we don't need to reload it.
@@ -237,14 +241,36 @@ class EditorContentManager : IContentManager
public bool IsLoaded(StringView identifier)
{
return _loadedAssets.ContainsKey(identifier);
return _handles.ContainsKey(identifier);
}
public Asset LoadAsset(StringView identifier)
public Asset GetAsset(Type assetType, AssetHandle handle)
{
if (_loadedAssets.TryGetValue(identifier, let asset))
Asset asset = null;
_handleToAsset.TryGetValue(handle, out asset);
if (assetType == null)
{
return asset..AddRef();
return asset;
}
else if (asset.GetType() == assetType)
{
return asset;
}
else
{
// TODO: get default asset
return null;
}
}
public AssetHandle LoadAsset(StringView identifier)
{
if (_handles.TryGetValue(identifier, let asset))
{
return asset;
}
// Find subasset name
@@ -265,7 +291,7 @@ class EditorContentManager : IContentManager
if (resultNode case .Err)
{
Log.EngineLogger.Error($"Could not find asset \"{filePath}\".");
return null;
return .Invalid;
}
AssetFile file = resultNode->Value.AssetFile;
@@ -294,20 +320,19 @@ class EditorContentManager : IContentManager
delete stream;
if (loadedAsset == null)
return null;
return .Invalid;
//String identifierString = new .(identifier);
//_identifiers.Add(identifierString);
//_loadedAssets[identifierString] = loadedAsset;
loadedAsset.Identifier = identifier;
ManageAsset(loadedAsset);
AssetHandle handle = ManageAsset(loadedAsset);
file.[Friend]_loadedAsset = loadedAsset;
return loadedAsset;
return handle;
}
/// Saves the asset.
@@ -413,37 +438,57 @@ class EditorContentManager : IContentManager
return fs;*/
}
public void ManageAsset(Asset asset)
public AssetHandle ManageAsset(Asset asset)
{
_loadedAssets.Add(asset.Identifier, asset);
AssetHandle handle = .(asset.Identifier);
// TODO: to ensure that no two assets with the same handle exist.
_handles.Add(asset.Identifier, handle);
_handleToAsset.Add(handle, asset);
asset.[Friend]_contentManager = this;
asset.[Friend]_handle = handle;
return handle;
}
public void UnmanageAsset(Asset asset)
public void UnmanageAsset(AssetHandle handle)
{
_loadedAssets.Remove(asset.Identifier);
Log.EngineLogger.AssertDebug(_handles.ContainsValue(handle), "Handle isn't managed by this content manager.");
Log.EngineLogger.AssertDebug(_handleToAsset.ContainsKey(handle), "Handle doesn't correspond to an asset.");
Asset asset = _handleToAsset[handle];
_handles.Remove(asset.Identifier);
_handleToAsset.Remove(handle);
asset.[Friend]_contentManager = null;
asset.ReleaseRef();
}
/// This will unregister all assets from this content manager.
/// Note: This will not release any assets.
private void UnmanageAllAssets()
{
for (let (_, asset) in _loadedAssets)
for (let (_, assetHandle) in _handles)
{
UnmanageAsset(asset);
UnmanageAsset(assetHandle);
}
}
public void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier)
{
// TODO: this is much harder with asste handles that are basically hashed identifiers!
Runtime.NotImplemented();
if (oldIdentifier == newIdentifier)
return;
Log.EngineLogger.Assert(_loadedAssets.ContainsKey(newIdentifier), "An asset with the same identifier is already managed by this content manager.");
Log.EngineLogger.Assert(_handles.ContainsKey(newIdentifier), "An asset with the same identifier is already managed by this content manager.");
// Since all we do in order to track assets is add them to a dictionary we can simply unmanage and manage it again.
UnmanageAsset(asset);
ManageAsset(asset);
//UnmanageAsset(asset);
//ManageAsset(asset);
}
}
+2
View File
@@ -34,6 +34,8 @@ namespace GlitchyEngine
public bool IsMinimized => _isMinimized;
public GameTime GameTime => _gameTime;
[Inline]
public static Application Get() => s_Instance;
+10 -3
View File
@@ -10,6 +10,8 @@ namespace GlitchyEngine.Content;
[BonTarget]
class Asset : RefCounter
{
internal AssetHandle _handle;
private append String _identifier;
internal IContentManager _contentManager;
@@ -33,6 +35,8 @@ class Asset : RefCounter
/// Gets the content manager that manages this asset; or null if this asset isn't managed.
public IContentManager ContentManager => _contentManager;
public AssetHandle Handle => _handle;
static this
{
gBonEnv.typeHandlers.Add(typeof(Asset),
@@ -43,7 +47,7 @@ class Asset : RefCounter
{
// TODO: crash when _contentManager is deleted first...
// TODO: unregister from content manager
_contentManager?.UnmanageAsset(this);
//_contentManager?.UnmanageAsset(this);
}
static void AssetSerialize(BonWriter writer, ValueView value, BonEnvironment environment, SerializeValueState state)
@@ -56,7 +60,10 @@ class Asset : RefCounter
static Result<void> AssetDeserialize(BonReader reader, ValueView value, BonEnvironment environment, DeserializeValueState state)
{
Log.EngineLogger.Assert(value.type == typeof(Asset));
// TODO!!!
return .Err;
/*Log.EngineLogger.Assert(value.type == typeof(Asset));
String identifier = scope .();
@@ -75,7 +82,7 @@ class Asset : RefCounter
else
{
Deserialize.Error!("Invalid resource path", reader, value.type);
}
}*/
}
//gBonEnv.typeHandlers.Add(typeof(Resource<>),
+211
View File
@@ -0,0 +1,211 @@
using System;
using xxHash;
using System.Collections;
using System.Reflection;
using System.Diagnostics;
namespace GlitchyEngine.Content;
struct AssetHandle : uint64
{
/// Defines an asset that is invalid. E.g. because it couldn't be loaded.
public const AssetHandle Invalid = (.)0;
public this(StringView name)
{
this = (uint64)xxHash.ComputeHash(name);
}
[Inline]
public T Get<T>(IContentManager contentManager = null) where T : Asset
{
return Content.GetAsset<T>(this, contentManager);
}
}
struct AssetHandle<T> where T : Asset
{
private AssetHandle _handle;
private IContentManager _contentManager;
/*
* We don't increment/decrement the reference counter since we guarantee that we query for the asset every frame.
*/
private T _asset;
private uint8 _currentFrame;
public this(AssetHandle handle, IContentManager contentManager = null)
{
_handle = handle;
_contentManager = contentManager;
_asset = handle.Get<T>(contentManager);
_contentManager = _asset.ContentManager;
_currentFrame = (uint8)Application.Get().GameTime.FrameCount;
}
public T Get(IContentManager contentManager = null) mut
{
// We only care whether we are in a different frame -> we only compare the lower 8 bits.
uint8 actualFrame = (uint8)Application.Get().GameTime.FrameCount;
if (actualFrame != _currentFrame)
{
_asset = Content.GetAsset<T>(_handle, contentManager == null ? _contentManager : contentManager);
}
return _asset;
}
[Comptime, OnCompile(.TypeInit)]
static void Init()
{
for (var field in typeof(T).GetFields())
{
if (field.IsStatic || !field.IsPublic)
continue;
String modifier = field.IsPublic ? "public" : "private";
String code = scope $"""
{modifier} {field.FieldType} {field.Name}
{{
get mut
{{
return Get().{field.Name};
}}
set mut
{{
Get().{field.Name} = value;
}}
}}
""";
Compiler.EmitTypeBody(typeof(Self), code);
}
Dictionary<StringView, (MethodInfo? Getter, MethodInfo? Setter)> properties = scope .();
for (var method in typeof(T).GetMethods())
{
if (method.IsStatic || !method.IsPublic || method.IsConstructor || method.IsDestructor)
continue;
// Filter out destructors
if (method.Name == "~this")
continue;
if (method.Name.StartsWith("get__"))
{
StringView name = method.Name;
name.RemoveFromStart(5);
if (!properties.TryGetValue(name, var propertyInfo))
{
propertyInfo = default;
}
propertyInfo.Getter = method;
properties[name] = propertyInfo;
continue;
}
if (method.Name.StartsWith("set__"))
{
StringView name = method.Name;
name.RemoveFromStart(5);
if (!properties.TryGetValue(name, var propertyInfo))
{
propertyInfo = default;
}
propertyInfo.Setter = method;
properties[name] = propertyInfo;
continue;
}
String modifier = method.IsPublic ? "public" : "private";
String parameters = scope String();
String arguments = scope String();
for (int param < method.ParamCount)
{
Type paramType = method.GetParamType(param);
StringView paramName = method.GetParamName(param);
//String buffer = scope .();
//method.GetParamsDecl(buffer);
if (param != 0)
{
parameters.Append(", ");
arguments.Append(", ");
}
// TODO: Default value
parameters.AppendF($"{paramType} {paramName}");
/*if (!buffer.IsEmpty)
{
parameters.AppendF($" = {buffer}");
}*/
arguments.AppendF($" {paramName}");
}
String code = scope $"""
{modifier} {method.ReturnType} {method.Name}({parameters}) mut
{{
return Get().{method.Name}({arguments});
}}
""";
Compiler.EmitTypeBody(typeof(Self), code);
}
for (var (propertyName, property) in properties)
{
Type propertyType = property.Getter?.ReturnType ?? property.Setter?.GetParamType(0);
String getter = scope .();
String setter = scope .();
if (property.Getter != null)
{
getter.AppendF($"""
get mut
{{
return Get().{propertyName};
}}
""");
}
if (property.Setter != null)
{
getter.AppendF($"""
set mut
{{
Get().{propertyName} = value;
}}
""");
}
String code = scope $"""
public {propertyType} {propertyName}
{{
{getter}{setter}
}}
""";
Compiler.EmitTypeBody(typeof(Self), code);
}
}
}
+36 -11
View File
@@ -59,19 +59,30 @@ namespace GlitchyEngine.Content
Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager);
}
static
static class Content
{
/// Loads the specified asset with the given contentManager or the current applications content manager.
public static T LoadAsset<T>(StringView assetIdentifier, IContentManager contentManager = null) where T : Asset
public static AssetHandle LoadAsset(StringView assetIdentifier, IContentManager contentManager = null)
{
var contentManager;
if (contentManager == null)
contentManager = Application.Get().ContentManager;
Asset asset = contentManager.LoadAsset(assetIdentifier);
AssetHandle handle = contentManager.LoadAsset(assetIdentifier);
Log.EngineLogger.AssertDebug(asset is T);
return handle;
}
/// Loads the specified asset with the given contentManager or the current applications content manager.
public static T GetAsset<T>(AssetHandle handle, IContentManager contentManager = null) where T : Asset
{
var contentManager;
if (contentManager == null)
contentManager = Application.Get().ContentManager;
Asset asset = contentManager.GetAsset(typeof(T), handle);
return (T)asset;
}
@@ -79,14 +90,23 @@ namespace GlitchyEngine.Content
interface IContentManager
{
/// Loads the given asset.
Asset LoadAsset(StringView assetIdentifier);
/// Loads the Asset with the given handle and returns the handle.
AssetHandle LoadAsset(StringView assetIdentifier);
/// Returns the asset for the given handle or null, if it isn't loaded.
Asset GetAsset(AssetHandle handle)
{
return GetAsset(null, handle);
}
/// Returns the asset for the given handle or the default asset of the given type.
Asset GetAsset(Type assetType, AssetHandle handle);
/// The content manager will manage the asset (e.g. provide it when LoadAsset is called with the assets identifier)
void ManageAsset(Asset asset);
AssetHandle ManageAsset(Asset asset);
/// The content manager will no longer manage the asset.
void UnmanageAsset(Asset asset);
void UnmanageAsset(AssetHandle asset);
// TODO: Maybe calling UnmanageAsset -> ManageAsset is enough....
/// Provides a method for the asset to tell its content manager that the identifer changed.
@@ -109,17 +129,22 @@ namespace GlitchyEngine.Content
Runtime.NotImplemented();
}
public Asset LoadAsset(StringView assetIdentifier)
public AssetHandle LoadAsset(StringView assetIdentifier)
{
Runtime.NotImplemented();
}
public void ManageAsset(Asset asset)
public Asset GetAsset(Type assetType, AssetHandle handle)
{
Runtime.NotImplemented();
}
public void UnmanageAsset(Asset asset)
public AssetHandle ManageAsset(Asset asset)
{
Runtime.NotImplemented();
}
public void UnmanageAsset(AssetHandle asset)
{
Runtime.NotImplemented();
}
@@ -20,11 +20,10 @@ namespace GlitchyEngine.Generators
outFileName.Append(name);
outText.AppendF(
$"""
namespace {Namespace}
namespace {Namespace};
struct {name}
{{
struct {name}
{{
}}
}}
""");
}
+15 -2
View File
@@ -134,7 +134,20 @@ public class Effect : Asset
BufferVariableCollection _variables ~ delete _;
typealias TextureEntry = (TextureViewBinding BoundTexture, ShaderTextureCollection.ResourceEntry* VsSlot, ShaderTextureCollection.ResourceEntry* PsSlot);
public struct TextureEntry
{
public TextureViewBinding BoundTexture;
public ShaderTextureCollection.ResourceEntry* VsSlot;
public ShaderTextureCollection.ResourceEntry* PsSlot;
public this(TextureViewBinding boundTexture, ShaderTextureCollection.ResourceEntry* vsSlot, ShaderTextureCollection.ResourceEntry* psSlot)
{
BoundTexture = boundTexture;
VsSlot = vsSlot;
PsSlot = psSlot;
}
}
Dictionary<String, TextureEntry> _textures ~ delete _;
public Dictionary<String, TextureEntry> Textures => _textures;
@@ -725,7 +738,7 @@ public class Effect : Asset
// Get existing entry or create new
if(!_textures.TryGetValue(shaderEntry.Name, out entry))
{
entry = (shaderEntry.BoundTexture, null, null);
entry = .(shaderEntry.BoundTexture, null, null);
entry.BoundTexture.AddRef();
}
+1
View File
@@ -2,6 +2,7 @@ using GlitchyEngine.Math;
using GlitchyEngine.World;
using System.Collections;
using System;
using GlitchyEngine.Content;
namespace GlitchyEngine.Renderer
{
+12 -8
View File
@@ -24,7 +24,7 @@ namespace GlitchyEngine.World
// Temporary target for camera. Needs to change as soon as we support multiple cameras
private RenderTargetGroup _cameraTarget ~ _.ReleaseRef();
private Effect _gammaCorrectEffect ~ _.ReleaseRef();
private AssetHandle _gammaCorrectEffect;
// Maps ids to the entities they represent.
private Dictionary<UUID, EcsEntity> _idToEntity = new .() ~ delete _;
@@ -76,7 +76,7 @@ namespace GlitchyEngine.World
DepthTargetDescription = .(.D24_UNorm_S8_UInt)
});
_gammaCorrectEffect = Content.LoadAsset<Effect>("Shaders/GammaCorrect.hlsl");//Application.Get().EffectLibrary.Load("content/Shaders/GammaCorrect.hlsl");
_gammaCorrectEffect = Content.LoadAsset("Shaders/GammaCorrect.hlsl");//Application.Get().EffectLibrary.Load("content/Shaders/GammaCorrect.hlsl");
}
public ~this()
@@ -269,10 +269,12 @@ namespace GlitchyEngine.World
RenderCommand.SetRenderTargetGroup(finalTarget, false);
RenderCommand.BindRenderTargets();
_gammaCorrectEffect.SetTexture("Texture", _compositeTarget, 0);
Effect gammaEffect = Content.GetAsset<Effect>(_gammaCorrectEffect);
gammaEffect.SetTexture("Texture", _compositeTarget, 0);
// TODO: iiihhh
_gammaCorrectEffect.ApplyChanges();
_gammaCorrectEffect.Bind();
gammaEffect.ApplyChanges();
gammaEffect.Bind();
FullscreenQuad.Draw();
}
@@ -486,10 +488,12 @@ namespace GlitchyEngine.World
RenderCommand.SetRenderTargetGroup(viewportTarget, false);
RenderCommand.BindRenderTargets();
_gammaCorrectEffect.SetTexture("Texture", _compositeTarget, 0);
Effect gammaEffect = Content.GetAsset<Effect>(_gammaCorrectEffect);
gammaEffect.SetTexture("Texture", _compositeTarget, 0);
// TODO: iiihhh
_gammaCorrectEffect.ApplyChanges();
_gammaCorrectEffect.Bind();
gammaEffect.ApplyChanges();
gammaEffect.Bind();
FullscreenQuad.Draw();
}
+13 -10
View File
@@ -1,4 +1,4 @@
using GlitchyEngine.Renderer;
/*using GlitchyEngine.Renderer;
using GlitchyEngine.Events;
using GlitchyEngine.ImGui;
using GlitchyEngine.Math;
@@ -63,8 +63,8 @@ namespace Sandbox
Material _checkerMaterial ~ _?.ReleaseRef();
Material _logoMaterial ~ _?.ReleaseRef();
Texture2D _texture ~ _?.ReleaseRef();
Texture2D _ge_logo ~ _?.ReleaseRef();
AssetHandle _texture;
AssetHandle _ge_logo;
BlendState _alphaBlendState ~ _?.ReleaseRef();
BlendState _opaqueBlendState ~ _?.ReleaseRef();
@@ -91,7 +91,7 @@ namespace Sandbox
//effectLibrary.LoadNoRefInc("content\\Shaders\\testShader.hlsl");
Effect textureEffect = Content.LoadAsset<Effect>("Shaders\\textureShader.hlsl");
Effect textureEffect = Content.GetAsset<Effect>(Content.LoadAsset("Shaders\\textureShader.hlsl"));
_depthTarget = new DepthStencilTarget(_context.SwapChain.Width, _context.SwapChain.Height);
@@ -99,7 +99,7 @@ namespace Sandbox
VertexLayout vertexLayout = new VertexLayout(VertexColorTexture.VertexElements, false);
textureEffect.ReleaseRef();
//textureEffect.ReleaseRef();
// Create hexagon
{
@@ -174,8 +174,11 @@ namespace Sandbox
rsDesc.FrontCounterClockwise = false;
_rasterizerStateClockWise = new RasterizerState(rsDesc);
_texture = Content.LoadAsset<Texture2D>("content/Textures/Checkerboard.dds");//new Texture2D("content/Textures/Checkerboard.dds");
_ge_logo = Content.LoadAsset<Texture2D>("content/Textures/GE_Logo.dds");//new Texture2D("content/Textures/GE_Logo.dds");
_texture = Content.LoadAsset("content/Textures/Checkerboard.dds");//new Texture2D("content/Textures/Checkerboard.dds");
_ge_logo = Content.LoadAsset("content/Textures/GE_Logo.dds");//new Texture2D("content/Textures/GE_Logo.dds");
Texture2D texture = Content.GetAsset<Texture2D>(_texture);
Texture2D ge_logo = Content.GetAsset<Texture2D>(_ge_logo);
let sampler = SamplerStateManager.GetSampler(
SamplerStateDescription()
@@ -183,8 +186,8 @@ namespace Sandbox
MagFilter = .Point
});
_texture.SamplerState = sampler;
_ge_logo.SamplerState = sampler;
texture.SamplerState = sampler;
ge_logo.SamplerState = sampler;
sampler.ReleaseRef();
@@ -518,4 +521,4 @@ namespace Sandbox
}
}
}
}*/