diff --git a/Ryujinx.Graphics.GAL/IShader.cs b/Ryujinx.Graphics.GAL/IShader.cs deleted file mode 100644 index be24adcda..000000000 --- a/Ryujinx.Graphics.GAL/IShader.cs +++ /dev/null @@ -1,6 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.GAL -{ - public interface IShader : IDisposable { } -} diff --git a/Ryujinx.Graphics.Vulkan/HelperShader.cs b/Ryujinx.Graphics.Vulkan/HelperShader.cs index 15e16bc82..8a5c998b5 100644 --- a/Ryujinx.Graphics.Vulkan/HelperShader.cs +++ b/Ryujinx.Graphics.Vulkan/HelperShader.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; using Silk.NET.Vulkan; using System; using VkFormat = Silk.NET.Vulkan.Format; @@ -127,12 +128,17 @@ void main() new[] { Constants.MaxTexturesPerStage * 2 }, Array.Empty()); - var colorBlitVertexShader = gd.CompileShader(ShaderStage.Vertex, vertexBindings, ColorBlitVertexShaderSource); - var colorBlitFragmentShader = gd.CompileShader(ShaderStage.Fragment, fragmentBindings, ColorBlitFragmentShaderSource); - var colorBlitClearAlphaFragmentShader = gd.CompileShader(ShaderStage.Fragment, fragmentBindings, ColorBlitClearAlphaFragmentShaderSource); + _programColorBlit = gd.CreateProgram(new[] + { + new ShaderSource(ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl), + new ShaderSource(ColorBlitFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl), + }, new ShaderInfo(-1)); - _programColorBlit = gd.CreateProgram(new[] { colorBlitVertexShader, colorBlitFragmentShader }, new ShaderInfo(-1)); - _programColorBlitClearAlpha = gd.CreateProgram(new[] { colorBlitVertexShader, colorBlitClearAlphaFragmentShader }, new ShaderInfo(-1)); + _programColorBlitClearAlpha = gd.CreateProgram(new[] + { + new ShaderSource(ColorBlitVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl), + new ShaderSource(ColorBlitClearAlphaFragmentShaderSource, fragmentBindings, ShaderStage.Fragment, TargetLanguage.Glsl), + }, new ShaderInfo(-1)); var fragmentBindings2 = new ShaderBindings( Array.Empty(), @@ -140,10 +146,11 @@ void main() Array.Empty(), Array.Empty()); - var colorClearVertexShader = gd.CompileShader(ShaderStage.Vertex, vertexBindings, ColorClearVertexShaderSource); - var colorClearFragmentShader = gd.CompileShader(ShaderStage.Fragment, fragmentBindings2, ColorClearFragmentShaderSource); - - _programColorClear = gd.CreateProgram(new[] { colorClearVertexShader, colorClearFragmentShader }, new ShaderInfo(-1)); + _programColorClear = gd.CreateProgram(new[] + { + new ShaderSource(ColorClearVertexShaderSource, vertexBindings, ShaderStage.Vertex, TargetLanguage.Glsl), + new ShaderSource(ColorClearFragmentShaderSource, fragmentBindings2, ShaderStage.Fragment, TargetLanguage.Glsl), + }, new ShaderInfo(-1)); } public void Blit( diff --git a/Ryujinx.Graphics.Vulkan/Shader.cs b/Ryujinx.Graphics.Vulkan/Shader.cs index 3f1896386..d15cfb2b0 100644 --- a/Ryujinx.Graphics.Vulkan/Shader.cs +++ b/Ryujinx.Graphics.Vulkan/Shader.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace Ryujinx.Graphics.Vulkan { - class Shader : IShader + class Shader { // The shaderc.net dependency's Options constructor and dispose are not thread safe. // Take this lock when using them. @@ -31,86 +31,33 @@ namespace Ryujinx.Graphics.Vulkan public readonly Task CompileTask; - public unsafe Shader(Vk api, Device device, ShaderStage stage, ShaderBindings bindings, string glsl) + public unsafe Shader(Vk api, Device device, ShaderSource shaderSource) { _api = api; _device = device; - _stage = stage.Convert(); - _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((void*)scr.CodePointer, (int)scr.CodeLength); - - uint[] code = new uint[(scr.CodeLength + 3) / 4]; - - spirvBytes.CopyTo(MemoryMarshal.Cast(new Span(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) - { - _api = api; - _device = device; - Bindings = bindings; + Bindings = shaderSource.Bindings; CompileStatus = ProgramLinkStatus.Incomplete; - _stage = stage.Convert(); + _stage = shaderSource.Stage.Convert(); _entryPointName = Marshal.StringToHGlobalAnsi("main"); CompileTask = Task.Run(() => { + byte[] spirv = shaderSource.BinaryCode; + + if (spirv == null) + { + spirv = GlslToSpirv(shaderSource.Code, shaderSource.Stage); + + if (spirv == null) + { + CompileStatus = ProgramLinkStatus.Failure; + + return; + } + } + fixed (byte* pCode = spirv) { var shaderModuleCreateInfo = new ShaderModuleCreateInfo() @@ -127,16 +74,46 @@ namespace Ryujinx.Graphics.Vulkan }); } - private static uint[] LoadShaderData(string filePath, out int codeSize) + private unsafe static byte[] GlslToSpirv(string glsl, ShaderStage stage) { - var fileBytes = File.ReadAllBytes(filePath); - var shaderData = new uint[(int)Math.Ceiling(fileBytes.Length / 4f)]; + // TODO: We should generate the correct code on the shader translator instead of doing this compensation. + glsl = glsl.Replace("gl_VertexID", "(gl_VertexIndex - gl_BaseVertex)"); + glsl = glsl.Replace("gl_InstanceID", "(gl_InstanceIndex - gl_BaseInstance)"); - System.Buffer.BlockCopy(fileBytes, 0, shaderData, 0, fileBytes.Length); + Options options; - codeSize = fileBytes.Length; + lock (_shaderOptionsLock) + { + options = new Options(false) + { + SourceLanguage = SourceLanguage.Glsl, + TargetSpirVVersion = new SpirVVersion(1, 5) + }; + } - return shaderData; + 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}"); + + return null; + } + + var spirvBytes = new Span((void*)scr.CodePointer, (int)scr.CodeLength); + + byte[] code = new byte[(scr.CodeLength + 3) & ~3]; + + spirvBytes.CopyTo(code.AsSpan().Slice(0, (int)scr.CodeLength)); + + return code; } private static ShaderKind GetShaderCShaderStage(ShaderStage stage) diff --git a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index de0ab192d..e8343517a 100644 --- a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Vulkan class ShaderCollection : IProgram { private readonly PipelineShaderStageCreateInfo[] _infos; - private readonly IShader[] _shaders; + private readonly Shader[] _shaders; private readonly PipelineLayoutCacheEntry _plce; @@ -49,11 +49,10 @@ namespace Ryujinx.Graphics.Vulkan private Task _compileTask; private bool _firstBackgroundUse; - public ShaderCollection(VulkanGraphicsDevice gd, Device device, IShader[] shaders) + public ShaderCollection(VulkanGraphicsDevice gd, Device device, ShaderSource[] shaders) { _gd = gd; _device = device; - _shaders = shaders; gd.Shaders.Add(this); @@ -67,7 +66,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < shaders.Length; i++) { - var shader = (Shader)shaders[i]; + var shader = new Shader(gd.Api, device, shaders[i]); stages |= 1u << shader.StageFlags switch { @@ -86,6 +85,8 @@ namespace Ryujinx.Graphics.Vulkan internalShaders[i] = shader; } + _shaders = internalShaders; + _plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, stages); Stages = stages; @@ -120,8 +121,8 @@ namespace Ryujinx.Graphics.Vulkan public ShaderCollection( VulkanGraphicsDevice gd, Device device, - IShader[] shaders, - ProgramPipelineState state) : this(gd, device, shaders) + ShaderSource[] sources, + ProgramPipelineState state) : this(gd, device, sources) { _state = state; @@ -131,9 +132,9 @@ namespace Ryujinx.Graphics.Vulkan private async Task BackgroundCompilation() { - await Task.WhenAll(_shaders.Select(shader => ((Shader)shader).CompileTask)); + await Task.WhenAll(_shaders.Select(shader => shader.CompileTask)); - if (_shaders.Any(shader => ((Shader)shader).CompileStatus == ProgramLinkStatus.Failure)) + if (_shaders.Any(shader => shader.CompileStatus == ProgramLinkStatus.Failure)) { LinkStatus = ProgramLinkStatus.Failure; @@ -169,7 +170,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < _shaders.Length; i++) { - var shader = (Shader)_shaders[i]; + var shader = _shaders[i]; if (shader.CompileStatus != ProgramLinkStatus.Success) { @@ -211,7 +212,7 @@ namespace Ryujinx.Graphics.Vulkan PipelineState pipeline = new PipelineState(); pipeline.Initialize(); - pipeline.Stages[0] = ((Shader)_shaders[0]).GetInfo(); + pipeline.Stages[0] = _shaders[0].GetInfo(); pipeline.StagesCount = 1; pipeline.CreateComputePipeline(_gd, _device, this, (_gd.Pipeline as PipelineBase).PipelineCache); @@ -236,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < _shaders.Length; i++) { - stages[i] = ((Shader)_shaders[i]).GetInfo(); + stages[i] = _shaders[i].GetInfo(); } pipeline.StagesCount = (uint)_shaders.Length; diff --git a/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs b/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs index 2a2561b70..7499ab703 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs @@ -298,52 +298,25 @@ namespace Ryujinx.Graphics.Vulkan _window = new ImageWindow(this, _physicalDevice, _device); } - public IShader CompileShader(ShaderStage stage, ShaderBindings bindings, string code) - { - return new Shader(Api, _device, stage, bindings, code); - } - - public IShader CompileShader(ShaderStage stage, ShaderBindings bindings, byte[] code) - { - return new Shader(Api, _device, stage, bindings, code); - } - public BufferHandle CreateBuffer(int size) { return BufferManager.CreateWithHandle(this, size, false); } - public IProgram CreateProgram(IShader[] shaders, ShaderInfo info) + public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info) { - bool isCompute = shaders.Length == 1 && ((Shader)shaders[0]).StageFlags == ShaderStageFlags.ShaderStageComputeBit; + bool isCompute = sources.Length == 1 && sources[0].Stage == ShaderStage.Compute; if (info.BackgroundCompile && (info.State.HasValue || isCompute) && VulkanConfiguration.UseDynamicState) { - return new ShaderCollection(this, _device, shaders, info.State.Value); + return new ShaderCollection(this, _device, sources, info.State.Value); } else { - return new ShaderCollection(this, _device, shaders); + return new ShaderCollection(this, _device, sources); } } - public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info) - { - IShader[] shaders = new IShader[sources.Length]; - - for (int index = 0; index < sources.Length; index++) - { - var source = sources[index]; - var shader = source.BinaryCode != null - ? CompileShader(source.Stage, source.Bindings, source.BinaryCode) - : CompileShader(source.Stage, source.Bindings, source.Code); - - shaders[index] = shader; - } - - return CreateProgram(shaders, info); - } - public ISampler CreateSampler(GAL.SamplerCreateInfo info) { return new SamplerHolder(this, _device, info);