Skip to content

Commit

Permalink
Added SampleType, Added BlendState, Added GraphicsHelper, Worked on S…
Browse files Browse the repository at this point in the history
…priteBatch
  • Loading branch information
MrScautHD committed Sep 7, 2024
1 parent 7fd3d4b commit 0d4003b
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 45 deletions.
7 changes: 3 additions & 4 deletions src/Bliss.Test/Game.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Numerics;
using Bliss.CSharp;
using Bliss.CSharp.Colors;
using Bliss.CSharp.Graphics;
using Bliss.CSharp.Graphics.Rendering.Sprites;
using Bliss.CSharp.Interact;
using Bliss.CSharp.Logging;
Expand Down Expand Up @@ -106,11 +107,9 @@ protected virtual void Draw(GraphicsDevice graphicsDevice, CommandList commandLi
this.CommandList.Begin();
this.CommandList.SetFramebuffer(this.GraphicsDevice.SwapchainFramebuffer);
this.CommandList.ClearColorTarget(0, Color.DarkGray.ToRgbaFloat());

this._spriteBatch.Begin(commandList);
this._spriteBatch.DrawTexture(this._texture, graphicsDevice.PointSampler, new Vector2(150, 150), default, default, new Vector2(216.0F / 2.0F, 85.0F / 2.0F), 0, Color.White, SpriteFlip.None);

//this._spriteBatch.DrawDebugRectangle(new Vector2(10, 10), new Vector2(200, 200), Color.Blue);
this._spriteBatch.Begin(commandList);
this._spriteBatch.DrawTexture(this._texture, SamplerType.Point, new Vector2(150, 150), default, default, new Vector2(216.0F / 2.0F, 85.0F / 2.0F), 0, Color.White, SpriteFlip.None);
this._spriteBatch.End();

this.CommandList.End();
Expand Down
43 changes: 43 additions & 0 deletions src/Bliss/CSharp/Graphics/BlendState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Bliss.CSharp.Logging;
using Veldrid;

namespace Bliss.CSharp.Graphics;

public class BlendState {

private static Dictionary<BlendStateDescription, BlendState> _cachedBlendStates = new();

public static BlendState Disabled => FromDescription(BlendStateDescription.SingleDisabled);
public static BlendState AdditiveBlend => FromDescription(BlendStateDescription.SingleAdditiveBlend);
public static BlendState AlphaBlend => FromDescription(BlendStateDescription.SingleAlphaBlend);
public static BlendState OverrideBlend => FromDescription(BlendStateDescription.SingleOverrideBlend);

public readonly BlendStateDescription Description;

/// <summary>
/// Represents a state for blending operations in graphics rendering. It provides predefined blend states such as
/// Empty, Disabled, AdditiveBlend, AlphaBlend, and OverrideBlend. This class allows for the creation or retrieval
/// of blend states based on a given description.
/// </summary>
private BlendState(BlendStateDescription description) {
this.Description = description;
}

/// <summary>
/// Creates a new or retrieves an existing <see cref="BlendState"/> instance based on the provided
/// <see cref="BlendStateDescription"/>.
/// </summary>
/// <param name="description">The description of the blend state to create or retrieve.</param>
/// <returns>The <see cref="BlendState"/> instance corresponding to the provided description.</returns>
public static BlendState FromDescription(BlendStateDescription description) {
if (!_cachedBlendStates.TryGetValue(description, out BlendState? state)) {
Logger.Info("Create a new BlendState.");
BlendState blendState = new BlendState(description);

_cachedBlendStates.Add(description, blendState);
return blendState;
}

return state;
}
}
21 changes: 21 additions & 0 deletions src/Bliss/CSharp/Graphics/GraphicsHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Veldrid;

namespace Bliss.CSharp.Graphics;

public static class GraphicsHelper {
/// <summary>
/// Retrieves a Sampler object based on the provided SamplerType.
/// </summary>
/// <param name="graphicsDevice">The graphics device used to create the sampler.</param>
/// <param name="samplerType">The type of sampler to retrieve.</param>
/// <returns>A Sampler object corresponding to the specified SamplerType.</returns>
/// <exception cref="ArgumentException">Thrown when an unsupported sampler type is provided.</exception>
public static Sampler GetSampler(GraphicsDevice graphicsDevice, SamplerType samplerType) {
return samplerType switch {
SamplerType.Point => graphicsDevice.PointSampler,
SamplerType.Linear => graphicsDevice.LinearSampler,
SamplerType.Aniso4X => graphicsDevice.Aniso4xSampler,
_ => throw new ArgumentException($"Unsupported sampler type: {samplerType}", nameof(samplerType))
};
}
}
98 changes: 57 additions & 41 deletions src/Bliss/CSharp/Graphics/Rendering/Sprites/SpriteBatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ public class SpriteBatch : Disposable {

public int DrawCallCount { get; private set; }

private Dictionary<(Effect, BlendState), SimplePipeline> _cachedPipelines;
private Dictionary<(Texture2D, Sampler), ResourceSet> _cachedTextures;

private Effect _defaultEffect;

private Vertex2D[] _vertices;
private ushort[] _indices;

Expand All @@ -42,21 +45,20 @@ public class SpriteBatch : Disposable {
private DeviceBuffer _transformBuffer;
private ResourceLayout _transformLayout;
private ResourceSet _transformSet;

private Effect _defaultEffect;
private ResourceLayout _defaultPipelineResourceLayout;
private SimplePipeline _defaultPipeline;

private bool _begun;

private ResourceLayout _pipelineLayout;

private CommandList _currentCommandList;
private uint _currentBatchCount;

private Effect _currentEffect;
private BlendState _currentBlendState;

private Texture2D? _currentTexture;
private Sampler? _currentSampler;

private SimplePipeline _currentPipeline;


/// <summary>
/// Initializes a new instance of the <see cref="SpriteBatch"/> class with the specified graphics device and optional capacity.
/// </summary>
Expand All @@ -65,9 +67,12 @@ public class SpriteBatch : Disposable {
public SpriteBatch(GraphicsDevice graphicsDevice, uint capacity = 15360) {
this.GraphicsDevice = graphicsDevice;
this.Capacity = capacity;


this._cachedPipelines = new Dictionary<(Effect, BlendState), SimplePipeline>();
this._cachedTextures = new Dictionary<(Texture2D, Sampler), ResourceSet>();

this._defaultEffect = new Effect(this.GraphicsDevice.ResourceFactory, Vertex2D.VertexLayout, "content/shaders/sprite.vert", "content/shaders/sprite.frag");

// Create vertex buffer.
this._vertices = new Vertex2D[capacity * VerticesPerQuad];
this._vertexBuffer = graphicsDevice.ResourceFactory.CreateBuffer(new BufferDescription((uint) (capacity * VerticesPerQuad * Marshal.SizeOf<Vertex2D>()), BufferUsage.VertexBuffer));
Expand All @@ -92,26 +97,26 @@ public SpriteBatch(GraphicsDevice graphicsDevice, uint capacity = 15360) {

graphicsDevice.UpdateBuffer(this._indexBuffer, 0, this._indices);

// Create transform buffer.
// Create transform buffer. // TODO: TAKE CARE FOR IT!
this._transformBuffer = graphicsDevice.ResourceFactory.CreateBuffer(new BufferDescription((uint) Marshal.SizeOf<Matrix4x4>(), BufferUsage.UniformBuffer));
this._transformLayout = graphicsDevice.ResourceFactory.CreateResourceLayout(new ResourceLayoutDescription(new ResourceLayoutElementDescription("ProjectionViewBuffer", ResourceKind.UniformBuffer, ShaderStages.Vertex)));
this._transformSet = graphicsDevice.ResourceFactory.CreateResourceSet(new ResourceSetDescription(this._transformLayout, this._transformBuffer));

this.CreateDefaultPipeline();
}

public void Begin(CommandList commandList, Matrix4x4? view = null, Matrix4x4? projection = null, SimplePipeline? pipeline = null) {
public void Begin(CommandList commandList, Effect? effect = null, BlendState? blendState = null, Matrix4x4? view = null, Matrix4x4? projection = null) {
if (this._begun) {
throw new Exception("The SpriteBatch has already begun!");
}
this._begun = true;

this._currentCommandList = commandList;

if (this._currentPipeline != pipeline) {
if (this._currentEffect != effect || this._currentBlendState != blendState) {
this.Flush();
}
this._currentPipeline = pipeline ?? this._defaultPipeline;

this._currentEffect = effect ?? this._defaultEffect;
this._currentBlendState = blendState ?? BlendState.AlphaBlend;

Matrix4x4 finalView = view ?? Matrix4x4.Identity;
Matrix4x4 finalProj = projection ?? Matrix4x4.CreateOrthographicOffCenter(0.0F, 1280.0F, 720.0F, 0.0F, 0.0F, 1.0F); // TODO SET RIGHT RES.
Expand Down Expand Up @@ -162,18 +167,20 @@ public void DrawDebugRectangle(Vector2 position, Vector2 size, Color color) {
/// Draws a texture using the specified parameters, including position, source rectangle, scale, origin, rotation, color, and flipping.
/// </summary>
/// <param name="texture">The texture to be drawn.</param>
/// <param name="sampler">The sampler state to use for texture sampling.</param>
/// <param name="samplerType">The sampler state to use for texture sampling.</param>
/// <param name="position">The position where the texture will be drawn.</param>
/// <param name="sourceRect">The source rectangle within the texture. If null, the entire texture is used.</param>
/// <param name="scale">The scale factor for resizing the texture. If null, the texture is drawn at its original size.</param>
/// <param name="origin">The origin point for rotation and scaling. If null, the origin is set to the top-left corner.</param>
/// <param name="rotation">The rotation angle in radians.</param>
/// <param name="color">The color to tint the texture. If null, the texture is drawn with its original colors.</param>
/// <param name="flip">Specifies how the texture should be flipped horizontally or vertically.</param>
public void DrawTexture(Texture2D texture, Sampler sampler, Vector2 position, Rectangle? sourceRect = null, Vector2? scale = null, Vector2? origin = null, float rotation = 0.0F, Color? color = null, SpriteFlip flip = SpriteFlip.None) {
public void DrawTexture(Texture2D texture, SamplerType samplerType, Vector2 position, Rectangle? sourceRect = null, Vector2? scale = null, Vector2? origin = null, float rotation = 0.0F, Color? color = null, SpriteFlip flip = SpriteFlip.None) {
if (!this._begun) {
throw new Exception("You must begin the SpriteBatch before calling draw methods!");
}

Sampler sampler = GraphicsHelper.GetSampler(this.GraphicsDevice, samplerType);

if (this._currentTexture != texture || this._currentSampler != sampler) {
this.Flush();
Expand Down Expand Up @@ -317,7 +324,7 @@ public void Flush() {
this._currentCommandList.SetIndexBuffer(this._indexBuffer, IndexFormat.UInt16);

// Set pipeline.
this._currentCommandList.SetPipeline(this._currentPipeline.Pipeline);
this._currentCommandList.SetPipeline(this.GetOrCreatePipeline(this._currentEffect, this._currentBlendState).Pipeline);

// Set transform buffer.
this._currentCommandList.SetGraphicsResourceSet(0, this._transformSet);
Expand All @@ -337,57 +344,66 @@ public void Flush() {
this.DrawCallCount++;
}

/// <summary>
/// Retrieves an existing pipeline associated with the given effect and blend state, or creates a new one if it doesn't exist.
/// </summary>
/// <param name="effect">The effect to be used for the pipeline.</param>
/// <param name="blendState">The blend state description for the pipeline.</param>
/// <returns>A SimplePipeline object configured with the provided effect and blend state.</returns>
private SimplePipeline GetOrCreatePipeline(Effect effect, BlendState blendState) { // TODO: TAKE A LOOK WHAT YOU DO WITH THE LAYOUTS!!!
if (!this._cachedPipelines.TryGetValue((effect, blendState), out SimplePipeline? pipeline)) {
this._pipelineLayout = this.GraphicsDevice.ResourceFactory.CreateResourceLayout(new ResourceLayoutDescription() {
Elements = [
new ResourceLayoutElementDescription("fTexture", ResourceKind.TextureReadOnly, ShaderStages.Fragment),
new ResourceLayoutElementDescription("fTextureSampler", ResourceKind.Sampler, ShaderStages.Fragment)
]
});

SimplePipeline newPipeline = new SimplePipeline(this.GraphicsDevice, effect, [this._transformLayout, this._pipelineLayout], this.GraphicsDevice.SwapchainFramebuffer.OutputDescription, blendState.Description, FaceCullMode.None);

this._cachedPipelines.Add((effect, blendState), newPipeline);
return newPipeline;
}

return pipeline;
}

/// <summary>
/// Retrieves an existing texture resource set or creates a new one if it doesn't exist.
/// </summary>
/// <param name="texture">The texture to be used in the resource set.</param>
/// <param name="sampler">The sampler to be used in the resource set.</param>
/// <returns>The resource set that includes the specified texture and sampler.</returns>
private ResourceSet GetOrCreateTextureResourceSet(Texture2D texture, Sampler sampler) {
private ResourceSet GetOrCreateTextureResourceSet(Texture2D texture, Sampler sampler) { // TODO: TAKE A LOOK WHAT YOU DO WITH THE LAYOUTS!!!
if (!this._cachedTextures.TryGetValue((texture, sampler), out ResourceSet? resourceSet)) {
ResourceSet newResourceSet = this.GraphicsDevice.ResourceFactory.CreateResourceSet(new ResourceSetDescription(this._currentPipeline.ResourceLayouts[1], texture.DeviceTexture, sampler));
ResourceSet newResourceSet = this.GraphicsDevice.ResourceFactory.CreateResourceSet(new ResourceSetDescription(this._pipelineLayout, texture.DeviceTexture, sampler));

this._cachedTextures.Add((texture, sampler), newResourceSet);
return newResourceSet;
}

return resourceSet;
}

/// <summary>
/// Creates the default rendering pipeline for the SpriteBatch.
/// This includes setting up the default shader programs and
/// configuring the necessary resource layouts and pipeline states.
/// </summary>
private void CreateDefaultPipeline() { // TODO: Replace this with GetOrCreateEffectPipeline.
this._defaultEffect = new Effect(this.GraphicsDevice.ResourceFactory, Vertex2D.VertexLayout, "content/shaders/sprite.vert", "content/shaders/sprite.frag");

this._defaultPipelineResourceLayout = this.GraphicsDevice.ResourceFactory.CreateResourceLayout(
new ResourceLayoutDescription(
new ResourceLayoutElementDescription("fTexture", ResourceKind.TextureReadOnly, ShaderStages.Fragment),
new ResourceLayoutElementDescription("fTextureSampler", ResourceKind.Sampler, ShaderStages.Fragment)
)
);

this._defaultPipeline = new SimplePipeline(this.GraphicsDevice, this._defaultEffect, [this._transformLayout, this._defaultPipelineResourceLayout], this.GraphicsDevice.SwapchainFramebuffer.OutputDescription, BlendStateDescription.SingleAlphaBlend, FaceCullMode.None);
}

protected override void Dispose(bool disposing) {
if (disposing) {
foreach (SimplePipeline pipeline in this._cachedPipelines.Values) {
pipeline.Dispose();
}

foreach (ResourceSet resourceSet in this._cachedTextures.Values) {
resourceSet.Dispose();
}

this._defaultEffect.Dispose();
this._pipelineLayout.Dispose();

this._vertexBuffer.Dispose();
this._indexBuffer.Dispose();

this._transformBuffer.Dispose();
this._transformLayout.Dispose();
this._transformSet.Dispose();

this._defaultEffect.Dispose();
this._defaultPipelineResourceLayout.Dispose();
this._defaultPipeline.Dispose();
}
}
}
7 changes: 7 additions & 0 deletions src/Bliss/CSharp/Graphics/SamplerType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Bliss.CSharp.Graphics;

public enum SamplerType {
Point,
Linear,
Aniso4X
}

0 comments on commit 0d4003b

Please sign in to comment.