Files
LuaCsForBarotraumaEP/Libraries/MonoGame.Framework/Src/Tools/2MGFX/ShaderData.mojo.cs
2019-06-25 16:00:44 +03:00

195 lines
6.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using TwoMGFX.TPGParser;
namespace TwoMGFX
{
internal partial class ShaderData
{
public static ShaderData CreateGLSL(byte[] byteCode, bool isVertexShader, List<ConstantBufferData> cbuffers, int sharedIndex, Dictionary<string, SamplerStateInfo> samplerStates, bool debug)
{
var dxshader = new ShaderData(isVertexShader, sharedIndex, byteCode);
// Use MojoShader to convert the HLSL bytecode to GLSL.
var parseDataPtr = MojoShader.NativeMethods.MOJOSHADER_parse (
"glsl",
byteCode,
byteCode.Length,
IntPtr.Zero,
0,
IntPtr.Zero,
0,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero);
var parseData = MarshalHelper.Unmarshal<MojoShader.MOJOSHADER_parseData> (parseDataPtr);
if (parseData.error_count > 0) {
var errors = MarshalHelper.UnmarshalArray<MojoShader.MOJOSHADER_error> (
parseData.errors,
parseData.error_count
);
throw new Exception (errors [0].error);
}
// Conver the attributes.
//
// TODO: Could this be done using DX shader reflection?
//
{
var attributes = MarshalHelper.UnmarshalArray<MojoShader.MOJOSHADER_attribute> (
parseData.attributes, parseData.attribute_count);
dxshader._attributes = new Attribute[attributes.Length];
for (var i = 0; i < attributes.Length; i++) {
dxshader._attributes [i].name = attributes [i].name;
dxshader._attributes [i].index = attributes [i].index;
dxshader._attributes [i].usage = EffectObject.ToXNAVertexElementUsage (attributes [i].usage);
}
}
var symbols = MarshalHelper.UnmarshalArray<MojoShader.MOJOSHADER_symbol> (
parseData.symbols, parseData.symbol_count);
//try to put the symbols in the order they are eventually packed into the uniform arrays
//this /should/ be done by pulling the info from mojoshader
Array.Sort (symbols, delegate(MojoShader.MOJOSHADER_symbol a, MojoShader.MOJOSHADER_symbol b) {
uint va = a.register_index;
if (a.info.elements == 1)
va += 1024; //hax. mojoshader puts array objects first
uint vb = b.register_index;
if (b.info.elements == 1)
vb += 1024;
return va.CompareTo (vb);
}
);//(a, b) => ((int)(a.info.elements > 1))a.register_index.CompareTo(b.register_index));
// NOTE: It seems the latest versions of MojoShader only
// output vec4 register sets. We leave the code below, but
// the runtime has been optimized for this case.
// For whatever reason the register indexing is
// incorrect from MojoShader.
{
uint bool_index = 0;
uint float4_index = 0;
uint int4_index = 0;
for (var i = 0; i < symbols.Length; i++) {
switch (symbols [i].register_set) {
case MojoShader.MOJOSHADER_symbolRegisterSet.MOJOSHADER_SYMREGSET_BOOL:
symbols [i].register_index = bool_index;
bool_index += symbols [i].register_count;
break;
case MojoShader.MOJOSHADER_symbolRegisterSet.MOJOSHADER_SYMREGSET_FLOAT4:
symbols [i].register_index = float4_index;
float4_index += symbols[i].register_count;
break;
case MojoShader.MOJOSHADER_symbolRegisterSet.MOJOSHADER_SYMREGSET_INT4:
symbols [i].register_index = int4_index;
int4_index += symbols [i].register_count;
break;
}
}
}
// Get the samplers.
var samplers = MarshalHelper.UnmarshalArray<MojoShader.MOJOSHADER_sampler> (
parseData.samplers, parseData.sampler_count);
dxshader._samplers = new Sampler[samplers.Length];
for (var i = 0; i < samplers.Length; i++)
{
// We need the original sampler name... look for that in the symbols.
var originalSamplerName =
symbols.First(e => e.register_set == MojoShader.MOJOSHADER_symbolRegisterSet.MOJOSHADER_SYMREGSET_SAMPLER &&
e.register_index == samplers[i].index
).name;
var sampler = new Sampler
{
//sampler mapping to parameter is unknown atm
parameter = -1,
// GLSL needs the MojoShader mangled sampler name.
samplerName = samplers[i].name,
// By default use the original sampler name for the parameter name.
parameterName = originalSamplerName,
textureSlot = samplers[i].index,
samplerSlot = samplers[i].index,
type = samplers[i].type,
};
SamplerStateInfo state;
if (samplerStates.TryGetValue(originalSamplerName, out state))
{
sampler.state = state.State;
sampler.parameterName = state.TextureName ?? originalSamplerName;
}
// Store the sampler.
dxshader._samplers[i] = sampler;
}
// Gather all the parameters used by this shader.
var symbol_types = new [] {
new { name = dxshader.IsVertexShader ? "vs_uniforms_bool" : "ps_uniforms_bool", set = MojoShader.MOJOSHADER_symbolRegisterSet.MOJOSHADER_SYMREGSET_BOOL, },
new { name = dxshader.IsVertexShader ? "vs_uniforms_ivec4" : "ps_uniforms_ivec4", set = MojoShader.MOJOSHADER_symbolRegisterSet.MOJOSHADER_SYMREGSET_INT4, },
new { name = dxshader.IsVertexShader ? "vs_uniforms_vec4" : "ps_uniforms_vec4", set = MojoShader.MOJOSHADER_symbolRegisterSet.MOJOSHADER_SYMREGSET_FLOAT4, },
};
var cbuffer_index = new List<int> ();
for (var i = 0; i < symbol_types.Length; i++) {
var cbuffer = new ConstantBufferData (symbol_types [i].name,
symbol_types [i].set,
symbols);
if (cbuffer.Size == 0)
continue;
var match = cbuffers.FindIndex (e => e.SameAs (cbuffer));
if (match == -1) {
cbuffer_index.Add (cbuffers.Count);
cbuffers.Add (cbuffer);
} else
cbuffer_index.Add (match);
}
dxshader._cbuffers = cbuffer_index.ToArray ();
var glslCode = parseData.output;
// TODO: This sort of sucks... why does MojoShader not produce
// code valid for GLES out of the box?
// GLES platforms do not like this.
glslCode = glslCode.Replace ("#version 110", "");
// Add the required precision specifiers for GLES.
var floatPrecision = dxshader.IsVertexShader ? "precision highp float;\r\n" : "precision mediump float;\r\n";
glslCode = "#ifdef GL_ES\r\n" +
floatPrecision +
"precision mediump int;\r\n" +
"#endif\r\n" +
glslCode;
// Enable standard derivatives extension as necessary
if ((glslCode.IndexOf("dFdx", StringComparison.InvariantCulture) >= 0)
|| (glslCode.IndexOf("dFdy", StringComparison.InvariantCulture) >= 0))
{
glslCode = "#extension GL_OES_standard_derivatives : enable\r\n" + glslCode;
}
// Store the code for serialization.
dxshader.ShaderCode = Encoding.ASCII.GetBytes (glslCode);
return dxshader;
}
}
}