Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Rendering #272

Merged
merged 6 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
"mounts": [
// Xauth cookies must be shared to the container.
// This is to prevent authentication errors when launching the application.
"source=${localEnv:XAUTHORITY},target=/home/vscode/.Xauthority,type=bind,consistency=cached"
"source=${localEnv:XAUTHORITY},target=/home/vscode/.Xauthority,type=bind,consistency=cached",

// Share NuGet config and packages
"source=${localEnv:HOME}/.nuget/NuGet/NuGet.Config,target=/home/vscode/.nuget/NuGet/NuGet.Config,type=bind,consistency=cached",
"source=${localEnv:HOME}/.nuget/packages,target=/home/vscode/.nuget/packages,type=bind"
],
"remoteEnv": {
"DISPLAY": "${localEnv:DISPLAY}"
Expand Down
35 changes: 35 additions & 0 deletions source/Vignette/Rendering/BlendMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Cosyne
// Licensed under GPL 3.0 with SDK Exception. See LICENSE for details.

namespace Vignette.Rendering;

/// <summary>
/// An enumeration of default blending modes.
/// </summary>
public enum BlendMode
{
/// <summary>
/// Non-premultiplied alpha blending the source and destination colors.
/// </summary>
NonPremultiplied,

/// <summary>
/// Disabled blending.
/// </summary>
Disabled,

/// <summary>
/// Additive blending where the destination color is appended to the source color.
/// </summary>
Additive,

/// <summary>
/// Opaque blending where the source is overwritten by the destination.
/// </summary>
Opaque,

/// <summary>
/// Alpha blending where the source and destination are blended using alpha.
/// </summary>
AlphaBlend,
}
223 changes: 223 additions & 0 deletions source/Vignette/Rendering/Effect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
// Copyright (c) Cosyne
// Licensed under GPL 3.0 with SDK Exception. See LICENSE for details.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using Sekai.Graphics;

namespace Vignette.Rendering;

/// <summary>
/// Defines how primitives should be drawn to the screen.
/// </summary>
public readonly struct Effect : IEquatable<Effect>
{
private readonly ShaderCode[] shaderCode;
private readonly IDictionary<string, IParameter> parameters;

public Effect()
{
shaderCode = Array.Empty<ShaderCode>();
parameters = ImmutableDictionary<string, IParameter>.Empty;
}

private Effect(ShaderCode shVert, ShaderCode shFrag, IDictionary<string, IParameter> parameters)
{
this.parameters = parameters;
this.shaderCode = new[] { shVert, shFrag };
}

/// <summary>
/// Checks whether a named parameter of a given type is present on this <see cref="Effect"/>.
/// </summary>
/// <typeparam name="T">The parameter type.</typeparam>
/// <param name="name">The parameter name.</param>
/// <returns><see langword="true"/> if the parameter exists. Otherwise, returns <see langword="false"/>.</returns>
public bool HasParameter<T>(string name)
where T : class
{
if (!parameters.TryGetValue(name, out var param))
{
return false;
}

return param is T;
}

/// <summary>
/// Gets the named parameter.
/// </summary>
/// <typeparam name="T">The parameter type.</typeparam>
/// <param name="name">The parameter name.</param>
/// <returns>The parameter.</returns>
/// <exception cref="KeyNotFoundException">Thrown when the parameter is not found in this <see cref="Effect"/>.</exception>
/// <exception cref="InvalidCastException">Thrown when the parameter exists but the type argument does not match the parameter type.</exception>
public Parameter<T> GetParameter<T>(string name)
where T : class
{
if (!parameters.TryGetValue(name, out var param))
{
throw new KeyNotFoundException($"The effect has no parameter named \"{name}\".");
}

if (param is not Parameter<T> typedParam)
{
throw new InvalidCastException($"The effect parameter \"{name}\" is not a {nameof(Parameter<T>)}.");
}

return typedParam;
}

/// <summary>
/// Gets all parameters this effect has.
/// </summary>
/// <returns>An enumeration of parameters.</returns>
public IEnumerable<IParameter> GetParameters() => parameters.Values;

/// <summary>
/// Gets the named parameter.
/// </summary>
/// <typeparam name="T">The parameter type.</typeparam>
/// <param name="name">The parameter name.</param>
/// <param name="typedParam">The retrieved parameter.</param>
/// <returns><see langword="true"/> if the parameter exists. Otherwise, returns <see langword="false"/>.</returns>
public bool TryGetParameter<T>(string name, out Parameter<T> typedParam)
where T : class
{
try
{
typedParam = GetParameter<T>(name);
return true;
}
catch
{
typedParam = default;
return false;
}
}

/// <summary>
/// Creates an <see cref="Effect"/> from a <see cref="Stream"/>.
/// </summary>
/// <param name="stream">The stream to be read.</param>
/// <param name="encoding">The text encoding.</param>
/// <returns>An effect.</returns>
/// <exception cref="InvalidOperationException">Thrown when the stream failed to be read.</exception>
public static Effect From(Stream stream, Encoding? encoding = null)
{
Span<byte> buffer = stackalloc byte[(int)stream.Length];

if (stream.Read(buffer) != stream.Length)
{
throw new InvalidOperationException("Failed to read stream.");
}

return From(buffer, encoding);
}

/// <summary>
/// Creates an <see cref="Effect"/> from a <see cref="ReadOnlySpan{byte}"/>.
/// </summary>
/// <param name="bytes">The bytes to be read.</param>
/// <param name="encoding">The text encoding.</param>
/// <returns>An effect.</returns>
public static Effect From(ReadOnlySpan<byte> bytes, Encoding? encoding = null)
{
return From((encoding ?? Encoding.UTF8).GetString(bytes));
}

/// <summary>
/// Creates an <see cref="Effect"/> from a <see langword="string"/>.
/// </summary>
/// <param name="text">The text to be compiled.</param>
/// <returns>An effect.</returns>
public static Effect From(string text)
{
string code = sh_common + text;

var shVert = ShaderCode.From(code, ShaderStage.Vertex, sh_vert, ShaderLanguage.HLSL);
var shFrag = ShaderCode.From(code, ShaderStage.Fragment, sh_frag, ShaderLanguage.HLSL);

var shVertReflect = shVert.Reflect();
var shFragReflect = shFrag.Reflect();

var parameters = new Dictionary<string, IParameter>();

if (shVertReflect.Uniforms is not null)
{
foreach (var buffer in shVertReflect.Uniforms)
parameters[buffer.Name] = new Parameter<GraphicsBuffer>(buffer.Name, buffer.Binding);
}

if (shVertReflect.Textures is not null)
{
foreach (var texture in shVertReflect.Textures)
parameters[texture.Name] = new Parameter<Texture>(texture.Name, texture.Binding);
}

if (shFragReflect.Uniforms is not null)
{
foreach (var buffer in shFragReflect.Uniforms)
parameters[buffer.Name] = new Parameter<GraphicsBuffer>(buffer.Name, buffer.Binding);
}

if (shFragReflect.Textures is not null)
{
foreach (var texture in shFragReflect.Textures)
parameters[texture.Name] = new Parameter<Texture>(texture.Name, texture.Binding);
}

return new(shVert, shFrag, parameters);
}

public bool Equals(Effect other)
{
return ((IStructuralEquatable)shaderCode).Equals(other.shaderCode, EqualityComparer<ShaderCode>.Default);
}

public override bool Equals([NotNullWhen(true)] object? obj)
{
return obj is Effect effect && Equals(effect);
}

public override int GetHashCode()
{
return ((IStructuralEquatable)shaderCode).GetHashCode(EqualityComparer<ShaderCode>.Default);
}

public static bool operator ==(Effect left, Effect right)
{
return left.Equals(right);
}

public static bool operator !=(Effect left, Effect right)
{
return !(left == right);
}

public static implicit operator ShaderCode[](Effect effect) => effect.shaderCode;

private const string sh_frag = "Pixel";
private const string sh_vert = "Vertex";

private const string sh_common =
LeNitrous marked this conversation as resolved.
Show resolved Hide resolved
@"
#define P_MATRIX g_internal_ProjMatrix
#define V_MATRIX g_internal_ViewMatrix
#define M_MATRIX g_internal_ModelMatrix
#define OBJECT_TO_CLIP(a) mul(mul(V_MATRIX, M_MATRIX), a)
#define OBJECT_TO_VIEW(a) mul(P_MATRIX, OBJECT_TO_CLIP(a))

cbuffer g_internal_Transform : register(b89)
{
float4x4 g_internal_ProjMatrix;
float4x4 g_internal_ViewMatrix;
float4x4 g_internal_ModelMatrix;
};
";
}
Loading