Skip to content

Commit

Permalink
Refactor and centralize resource management.
Browse files Browse the repository at this point in the history
Introduce a new `GlobalResource` class to centralize graphics device, default effect, and texture management, reducing redundancy. Updated multiple components to utilize shared resources. Enhanced pipeline description handling and caching, optimizing pipeline setup and reuse.
  • Loading branch information
MrScautHD committed Dec 22, 2024
1 parent f2fbc11 commit 552f8f3
Show file tree
Hide file tree
Showing 16 changed files with 295 additions and 118 deletions.
13 changes: 10 additions & 3 deletions src/Bliss.Test/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ public void Run() {
this.SetTargetFps(this.Settings.TargetFps);

Logger.Info("Initialize command list...");
this.CommandList = this.GraphicsDevice.ResourceFactory.CreateCommandList();
this.CommandList = graphicsDevice.ResourceFactory.CreateCommandList();

Logger.Info("Initialize global resources...");
GlobalResource.Init(graphicsDevice);

Logger.Info("Initialize input...");
if (this.MainWindow is Sdl3Window) {
Expand Down Expand Up @@ -133,7 +136,7 @@ public void Run() {
this._fixedUpdateTimer -= this._fixedUpdateTimeStep;
}

this.Draw(this.GraphicsDevice, this.CommandList);
this.Draw(graphicsDevice, this.CommandList);
Input.End();
}

Expand Down Expand Up @@ -217,7 +220,7 @@ protected virtual void Draw(GraphicsDevice graphicsDevice, CommandList commandLi
commandList.ResolveTexture(this.FullScreenTexture.ColorTexture, this.FullScreenTexture.DestinationTexture);
}

commandList.SetFramebuffer(this.GraphicsDevice.SwapchainFramebuffer);
commandList.SetFramebuffer(graphicsDevice.SwapchainFramebuffer);
commandList.ClearColorTarget(0, Color.DarkGray.ToRgbaFloat());

this.FullScreenRenderPass.Draw(commandList, this.FullScreenTexture, SamplerType.Point);
Expand Down Expand Up @@ -249,6 +252,10 @@ protected override void Dispose(bool disposing) {
this.GraphicsDevice.Dispose();
this.MainWindow.Dispose();
Input.Destroy();
GlobalResource.Destroy();

this._playerModel.Dispose();
this._planeModel.Dispose();
}
}
}
62 changes: 49 additions & 13 deletions src/Bliss/CSharp/Effects/Effect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* https://github.com/MrScautHD/Bliss/blob/main/LICENSE
*/

using Bliss.CSharp.Graphics.Pipelines;
using Bliss.CSharp.Logging;
using Veldrid;
using Veldrid.SPIRV;
Expand All @@ -14,6 +15,11 @@ namespace Bliss.CSharp.Effects;

public class Effect : Disposable {

/// <summary>
/// The graphics device used for creating and managing graphical resources.
/// </summary>
public GraphicsDevice GraphicsDevice { get; private set; }

/// <summary>
/// Represents a pair of shaders consisting of a vertex shader and a fragment shader.
/// </summary>
Expand All @@ -25,31 +31,40 @@ public class Effect : Disposable {
public readonly VertexLayoutDescription VertexLayout;

/// <summary>
/// Initializes a new instance of the <see cref="Effect"/> class by loading and compiling shaders and setting up the vertex layout.
/// A cache of pipelines created for specific pipeline descriptions, enabling reuse.
/// </summary>
/// <param name="resourceFactory">The resource factory used to create GPU resources.</param>
/// <param name="vertexLayout">The vertex layout description to be used with this effect.</param>
private Dictionary<SimplePipelineDescription, SimplePipeline> _cachedPipelines;

/// <summary>
/// Initializes a new instance of the <see cref="Effect"/> class by loading shaders from file paths.
/// </summary>
/// <param name="graphicsDevice">The graphics device used for creating resources.</param>
/// <param name="vertexLayout">The vertex layout description for the pipeline.</param>
/// <param name="vertPath">The file path to the vertex shader source code.</param>
/// <param name="fragPath">The file path to the fragment shader source code.</param>
public Effect(ResourceFactory resourceFactory, VertexLayoutDescription vertexLayout, string vertPath, string fragPath) : this(resourceFactory, vertexLayout, LoadBytecode(vertPath), LoadBytecode(fragPath)) { }

public Effect(GraphicsDevice graphicsDevice, VertexLayoutDescription vertexLayout, string vertPath, string fragPath) : this(graphicsDevice, vertexLayout, LoadBytecode(vertPath), LoadBytecode(fragPath)) { }
/// <summary>
/// Initializes a new instance of the <see cref="Effect"/> class with the specified vertex layout and shader bytecode.
/// Initializes a new instance of the <see cref="Effect"/> class with precompiled shader bytecode.
/// </summary>
/// <param name="resourceFactory">The resource factory used to create shaders and resources.</param>
/// <param name="vertexLayout">The layout of the vertex data for this effect.</param>
/// <param name="vertBytes">A byte array containing the vertex shader bytecode.</param>
/// <param name="fragBytes">A byte array containing the fragment shader bytecode.</param>
public Effect(ResourceFactory resourceFactory, VertexLayoutDescription vertexLayout, byte[] vertBytes, byte[] fragBytes) {
/// <param name="graphicsDevice">The graphics device used for creating resources.</param>
/// <param name="vertexLayout">The vertex layout description for the pipeline.</param>
/// <param name="vertBytes">The bytecode for the vertex shader.</param>
/// <param name="fragBytes">The bytecode for the fragment shader.</param>
public Effect(GraphicsDevice graphicsDevice, VertexLayoutDescription vertexLayout, byte[] vertBytes, byte[] fragBytes) {
this.GraphicsDevice = graphicsDevice;

ShaderDescription vertDescription = new ShaderDescription(ShaderStages.Vertex, vertBytes, "main");
ShaderDescription fragDescription = new ShaderDescription(ShaderStages.Fragment, fragBytes, "main");

Shader[] shaders = resourceFactory.CreateFromSpirv(vertDescription, fragDescription);
Shader[] shaders = graphicsDevice.ResourceFactory.CreateFromSpirv(vertDescription, fragDescription);

this.Shader.Item1 = shaders[0];
this.Shader.Item2 = shaders[1];

this.VertexLayout = vertexLayout;

this._cachedPipelines = new Dictionary<SimplePipelineDescription, SimplePipeline>();
}

/// <summary>
Expand All @@ -69,9 +84,26 @@ private static byte[] LoadBytecode(string path) {
Logger.Info($"Shader bytes loaded successfully from path: [{path}]");
return File.ReadAllBytes(path);
}

/// <summary>
/// Retrieves or creates a pipeline for the given pipeline description.
/// </summary>
/// <param name="pipelineDescription">The description of the pipeline to retrieve or create.</param>
/// <returns>A <see cref="SimplePipeline"/> configured with the specified description.</returns>
public SimplePipeline GetPipeline(SimplePipelineDescription pipelineDescription) {
if (!this._cachedPipelines.TryGetValue(pipelineDescription, out SimplePipeline? pipeline)) {
SimplePipeline newPipeline = new SimplePipeline(this.GraphicsDevice, pipelineDescription);

Logger.Error(pipelineDescription.ToString());

this._cachedPipelines.Add(pipelineDescription, newPipeline);
return newPipeline;
}

return pipeline;
}

// TODO: Adding Location system, for Material(Texture, Color) and in generel for buffers...

// TODO: ADD MATERIAL param here.
/// <summary>
/// Apply the state effect immediately before rendering it.
Expand All @@ -82,6 +114,10 @@ protected override void Dispose(bool disposing) {
if (disposing) {
this.Shader.Item1.Dispose();
this.Shader.Item2.Dispose();

foreach (SimplePipeline pipeline in this._cachedPipelines.Values) {
pipeline.Dispose();
}
}
}
}
8 changes: 8 additions & 0 deletions src/Bliss/CSharp/Geometry/Animations/Keyframes/QuatKey.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* Copyright (c) 2024 Elias Springer (@MrScautHD)
* License-Identifier: Bliss License 1.0
*
* For full license details, see:
* https://github.com/MrScautHD/Bliss/blob/main/LICENSE
*/

using System.Numerics;

namespace Bliss.CSharp.Geometry.Animations.Keyframes;
Expand Down
8 changes: 8 additions & 0 deletions src/Bliss/CSharp/Geometry/Animations/Keyframes/Vector3Key.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* Copyright (c) 2024 Elias Springer (@MrScautHD)
* License-Identifier: Bliss License 1.0
*
* For full license details, see:
* https://github.com/MrScautHD/Bliss/blob/main/LICENSE
*/

using System.Numerics;

namespace Bliss.CSharp.Geometry.Animations.Keyframes;
Expand Down
4 changes: 2 additions & 2 deletions src/Bliss/CSharp/Geometry/Animations/MeshAmateurBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,8 @@ private Matrix4x4 InterpolateScale(NodeAnimChannel channel, ModelAnimation anima
}
}

Vector3Key currentFrame = channel.Scales[(int)frameIndex];
Vector3Key nextFrame = channel.Scales[(int)((frameIndex + 1) % channel.Scales.Count)];
Vector3Key currentFrame = channel.Scales[(int) frameIndex];
Vector3Key nextFrame = channel.Scales[(int) ((frameIndex + 1) % channel.Scales.Count)];

double delta = (frameTime - currentFrame.Time) / (nextFrame.Time - currentFrame.Time);

Expand Down
8 changes: 8 additions & 0 deletions src/Bliss/CSharp/Geometry/Animations/NodeAnimChannel.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* Copyright (c) 2024 Elias Springer (@MrScautHD)
* License-Identifier: Bliss License 1.0
*
* For full license details, see:
* https://github.com/MrScautHD/Bliss/blob/main/LICENSE
*/

using Bliss.CSharp.Geometry.Animations.Keyframes;

namespace Bliss.CSharp.Geometry.Animations;
Expand Down
92 changes: 43 additions & 49 deletions src/Bliss/CSharp/Geometry/Mesh.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ namespace Bliss.CSharp.Geometry;

public class Mesh : Disposable {

/// <summary>
/// A dictionary that caches instances of SimplePipeline based on Material keys.
/// This helps in reusing pipeline configurations for materials, enhancing rendering performance and reducing redundant pipeline creation.
/// </summary>
private static Dictionary<Material, SimplePipeline> _cachedPipelines = new(); // TODO: Take care of disposing it idk maybe a system that checks if its the last mesh that using it and dispose it with it.

/// <summary>
/// Represents the graphics device used for rendering operations.
/// This property provides access to the underlying GraphicsDevice instance responsible for managing GPU resources and executing rendering commands.
Expand Down Expand Up @@ -101,6 +95,13 @@ public class Mesh : Disposable {
/// This buffer holds an array of structures representing bone matrices and is utilized during rendering to apply bone transformations to vertices.
/// </summary>
private SimpleBuffer<Matrix4x4> _boneBuffer;

/// <summary>
/// Defines the characteristics of the rendering pipeline used by the mesh.
/// This field specifies the pipeline configurations such as blending, depth stencil, rasterizer state,
/// primitive topology, associated buffers, texture layouts, shader set, and output descriptions.
/// </summary>
private SimplePipelineDescription _pipelineDescription;

/// <summary>
/// Initializes a new instance of the <see cref="Mesh"/> class with the specified properties.
Expand Down Expand Up @@ -138,6 +139,8 @@ public Mesh(GraphicsDevice graphicsDevice, Material material, Vertex3D[]? vertic
}

this._boneBuffer.UpdateBufferImmediate();

this._pipelineDescription = this.CreatePipelineDescription();
}

/// <summary>
Expand Down Expand Up @@ -191,6 +194,11 @@ public void Draw(CommandList commandList, OutputDescription output, Transform tr
this._modelMatrixBuffer.SetValue(1, cam3D.GetView());
this._modelMatrixBuffer.SetValue(2, transform.GetTransform());
this._modelMatrixBuffer.UpdateBuffer(commandList);

// Update pipeline description.
this._pipelineDescription.BlendState = this.Material.BlendState.Description;
this._pipelineDescription.TextureLayouts = this.Material.GetTextureLayouts();
this._pipelineDescription.Outputs = output;

if (this.IndexCount > 0) {

Expand All @@ -199,7 +207,7 @@ public void Draw(CommandList commandList, OutputDescription output, Transform tr
commandList.SetIndexBuffer(this._indexBuffer, IndexFormat.UInt32);

// Set pipeline.
commandList.SetPipeline(this.GetOrCreatePipeline(this.Material, output).Pipeline);
commandList.SetPipeline(this.Material.Effect.GetPipeline(this._pipelineDescription).Pipeline);

// Set projection view buffer.
commandList.SetGraphicsResourceSet(0, this._modelMatrixBuffer.ResourceSet);
Expand All @@ -226,7 +234,7 @@ public void Draw(CommandList commandList, OutputDescription output, Transform tr
commandList.SetVertexBuffer(0, this._vertexBuffer);

// Set pipeline.
commandList.SetPipeline(this.GetOrCreatePipeline(this.Material, output).Pipeline);
commandList.SetPipeline(this.Material.Effect.GetPipeline(this._pipelineDescription).Pipeline);

// Set projection view buffer.
commandList.SetGraphicsResourceSet(0, this._modelMatrixBuffer.ResourceSet);
Expand All @@ -252,49 +260,35 @@ public void Draw(CommandList commandList, OutputDescription output, Transform tr
this.Material.SetMapColor(MaterialMapType.Albedo.ToString(), cachedColor);
}

/// <summary>
/// Retrieves an existing pipeline associated with the given material and output description, or creates a new one if it doesn't exist.
/// </summary>
/// <param name="material">The material associated with the pipeline.</param>
/// <param name="output">The output description for the pipeline.</param>
/// <returns>A pipeline that matches the specified material and output description.</returns>
private SimplePipeline GetOrCreatePipeline(Material material, OutputDescription output) {
if (!_cachedPipelines.TryGetValue(material, out SimplePipeline? pipeline)) {
SimplePipeline newPipeline = new SimplePipeline(this.GraphicsDevice, new SimplePipelineDescription() {
BlendState = material.BlendState.Description,
DepthStencilState = new DepthStencilStateDescription(true, true, ComparisonKind.LessEqual),
RasterizerState = new RasterizerStateDescription() {
CullMode = FaceCullMode.Back,
FillMode = PolygonFillMode.Solid,
FrontFace = FrontFace.Clockwise,
DepthClipEnabled = true,
ScissorTestEnabled = false
},
PrimitiveTopology = PrimitiveTopology.TriangleList,
Buffers = [
this._modelMatrixBuffer,
this._boneBuffer
private SimplePipelineDescription CreatePipelineDescription() {
return new SimplePipelineDescription() {
BlendState = this.Material.BlendState.Description,
DepthStencilState = new DepthStencilStateDescription(true, true, ComparisonKind.LessEqual),
RasterizerState = new RasterizerStateDescription() {
CullMode = FaceCullMode.Back,
FillMode = PolygonFillMode.Solid,
FrontFace = FrontFace.Clockwise,
DepthClipEnabled = true,
ScissorTestEnabled = false
},
PrimitiveTopology = PrimitiveTopology.TriangleList,
Buffers = [
this._modelMatrixBuffer,
this._boneBuffer
],
TextureLayouts = this.Material.GetTextureLayouts(),
ShaderSet = new ShaderSetDescription() {
VertexLayouts = [
this.Material.Effect.VertexLayout
],
TextureLayouts = material.GetTextureLayouts(),
ShaderSet = new ShaderSetDescription() {
VertexLayouts = [
material.Effect.VertexLayout
],
Shaders = [
material.Effect.Shader.Item1,
material.Effect.Shader.Item2
]
},
Outputs = output
});

_cachedPipelines.Add(material, newPipeline);
return newPipeline;
}

return pipeline;
Shaders = [
this.Material.Effect.Shader.Item1,
this.Material.Effect.Shader.Item2
]
}
};
}

/// <summary>
/// Calculates the bounding box for the current mesh based on its vertices.
/// </summary>
Expand Down
Loading

0 comments on commit 552f8f3

Please sign in to comment.