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);
}
}