SPIR-V Intermediate Shading Language support
* Enable from Ryujinx.conf * Adapt to use OpenTK.NetStandard * Implement usage of UBOs for GLSL and SPIR-V * Fix a NVidia related issue * Use constant from UniformBinding
This commit is contained in:
parent
69697957e6
commit
cc298c676a
20 changed files with 5454 additions and 154 deletions
|
@ -173,10 +173,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0);
|
Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Size = AttribElements[Attrib.Size];
|
int Location = Attrib.Index + 1;
|
||||||
int Offset = Attrib.Offset;
|
int Size = AttribElements[Attrib.Size];
|
||||||
|
int Offset = Attrib.Offset;
|
||||||
|
|
||||||
GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Stride, Offset);
|
GL.VertexAttribPointer(Location, Size, Type, Normalize, Stride, Offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
GL.BindVertexArray(0);
|
GL.BindVertexArray(0);
|
||||||
|
|
|
@ -9,40 +9,33 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
class OGLShader
|
class OGLShader
|
||||||
{
|
{
|
||||||
private class ShaderStage : IDisposable
|
private enum ShadingLanguage
|
||||||
{
|
{
|
||||||
public int Handle { get; private set; }
|
Unknown,
|
||||||
|
GLSL,
|
||||||
|
SPIRV
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsCompiled { get; private set; }
|
private abstract class ShaderStage : IDisposable
|
||||||
|
{
|
||||||
|
public int Handle { get; protected set; }
|
||||||
|
|
||||||
public GalShaderType Type { get; private set; }
|
public GalShaderType Type { get; private set; }
|
||||||
|
|
||||||
public string Code { get; private set; }
|
|
||||||
|
|
||||||
public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
|
public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
|
||||||
public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; }
|
public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; }
|
||||||
|
|
||||||
public ShaderStage(
|
public ShaderStage(
|
||||||
GalShaderType Type,
|
GalShaderType Type,
|
||||||
string Code,
|
|
||||||
IEnumerable<ShaderDeclInfo> TextureUsage,
|
IEnumerable<ShaderDeclInfo> TextureUsage,
|
||||||
IEnumerable<ShaderDeclInfo> UniformUsage)
|
IEnumerable<ShaderDeclInfo> UniformUsage)
|
||||||
{
|
{
|
||||||
this.Type = Type;
|
this.Type = Type;
|
||||||
this.Code = Code;
|
|
||||||
this.TextureUsage = TextureUsage;
|
this.TextureUsage = TextureUsage;
|
||||||
this.UniformUsage = UniformUsage;
|
this.UniformUsage = UniformUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Compile()
|
public abstract void Compile();
|
||||||
{
|
|
||||||
if (Handle == 0)
|
|
||||||
{
|
|
||||||
Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
|
|
||||||
|
|
||||||
CompileAndCheck(Handle, Code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
@ -60,6 +53,69 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class GlslStage : ShaderStage
|
||||||
|
{
|
||||||
|
public string Code { get; private set; }
|
||||||
|
|
||||||
|
public GlslStage(
|
||||||
|
GalShaderType Type,
|
||||||
|
string Code,
|
||||||
|
IEnumerable<ShaderDeclInfo> TextureUsage,
|
||||||
|
IEnumerable<ShaderDeclInfo> UniformUsage)
|
||||||
|
: base(Type, TextureUsage, UniformUsage)
|
||||||
|
{
|
||||||
|
this.Code = Code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Compile()
|
||||||
|
{
|
||||||
|
if (Handle == 0)
|
||||||
|
{
|
||||||
|
Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
|
||||||
|
|
||||||
|
GL.ShaderSource(Handle, Code);
|
||||||
|
GL.CompileShader(Handle);
|
||||||
|
|
||||||
|
CheckCompilation(Handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SpirvStage : ShaderStage
|
||||||
|
{
|
||||||
|
public byte[] Bytecode { get; private set; }
|
||||||
|
|
||||||
|
public IDictionary<string, int> Locations { get; private set; }
|
||||||
|
|
||||||
|
public SpirvStage(
|
||||||
|
GalShaderType Type,
|
||||||
|
byte[] Bytecode,
|
||||||
|
IEnumerable<ShaderDeclInfo> TextureUsage,
|
||||||
|
IEnumerable<ShaderDeclInfo> UniformUsage,
|
||||||
|
IDictionary<string, int> Locations)
|
||||||
|
: base(Type, TextureUsage, UniformUsage)
|
||||||
|
{
|
||||||
|
this.Bytecode = Bytecode;
|
||||||
|
this.Locations = Locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Compile()
|
||||||
|
{
|
||||||
|
if (Handle == 0)
|
||||||
|
{
|
||||||
|
Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
|
||||||
|
|
||||||
|
BinaryFormat SpirvFormat = (BinaryFormat)0x9551;
|
||||||
|
|
||||||
|
GL.ShaderBinary(1, new int[]{Handle}, SpirvFormat, Bytecode, Bytecode.Length);
|
||||||
|
|
||||||
|
GL.SpecializeShader(Handle, "main", 0, new int[]{}, new int[]{});
|
||||||
|
|
||||||
|
CheckCompilation(Handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct ShaderProgram
|
private struct ShaderProgram
|
||||||
{
|
{
|
||||||
public ShaderStage Vertex;
|
public ShaderStage Vertex;
|
||||||
|
@ -69,12 +125,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
public ShaderStage Fragment;
|
public ShaderStage Fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const int BuffersPerStage = Shader.UniformBinding.BuffersPerStage;
|
||||||
|
|
||||||
|
private const int BufferSize = 16 * 1024; //ARB_uniform_buffer, 16 KiB
|
||||||
|
|
||||||
private ShaderProgram Current;
|
private ShaderProgram Current;
|
||||||
|
|
||||||
private ConcurrentDictionary<long, ShaderStage> Stages;
|
private ConcurrentDictionary<long, ShaderStage> Stages;
|
||||||
|
|
||||||
private Dictionary<ShaderProgram, int> Programs;
|
private Dictionary<ShaderProgram, int> Programs;
|
||||||
|
|
||||||
|
private ShadingLanguage Language = ShadingLanguage.Unknown;
|
||||||
|
|
||||||
|
private OGLStreamBuffer[][] Buffers;
|
||||||
|
|
||||||
public int CurrentProgramHandle { get; private set; }
|
public int CurrentProgramHandle { get; private set; }
|
||||||
|
|
||||||
public OGLShader()
|
public OGLShader()
|
||||||
|
@ -82,6 +146,48 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
Stages = new ConcurrentDictionary<long, ShaderStage>();
|
Stages = new ConcurrentDictionary<long, ShaderStage>();
|
||||||
|
|
||||||
Programs = new Dictionary<ShaderProgram, int>();
|
Programs = new Dictionary<ShaderProgram, int>();
|
||||||
|
|
||||||
|
Buffers = new OGLStreamBuffer[5][]; //one per stage
|
||||||
|
|
||||||
|
for (int Stage = 0; Stage < 5; Stage++)
|
||||||
|
{
|
||||||
|
Buffers[Stage] = new OGLStreamBuffer[BuffersPerStage];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Prepare(bool TrySPIRV)
|
||||||
|
{
|
||||||
|
Console.WriteLine(GL.GetInteger(GetPName.MaxUniformBufferBindings));
|
||||||
|
|
||||||
|
for (int Stage = 0; Stage < 5; Stage++)
|
||||||
|
{
|
||||||
|
for (int Cbuf = 0; Cbuf < BuffersPerStage; Cbuf++)
|
||||||
|
{
|
||||||
|
OGLStreamBuffer Buffer = OGLStreamBuffer.Create(BufferTarget.UniformBuffer, BufferSize);
|
||||||
|
|
||||||
|
Buffer.Allocate();
|
||||||
|
|
||||||
|
Buffers[Stage][Cbuf] = Buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TrySPIRV)
|
||||||
|
{
|
||||||
|
if (HasSPIRV())
|
||||||
|
{
|
||||||
|
Console.WriteLine("SPIR-V Shading Language");
|
||||||
|
Language = ShadingLanguage.SPIRV;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("GLSL fallback (SPIR-V not available)");
|
||||||
|
Language = ShadingLanguage.GLSL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Language = ShadingLanguage.GLSL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Create(IGalMemory Memory, long Tag, GalShaderType Type)
|
public void Create(IGalMemory Memory, long Tag, GalShaderType Type)
|
||||||
|
@ -91,13 +197,34 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
private ShaderStage ShaderStageFactory(IGalMemory Memory, long Position, GalShaderType Type)
|
private ShaderStage ShaderStageFactory(IGalMemory Memory, long Position, GalShaderType Type)
|
||||||
{
|
{
|
||||||
GlslProgram Program = GetGlslProgram(Memory, Position, Type);
|
switch (Language)
|
||||||
|
{
|
||||||
|
case ShadingLanguage.SPIRV:
|
||||||
|
{
|
||||||
|
SpirvProgram Program = GetSpirvProgram(Memory, Position, Type);
|
||||||
|
|
||||||
return new ShaderStage(
|
return new SpirvStage(
|
||||||
Type,
|
Type,
|
||||||
Program.Code,
|
Program.Bytecode,
|
||||||
Program.Textures,
|
Program.Textures,
|
||||||
Program.Uniforms);
|
Program.Uniforms,
|
||||||
|
Program.Locations);
|
||||||
|
}
|
||||||
|
|
||||||
|
case ShadingLanguage.GLSL:
|
||||||
|
{
|
||||||
|
GlslProgram Program = GetGlslProgram(Memory, Position, Type);
|
||||||
|
|
||||||
|
return new GlslStage(
|
||||||
|
Type,
|
||||||
|
Program.Code,
|
||||||
|
Program.Textures,
|
||||||
|
Program.Uniforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private GlslProgram GetGlslProgram(IGalMemory Memory, long Position, GalShaderType Type)
|
private GlslProgram GetGlslProgram(IGalMemory Memory, long Position, GalShaderType Type)
|
||||||
|
@ -107,6 +234,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
return Decompiler.Decompile(Memory, Position + 0x50, Type);
|
return Decompiler.Decompile(Memory, Position + 0x50, Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SpirvProgram GetSpirvProgram(IGalMemory Memory, long Position, GalShaderType Type)
|
||||||
|
{
|
||||||
|
SpirvDecompiler Decompiler = new SpirvDecompiler();
|
||||||
|
|
||||||
|
return Decompiler.Decompile(Memory, Position + 0x50, Type);
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag)
|
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag)
|
||||||
{
|
{
|
||||||
if (Stages.TryGetValue(Tag, out ShaderStage Stage))
|
if (Stages.TryGetValue(Tag, out ShaderStage Stage))
|
||||||
|
@ -117,6 +251,73 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
return Enumerable.Empty<ShaderDeclInfo>();
|
return Enumerable.Empty<ShaderDeclInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int GetUniformLocation(string Name, ShaderStage Stage)
|
||||||
|
{
|
||||||
|
switch (Language)
|
||||||
|
{
|
||||||
|
case ShadingLanguage.SPIRV:
|
||||||
|
{
|
||||||
|
SpirvStage Spirv = (SpirvStage)Stage;
|
||||||
|
|
||||||
|
if (Spirv.Locations.TryGetValue(Name, out int Location))
|
||||||
|
{
|
||||||
|
return Location;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ShadingLanguage.GLSL:
|
||||||
|
{
|
||||||
|
return GL.GetUniformLocation(CurrentProgramHandle, Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TrySpirvStageLocation(string Name, ShaderStage Stage, out int Location)
|
||||||
|
{
|
||||||
|
if (Stage == null)
|
||||||
|
{
|
||||||
|
Location = -1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpirvStage Spirv = (SpirvStage)Stage;
|
||||||
|
|
||||||
|
return Spirv.Locations.TryGetValue(Name, out Location);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetSpirvLocation(string Name)
|
||||||
|
{
|
||||||
|
int Location;
|
||||||
|
|
||||||
|
if (TrySpirvStageLocation(Name, Current.Vertex, out Location)
|
||||||
|
|| TrySpirvStageLocation(Name, Current.TessControl, out Location)
|
||||||
|
|| TrySpirvStageLocation(Name, Current.TessEvaluation, out Location)
|
||||||
|
|| TrySpirvStageLocation(Name, Current.Geometry, out Location)
|
||||||
|
|| TrySpirvStageLocation(Name, Current.Fragment, out Location))
|
||||||
|
{
|
||||||
|
return Location;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetUniformLocation(string Name)
|
||||||
|
{
|
||||||
|
switch (Language)
|
||||||
|
{
|
||||||
|
case ShadingLanguage.SPIRV:
|
||||||
|
return GetSpirvLocation(Name);
|
||||||
|
|
||||||
|
case ShadingLanguage.GLSL:
|
||||||
|
return GL.GetUniformLocation(CurrentProgramHandle, Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetConstBuffer(long Tag, int Cbuf, byte[] Data)
|
public void SetConstBuffer(long Tag, int Cbuf, byte[] Data)
|
||||||
{
|
{
|
||||||
BindProgram();
|
BindProgram();
|
||||||
|
@ -125,21 +326,21 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf))
|
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf))
|
||||||
{
|
{
|
||||||
int Location = GL.GetUniformLocation(CurrentProgramHandle, DeclInfo.Name);
|
if (Cbuf >= BuffersPerStage)
|
||||||
|
|
||||||
int Count = Data.Length >> 2;
|
|
||||||
|
|
||||||
//The Index is the index of the last element,
|
|
||||||
//so we can add 1 to get the uniform array size.
|
|
||||||
Count = Math.Min(Count, DeclInfo.Index + 1);
|
|
||||||
|
|
||||||
unsafe
|
|
||||||
{
|
{
|
||||||
fixed (byte* Ptr = Data)
|
string Message = $"Game tried to write constant buffer #{Cbuf} but only 0-#{BuffersPerStage-1} are supported";
|
||||||
{
|
throw new NotSupportedException(Message);
|
||||||
GL.Uniform1(Location, Count, (float*)Ptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OGLStreamBuffer Buffer = Buffers[(int)Stage.Type][Cbuf];
|
||||||
|
|
||||||
|
int Size = Math.Min(Data.Length, BufferSize);
|
||||||
|
|
||||||
|
byte[] Destiny = Buffer.Map(Size);
|
||||||
|
|
||||||
|
Array.Copy(Data, Destiny, Size);
|
||||||
|
|
||||||
|
Buffer.Unmap(Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,7 +349,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
BindProgram();
|
BindProgram();
|
||||||
|
|
||||||
int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName);
|
int Location = GetUniformLocation(UniformName);
|
||||||
|
|
||||||
GL.Uniform1(Location, Value);
|
GL.Uniform1(Location, Value);
|
||||||
}
|
}
|
||||||
|
@ -157,7 +358,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
{
|
{
|
||||||
BindProgram();
|
BindProgram();
|
||||||
|
|
||||||
int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName);
|
int Location = GetUniformLocation(UniformName);
|
||||||
|
|
||||||
GL.Uniform2(Location, X, Y);
|
GL.Uniform2(Location, X, Y);
|
||||||
}
|
}
|
||||||
|
@ -204,11 +405,33 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
CheckProgramLink(Handle);
|
CheckProgramLink(Handle);
|
||||||
|
|
||||||
|
if (Language == ShadingLanguage.GLSL)
|
||||||
|
{
|
||||||
|
BindUniformBlocksIfNotNull(Handle, Current.Vertex);
|
||||||
|
BindUniformBlocksIfNotNull(Handle, Current.TessControl);
|
||||||
|
BindUniformBlocksIfNotNull(Handle, Current.TessEvaluation);
|
||||||
|
BindUniformBlocksIfNotNull(Handle, Current.Geometry);
|
||||||
|
BindUniformBlocksIfNotNull(Handle, Current.Fragment);
|
||||||
|
}
|
||||||
|
|
||||||
Programs.Add(Current, Handle);
|
Programs.Add(Current, Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
GL.UseProgram(Handle);
|
GL.UseProgram(Handle);
|
||||||
|
|
||||||
|
//TODO: This could be done once, right?
|
||||||
|
for (int Stage = 0; Stage < 5; Stage++)
|
||||||
|
{
|
||||||
|
for (int Cbuf = 0; Cbuf < BuffersPerStage; Cbuf++)
|
||||||
|
{
|
||||||
|
OGLStreamBuffer Buffer = Buffers[Stage][Cbuf];
|
||||||
|
|
||||||
|
int Binding = Shader.UniformBinding.Get((GalShaderType)Stage, Cbuf);
|
||||||
|
|
||||||
|
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, Binding, Buffer.Handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CurrentProgramHandle = Handle;
|
CurrentProgramHandle = Handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,12 +445,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void CompileAndCheck(int Handle, string Code)
|
private void BindUniformBlocksIfNotNull(int ProgramHandle, ShaderStage Stage)
|
||||||
{
|
{
|
||||||
GL.ShaderSource(Handle, Code);
|
if (Stage != null)
|
||||||
GL.CompileShader(Handle);
|
{
|
||||||
|
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage)
|
||||||
|
{
|
||||||
|
int BlockIndex = GL.GetUniformBlockIndex(ProgramHandle, DeclInfo.Name);
|
||||||
|
|
||||||
CheckCompilation(Handle);
|
if (BlockIndex < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Binding = Shader.UniformBinding.Get(Stage.Type, DeclInfo.Cbuf);
|
||||||
|
|
||||||
|
GL.UniformBlockBinding(ProgramHandle, BlockIndex, Binding);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CheckCompilation(int Handle)
|
private static void CheckCompilation(int Handle)
|
||||||
|
@ -253,5 +488,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
throw new ShaderException(GL.GetProgramInfoLog(Handle));
|
throw new ShaderException(GL.GetProgramInfoLog(Handle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool HasSPIRV()
|
||||||
|
{
|
||||||
|
int ExtensionCount = GL.GetInteger(GetPName.NumExtensions);
|
||||||
|
|
||||||
|
for (int i = 0; i < ExtensionCount; i++)
|
||||||
|
{
|
||||||
|
if (GL.GetString(StringNameIndexed.Extensions, i) == "GL_ARB_gl_spirv")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
125
Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs
Normal file
125
Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
using System;
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
{
|
||||||
|
abstract class OGLStreamBuffer : IDisposable
|
||||||
|
{
|
||||||
|
public int Handle { get; protected set; }
|
||||||
|
|
||||||
|
protected BufferTarget Target;
|
||||||
|
|
||||||
|
protected int Size;
|
||||||
|
|
||||||
|
private bool Mapped = false;
|
||||||
|
|
||||||
|
public OGLStreamBuffer(BufferTarget Target, int MaxSize)
|
||||||
|
{
|
||||||
|
Handle = 0;
|
||||||
|
Mapped = false;
|
||||||
|
|
||||||
|
this.Target = Target;
|
||||||
|
this.Size = MaxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OGLStreamBuffer Create(BufferTarget Target, int MaxSize)
|
||||||
|
{
|
||||||
|
return new SubDataBuffer(Target, MaxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Allocate()
|
||||||
|
{
|
||||||
|
if (this.Handle == 0)
|
||||||
|
{
|
||||||
|
GL.CreateBuffers(1, out int Handle);
|
||||||
|
|
||||||
|
this.Handle = Handle;
|
||||||
|
|
||||||
|
InternAllocate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Map(int Size)
|
||||||
|
{
|
||||||
|
if (Handle == 0 || Mapped || Size > this.Size)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] Memory = InternMap(Size);
|
||||||
|
|
||||||
|
Mapped = true;
|
||||||
|
|
||||||
|
return Memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unmap(int UsedSize)
|
||||||
|
{
|
||||||
|
if (Handle == 0 || !Mapped)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
InternUnmap(UsedSize);
|
||||||
|
|
||||||
|
Mapped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void InternAllocate();
|
||||||
|
|
||||||
|
protected abstract byte[] InternMap(int Size);
|
||||||
|
|
||||||
|
protected abstract void InternUnmap(int UsedSize);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool Disposing)
|
||||||
|
{
|
||||||
|
if (Disposing && Handle != 0)
|
||||||
|
{
|
||||||
|
GL.DeleteBuffer(Handle);
|
||||||
|
|
||||||
|
Handle = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SubDataBuffer : OGLStreamBuffer
|
||||||
|
{
|
||||||
|
private byte[] Memory;
|
||||||
|
|
||||||
|
public SubDataBuffer(BufferTarget Target, int MaxSize)
|
||||||
|
: base(Target, MaxSize)
|
||||||
|
{
|
||||||
|
Memory = new byte[MaxSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InternAllocate()
|
||||||
|
{
|
||||||
|
GL.BindBuffer(Target, Handle);
|
||||||
|
|
||||||
|
GL.BufferData(Target, Size, IntPtr.Zero, BufferUsageHint.StreamDraw);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override byte[] InternMap(int Size)
|
||||||
|
{
|
||||||
|
return Memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InternUnmap(int UsedSize)
|
||||||
|
{
|
||||||
|
GL.BindBuffer(Target, Handle);
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
fixed (byte* MemoryPtr = Memory)
|
||||||
|
{
|
||||||
|
GL.BufferSubData(Target, IntPtr.Zero, UsedSize, (IntPtr)MemoryPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
ActionsQueue = new ConcurrentQueue<Action>();
|
ActionsQueue = new ConcurrentQueue<Action>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Initialize(bool TrySPIRV = false)
|
||||||
|
{
|
||||||
|
ActionsQueue.Enqueue(() =>
|
||||||
|
{
|
||||||
|
Shader.Prepare(TrySPIRV);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void QueueAction(Action ActionMthd)
|
public void QueueAction(Action ActionMthd)
|
||||||
{
|
{
|
||||||
ActionsQueue.Enqueue(ActionMthd);
|
ActionsQueue.Enqueue(ActionMthd);
|
||||||
|
|
|
@ -6,19 +6,12 @@ using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gal.Shader
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
{
|
{
|
||||||
public class GlslDecompiler
|
public class GlslDecompiler : ShaderDecompiler
|
||||||
{
|
{
|
||||||
private delegate string GetInstExpr(ShaderIrOp Op);
|
private delegate string GetInstExpr(ShaderIrOp Op);
|
||||||
|
|
||||||
private Dictionary<ShaderIrInst, GetInstExpr> InstsExpr;
|
private Dictionary<ShaderIrInst, GetInstExpr> InstsExpr;
|
||||||
|
|
||||||
private enum OperType
|
|
||||||
{
|
|
||||||
Bool,
|
|
||||||
F32,
|
|
||||||
I32
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string IdentationStr = " ";
|
private const string IdentationStr = " ";
|
||||||
|
|
||||||
private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" };
|
private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" };
|
||||||
|
@ -145,7 +138,9 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
|
||||||
foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector))
|
foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector))
|
||||||
{
|
{
|
||||||
SB.AppendLine($"uniform {GetDecl(DeclInfo)}[{DeclInfo.Index + 1}];");
|
SB.AppendLine($"layout (std140) uniform {DeclInfo.Name} {{");
|
||||||
|
SB.AppendLine($" vec4 {DeclInfo.Name}_data[{DeclInfo.Index / 4 + 1}];");
|
||||||
|
SB.AppendLine($"}};");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Decl.Uniforms.Count > 0)
|
if (Decl.Uniforms.Count > 0)
|
||||||
|
@ -158,7 +153,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
{
|
{
|
||||||
if (Decl.ShaderType == GalShaderType.Fragment)
|
if (Decl.ShaderType == GalShaderType.Fragment)
|
||||||
{
|
{
|
||||||
SB.AppendLine("in vec4 " + GlslDecl.PositionOutAttrName + ";");
|
SB.AppendLine("layout (location = 0) in vec4 " + GlslDecl.PositionOutAttrName + ";");
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintDeclAttributes(Decl.InAttributes.Values, "in");
|
PrintDeclAttributes(Decl.InAttributes.Values, "in");
|
||||||
|
@ -168,7 +163,7 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
{
|
{
|
||||||
if (Decl.ShaderType == GalShaderType.Vertex)
|
if (Decl.ShaderType == GalShaderType.Vertex)
|
||||||
{
|
{
|
||||||
SB.AppendLine("out vec4 " + GlslDecl.PositionOutAttrName + ";");
|
SB.AppendLine("layout (location = 0) out vec4 " + GlslDecl.PositionOutAttrName + ";");
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintDeclAttributes(Decl.OutAttributes.Values, "out");
|
PrintDeclAttributes(Decl.OutAttributes.Values, "out");
|
||||||
|
@ -182,7 +177,9 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
{
|
{
|
||||||
if (DeclInfo.Index >= 0)
|
if (DeclInfo.Index >= 0)
|
||||||
{
|
{
|
||||||
SB.AppendLine("layout (location = " + DeclInfo.Index + ") " + InOut + " " + GetDecl(DeclInfo) + ";");
|
int Location = DeclInfo.Index + 1;
|
||||||
|
|
||||||
|
SB.AppendLine("layout (location = " + Location + ") " + InOut + " " + GetDecl(DeclInfo) + ";");
|
||||||
|
|
||||||
Count++;
|
Count++;
|
||||||
}
|
}
|
||||||
|
@ -232,11 +229,6 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int DeclKeySelector(ShaderDeclInfo DeclInfo)
|
|
||||||
{
|
|
||||||
return DeclInfo.Cbuf << 24 | DeclInfo.Index;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetDecl(ShaderDeclInfo DeclInfo)
|
private string GetDecl(ShaderDeclInfo DeclInfo)
|
||||||
{
|
{
|
||||||
return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name;
|
return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name;
|
||||||
|
@ -437,20 +429,6 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
return Tail;
|
return Tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsValidOutOper(ShaderIrNode Node)
|
|
||||||
{
|
|
||||||
if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (Node is ShaderIrOperPred Pred && Pred.IsConst)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetDstOperName(ShaderIrNode Node)
|
private string GetDstOperName(ShaderIrNode Node)
|
||||||
{
|
{
|
||||||
if (Node is ShaderIrOperAbuf Abuf)
|
if (Node is ShaderIrOperAbuf Abuf)
|
||||||
|
@ -534,11 +512,13 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
//This may not be aways the case.
|
//This may not be aways the case.
|
||||||
string Offset = "(floatBitsToInt(" + GetSrcExpr(Cbuf.Offs) + ") >> 2)";
|
string Offset = "(floatBitsToInt(" + GetSrcExpr(Cbuf.Offs) + ") >> 2)";
|
||||||
|
|
||||||
return DeclInfo.Name + "[" + Cbuf.Pos + " + " + Offset + "]";
|
string Index = "(" + Cbuf.Pos + " + " + Offset + ")";
|
||||||
|
|
||||||
|
return $"{DeclInfo.Name}_data[{Index} / 4][{Index} % 4]";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return DeclInfo.Name + "[" + Cbuf.Pos + "]";
|
return $"{DeclInfo.Name}_data[{Cbuf.Pos / 4}][{Cbuf.Pos % 4}]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -950,65 +930,5 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
|
||||||
return Expr;
|
return Expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperType GetDstNodeType(ShaderIrNode Node)
|
|
||||||
{
|
|
||||||
//Special case instructions with the result type different
|
|
||||||
//from the input types (like integer <-> float conversion) here.
|
|
||||||
if (Node is ShaderIrOp Op)
|
|
||||||
{
|
|
||||||
switch (Op.Inst)
|
|
||||||
{
|
|
||||||
case ShaderIrInst.Stof:
|
|
||||||
case ShaderIrInst.Txlf:
|
|
||||||
case ShaderIrInst.Utof:
|
|
||||||
return OperType.F32;
|
|
||||||
|
|
||||||
case ShaderIrInst.Ftos:
|
|
||||||
case ShaderIrInst.Ftou:
|
|
||||||
return OperType.I32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetSrcNodeType(Node);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static OperType GetSrcNodeType(ShaderIrNode Node)
|
|
||||||
{
|
|
||||||
switch (Node)
|
|
||||||
{
|
|
||||||
case ShaderIrOperAbuf Abuf:
|
|
||||||
return Abuf.Offs == GlslDecl.VertexIdAttr ||
|
|
||||||
Abuf.Offs == GlslDecl.InstanceIdAttr
|
|
||||||
? OperType.I32
|
|
||||||
: OperType.F32;
|
|
||||||
|
|
||||||
case ShaderIrOperCbuf Cbuf: return OperType.F32;
|
|
||||||
case ShaderIrOperGpr Gpr: return OperType.F32;
|
|
||||||
case ShaderIrOperImm Imm: return OperType.I32;
|
|
||||||
case ShaderIrOperImmf Immf: return OperType.F32;
|
|
||||||
case ShaderIrOperPred Pred: return OperType.Bool;
|
|
||||||
|
|
||||||
case ShaderIrOp Op:
|
|
||||||
if (Op.Inst > ShaderIrInst.B_Start &&
|
|
||||||
Op.Inst < ShaderIrInst.B_End)
|
|
||||||
{
|
|
||||||
return OperType.Bool;
|
|
||||||
}
|
|
||||||
else if (Op.Inst > ShaderIrInst.F_Start &&
|
|
||||||
Op.Inst < ShaderIrInst.F_End)
|
|
||||||
{
|
|
||||||
return OperType.F32;
|
|
||||||
}
|
|
||||||
else if (Op.Inst > ShaderIrInst.I_Start &&
|
|
||||||
Op.Inst < ShaderIrInst.I_End)
|
|
||||||
{
|
|
||||||
return OperType.I32;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException(nameof(Node));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,21 +2,17 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gal.Shader
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
{
|
{
|
||||||
public struct GlslProgram
|
public class GlslProgram : ShaderProgram
|
||||||
{
|
{
|
||||||
public string Code { get; private set; }
|
public string Code { get; private set; }
|
||||||
|
|
||||||
public IEnumerable<ShaderDeclInfo> Textures { get; private set; }
|
|
||||||
public IEnumerable<ShaderDeclInfo> Uniforms { get; private set; }
|
|
||||||
|
|
||||||
public GlslProgram(
|
public GlslProgram(
|
||||||
string Code,
|
string Code,
|
||||||
IEnumerable<ShaderDeclInfo> Textures,
|
IEnumerable<ShaderDeclInfo> Textures,
|
||||||
IEnumerable<ShaderDeclInfo> Uniforms)
|
IEnumerable<ShaderDeclInfo> Uniforms)
|
||||||
|
: base(Textures, Uniforms)
|
||||||
{
|
{
|
||||||
this.Code = Code;
|
this.Code = Code;
|
||||||
this.Textures = Textures;
|
|
||||||
this.Uniforms = Uniforms;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
70
Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvAssembler.cs
Normal file
70
Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvAssembler.cs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader.SPIRV
|
||||||
|
{
|
||||||
|
public class Assembler
|
||||||
|
{
|
||||||
|
private List<Instruction> Instructions;
|
||||||
|
|
||||||
|
public Assembler()
|
||||||
|
{
|
||||||
|
Instructions = new List<Instruction>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(Stream Output)
|
||||||
|
{
|
||||||
|
uint Bound = DoBindings();
|
||||||
|
|
||||||
|
BinaryWriter BW = new BinaryWriter(Output);
|
||||||
|
|
||||||
|
BW.Write((uint)BinaryForm.MagicNumber);
|
||||||
|
BW.Write((uint)BinaryForm.VersionNumber);
|
||||||
|
BW.Write((uint)BinaryForm.GeneratorMagicNumber);
|
||||||
|
BW.Write((uint)Bound);
|
||||||
|
BW.Write((uint)0); // Reserved for instruction schema
|
||||||
|
|
||||||
|
foreach (Instruction Instruction in Instructions)
|
||||||
|
{
|
||||||
|
Instruction.Write(BW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(Instruction Instruction)
|
||||||
|
{
|
||||||
|
Instructions.Add(Instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(Instruction[] Instructions)
|
||||||
|
{
|
||||||
|
foreach (Instruction Instruction in Instructions)
|
||||||
|
{
|
||||||
|
Add(Instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(List<Instruction> Instructions)
|
||||||
|
{
|
||||||
|
foreach (Instruction Instruction in Instructions)
|
||||||
|
{
|
||||||
|
Add(Instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint DoBindings()
|
||||||
|
{
|
||||||
|
uint Bind = 1;
|
||||||
|
|
||||||
|
foreach (Instruction Instruction in Instructions)
|
||||||
|
{
|
||||||
|
if (Instruction.HoldsResultId)
|
||||||
|
{
|
||||||
|
Instruction.ResultId = Bind;
|
||||||
|
Bind++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Bind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1125
Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvBinaryForm.cs
Normal file
1125
Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvBinaryForm.cs
Normal file
File diff suppressed because it is too large
Load diff
1739
Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvInstruction.cs
Normal file
1739
Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvInstruction.cs
Normal file
File diff suppressed because it is too large
Load diff
162
Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvOperand.cs
Normal file
162
Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvOperand.cs
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader.SPIRV
|
||||||
|
{
|
||||||
|
public abstract class Operand
|
||||||
|
{
|
||||||
|
public abstract int GetWordCount();
|
||||||
|
|
||||||
|
public abstract void Write(BinaryWriter BinaryWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Id: Operand
|
||||||
|
{
|
||||||
|
private Instruction Instruction;
|
||||||
|
|
||||||
|
public Id(Instruction Instruction)
|
||||||
|
{
|
||||||
|
this.Instruction = Instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(BinaryWriter BinaryWriter)
|
||||||
|
{
|
||||||
|
BinaryWriter.Write((uint)Instruction.ResultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetWordCount()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class Literal: Operand
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LiteralString: Literal
|
||||||
|
{
|
||||||
|
public byte[] Value;
|
||||||
|
|
||||||
|
public LiteralString(string String)
|
||||||
|
{
|
||||||
|
Value = Encoding.UTF8.GetBytes(String);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(BinaryWriter BinaryWriter)
|
||||||
|
{
|
||||||
|
BinaryWriter.Write(Value);
|
||||||
|
|
||||||
|
// Write remaining zero bytes
|
||||||
|
for (int i = 0; i < 4 - (Value.Length % 4); i++)
|
||||||
|
{
|
||||||
|
BinaryWriter.Write((byte)0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetWordCount()
|
||||||
|
{
|
||||||
|
return Value.Length / 4 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object Object)
|
||||||
|
{
|
||||||
|
if (Object is LiteralString Other)
|
||||||
|
{
|
||||||
|
return this.Value == Other.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Value.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LiteralNumber: Literal
|
||||||
|
{
|
||||||
|
public TypeCode Type;
|
||||||
|
|
||||||
|
public int Integer;
|
||||||
|
|
||||||
|
public float Float32;
|
||||||
|
|
||||||
|
public double Float64;
|
||||||
|
|
||||||
|
public LiteralNumber(int Value)
|
||||||
|
{
|
||||||
|
Integer = Value;
|
||||||
|
Type = Value.GetTypeCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteralNumber(float Value)
|
||||||
|
{
|
||||||
|
Float32 = Value;
|
||||||
|
Type = Value.GetTypeCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiteralNumber(double Value)
|
||||||
|
{
|
||||||
|
Float64 = Value;
|
||||||
|
Type = Value.GetTypeCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(BinaryWriter BinaryWriter)
|
||||||
|
{
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case TypeCode.Int32:
|
||||||
|
BinaryWriter.Write(Integer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeCode.Single:
|
||||||
|
BinaryWriter.Write(Float32);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TypeCode.Double:
|
||||||
|
BinaryWriter.Write(Float64);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetWordCount()
|
||||||
|
{
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case TypeCode.Int32:
|
||||||
|
case TypeCode.Single:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case TypeCode.Double:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object Object)
|
||||||
|
{
|
||||||
|
if (Object is LiteralNumber Other && this.Type == Other.Type)
|
||||||
|
{
|
||||||
|
return this.Integer == Other.Integer
|
||||||
|
&& this.Float32 == Other.Float32
|
||||||
|
&& this.Float64 == Other.Float64;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Type.GetHashCode() + Integer.GetHashCode()
|
||||||
|
+ Float32.GetHashCode() + Float64.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
Ryujinx.Graphics/Gal/Shader/ShaderBinding.cs
Normal file
28
Ryujinx.Graphics/Gal/Shader/ShaderBinding.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
static class UniformBinding
|
||||||
|
{
|
||||||
|
public const int BuffersPerStage = 12; //ARB_uniform_buffer
|
||||||
|
|
||||||
|
public static int Get(GalShaderType Stage, int Cbuf)
|
||||||
|
{
|
||||||
|
return GetStageIndex(Stage) * BuffersPerStage + Cbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetStageIndex(GalShaderType Stage)
|
||||||
|
{
|
||||||
|
switch (Stage)
|
||||||
|
{
|
||||||
|
case GalShaderType.Vertex: return 0;
|
||||||
|
case GalShaderType.Fragment: return 1;
|
||||||
|
case GalShaderType.Geometry: return 2;
|
||||||
|
case GalShaderType.TessControl: return 3;
|
||||||
|
case GalShaderType.TessEvaluation: return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
Ryujinx.Graphics/Gal/Shader/ShaderDecompiler.cs
Normal file
93
Ryujinx.Graphics/Gal/Shader/ShaderDecompiler.cs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
public class ShaderDecompiler
|
||||||
|
{
|
||||||
|
protected enum OperType
|
||||||
|
{
|
||||||
|
Bool,
|
||||||
|
F32,
|
||||||
|
I32
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static bool IsValidOutOper(ShaderIrNode Node)
|
||||||
|
{
|
||||||
|
if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (Node is ShaderIrOperPred Pred && Pred.IsConst)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static OperType GetDstNodeType(ShaderIrNode Node)
|
||||||
|
{
|
||||||
|
//Special case instructions with the result type different
|
||||||
|
//from the input types (like integer <-> float conversion) here.
|
||||||
|
if (Node is ShaderIrOp Op)
|
||||||
|
{
|
||||||
|
switch (Op.Inst)
|
||||||
|
{
|
||||||
|
case ShaderIrInst.Stof:
|
||||||
|
case ShaderIrInst.Txlf:
|
||||||
|
case ShaderIrInst.Utof:
|
||||||
|
return OperType.F32;
|
||||||
|
|
||||||
|
case ShaderIrInst.Ftos:
|
||||||
|
case ShaderIrInst.Ftou:
|
||||||
|
return OperType.I32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetSrcNodeType(Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static OperType GetSrcNodeType(ShaderIrNode Node)
|
||||||
|
{
|
||||||
|
switch (Node)
|
||||||
|
{
|
||||||
|
case ShaderIrOperAbuf Abuf:
|
||||||
|
return Abuf.Offs == GlslDecl.VertexIdAttr ||
|
||||||
|
Abuf.Offs == GlslDecl.InstanceIdAttr
|
||||||
|
? OperType.I32
|
||||||
|
: OperType.F32;
|
||||||
|
|
||||||
|
case ShaderIrOperCbuf Cbuf: return OperType.F32;
|
||||||
|
case ShaderIrOperGpr Gpr: return OperType.F32;
|
||||||
|
case ShaderIrOperImm Imm: return OperType.I32;
|
||||||
|
case ShaderIrOperImmf Immf: return OperType.F32;
|
||||||
|
case ShaderIrOperPred Pred: return OperType.Bool;
|
||||||
|
|
||||||
|
case ShaderIrOp Op:
|
||||||
|
if (Op.Inst > ShaderIrInst.B_Start &&
|
||||||
|
Op.Inst < ShaderIrInst.B_End)
|
||||||
|
{
|
||||||
|
return OperType.Bool;
|
||||||
|
}
|
||||||
|
else if (Op.Inst > ShaderIrInst.F_Start &&
|
||||||
|
Op.Inst < ShaderIrInst.F_End)
|
||||||
|
{
|
||||||
|
return OperType.F32;
|
||||||
|
}
|
||||||
|
else if (Op.Inst > ShaderIrInst.I_Start &&
|
||||||
|
Op.Inst < ShaderIrInst.I_End)
|
||||||
|
{
|
||||||
|
return OperType.I32;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException(nameof(Node));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int DeclKeySelector(ShaderDeclInfo DeclInfo)
|
||||||
|
{
|
||||||
|
return DeclInfo.Cbuf << 24 | DeclInfo.Index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.Graphics.Gal.Shader
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
{
|
{
|
||||||
class ShaderIrNode { }
|
public class ShaderIrNode { }
|
||||||
}
|
}
|
18
Ryujinx.Graphics/Gal/Shader/ShaderProgram.cs
Normal file
18
Ryujinx.Graphics/Gal/Shader/ShaderProgram.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
public class ShaderProgram
|
||||||
|
{
|
||||||
|
public IEnumerable<ShaderDeclInfo> Textures { get; private set; }
|
||||||
|
public IEnumerable<ShaderDeclInfo> Uniforms { get; private set; }
|
||||||
|
|
||||||
|
public ShaderProgram(
|
||||||
|
IEnumerable<ShaderDeclInfo> Textures,
|
||||||
|
IEnumerable<ShaderDeclInfo> Uniforms)
|
||||||
|
{
|
||||||
|
this.Textures = Textures;
|
||||||
|
this.Uniforms = Uniforms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1709
Ryujinx.Graphics/Gal/Shader/SpirvDecompiler.cs
Normal file
1709
Ryujinx.Graphics/Gal/Shader/SpirvDecompiler.cs
Normal file
File diff suppressed because it is too large
Load diff
22
Ryujinx.Graphics/Gal/Shader/SpirvProgram.cs
Normal file
22
Ryujinx.Graphics/Gal/Shader/SpirvProgram.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gal.Shader
|
||||||
|
{
|
||||||
|
public class SpirvProgram : ShaderProgram
|
||||||
|
{
|
||||||
|
public byte[] Bytecode { get; private set; }
|
||||||
|
|
||||||
|
public IDictionary<string, int> Locations { get; private set; }
|
||||||
|
|
||||||
|
public SpirvProgram(
|
||||||
|
byte[] Bytecode,
|
||||||
|
IDictionary<string, int> Locations,
|
||||||
|
IEnumerable<ShaderDeclInfo> Textures,
|
||||||
|
IEnumerable<ShaderDeclInfo> Uniforms)
|
||||||
|
: base(Textures, Uniforms)
|
||||||
|
{
|
||||||
|
this.Bytecode = Bytecode;
|
||||||
|
this.Locations = Locations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
using Ryujinx.Graphics.Gal.Shader;
|
using Ryujinx.Graphics.Gal.Shader;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.ShaderTools
|
namespace Ryujinx.ShaderTools
|
||||||
{
|
{
|
||||||
|
@ -9,13 +10,11 @@ namespace Ryujinx.ShaderTools
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
if (args.Length == 2)
|
if (args.Length == 4)
|
||||||
{
|
{
|
||||||
GlslDecompiler Decompiler = new GlslDecompiler();
|
|
||||||
|
|
||||||
GalShaderType ShaderType = GalShaderType.Vertex;
|
GalShaderType ShaderType = GalShaderType.Vertex;
|
||||||
|
|
||||||
switch (args[0].ToLower())
|
switch (args[1].ToLower())
|
||||||
{
|
{
|
||||||
case "v": ShaderType = GalShaderType.Vertex; break;
|
case "v": ShaderType = GalShaderType.Vertex; break;
|
||||||
case "tc": ShaderType = GalShaderType.TessControl; break;
|
case "tc": ShaderType = GalShaderType.TessControl; break;
|
||||||
|
@ -24,18 +23,40 @@ namespace Ryujinx.ShaderTools
|
||||||
case "f": ShaderType = GalShaderType.Fragment; break;
|
case "f": ShaderType = GalShaderType.Fragment; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (FileStream FS = new FileStream(args[1], FileMode.Open, FileAccess.Read))
|
using (FileStream Output = new FileStream(args[3], FileMode.Create))
|
||||||
|
using (FileStream FS = new FileStream(args[2], FileMode.Open, FileAccess.Read))
|
||||||
{
|
{
|
||||||
Memory Mem = new Memory(FS);
|
Memory Mem = new Memory(FS);
|
||||||
|
|
||||||
GlslProgram Program = Decompiler.Decompile(Mem, 0, ShaderType);
|
switch (args[0].ToLower())
|
||||||
|
{
|
||||||
|
case "glsl":
|
||||||
|
{
|
||||||
|
GlslDecompiler GlslDecompiler = new GlslDecompiler();
|
||||||
|
|
||||||
Console.WriteLine(Program.Code);
|
GlslProgram Program = GlslDecompiler.Decompile(Mem, 0, ShaderType);
|
||||||
|
|
||||||
|
Output.Write(System.Text.Encoding.UTF8.GetBytes(Program.Code));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "spirv":
|
||||||
|
{
|
||||||
|
SpirvDecompiler SpirvDecompiler = new SpirvDecompiler();
|
||||||
|
|
||||||
|
SpirvProgram Program = SpirvDecompiler.Decompile(Mem, 0, ShaderType);
|
||||||
|
|
||||||
|
Output.Write(Program.Bytecode);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine("Usage: Ryujinx.ShaderTools [v|tc|te|g|f] shader.bin");
|
Console.WriteLine("Usage: Ryujinx.ShaderTools [spirv|glsl] [v|tc|te|g|f] shader.bin output.bin");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ namespace Ryujinx
|
||||||
{
|
{
|
||||||
public static JoyCon FakeJoyCon { get; private set; }
|
public static JoyCon FakeJoyCon { get; private set; }
|
||||||
|
|
||||||
|
public static bool EnableSPIRV { get; private set; }
|
||||||
|
|
||||||
public static void Read(Logger Log)
|
public static void Read(Logger Log)
|
||||||
{
|
{
|
||||||
string IniFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
string IniFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||||
|
@ -22,6 +24,8 @@ namespace Ryujinx
|
||||||
|
|
||||||
AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
|
AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
|
||||||
|
|
||||||
|
EnableSPIRV = Convert.ToBoolean(Parser.Value("Enable_SPIRV"));
|
||||||
|
|
||||||
Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug")));
|
Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug")));
|
||||||
Log.SetEnable(LogLevel.Stub, Convert.ToBoolean(Parser.Value("Logging_Enable_Stub")));
|
Log.SetEnable(LogLevel.Stub, Convert.ToBoolean(Parser.Value("Logging_Enable_Stub")));
|
||||||
Log.SetEnable(LogLevel.Info, Convert.ToBoolean(Parser.Value("Logging_Enable_Info")));
|
Log.SetEnable(LogLevel.Info, Convert.ToBoolean(Parser.Value("Logging_Enable_Info")));
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#Enable cpu memory checks (slow)
|
#Enable cpu memory checks (slow)
|
||||||
Enable_Memory_Checks = false
|
Enable_Memory_Checks = false
|
||||||
|
|
||||||
|
#Enable SPIR-V shading language (experimental)
|
||||||
|
Enable_SPIRV = false
|
||||||
|
|
||||||
#Enable print debug logs
|
#Enable print debug logs
|
||||||
Logging_Enable_Debug = false
|
Logging_Enable_Debug = false
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
using Ryujinx.Graphics.Gal;
|
using Ryujinx.Graphics.Gal;
|
||||||
|
using Ryujinx.Graphics.Gal.OpenGL;
|
||||||
using Ryujinx.HLE;
|
using Ryujinx.HLE;
|
||||||
using Ryujinx.HLE.Input;
|
using Ryujinx.HLE.Input;
|
||||||
using System;
|
using System;
|
||||||
|
@ -42,6 +43,11 @@ namespace Ryujinx
|
||||||
{
|
{
|
||||||
VSync = VSyncMode.On;
|
VSync = VSyncMode.On;
|
||||||
|
|
||||||
|
if (Renderer is OpenGLRenderer OGL)
|
||||||
|
{
|
||||||
|
OGL.Initialize(Config.EnableSPIRV);
|
||||||
|
}
|
||||||
|
|
||||||
Renderer.SetWindowSize(Width, Height);
|
Renderer.SetWindowSize(Width, Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue