From c22d122921d3e381407efaf6684bc7ae559ceada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Mon, 13 Mar 2023 20:35:30 +0100 Subject: [PATCH] Start of asset handles --- GlitchyEditor/src/EditorContentManager.bf | 85 +++++-- GlitchyEngine/src/Application.bf | 2 + GlitchyEngine/src/Content/Asset.bf | 13 +- GlitchyEngine/src/Content/AssetHandle.bf | 211 ++++++++++++++++++ GlitchyEngine/src/Content/ContentManager.bf | 47 +++- .../src/Generators/NewStructGenerator.bf | 7 +- GlitchyEngine/src/Renderer/Effect.bf | 17 +- GlitchyEngine/src/Renderer/Renderer.bf | 1 + GlitchyEngine/src/World/Scene.bf | 24 +- Sandbox/src/ExampleLayer.bf | 23 +- 10 files changed, 370 insertions(+), 60 deletions(-) create mode 100644 GlitchyEngine/src/Content/AssetHandle.bf diff --git a/GlitchyEditor/src/EditorContentManager.bf b/GlitchyEditor/src/EditorContentManager.bf index 7e583b7..0db13cc 100644 --- a/GlitchyEditor/src/EditorContentManager.bf +++ b/GlitchyEditor/src/EditorContentManager.bf @@ -81,8 +81,10 @@ class EditorContentManager : IContentManager //private append List _identifiers = .() ~ _.ClearAndDeleteItems(); - private append Dictionary _loadedAssets = .(); // Check if all resources are unloaded - + private append Dictionary _handles = .(); // TODO: Check if all resources are unloaded + + private append Dictionary _handleToAsset = .(); + private append AssetHierarchy _assetHierarchy = .(this); public AssetHierarchy AssetHierarchy => _assetHierarchy; @@ -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 GetAsset(Type assetType, AssetHandle handle) + { + Asset asset = null; + + _handleToAsset.TryGetValue(handle, out asset); + + if (assetType == null) + { + return asset; + } + else if (asset.GetType() == assetType) + { + return asset; + } + else + { + // TODO: get default asset + + return null; + } } - public Asset LoadAsset(StringView identifier) + public AssetHandle LoadAsset(StringView identifier) { - if (_loadedAssets.TryGetValue(identifier, let asset)) + if (_handles.TryGetValue(identifier, let asset)) { - return asset..AddRef(); + 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); } } \ No newline at end of file diff --git a/GlitchyEngine/src/Application.bf b/GlitchyEngine/src/Application.bf index 5f951c6..0aad8ba 100644 --- a/GlitchyEngine/src/Application.bf +++ b/GlitchyEngine/src/Application.bf @@ -34,6 +34,8 @@ namespace GlitchyEngine public bool IsMinimized => _isMinimized; + public GameTime GameTime => _gameTime; + [Inline] public static Application Get() => s_Instance; diff --git a/GlitchyEngine/src/Content/Asset.bf b/GlitchyEngine/src/Content/Asset.bf index da33c2f..efd0ffb 100644 --- a/GlitchyEngine/src/Content/Asset.bf +++ b/GlitchyEngine/src/Content/Asset.bf @@ -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 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<>), diff --git a/GlitchyEngine/src/Content/AssetHandle.bf b/GlitchyEngine/src/Content/AssetHandle.bf new file mode 100644 index 0000000..47e3097 --- /dev/null +++ b/GlitchyEngine/src/Content/AssetHandle.bf @@ -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(IContentManager contentManager = null) where T : Asset + { + return Content.GetAsset(this, contentManager); + } +} + +struct AssetHandle 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(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(_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 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); + } + } +} \ No newline at end of file diff --git a/GlitchyEngine/src/Content/ContentManager.bf b/GlitchyEngine/src/Content/ContentManager.bf index 41d6a94..36c5f26 100644 --- a/GlitchyEngine/src/Content/ContentManager.bf +++ b/GlitchyEngine/src/Content/ContentManager.bf @@ -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(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(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(); } diff --git a/GlitchyEngine/src/Generators/NewStructGenerator.bf b/GlitchyEngine/src/Generators/NewStructGenerator.bf index d7fc1bd..dfe5815 100644 --- a/GlitchyEngine/src/Generators/NewStructGenerator.bf +++ b/GlitchyEngine/src/Generators/NewStructGenerator.bf @@ -20,11 +20,10 @@ namespace GlitchyEngine.Generators outFileName.Append(name); outText.AppendF( $""" - namespace {Namespace} + namespace {Namespace}; + + struct {name} {{ - struct {name} - {{ - }} }} """); } diff --git a/GlitchyEngine/src/Renderer/Effect.bf b/GlitchyEngine/src/Renderer/Effect.bf index 657b346..961a401 100644 --- a/GlitchyEngine/src/Renderer/Effect.bf +++ b/GlitchyEngine/src/Renderer/Effect.bf @@ -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 _textures ~ delete _; public Dictionary 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(); } diff --git a/GlitchyEngine/src/Renderer/Renderer.bf b/GlitchyEngine/src/Renderer/Renderer.bf index 3dc39aa..d94d5de 100644 --- a/GlitchyEngine/src/Renderer/Renderer.bf +++ b/GlitchyEngine/src/Renderer/Renderer.bf @@ -2,6 +2,7 @@ using GlitchyEngine.Math; using GlitchyEngine.World; using System.Collections; using System; +using GlitchyEngine.Content; namespace GlitchyEngine.Renderer { diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index 7531afb..806bcf7 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -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 _idToEntity = new .() ~ delete _; @@ -76,7 +76,7 @@ namespace GlitchyEngine.World DepthTargetDescription = .(.D24_UNorm_S8_UInt) }); - _gammaCorrectEffect = Content.LoadAsset("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() @@ -268,11 +268,13 @@ namespace GlitchyEngine.World RenderCommand.UnbindRenderTargets(); RenderCommand.SetRenderTargetGroup(finalTarget, false); RenderCommand.BindRenderTargets(); - - _gammaCorrectEffect.SetTexture("Texture", _compositeTarget, 0); + + Effect gammaEffect = Content.GetAsset(_gammaCorrectEffect); + + gammaEffect.SetTexture("Texture", _compositeTarget, 0); // TODO: iiihhh - _gammaCorrectEffect.ApplyChanges(); - _gammaCorrectEffect.Bind(); + gammaEffect.ApplyChanges(); + gammaEffect.Bind(); FullscreenQuad.Draw(); } @@ -485,11 +487,13 @@ namespace GlitchyEngine.World RenderCommand.UnbindRenderTargets(); RenderCommand.SetRenderTargetGroup(viewportTarget, false); RenderCommand.BindRenderTargets(); - - _gammaCorrectEffect.SetTexture("Texture", _compositeTarget, 0); + + Effect gammaEffect = Content.GetAsset(_gammaCorrectEffect); + + gammaEffect.SetTexture("Texture", _compositeTarget, 0); // TODO: iiihhh - _gammaCorrectEffect.ApplyChanges(); - _gammaCorrectEffect.Bind(); + gammaEffect.ApplyChanges(); + gammaEffect.Bind(); FullscreenQuad.Draw(); } diff --git a/Sandbox/src/ExampleLayer.bf b/Sandbox/src/ExampleLayer.bf index 0a832e8..5725797 100644 --- a/Sandbox/src/ExampleLayer.bf +++ b/Sandbox/src/ExampleLayer.bf @@ -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("Shaders\\textureShader.hlsl"); + Effect textureEffect = Content.GetAsset(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("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"); + _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(_texture); + Texture2D ge_logo = Content.GetAsset(_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 } } -} \ No newline at end of file +}*/ \ No newline at end of file