mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-02-23 01:20:21 +00:00
Allow multithreading shaderc and vkCreateShaderModule
You'll only really see the benefit here with threaded-gal or parallel shader cache compile. Fix shaderc multithreaded changes Thread safety for shaderc Options constructor Dunno how they managed to make a constructor not thread safe, but you do you. May avoid some freezes.
This commit is contained in:
parent
c6fbaf844e
commit
e0edaa177e
3 changed files with 148 additions and 56 deletions
|
@ -223,7 +223,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
|
public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
|
||||||
{
|
{
|
||||||
if (_program.LinkStatus != ProgramLinkStatus.Success)
|
if (!_program.IsLinked)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -238,7 +238,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
// System.Console.WriteLine("draw");
|
// System.Console.WriteLine("draw");
|
||||||
|
|
||||||
if (_program.LinkStatus != ProgramLinkStatus.Success)
|
if (!_program.IsLinked)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -266,7 +266,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
// System.Console.WriteLine("draw indexed");
|
// System.Console.WriteLine("draw indexed");
|
||||||
|
|
||||||
if (_program.LinkStatus != ProgramLinkStatus.Success)
|
if (!_program.IsLinked)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,72 +6,95 @@ using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
class Shader : IShader
|
class Shader : IShader
|
||||||
{
|
{
|
||||||
|
// The shaderc.net dependency's Options constructor and dispose are not thread safe.
|
||||||
|
// Take this lock when using them.
|
||||||
|
private static object _shaderOptionsLock = new object();
|
||||||
|
|
||||||
private readonly Vk _api;
|
private readonly Vk _api;
|
||||||
private readonly Device _device;
|
private readonly Device _device;
|
||||||
private readonly ShaderModule _module;
|
|
||||||
private readonly ShaderStageFlags _stage;
|
private readonly ShaderStageFlags _stage;
|
||||||
|
private readonly Task _compileTask;
|
||||||
|
|
||||||
private IntPtr _entryPointName;
|
private IntPtr _entryPointName;
|
||||||
|
private ShaderModule _module;
|
||||||
|
|
||||||
public ShaderStageFlags StageFlags => _stage;
|
public ShaderStageFlags StageFlags => _stage;
|
||||||
|
|
||||||
public ShaderBindings Bindings { get; }
|
public ShaderBindings Bindings { get; }
|
||||||
|
|
||||||
public bool Valid { get; }
|
public ProgramLinkStatus CompileStatus { private set; get; }
|
||||||
|
|
||||||
public unsafe Shader(Vk api, Device device, ShaderStage stage, ShaderBindings bindings, string glsl)
|
public unsafe Shader(Vk api, Device device, ShaderStage stage, ShaderBindings bindings, string glsl)
|
||||||
{
|
{
|
||||||
_api = api;
|
_api = api;
|
||||||
_device = device;
|
_device = device;
|
||||||
Bindings = bindings;
|
|
||||||
|
|
||||||
glsl = glsl.Replace("gl_VertexID", "(gl_VertexIndex - gl_BaseVertex)");
|
|
||||||
glsl = glsl.Replace("gl_InstanceID", "(gl_InstanceIndex - gl_BaseInstance)");
|
|
||||||
|
|
||||||
// System.Console.WriteLine(glsl);
|
|
||||||
|
|
||||||
Options options = new Options(false)
|
|
||||||
{
|
|
||||||
SourceLanguage = SourceLanguage.Glsl,
|
|
||||||
TargetSpirVVersion = new SpirVVersion(1, 5)
|
|
||||||
};
|
|
||||||
options.SetTargetEnvironment(TargetEnvironment.Vulkan, EnvironmentVersion.Vulkan_1_2);
|
|
||||||
Compiler compiler = new Compiler(options);
|
|
||||||
var scr = compiler.Compile(glsl, "Ryu", GetShaderCShaderStage(stage));
|
|
||||||
|
|
||||||
if (scr.Status != Status.Success)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Gpu, $"Shader compilation error: {scr.Status} {scr.ErrorMessage}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Valid = true;
|
|
||||||
|
|
||||||
var spirvBytes = new Span<byte>((void*)scr.CodePointer, (int)scr.CodeLength);
|
|
||||||
|
|
||||||
uint[] code = new uint[(scr.CodeLength + 3) / 4];
|
|
||||||
|
|
||||||
spirvBytes.CopyTo(MemoryMarshal.Cast<uint, byte>(new Span<uint>(code)).Slice(0, (int)scr.CodeLength));
|
|
||||||
|
|
||||||
fixed (uint* pCode = code)
|
|
||||||
{
|
|
||||||
var shaderModuleCreateInfo = new ShaderModuleCreateInfo()
|
|
||||||
{
|
|
||||||
SType = StructureType.ShaderModuleCreateInfo,
|
|
||||||
CodeSize = scr.CodeLength,
|
|
||||||
PCode = pCode
|
|
||||||
};
|
|
||||||
|
|
||||||
api.CreateShaderModule(device, shaderModuleCreateInfo, null, out _module).ThrowOnError();
|
|
||||||
}
|
|
||||||
|
|
||||||
_stage = stage.Convert();
|
_stage = stage.Convert();
|
||||||
_entryPointName = Marshal.StringToHGlobalAnsi("main");
|
_entryPointName = Marshal.StringToHGlobalAnsi("main");
|
||||||
|
|
||||||
|
Bindings = bindings;
|
||||||
|
|
||||||
|
_compileTask = Task.Run(() =>
|
||||||
|
{
|
||||||
|
glsl = glsl.Replace("gl_VertexID", "(gl_VertexIndex - gl_BaseVertex)");
|
||||||
|
glsl = glsl.Replace("gl_InstanceID", "(gl_InstanceIndex - gl_BaseInstance)");
|
||||||
|
|
||||||
|
// System.Console.WriteLine(glsl);
|
||||||
|
|
||||||
|
Options options;
|
||||||
|
|
||||||
|
lock (_shaderOptionsLock)
|
||||||
|
{
|
||||||
|
options = new Options(false)
|
||||||
|
{
|
||||||
|
SourceLanguage = SourceLanguage.Glsl,
|
||||||
|
TargetSpirVVersion = new SpirVVersion(1, 5)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
options.SetTargetEnvironment(TargetEnvironment.Vulkan, EnvironmentVersion.Vulkan_1_2);
|
||||||
|
Compiler compiler = new Compiler(options);
|
||||||
|
var scr = compiler.Compile(glsl, "Ryu", GetShaderCShaderStage(stage));
|
||||||
|
|
||||||
|
lock (_shaderOptionsLock)
|
||||||
|
{
|
||||||
|
options.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scr.Status != Status.Success)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Gpu, $"Shader compilation error: {scr.Status} {scr.ErrorMessage}");
|
||||||
|
|
||||||
|
CompileStatus = ProgramLinkStatus.Failure;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var spirvBytes = new Span<byte>((void*)scr.CodePointer, (int)scr.CodeLength);
|
||||||
|
|
||||||
|
uint[] code = new uint[(scr.CodeLength + 3) / 4];
|
||||||
|
|
||||||
|
spirvBytes.CopyTo(MemoryMarshal.Cast<uint, byte>(new Span<uint>(code)).Slice(0, (int)scr.CodeLength));
|
||||||
|
|
||||||
|
fixed (uint* pCode = code)
|
||||||
|
{
|
||||||
|
var shaderModuleCreateInfo = new ShaderModuleCreateInfo()
|
||||||
|
{
|
||||||
|
SType = StructureType.ShaderModuleCreateInfo,
|
||||||
|
CodeSize = scr.CodeLength,
|
||||||
|
PCode = pCode
|
||||||
|
};
|
||||||
|
|
||||||
|
api.CreateShaderModule(device, shaderModuleCreateInfo, null, out _module).ThrowOnError();
|
||||||
|
}
|
||||||
|
|
||||||
|
CompileStatus = ProgramLinkStatus.Success;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe Shader(Vk api, Device device, ShaderStage stage, ShaderBindings bindings, byte[] spirv)
|
public unsafe Shader(Vk api, Device device, ShaderStage stage, ShaderBindings bindings, byte[] spirv)
|
||||||
|
@ -80,7 +103,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
_device = device;
|
_device = device;
|
||||||
Bindings = bindings;
|
Bindings = bindings;
|
||||||
|
|
||||||
Valid = true;
|
CompileStatus = ProgramLinkStatus.Success;
|
||||||
|
|
||||||
fixed (byte* pCode = spirv)
|
fixed (byte* pCode = spirv)
|
||||||
{
|
{
|
||||||
|
@ -144,6 +167,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void WaitForCompile()
|
||||||
|
{
|
||||||
|
_compileTask.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
public unsafe void Dispose()
|
public unsafe void Dispose()
|
||||||
{
|
{
|
||||||
if (_entryPointName != IntPtr.Zero)
|
if (_entryPointName != IntPtr.Zero)
|
||||||
|
|
|
@ -19,13 +19,27 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
public int[][][] Bindings { get; }
|
public int[][][] Bindings { get; }
|
||||||
|
|
||||||
public ProgramLinkStatus LinkStatus { get; }
|
public ProgramLinkStatus LinkStatus { private set; get; }
|
||||||
|
|
||||||
|
public bool IsLinked
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (LinkStatus == ProgramLinkStatus.Incomplete)
|
||||||
|
{
|
||||||
|
CheckProgramLink(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LinkStatus == ProgramLinkStatus.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private HashTableSlim<PipelineUid, Auto<DisposablePipeline>> _graphicsPipelineCache;
|
private HashTableSlim<PipelineUid, Auto<DisposablePipeline>> _graphicsPipelineCache;
|
||||||
private Auto<DisposablePipeline> _computePipeline;
|
private Auto<DisposablePipeline> _computePipeline;
|
||||||
|
|
||||||
private VulkanGraphicsDevice _gd;
|
private VulkanGraphicsDevice _gd;
|
||||||
private Device _device;
|
private Device _device;
|
||||||
|
private bool _initialized;
|
||||||
|
|
||||||
public ShaderCollection(VulkanGraphicsDevice gd, Device device, IShader[] shaders)
|
public ShaderCollection(VulkanGraphicsDevice gd, Device device, IShader[] shaders)
|
||||||
{
|
{
|
||||||
|
@ -39,17 +53,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
|
|
||||||
_infos = new PipelineShaderStageCreateInfo[shaders.Length];
|
_infos = new PipelineShaderStageCreateInfo[shaders.Length];
|
||||||
|
|
||||||
LinkStatus = ProgramLinkStatus.Success;
|
LinkStatus = ProgramLinkStatus.Incomplete;
|
||||||
|
|
||||||
uint stages = 0;
|
uint stages = 0;
|
||||||
|
|
||||||
for (int i = 0; i < shaders.Length; i++)
|
for (int i = 0; i < shaders.Length; i++)
|
||||||
{
|
{
|
||||||
var shader = (Shader)shaders[i];
|
var shader = (Shader)shaders[i];
|
||||||
if (!shader.Valid)
|
|
||||||
{
|
|
||||||
LinkStatus = ProgramLinkStatus.Failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
stages |= 1u << shader.StageFlags switch
|
stages |= 1u << shader.StageFlags switch
|
||||||
{
|
{
|
||||||
|
@ -61,8 +71,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
};
|
};
|
||||||
|
|
||||||
internalShaders[i] = shader;
|
internalShaders[i] = shader;
|
||||||
|
|
||||||
_infos[i] = internalShaders[i].GetInfo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, stages);
|
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, stages);
|
||||||
|
@ -95,13 +103,69 @@ namespace Ryujinx.Graphics.Vulkan
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EnsureShadersReady()
|
||||||
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
{
|
||||||
|
CheckProgramLink(true);
|
||||||
|
|
||||||
|
ProgramLinkStatus resultStatus = ProgramLinkStatus.Success;
|
||||||
|
|
||||||
|
for (int i = 0; i < _shaders.Length; i++)
|
||||||
|
{
|
||||||
|
var shader = (Shader)_shaders[i];
|
||||||
|
|
||||||
|
if (shader.CompileStatus != ProgramLinkStatus.Success)
|
||||||
|
{
|
||||||
|
resultStatus = ProgramLinkStatus.Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
_infos[i] = shader.GetInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkStatus = resultStatus;
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public PipelineShaderStageCreateInfo[] GetInfos()
|
public PipelineShaderStageCreateInfo[] GetInfos()
|
||||||
{
|
{
|
||||||
|
EnsureShadersReady();
|
||||||
|
|
||||||
return _infos;
|
return _infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProgramLinkStatus CheckProgramLink(bool blocking)
|
public ProgramLinkStatus CheckProgramLink(bool blocking)
|
||||||
{
|
{
|
||||||
|
if (LinkStatus == ProgramLinkStatus.Incomplete)
|
||||||
|
{
|
||||||
|
ProgramLinkStatus resultStatus = ProgramLinkStatus.Success;
|
||||||
|
|
||||||
|
foreach (Shader shader in _shaders)
|
||||||
|
{
|
||||||
|
if (shader.CompileStatus == ProgramLinkStatus.Incomplete)
|
||||||
|
{
|
||||||
|
if (blocking)
|
||||||
|
{
|
||||||
|
// Wait for this shader to finish compiling.
|
||||||
|
shader.WaitForCompile();
|
||||||
|
|
||||||
|
if (shader.CompileStatus != ProgramLinkStatus.Success)
|
||||||
|
{
|
||||||
|
resultStatus = ProgramLinkStatus.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ProgramLinkStatus.Incomplete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultStatus;
|
||||||
|
}
|
||||||
|
|
||||||
return LinkStatus;
|
return LinkStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue