2019-12-29 00:45:33 +01:00
|
|
|
using OpenTK.Graphics.OpenGL;
|
|
|
|
using Ryujinx.Common.Logging;
|
2019-10-13 08:02:07 +02:00
|
|
|
using Ryujinx.Graphics.GAL;
|
2020-07-15 05:01:10 +02:00
|
|
|
using System;
|
2020-11-13 00:15:34 +01:00
|
|
|
using System.Buffers.Binary;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
|
|
|
namespace Ryujinx.Graphics.OpenGL
|
|
|
|
{
|
|
|
|
class Program : IProgram
|
|
|
|
{
|
|
|
|
public int Handle { get; private set; }
|
|
|
|
|
2021-03-29 22:52:25 +02:00
|
|
|
public bool IsLinked
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
if (_status == ProgramLinkStatus.Incomplete)
|
|
|
|
{
|
|
|
|
CheckProgramLink(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return _status == ProgramLinkStatus.Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete;
|
|
|
|
private IShader[] _shaders;
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2022-02-16 23:15:39 +01:00
|
|
|
public bool HasFragmentShader;
|
|
|
|
public int FragmentOutputMap { get; }
|
|
|
|
|
|
|
|
public Program(IShader[] shaders, int fragmentOutputMap)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
|
|
|
Handle = GL.CreateProgram();
|
|
|
|
|
2020-11-13 00:15:34 +01:00
|
|
|
GL.ProgramParameter(Handle, ProgramParameterName.ProgramBinaryRetrievableHint, 1);
|
|
|
|
|
2019-10-13 08:02:07 +02:00
|
|
|
for (int index = 0; index < shaders.Length; index++)
|
|
|
|
{
|
2022-02-16 23:15:39 +01:00
|
|
|
Shader shader = (Shader)shaders[index];
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2022-02-16 23:15:39 +01:00
|
|
|
if (shader.IsFragment)
|
|
|
|
{
|
|
|
|
HasFragmentShader = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
GL.AttachShader(Handle, shader.Handle);
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
GL.LinkProgram(Handle);
|
|
|
|
|
2021-03-29 22:52:25 +02:00
|
|
|
_shaders = shaders;
|
2022-02-16 23:15:39 +01:00
|
|
|
FragmentOutputMap = fragmentOutputMap;
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
2022-02-16 23:15:39 +01:00
|
|
|
public Program(ReadOnlySpan<byte> code, bool hasFragmentShader, int fragmentOutputMap)
|
2020-11-13 00:15:34 +01:00
|
|
|
{
|
|
|
|
BinaryFormat binaryFormat = (BinaryFormat)BinaryPrimitives.ReadInt32LittleEndian(code.Slice(code.Length - 4, 4));
|
|
|
|
|
|
|
|
Handle = GL.CreateProgram();
|
|
|
|
|
|
|
|
unsafe
|
|
|
|
{
|
|
|
|
fixed (byte* ptr = code)
|
|
|
|
{
|
|
|
|
GL.ProgramBinary(Handle, binaryFormat, (IntPtr)ptr, code.Length - 4);
|
|
|
|
}
|
|
|
|
}
|
2022-02-16 23:15:39 +01:00
|
|
|
|
|
|
|
HasFragmentShader = hasFragmentShader;
|
|
|
|
FragmentOutputMap = fragmentOutputMap;
|
2020-11-13 00:15:34 +01:00
|
|
|
}
|
|
|
|
|
2019-10-13 08:02:07 +02:00
|
|
|
public void Bind()
|
|
|
|
{
|
|
|
|
GL.UseProgram(Handle);
|
|
|
|
}
|
|
|
|
|
2021-03-29 22:52:25 +02:00
|
|
|
public ProgramLinkStatus CheckProgramLink(bool blocking)
|
2019-10-13 08:02:07 +02:00
|
|
|
{
|
2021-03-29 22:52:25 +02:00
|
|
|
if (!blocking && HwCapabilities.SupportsParallelShaderCompile)
|
|
|
|
{
|
|
|
|
GL.GetProgram(Handle, (GetProgramParameterName)ArbParallelShaderCompile.CompletionStatusArb, out int completed);
|
|
|
|
|
|
|
|
if (completed == 0)
|
|
|
|
{
|
|
|
|
return ProgramLinkStatus.Incomplete;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-29 00:45:33 +01:00
|
|
|
GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out int status);
|
2019-10-13 08:02:07 +02:00
|
|
|
|
2021-03-29 22:52:25 +02:00
|
|
|
if (_shaders != null)
|
|
|
|
{
|
|
|
|
for (int index = 0; index < _shaders.Length; index++)
|
|
|
|
{
|
|
|
|
int shaderHandle = ((Shader)_shaders[index]).Handle;
|
|
|
|
|
|
|
|
GL.DetachShader(Handle, shaderHandle);
|
|
|
|
}
|
|
|
|
|
|
|
|
_shaders = null;
|
|
|
|
}
|
|
|
|
|
2019-10-13 08:02:07 +02:00
|
|
|
if (status == 0)
|
|
|
|
{
|
2019-12-30 18:47:20 +01:00
|
|
|
// Use GL.GetProgramInfoLog(Handle), it may be too long to print on the log.
|
2021-03-29 22:52:25 +02:00
|
|
|
_status = ProgramLinkStatus.Failure;
|
2020-08-04 01:32:53 +02:00
|
|
|
Logger.Debug?.Print(LogClass.Gpu, "Shader linking failed.");
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-03-29 22:52:25 +02:00
|
|
|
_status = ProgramLinkStatus.Success;
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
2021-03-29 22:52:25 +02:00
|
|
|
|
|
|
|
return _status;
|
2019-10-13 08:02:07 +02:00
|
|
|
}
|
|
|
|
|
2020-11-13 00:15:34 +01:00
|
|
|
public byte[] GetBinary()
|
|
|
|
{
|
|
|
|
GL.GetProgram(Handle, (GetProgramParameterName)All.ProgramBinaryLength, out int size);
|
|
|
|
|
|
|
|
byte[] data = new byte[size + 4];
|
|
|
|
|
|
|
|
GL.GetProgramBinary(Handle, size, out _, out BinaryFormat binFormat, data);
|
|
|
|
|
2022-02-22 14:32:10 +01:00
|
|
|
BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(size, 4), (int)binFormat);
|
2020-11-13 00:15:34 +01:00
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2019-10-13 08:02:07 +02:00
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
if (Handle != 0)
|
|
|
|
{
|
|
|
|
GL.DeleteProgram(Handle);
|
|
|
|
|
|
|
|
Handle = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|