Migrating to Factory/Wrapper pattern - Program complete
This commit is contained in:
@@ -11,11 +11,13 @@ namespace Diamond
|
||||
public abstract class GLObject : IDisposable
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public abstract int Id { get; }
|
||||
public string Name { get; protected set; } = "GLObject";
|
||||
|
||||
protected abstract GLWrapper Wrapper { get; }
|
||||
internal abstract GLWrapper Wrapper { get; }
|
||||
public int Id => Wrapper.Id;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
@@ -25,7 +27,7 @@ namespace Diamond
|
||||
if (disposing)
|
||||
{
|
||||
if (GraphicsContext.CurrentContext == null)
|
||||
Logger.Warn("No graphics context, cannot dispose GLObject: {0}", Wrapper);
|
||||
Logger.Error("No graphics context, cannot delete {0}", this);
|
||||
else
|
||||
Wrapper.Dispose();
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ namespace Diamond
|
||||
{
|
||||
internal abstract class GLWrapper : IDisposable
|
||||
{
|
||||
protected static Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public int Id { get; protected set; }
|
||||
|
||||
public override string ToString() => $"{GetType().Name} {Id}";
|
||||
|
||||
public static explicit operator int(GLWrapper o) => o.Id;
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public abstract void GLDelete();
|
||||
@@ -24,18 +24,26 @@ namespace Diamond
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
// no managed resources to dispose
|
||||
|
||||
GLDelete();
|
||||
|
||||
Id = 0;
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~GLWrapper()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static explicit operator int(GLWrapper o) => o.Id;
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,8 @@ namespace Diamond.Level
|
||||
var texturePaths = levelData["textures"]
|
||||
.Select(path => (string) path)
|
||||
.ToArray();
|
||||
var textures = texturePaths.Select(path => Texture.FromBitmap(new Bitmap(Path.Combine(dir, path)))).ToArray();
|
||||
var textures = texturePaths.Select(path => Texture.FromBitmap(new Bitmap(Path.Combine(dir, path))))
|
||||
.ToArray();
|
||||
var textureMap = texturePaths.Select((path, i) => new {path = path, i = i})
|
||||
.ToDictionary(v => v.path, v => v.i);
|
||||
|
||||
@@ -125,9 +126,17 @@ namespace Diamond.Level
|
||||
{
|
||||
var pgm = tileGroup.Program;
|
||||
pgm.Use();
|
||||
GL.Uniform1(pgm.GetUniform("tex"), tileGroup.Texture);
|
||||
|
||||
var loc = pgm.UniformLocation("tex");
|
||||
|
||||
if (!loc.HasValue)
|
||||
continue;
|
||||
|
||||
GL.Uniform1((int)loc, tileGroup.Texture);
|
||||
|
||||
pgm.SetAttribPointers(_vertexBuffer);
|
||||
pgm.SetAttribPointers(_tileBuffer);
|
||||
|
||||
tileGroup.Mesh.DrawInstanced(tileGroup.Tiles);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,103 +1,82 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Diamond.Buffers;
|
||||
using NLog;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
|
||||
namespace Diamond.Shaders
|
||||
{
|
||||
public class Program : GLWrapper
|
||||
internal class ProgramWrapper : GLWrapper
|
||||
{
|
||||
internal ProgramWrapper()
|
||||
{
|
||||
Id = GL.CreateProgram();
|
||||
}
|
||||
|
||||
public int ActiveUniforms => Get(GetProgramParameterName.ActiveUniforms);
|
||||
public int ActiveAttributes => Get(GetProgramParameterName.ActiveAttributes);
|
||||
public bool Linked => Get(GetProgramParameterName.LinkStatus) != 0;
|
||||
|
||||
public int Get(GetProgramParameterName parameter)
|
||||
{
|
||||
GL.GetProgram(Id, parameter, out int res);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void Link() => GL.LinkProgram(Id);
|
||||
public string InfoLog => GL.GetProgramInfoLog(Id).Trim();
|
||||
|
||||
public void Attach(ShaderWrapper shader) => GL.AttachShader(Id, shader.Id);
|
||||
|
||||
public void Use() => GL.UseProgram(Id);
|
||||
|
||||
public string UniformName(int location)
|
||||
{
|
||||
var sb = new StringBuilder(64);
|
||||
GL.GetActiveUniformName(Id, location, sb.Capacity, out int length, sb);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string AttributeName(int location)
|
||||
{
|
||||
var sb = new StringBuilder(64);
|
||||
GL.GetActiveAttrib(Id, location, sb.Capacity, out int length, out int size, out ActiveAttribType type, sb);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override void GLDelete() => GL.DeleteProgram(Id);
|
||||
}
|
||||
|
||||
public class Program : GLObject
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
internal ProgramWrapper _program;
|
||||
internal override GLWrapper Wrapper => _program;
|
||||
|
||||
public static Program Current { get; private set; }
|
||||
|
||||
private readonly Dictionary<string, int> _uniforms = new Dictionary<string, int>();
|
||||
private readonly Dictionary<string, int> _attributes = new Dictionary<string, int>();
|
||||
|
||||
public string InfoLog => GL.GetProgramInfoLog(Id).Trim();
|
||||
|
||||
public Program()
|
||||
private Program(ProgramWrapper program, string name)
|
||||
{
|
||||
Id = GL.CreateProgram();
|
||||
_program = program;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
protected override void Delete()
|
||||
public int? UniformLocation(string name)
|
||||
{
|
||||
GL.DeleteProgram(Id);
|
||||
if (_uniforms.ContainsKey(name)) return _uniforms[name];
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Attach(Shader shader)
|
||||
public int? AttributeLocation(string name)
|
||||
{
|
||||
GL.AttachShader(Id, shader.Id);
|
||||
}
|
||||
|
||||
public bool Linked
|
||||
{
|
||||
get
|
||||
{
|
||||
GL.GetProgram(Id, GetProgramParameterName.LinkStatus, out int success);
|
||||
return success != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Link()
|
||||
{
|
||||
_uniforms.Clear();
|
||||
_attributes.Clear();
|
||||
|
||||
GL.LinkProgram(Id);
|
||||
|
||||
if (!Linked)
|
||||
{
|
||||
Logger.Warn("Failed to link Program {0}", Id);
|
||||
Logger.Debug("Program {0} InfoLog\n{1}", Id, InfoLog);
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger.Info("Successfully linked Program {0}", Id);
|
||||
|
||||
GL.GetProgram(Id, GetProgramParameterName.ActiveUniforms, out int uniformcount);
|
||||
for (var i = 0; i < uniformcount; i++)
|
||||
{
|
||||
var sb = new StringBuilder(256);
|
||||
GL.GetActiveUniformName(Id, i, sb.Capacity, out int length, sb);
|
||||
_uniforms[sb.ToString()] = i;
|
||||
}
|
||||
|
||||
GL.GetProgram(Id, GetProgramParameterName.ActiveAttributes, out int attributecount);
|
||||
for (var i = 0; i < attributecount; i++)
|
||||
{
|
||||
var sb = new StringBuilder(256);
|
||||
GL.GetActiveAttrib(Id, i, sb.Capacity, out int length, out int size,
|
||||
out ActiveAttribType type, sb);
|
||||
_attributes[sb.ToString()] = i;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetUniform(string name, out int id)
|
||||
{
|
||||
return _uniforms.TryGetValue(name, out id);
|
||||
}
|
||||
|
||||
public int GetUniform(string name)
|
||||
{
|
||||
if (TryGetUniform(name, out int id)) return id;
|
||||
|
||||
Logger.Warn("Attempted to access uniform {0} on Program {1}", name, Id);
|
||||
throw new ShaderException($"Shader Program {Id} does not contain uniform '{name}'");
|
||||
}
|
||||
|
||||
public bool TryGetAttribute(string name, out int id)
|
||||
{
|
||||
return _attributes.TryGetValue(name, out id);
|
||||
}
|
||||
|
||||
public int GetAttribute(string name)
|
||||
{
|
||||
if (TryGetAttribute(name, out int id)) return id;
|
||||
|
||||
Logger.Warn("Attempted to access attribute {0} on Program {1}", name, Id);
|
||||
throw new ShaderException($"Shader Program {Id} does not contain id '{name}'");
|
||||
if (_attributes.ContainsKey(name)) return _attributes[name];
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetAttribPointers<T>(GLBuffer<T> buff) where T : struct
|
||||
@@ -107,9 +86,9 @@ namespace Diamond.Shaders
|
||||
buff.Bind();
|
||||
foreach (var attr in vdi.Pointers)
|
||||
{
|
||||
if (!TryGetAttribute(attr.Name, out int loc)) continue;
|
||||
|
||||
GL.VertexAttribPointer(loc, attr.Size, attr.Type, attr.Normalized, vdi.Stride, attr.Offset);
|
||||
var loc = AttributeLocation(attr.Name);
|
||||
if (loc.HasValue)
|
||||
GL.VertexAttribPointer((int) loc, attr.Size, attr.Type, attr.Normalized, vdi.Stride, attr.Offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,39 +98,86 @@ namespace Diamond.Shaders
|
||||
Current = this;
|
||||
}
|
||||
|
||||
//? Could create static Program instance which wraps the default shader
|
||||
// ie Shader.Default.Use()
|
||||
// would also allow sending arrays to the default attribs like gl_Vertex etc.
|
||||
public static void UseDefault()
|
||||
{
|
||||
GL.UseProgram(0);
|
||||
Current = null;
|
||||
}
|
||||
|
||||
public static Program FromShaders(params Shader[] shaders)
|
||||
private bool Link()
|
||||
{
|
||||
var p = new Program();
|
||||
_uniforms.Clear();
|
||||
_attributes.Clear();
|
||||
|
||||
foreach (var shader in shaders)
|
||||
_program.Link();
|
||||
|
||||
if (!_program.Linked)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < _program.ActiveUniforms; i++)
|
||||
_uniforms[_program.UniformName(i)] = i;
|
||||
|
||||
for (var i = 0; i < _program.ActiveAttributes; i++)
|
||||
_attributes[_program.AttributeName(i)] = i;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString() => $"\'{Name}\' ({Id})";
|
||||
|
||||
#region Factory Methods
|
||||
|
||||
public static Program FromShaders(string name, params Shader[] shaders) => FromShaders(name,
|
||||
(IEnumerable<Shader>) shaders);
|
||||
|
||||
public static Program FromShaders(string name, IEnumerable<Shader>shaders)
|
||||
{
|
||||
if (shaders == null)
|
||||
{
|
||||
p.Attach(shader);
|
||||
Logger.Error("Cannot create a program with no shaders.");
|
||||
return null;
|
||||
}
|
||||
|
||||
p.Link();
|
||||
var wrapper = new ProgramWrapper();
|
||||
var service = new Program(wrapper, name);
|
||||
|
||||
return p;
|
||||
Logger.Debug("Created {0}", service);
|
||||
|
||||
foreach (var shader in shaders)
|
||||
wrapper.Attach((ShaderWrapper) shader.Wrapper);
|
||||
|
||||
service.Link();
|
||||
|
||||
if (!wrapper.Linked)
|
||||
{
|
||||
Logger.Warn("Failed to link {0}", service);
|
||||
Logger.Debug("InfoLog for {0}", service);
|
||||
wrapper.Dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
Logger.Debug("Successfully linked {0}", service);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
public static Program FromFiles(params string[] files)
|
||||
|
||||
public static Program FromShaders(params Shader[] shaders) => FromShaders((IEnumerable<Shader>) shaders);
|
||||
|
||||
public static Program FromShaders(IEnumerable<Shader> shaders)
|
||||
{
|
||||
var shaders = new Shader[files.Length];
|
||||
|
||||
for (var i = 0; i < files.Length; i++)
|
||||
shaders[i] = Shader.FromFile(files[i]);
|
||||
|
||||
var p = FromShaders(shaders);
|
||||
|
||||
for (var i = 0; i < shaders.Length; i++)
|
||||
shaders[i]?.Dispose();
|
||||
|
||||
return p;
|
||||
var shaderList = shaders.ToList(); // prevent multiple enumeration
|
||||
var shaderNames = shaderList.Select(s => s.Name);
|
||||
string name = $"[{string.Join(", ", shaderNames)}]";
|
||||
return FromShaders(name, shaderList);
|
||||
}
|
||||
|
||||
public static Program FromFiles(string name, params string[] paths) => FromShaders(name, paths.Select(Shader.FromFile));
|
||||
public static Program FromFiles(params string[] paths) => FromShaders(paths.Select(Shader.FromFile));
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,12 @@ namespace Diamond.Shaders
|
||||
{
|
||||
internal sealed class ShaderWrapper : GLWrapper
|
||||
{
|
||||
internal ShaderWrapper(ShaderType shaderType)
|
||||
{
|
||||
Id = GL.CreateShader(shaderType);
|
||||
ShaderType = shaderType;
|
||||
}
|
||||
|
||||
public readonly ShaderType ShaderType;
|
||||
|
||||
public string Source
|
||||
@@ -33,21 +39,9 @@ namespace Diamond.Shaders
|
||||
|
||||
public string InfoLog => GL.GetShaderInfoLog(Id).Trim();
|
||||
|
||||
internal ShaderWrapper(ShaderType shaderType)
|
||||
{
|
||||
Id = GL.CreateShader(shaderType);
|
||||
ShaderType = shaderType;
|
||||
}
|
||||
public void Compile() => GL.CompileShader(Id);
|
||||
|
||||
public override void GLDelete()
|
||||
{
|
||||
GL.DeleteShader(Id);
|
||||
}
|
||||
|
||||
public void Compile()
|
||||
{
|
||||
GL.CompileShader(Id);
|
||||
}
|
||||
public override void GLDelete() => GL.DeleteShader(Id);
|
||||
}
|
||||
|
||||
public class Shader : GLObject
|
||||
@@ -55,40 +49,52 @@ namespace Diamond.Shaders
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private readonly ShaderWrapper _shader;
|
||||
internal override GLWrapper Wrapper => _shader;
|
||||
|
||||
public override int Id => _shader.Id;
|
||||
protected override GLWrapper Wrapper => _shader;
|
||||
|
||||
public string Name { get; }
|
||||
public string Source { get; }
|
||||
public ShaderType Type { get; }
|
||||
|
||||
private Shader(ShaderWrapper shader, string source, string name)
|
||||
private Shader(ShaderWrapper shader, string source, ShaderType type, string name)
|
||||
{
|
||||
_shader = shader;
|
||||
Name = name;
|
||||
Source = source;
|
||||
Type = type;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public override string ToString() => $"{Type} \'{Name}\' ({Id})";
|
||||
|
||||
#region Factory Methods
|
||||
|
||||
private static readonly Dictionary<string, ShaderType> _extensions = new Dictionary<string, ShaderType>
|
||||
{
|
||||
[".vs"] = ShaderType.VertexShader,
|
||||
[".vert"] = ShaderType.VertexShader,
|
||||
[".fs"] = ShaderType.VertexShader,
|
||||
[".frag"] = ShaderType.VertexShader,
|
||||
};
|
||||
|
||||
public static Shader FromSource(string source, ShaderType type, string name = "Shader")
|
||||
{
|
||||
var wrapper = new ShaderWrapper(type);
|
||||
Logger.Debug("Created {0} \'{1}\' {2}", type, name, wrapper.Id);
|
||||
var service = new Shader(wrapper, source, type, name);
|
||||
|
||||
Logger.Debug("Created {0}", service);
|
||||
|
||||
wrapper.Source = source;
|
||||
wrapper.Compile();
|
||||
|
||||
if (!wrapper.Compiled)
|
||||
{
|
||||
Logger.Warn("Failed to compile {0} \'{1}\' {2}", type, name, wrapper.Id);
|
||||
Logger.Debug("InfoLog for {0} \'{1}\' {2}", type, name, wrapper.Id);
|
||||
Logger.Warn("Failed to compile {0}", service);
|
||||
Logger.Debug("InfoLog for {0}", service);
|
||||
wrapper.Dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
Logger.Debug("Successfully compiled {0} \'{1}\' {2}", type, name, wrapper.Id);
|
||||
return new Shader(wrapper, source, name);
|
||||
Logger.Debug("Successfully compiled {0}", service);
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
public static Shader FromFile(string path, ShaderType type)
|
||||
@@ -114,25 +120,18 @@ namespace Diamond.Shaders
|
||||
var ext = Path.GetExtension(path);
|
||||
var name = Path.GetFileNameWithoutExtension(path);
|
||||
|
||||
var extensions = new Dictionary<string, ShaderType>
|
||||
{
|
||||
[".vs"] = ShaderType.VertexShader,
|
||||
[".vert"] = ShaderType.VertexShader,
|
||||
[".fs"] = ShaderType.VertexShader,
|
||||
[".frag"] = ShaderType.VertexShader,
|
||||
};
|
||||
|
||||
if (ext != null)
|
||||
if (!extensions.ContainsKey(ext))
|
||||
if (!_extensions.ContainsKey(ext))
|
||||
ext = Path.GetExtension(name);
|
||||
|
||||
if (ext == null || !extensions.ContainsKey(ext))
|
||||
if (ext == null || !_extensions.ContainsKey(ext))
|
||||
{
|
||||
Logger.Warn("Could not infer shader type from glsl file name {0}", path);
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = extensions[ext];
|
||||
var type = _extensions[ext];
|
||||
return FromFile(path, type);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user