using Ghost.Core; using Ghost.Core.Graphics; using Ghost.Graphics.Core; using Ghost.Graphics.RHI; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; namespace Ghost.Graphics; public interface IResourceManager { IResourceAllocator ResourceAllocator { get; } IResourceDatabase ResourceDatabase { get; } /// /// Creates a new mesh from the specified vertex and index data. /// /// A UnsafeList containing the vertices that define the geometry of the mesh. Must contain at least one vertex. /// A UnsafeList containing the indices that specify how vertices are connected to form primitives. Must contain at least one index. /// An representing the newly created mesh. Handle CreateMesh(UnsafeList vertices, UnsafeList indices); /// /// Creates a new material instance using the specified shader. /// /// The identifier of the shader to associate with the new material. /// An representing the newly created material. Handle CreateMaterial(Identifier shader); /// /// Creates a new shader and returns its unique identifier. /// /// An representing the newly created shader. /// The viewGroup containing the shader's properties and passes. Identifier CreateGraphicsShader(ShaderDescriptor descriptor); /// /// Determines whether a mesh with the specified Handle exists. /// /// The handle of the mesh to check for existence. Cannot be null. /// true if a mesh with the specified Handle exists; otherwise, false. bool HasMesh(Handle handle); /// /// Returns a reference to the mesh associated with the specified handle. /// /// The handle of the mesh to retrieve. Must refer to a valid mesh; otherwise, the behavior is undefined. /// A result containing a reference to the mesh corresponding to the specified handle, or an error status if the handle is invalid. RefResult GetMeshReference(Handle handle); /// /// Releases the mesh resource associated with the specified handle, freeing any resources held by it. Includes both CPU and GPU resources. /// /// The handle of the mesh to release. Must refer to a mesh that was previously created and not already released. void ReleaseMesh(Handle handle); /// /// Determines whether a material with the specified handle exists in the collection. /// /// The handle of the material to check for existence. /// true if a material with the specified handle exists; otherwise, false. bool HasMaterial(Handle handle); /// /// Gets a reference to the material associated with the specified handle. /// /// The handle of the material to retrieve. Must refer to a valid material. /// A result containing a reference to the material corresponding to the specified handle, or an error status if the handle is invalid. RefResult GetMaterialReference(Handle handle); /// /// Releases the material associated with the specified handle, making it available for reuse or disposal. /// /// The handle of the material to release. Must refer to a material that has been previously acquired. void ReleaseMaterial(Handle handle); /// /// Determines whether a shader with the specified identifier exists in the collection. /// /// The identifier of the shader to check for existence. /// true if a shader with the specified identifier exists; otherwise, false. bool HasShader(Identifier id); /// /// Returns a reference to the shader associated with the specified identifier. /// /// The identifier of the shader to retrieve. Must refer to a valid shader. /// A result containing a reference to the shader corresponding to the specified identifier, or an error status if the identifier is invalid. RefResult GetShaderReference(Identifier id); /// /// Releases the shader associated with the specified identifier, freeing any resources allocated to it. /// /// The identifier of the shader to release. Must refer to a valid, previously created shader. void ReleaseShader(Identifier id); } internal sealed class ResourceManager : IResourceManager, IDisposable { private readonly IResourceAllocator _resourceAllocator; private readonly IResourceDatabase _resourceDatabase; private UnsafeSlotMap _meshes; private UnsafeSlotMap _materials; private UnsafeList _shaders; // TODO: Use SlotMap? private bool _disposed; public IResourceAllocator ResourceAllocator => _resourceAllocator; public IResourceDatabase ResourceDatabase => _resourceDatabase; public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase) { _resourceAllocator = resourceAllocator; _resourceDatabase = resourceDatabase; _meshes = new UnsafeSlotMap(64, Allocator.Persistent); _materials = new UnsafeSlotMap(16, Allocator.Persistent); _shaders = new UnsafeList(16, Allocator.Persistent); } ~ResourceManager() { Dispose(); } public unsafe Handle CreateMesh(UnsafeList vertices, UnsafeList indices) { ObjectDisposedException.ThrowIf(_disposed, this); var vertexBufferDesc = new BufferDesc { Size = (uint)(vertices.Count * sizeof(Vertex)), Stride = (uint)sizeof(Vertex), Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw, MemoryType = ResourceMemoryType.Default, }; var indexBufferDesc = new BufferDesc { Size = (uint)(indices.Count * sizeof(uint)), Stride = sizeof(uint), Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw, MemoryType = ResourceMemoryType.Default, }; var objectBufferDesc = new BufferDesc { Size = (uint)sizeof(PerObjectData), Stride = (uint)sizeof(PerObjectData), Usage = BufferUsage.Raw | BufferUsage.ShaderResource, MemoryType = ResourceMemoryType.Default, }; var vertexBuffer = _resourceAllocator.CreateBuffer(in vertexBufferDesc, "VertexBuffer"); var indexBuffer = _resourceAllocator.CreateBuffer(in indexBufferDesc, "IndexBuffer"); var objectBuffer = _resourceAllocator.CreateBuffer(in objectBufferDesc, "ObjectBuffer"); var mesh = new Mesh { Vertices = vertices, Indices = indices, VertexBuffer = vertexBuffer, IndexBuffer = indexBuffer, ObjectDataBuffer = objectBuffer, }; var id = _meshes.Add(mesh, out var generation); return new Handle(id, generation); } public Handle CreateMaterial(Identifier shader) { ObjectDisposedException.ThrowIf(_disposed, this); var material = new Material(); if (material.SetShader(shader, this) != Error.None) { return Handle.Invalid; } var id = _materials.Add(material, out var generation); return new Handle(id, generation); } public Identifier CreateGraphicsShader(ShaderDescriptor descriptor) { ObjectDisposedException.ThrowIf(_disposed, this); var shader = new Shader(descriptor); var id = _shaders.Count; _shaders.Add(shader); return new Identifier(id); } public bool HasMesh(Handle handle) { ObjectDisposedException.ThrowIf(_disposed, this); return _meshes.Contains(handle.ID, handle.Generation); } public RefResult GetMeshReference(Handle handle) { ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist); if (!exist) { return Error.NotFound; } return RefResult.Success(ref mesh); } public void ReleaseMesh(Handle handle) { ObjectDisposedException.ThrowIf(_disposed, this); if (!_meshes.TryGetElementAt(handle.ID, handle.Generation, out var mesh)) { return; } _meshes.Remove(handle.ID, handle.Generation); mesh.ReleaseResource(_resourceDatabase); } public bool HasMaterial(Handle handle) { ObjectDisposedException.ThrowIf(_disposed, this); return _materials.Contains(handle.ID, handle.Generation); } public RefResult GetMaterialReference(Handle handle) { ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist); if (!exist) { return Error.NotFound; } return RefResult.Success(ref material); } public void ReleaseMaterial(Handle handle) { ObjectDisposedException.ThrowIf(_disposed, this); var material = _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist); if (!exist) { return; } _materials.Remove(handle.ID, handle.Generation); material.ReleaseResource(_resourceDatabase); } public bool HasShader(Identifier id) { ObjectDisposedException.ThrowIf(_disposed, this); return id.Value >= 0 && id.Value < _shaders.Count; } public RefResult GetShaderReference(Identifier id) { if (!HasShader(id)) { return Error.NotFound; } return RefResult.Success(ref _shaders[id.Value]); } public void ReleaseShader(Identifier id) { ObjectDisposedException.ThrowIf(_disposed, this); if (!HasShader(id)) { return; } var shader = _shaders[id.Value]; shader.ReleaseResource(_resourceDatabase); } public void Dispose() { if (_disposed) { return; } foreach (var mesh in _meshes) { mesh.ReleaseResource(_resourceDatabase); } foreach (var material in _materials) { material.ReleaseResource(_resourceDatabase); } foreach (var shader in _shaders) { shader.ReleaseResource(_resourceDatabase); } _meshes.Dispose(); _materials.Dispose(); _shaders.Dispose(); _disposed = true; GC.SuppressFinalize(this); } }