SND\AstrorEnales_cp 772d4be8d3 - OpenGL render system now keeps track of all it's resources and Disposes and Recreates them if needed
- OpenGL Device is now Reset correctly when for example the window size changes (Recreating all resources)
- OpenGL render system doesn't crash anymore when closing the application
- Introduced TestStateAttribute for development to mark a file as tested/untested
2012-02-18 22:43:08 +00:00

370 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using ANX.Framework.Graphics;
using ANX.Framework.NonXNA;
using ANX.Framework.Windows.GL3.Helpers;
using OpenTK.Graphics.OpenGL;
#region License
//
// This file is part of the ANX.Framework created by the "ANX.Framework developer group".
//
// This file is released under the Ms-PL license.
//
//
//
// Microsoft Public License (Ms-PL)
//
// This license governs use of the accompanying software. If you use the software, you accept this license.
// If you do not accept the license, do not use the software.
//
// 1.Definitions
// The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning
// here as under U.S. copyright law.
// A "contribution" is the original software, or any additions or changes to the software.
// A "contributor" is any person that distributes its contribution under this license.
// "Licensed patents" are a contributor's patent claims that read directly on its contribution.
//
// 2.Grant of Rights
// (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations
// in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to
// reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution
// or any derivative works that you create.
// (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in
// section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed
// patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution
// in the software or derivative works of the contribution in the software.
//
// 3.Conditions and Limitations
// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your
// patent license from such contributor to the software ends automatically.
// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution
// notices that are present in the software.
// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including
// a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or
// object code form, you may only do so under a license that complies with this license.
// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees,
// or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the
// extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a
// particular purpose and non-infringement.
#endregion // License
namespace ANX.Framework.Windows.GL3
{
/// <summary>
/// Native OpenGL Effect implementation.
///
/// http://wiki.delphigl.com/index.php/Tutorial_glsl
/// </summary>
public class EffectGL3 : INativeEffect
{
#region Private
/// <summary>
/// The managed effect instance of this shader.
/// </summary>
private Effect managedEffect;
/// <summary>
/// The loaded shader data from the shader file.
/// </summary>
private ShaderData shaderData;
/// <summary>
/// The available techniques of this shader.
/// </summary>
private List<EffectTechnique> techniques;
/// <summary>
/// The current native technique.
/// </summary>
internal EffectTechniqueGL3 CurrentTechnique
{
get
{
if (managedEffect.CurrentTechnique == null)
{
return null;
}
return managedEffect.CurrentTechnique.NativeTechnique as EffectTechniqueGL3;
}
}
/// <summary>
/// The active uniforms of this technique.
/// </summary>
internal List<EffectParameter> parameters;
internal bool IsDisposed;
#endregion
#region Public
#region Techniques
public IEnumerable<EffectTechnique> Techniques
{
get
{
if (techniques.Count == 0)
{
Compile();
}
return techniques;
}
}
#endregion
#region Parameters
public IEnumerable<EffectParameter> Parameters
{
get
{
if (techniques.Count == 0)
{
Compile();
}
return parameters;
}
}
#endregion
#endregion
#region Constructor
/// <summary>
/// Private helper constructor for the basic initialization.
/// </summary>
/// <param name="setManagedEffect"></param>
private EffectGL3(Effect setManagedEffect)
{
GraphicsResourceManager.UpdateResource(this, true);
parameters = new List<EffectParameter>();
techniques = new List<EffectTechnique>();
managedEffect = setManagedEffect;
}
~EffectGL3()
{
GraphicsResourceManager.UpdateResource(this, false);
}
/// <summary>
/// Create a new effect instance of separate streams.
/// </summary>
/// <param name="vertexShaderByteCode">The vertex shader code.</param>
/// <param name="pixelShaderByteCode">The fragment shader code.</param>
public EffectGL3(Effect setManagedEffect, Stream vertexShaderByteCode,
Stream pixelShaderByteCode)
: this(setManagedEffect)
{
// TODO: this is probably not right!
throw new NotImplementedException("TODO: implement effect constructor with vertex and fragment streams, check HOWTO...");
//CreateShader(ShaderHelper.LoadShaderCode(vertexShaderByteCode),
// ShaderHelper.LoadShaderCode(pixelShaderByteCode));
}
/// <summary>
/// Create a new effect instance of one streams.
/// </summary>
/// <param name="byteCode">The byte code of the shader.</param>
public EffectGL3(Effect setManagedEffect, Stream byteCode)
: this(setManagedEffect)
{
string source = ShaderHelper.LoadShaderCode(byteCode);
shaderData = ShaderHelper.ParseShaderCode(source);
}
#endregion
#region RecreateData
internal void RecreateData()
{
Compile();
}
#endregion
#region Compile
private void Compile()
{
parameters.Clear();
techniques.Clear();
Dictionary<string, int> vertexShaders = new Dictionary<string, int>();
Dictionary<string, int> fragmentShaders = new Dictionary<string, int>();
List<string> parameterNames = new List<string>();
#region Compile vertex shaders
foreach (string vertexName in shaderData.VertexShaderCodes.Keys)
{
string vertexSource = shaderData.VertexGlobalCode +
shaderData.VertexShaderCodes[vertexName];
int vertexShader = GL.CreateShader(ShaderType.VertexShader);
string vertexError = CompileShader(vertexShader, vertexSource);
if (String.IsNullOrEmpty(vertexError) == false)
{
throw new InvalidDataException("Failed to compile the vertex " +
"shader '" + vertexName + "' because of: " + vertexError);
}
vertexShaders.Add(vertexName, vertexShader);
}
#endregion
#region Compile fragment shaders
foreach (string fragmentName in shaderData.FragmentShaderCodes.Keys)
{
string fragmentSource = shaderData.FragmentGlobalCode +
shaderData.FragmentShaderCodes[fragmentName];
int fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
string vertexError = CompileShader(fragmentShader, fragmentSource);
if (String.IsNullOrEmpty(vertexError) == false)
{
throw new InvalidDataException("Failed to compile the fragment " +
"shader '" + fragmentName + "' because of: " + vertexError);
}
fragmentShaders.Add(fragmentName, fragmentShader);
}
#endregion
#region Compile programs
foreach (string programName in shaderData.Techniques.Keys)
{
string vertexName = shaderData.Techniques[programName].Key;
string fragmentName = shaderData.Techniques[programName].Value;
int programHandle = GL.CreateProgram();
ErrorHelper.Check("CreateProgram");
GL.AttachShader(programHandle, vertexShaders[vertexName]);
ErrorHelper.Check("AttachShader vertexShader");
GL.AttachShader(programHandle, fragmentShaders[fragmentName]);
ErrorHelper.Check("AttachShader fragmentShader");
GL.LinkProgram(programHandle);
int result;
GL.GetProgram(programHandle, ProgramParameter.LinkStatus, out result);
if (result == 0)
{
string programError;
GL.GetProgramInfoLog(programHandle, out programError);
throw new InvalidDataException("Failed to link the shader program '" +
programName + "' because of: " + programError);
}
EffectTechniqueGL3 technique = new EffectTechniqueGL3(managedEffect, programName,
programHandle);
techniques.Add(new EffectTechnique(managedEffect, technique));
int uniformCount;
GL.GetProgram(programHandle, ProgramParameter.ActiveUniforms,
out uniformCount);
ErrorHelper.Check("GetProgram ActiveUniforms");
for (int index = 0; index < uniformCount; index++)
{
string name = GL.GetActiveUniformName(programHandle, index);
ErrorHelper.Check("GetActiveUniformName name=" + name);
if (parameterNames.Contains(name) == false)
{
parameterNames.Add(name);
int uniformIndex = GL.GetUniformLocation(programHandle, name);
ErrorHelper.Check("GetUniformLocation name=" + name +
" uniformIndex=" + uniformIndex);
parameters.Add(new EffectParameter()
{
NativeParameter =
new EffectParameterGL3(technique, name, uniformIndex),
});
}
}
}
#endregion
}
#endregion
#region CompileShader
private string CompileShader(int shader, string source)
{
GL.ShaderSource(shader, source);
GL.CompileShader(shader);
int result;
GL.GetShader(shader, ShaderParameter.CompileStatus, out result);
if (result == 0)
{
string error = "";
GL.GetShaderInfoLog(shader, out error);
GL.DeleteShader(shader);
return error;
}
return null;
}
#endregion
#region Apply (TODO)
public void Apply(GraphicsDevice graphicsDevice)
{
if (GraphicsDeviceWindowsGL3.activeEffect != this)
{
GL.Enable(EnableCap.Blend);
GL.UseProgram(CurrentTechnique.programHandle);
GraphicsDeviceWindowsGL3.activeEffect = this;
ErrorHelper.Check("UseProgram");
}
}
#endregion
#region Dispose
/// <summary>
/// Dispose the native shader data.
/// </summary>
public void Dispose()
{
if (IsDisposed == false)
{
IsDisposed = true;
DisposeResource();
}
}
internal void DisposeResource()
{
if (GraphicsDeviceWindowsGL3.IsContextCurrent == false)
{
return;
}
foreach (EffectTechnique technique in techniques)
{
int programHandle =
(technique.NativeTechnique as EffectTechniqueGL3).programHandle;
GL.DeleteProgram(programHandle);
ErrorHelper.Check("DeleteProgram");
int result;
GL.GetProgram(programHandle, ProgramParameter.DeleteStatus, out result);
if (result == 0)
{
string deleteError;
GL.GetProgramInfoLog(programHandle, out deleteError);
throw new Exception("Failed to delete the shader program '" +
technique.Name + "' because of: " + deleteError);
}
}
techniques.Clear();
parameters.Clear();
}
#endregion
}
}