From ddf4b92a9cfbe98f798dd86a7c123b065a832d51 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Mon, 29 Mar 2021 21:52:25 +0100 Subject: Implement parallel host shader cache compilation. --- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 436 ++++++++++++++--------- Ryujinx.Graphics.Gpu/Shader/ShaderCompileTask.cs | 81 +++++ 2 files changed, 348 insertions(+), 169 deletions(-) create mode 100644 Ryujinx.Graphics.Gpu/Shader/ShaderCompileTask.cs (limited to 'Ryujinx.Graphics.Gpu/Shader') diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index cd20a5a2..96b836c5 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; +using System.Threading.Tasks; namespace Ryujinx.Graphics.Gpu.Shader { @@ -102,234 +103,327 @@ namespace Ryujinx.Graphics.Gpu.Shader progressReportThread.Start(progressReportEvent); } - for (int programIndex = 0; programIndex < guestProgramList.Length; programIndex++) - { - Hash128 key = guestProgramList[programIndex]; - - byte[] hostProgramBinary = _cacheManager.GetHostProgramByHash(ref key); - bool hasHostCache = hostProgramBinary != null; + // Make sure these are initialized before doing compilation. + Capabilities caps = _context.Capabilities; - IProgram hostProgram = null; + int maxTaskCount = Math.Min(Environment.ProcessorCount, 8); + int programIndex = 0; + List activeTasks = new List(); - // If the program sources aren't in the cache, compile from saved guest program. - byte[] guestProgram = _cacheManager.GetGuestProgramByHash(ref key); - - if (guestProgram == null) + // This thread dispatches tasks to do shader translation, and creates programs that OpenGL will link in the background. + // The program link status is checked in a non-blocking manner so that multiple shaders can be compiled at once. + + while (programIndex < guestProgramList.Length || activeTasks.Count > 0) + { + if (activeTasks.Count < maxTaskCount && programIndex < guestProgramList.Length) { - Logger.Error?.Print(LogClass.Gpu, $"Ignoring orphan shader hash {key} in cache (is the cache incomplete?)"); - - // Should not happen, but if someone messed with the cache it's better to catch it. - invalidEntries?.Add(key); - - continue; - } + // Begin a new shader compilation. + Hash128 key = guestProgramList[programIndex]; - ReadOnlySpan guestProgramReadOnlySpan = guestProgram; + byte[] hostProgramBinary = _cacheManager.GetHostProgramByHash(ref key); + bool hasHostCache = hostProgramBinary != null; - ReadOnlySpan cachedShaderEntries = GuestShaderCacheEntry.Parse(ref guestProgramReadOnlySpan, out GuestShaderCacheHeader fileHeader); + IProgram hostProgram = null; - if (cachedShaderEntries[0].Header.Stage == ShaderStage.Compute) - { - Debug.Assert(cachedShaderEntries.Length == 1); - - GuestShaderCacheEntry entry = cachedShaderEntries[0]; + // If the program sources aren't in the cache, compile from saved guest program. + byte[] guestProgram = _cacheManager.GetGuestProgramByHash(ref key); - HostShaderCacheEntry[] hostShaderEntries = null; - - // Try loading host shader binary. - if (hasHostCache) + if (guestProgram == null) { - hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan hostProgramBinarySpan); - hostProgramBinary = hostProgramBinarySpan.ToArray(); - hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary); - } + Logger.Error?.Print(LogClass.Gpu, $"Ignoring orphan shader hash {key} in cache (is the cache incomplete?)"); - bool isHostProgramValid = hostProgram != null; + // Should not happen, but if someone messed with the cache it's better to catch it. + invalidEntries?.Add(key); - ShaderProgram program; - ShaderProgramInfo shaderProgramInfo; - - // Reconstruct code holder. - if (isHostProgramValid) - { - program = new ShaderProgram(entry.Header.Stage, ""); - shaderProgramInfo = hostShaderEntries[0].ToShaderProgramInfo(); + continue; } - else - { - IGpuAccessor gpuAccessor = new CachedGpuAccessor(_context, entry.Code, entry.Header.GpuAccessorHeader, entry.TextureDescriptors); - program = Translator.CreateContext(0, gpuAccessor, DefaultFlags | TranslationFlags.Compute).Translate(out shaderProgramInfo); - } + ReadOnlySpan guestProgramReadOnlySpan = guestProgram; - ShaderCodeHolder shader = new ShaderCodeHolder(program, shaderProgramInfo, entry.Code); + ReadOnlySpan cachedShaderEntries = GuestShaderCacheEntry.Parse(ref guestProgramReadOnlySpan, out GuestShaderCacheHeader fileHeader); - // If the host program was rejected by the gpu driver or isn't in cache, try to build from program sources again. - if (hostProgram == null) + if (cachedShaderEntries[0].Header.Stage == ShaderStage.Compute) { - Logger.Info?.Print(LogClass.Gpu, $"Host shader {key} got invalidated, rebuilding from guest..."); + Debug.Assert(cachedShaderEntries.Length == 1); - // Compile shader and create program as the shader program binary got invalidated. - shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code); - hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null); + GuestShaderCacheEntry entry = cachedShaderEntries[0]; - // As the host program was invalidated, save the new entry in the cache. - hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), new ShaderCodeHolder[] { shader }); + HostShaderCacheEntry[] hostShaderEntries = null; - if (!isReadOnly) + // Try loading host shader binary. + if (hasHostCache) { - if (hasHostCache) - { - _cacheManager.ReplaceHostProgram(ref key, hostProgramBinary); - } - else - { - Logger.Warning?.Print(LogClass.Gpu, $"Add missing host shader {key} in cache (is the cache incomplete?)"); - - _cacheManager.AddHostProgram(ref key, hostProgramBinary); - } + hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan hostProgramBinarySpan); + hostProgramBinary = hostProgramBinarySpan.ToArray(); + hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary); } - } - - _cpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shader)); - } - else - { - Debug.Assert(cachedShaderEntries.Length == Constants.ShaderStages); - ShaderCodeHolder[] shaders = new ShaderCodeHolder[cachedShaderEntries.Length]; - List shaderPrograms = new List(); + ShaderCompileTask task = new ShaderCompileTask(); + activeTasks.Add(task); - TransformFeedbackDescriptor[] tfd = CacheHelper.ReadTransformFeedbackInformation(ref guestProgramReadOnlySpan, fileHeader); - - TranslationFlags flags = DefaultFlags; + task.OnCompiled(hostProgram, (bool isHostProgramValid, ShaderCompileTask task) => + { + ShaderProgram program = null; + ShaderProgramInfo shaderProgramInfo = null; - if (tfd != null) - { - flags |= TranslationFlags.Feedback; + Task compileTask = Task.Run(() => + { + // Reconstruct code holder. + if (isHostProgramValid) + { + program = new ShaderProgram(entry.Header.Stage, ""); + shaderProgramInfo = hostShaderEntries[0].ToShaderProgramInfo(); + } + else + { + IGpuAccessor gpuAccessor = new CachedGpuAccessor(_context, entry.Code, entry.Header.GpuAccessorHeader, entry.TextureDescriptors); + + program = Translator.CreateContext(0, gpuAccessor, DefaultFlags | TranslationFlags.Compute).Translate(out shaderProgramInfo); + } + }); + + task.OnTask(compileTask, (bool _, ShaderCompileTask task) => + { + ShaderCodeHolder shader = new ShaderCodeHolder(program, shaderProgramInfo, entry.Code); + + // If the host program was rejected by the gpu driver or isn't in cache, try to build from program sources again. + if (!isHostProgramValid) + { + Logger.Info?.Print(LogClass.Gpu, $"Host shader {key} got invalidated, rebuilding from guest..."); + + // Compile shader and create program as the shader program binary got invalidated. + shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code); + hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null); + + task.OnCompiled(hostProgram, (bool isNewProgramValid, ShaderCompileTask task) => + { + if (!isNewProgramValid) + { + return true; + } + + // As the host program was invalidated, save the new entry in the cache. + hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), new ShaderCodeHolder[] { shader }); + + if (!isReadOnly) + { + if (hasHostCache) + { + _cacheManager.ReplaceHostProgram(ref key, hostProgramBinary); + } + else + { + Logger.Warning?.Print(LogClass.Gpu, $"Add missing host shader {key} in cache (is the cache incomplete?)"); + + _cacheManager.AddHostProgram(ref key, hostProgramBinary); + } + } + + _cpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shader)); + + return true; + }); + + return false; // Not finished: still need to compile the host program. + } + else + { + _cpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shader)); + + return true; + } + }); + + return false; // Not finished: translating the shaders. + }); + } + else + { + Debug.Assert(cachedShaderEntries.Length == Constants.ShaderStages); - TranslationCounts counts = new TranslationCounts(); + ShaderCodeHolder[] shaders = new ShaderCodeHolder[cachedShaderEntries.Length]; + List shaderPrograms = new List(); - HostShaderCacheEntry[] hostShaderEntries = null; + TransformFeedbackDescriptor[] tfd = CacheHelper.ReadTransformFeedbackInformation(ref guestProgramReadOnlySpan, fileHeader); - // Try loading host shader binary. - if (hasHostCache) - { - hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan hostProgramBinarySpan); - hostProgramBinary = hostProgramBinarySpan.ToArray(); - hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary); - } + TranslationFlags flags = DefaultFlags; + + if (tfd != null) + { + flags |= TranslationFlags.Feedback; + } - bool isHostProgramValid = hostProgram != null; + TranslationCounts counts = new TranslationCounts(); - // Reconstruct code holder. - for (int i = 0; i < cachedShaderEntries.Length; i++) - { - GuestShaderCacheEntry entry = cachedShaderEntries[i]; + HostShaderCacheEntry[] hostShaderEntries = null; - if (entry == null) + // Try loading host shader binary. + if (hasHostCache) { - continue; + hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan hostProgramBinarySpan); + hostProgramBinary = hostProgramBinarySpan.ToArray(); + hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary); } - ShaderProgram program; + ShaderCompileTask task = new ShaderCompileTask(); + activeTasks.Add(task); - if (entry.Header.SizeA != 0) - { - ShaderProgramInfo shaderProgramInfo; + GuestShaderCacheEntry[] entries = cachedShaderEntries.ToArray(); - if (isHostProgramValid) + task.OnCompiled(hostProgram, (bool isHostProgramValid, ShaderCompileTask task) => + { + Task compileTask = Task.Run(() => { - program = new ShaderProgram(entry.Header.Stage, ""); - shaderProgramInfo = hostShaderEntries[i].ToShaderProgramInfo(); - } - else + // Reconstruct code holder. + for (int i = 0; i < entries.Length; i++) + { + GuestShaderCacheEntry entry = entries[i]; + + if (entry == null) + { + continue; + } + + ShaderProgram program; + + if (entry.Header.SizeA != 0) + { + ShaderProgramInfo shaderProgramInfo; + + if (isHostProgramValid) + { + program = new ShaderProgram(entry.Header.Stage, ""); + shaderProgramInfo = hostShaderEntries[i].ToShaderProgramInfo(); + } + else + { + IGpuAccessor gpuAccessor = new CachedGpuAccessor(_context, entry.Code, entry.Header.GpuAccessorHeader, entry.TextureDescriptors); + + TranslatorContext translatorContext = Translator.CreateContext(0, gpuAccessor, flags, counts); + TranslatorContext translatorContext2 = Translator.CreateContext((ulong)entry.Header.Size, gpuAccessor, flags | TranslationFlags.VertexA, counts); + + program = translatorContext.Translate(out shaderProgramInfo, translatorContext2); + } + + // NOTE: Vertex B comes first in the shader cache. + byte[] code = entry.Code.AsSpan().Slice(0, entry.Header.Size).ToArray(); + byte[] code2 = entry.Code.AsSpan().Slice(entry.Header.Size, entry.Header.SizeA).ToArray(); + + shaders[i] = new ShaderCodeHolder(program, shaderProgramInfo, code, code2); + } + else + { + ShaderProgramInfo shaderProgramInfo; + + if (isHostProgramValid) + { + program = new ShaderProgram(entry.Header.Stage, ""); + shaderProgramInfo = hostShaderEntries[i].ToShaderProgramInfo(); + } + else + { + IGpuAccessor gpuAccessor = new CachedGpuAccessor(_context, entry.Code, entry.Header.GpuAccessorHeader, entry.TextureDescriptors); + + program = Translator.CreateContext(0, gpuAccessor, flags, counts).Translate(out shaderProgramInfo); + } + + shaders[i] = new ShaderCodeHolder(program, shaderProgramInfo, entry.Code); + } + + shaderPrograms.Add(program); + } + }); + + task.OnTask(compileTask, (bool _, ShaderCompileTask task) => { - IGpuAccessor gpuAccessor = new CachedGpuAccessor(_context, entry.Code, entry.Header.GpuAccessorHeader, entry.TextureDescriptors); + // If the host program was rejected by the gpu driver or isn't in cache, try to build from program sources again. + if (!isHostProgramValid) + { + Logger.Info?.Print(LogClass.Gpu, $"Host shader {key} got invalidated, rebuilding from guest..."); - TranslatorContext translatorContext = Translator.CreateContext(0, gpuAccessor, flags, counts); - TranslatorContext translatorContext2 = Translator.CreateContext((ulong)entry.Header.Size, gpuAccessor, flags | TranslationFlags.VertexA, counts); + List hostShaders = new List(); - program = translatorContext.Translate(out shaderProgramInfo, translatorContext2); - } + // Compile shaders and create program as the shader program binary got invalidated. + for (int stage = 0; stage < Constants.ShaderStages; stage++) + { + ShaderProgram program = shaders[stage]?.Program; - // NOTE: Vertex B comes first in the shader cache. - byte[] code = entry.Code.AsSpan().Slice(0, entry.Header.Size).ToArray(); - byte[] code2 = entry.Code.AsSpan().Slice(entry.Header.Size, entry.Header.SizeA).ToArray(); + if (program == null) + { + continue; + } - shaders[i] = new ShaderCodeHolder(program, shaderProgramInfo, code, code2); - } - else - { - ShaderProgramInfo shaderProgramInfo; + IShader hostShader = _context.Renderer.CompileShader(program.Stage, program.Code); - if (isHostProgramValid) - { - program = new ShaderProgram(entry.Header.Stage, ""); - shaderProgramInfo = hostShaderEntries[i].ToShaderProgramInfo(); - } - else - { - IGpuAccessor gpuAccessor = new CachedGpuAccessor(_context, entry.Code, entry.Header.GpuAccessorHeader, entry.TextureDescriptors); + shaders[stage].HostShader = hostShader; - program = Translator.CreateContext(0, gpuAccessor, flags, counts).Translate(out shaderProgramInfo); - } + hostShaders.Add(hostShader); + } - shaders[i] = new ShaderCodeHolder(program, shaderProgramInfo, entry.Code); - } + hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), tfd); - shaderPrograms.Add(program); - } + task.OnCompiled(hostProgram, (bool isNewProgramValid, ShaderCompileTask task) => + { + if (!isNewProgramValid) + { + return true; + } - // If the host program was rejected by the gpu driver or isn't in cache, try to build from program sources again. - if (!isHostProgramValid) - { - Logger.Info?.Print(LogClass.Gpu, $"Host shader {key} got invalidated, rebuilding from guest..."); + // As the host program was invalidated, save the new entry in the cache. + hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), shaders); - List hostShaders = new List(); + if (!isReadOnly) + { + if (hasHostCache) + { + _cacheManager.ReplaceHostProgram(ref key, hostProgramBinary); + } + else + { + Logger.Warning?.Print(LogClass.Gpu, $"Add missing host shader {key} in cache (is the cache incomplete?)"); - // Compile shaders and create program as the shader program binary got invalidated. - for (int stage = 0; stage < Constants.ShaderStages; stage++) - { - ShaderProgram program = shaders[stage]?.Program; + _cacheManager.AddHostProgram(ref key, hostProgramBinary); + } + } - if (program == null) - { - continue; - } + _gpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shaders)); - IShader hostShader = _context.Renderer.CompileShader(program.Stage, program.Code); + return true; + }); - shaders[stage].HostShader = hostShader; + return false; // Not finished: still need to compile the host program. + } + else + { + _gpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shaders)); - hostShaders.Add(hostShader); - } + return true; + } + }); - hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), tfd); + return false; // Not finished: translating the shaders. + }); + } - // As the host program was invalidated, save the new entry in the cache. - hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), shaders); + _shaderCount = ++programIndex; + } - if (!isReadOnly) - { - if (hasHostCache) - { - _cacheManager.ReplaceHostProgram(ref key, hostProgramBinary); - } - else - { - Logger.Warning?.Print(LogClass.Gpu, $"Add missing host shader {key} in cache (is the cache incomplete?)"); + // Process the queue. + for (int i = 0; i < activeTasks.Count; i++) + { + ShaderCompileTask task = activeTasks[i]; - _cacheManager.AddHostProgram(ref key, hostProgramBinary); - } - } + if (task.IsDone()) + { + activeTasks.RemoveAt(i--); } - - _gpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shaders)); } - _shaderCount = programIndex + 1; + if (activeTasks.Count == maxTaskCount) + { + Thread.Sleep(1); + } } if (!isReadOnly) @@ -458,6 +552,8 @@ namespace Ryujinx.Graphics.Gpu.Shader IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null); + hostProgram.CheckProgramLink(true); + byte[] hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), new ShaderCodeHolder[] { shader }); cpShader = new ShaderBundle(hostProgram, shader); @@ -598,6 +694,8 @@ namespace Ryujinx.Graphics.Gpu.Shader IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), tfd); + hostProgram.CheckProgramLink(true); + byte[] hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), shaders); gpShaders = new ShaderBundle(hostProgram, shaders); diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCompileTask.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCompileTask.cs new file mode 100644 index 00000000..cc1b322b --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCompileTask.cs @@ -0,0 +1,81 @@ +using Ryujinx.Graphics.GAL; +using System; +using System.Threading.Tasks; + +namespace Ryujinx.Graphics.Gpu.Shader +{ + delegate bool ShaderCompileTaskCallback(bool success, ShaderCompileTask task); + + /// + /// A class that represents a shader compilation. + /// + class ShaderCompileTask + { + private bool _compiling; + + private Task _programsTask; + private IProgram _program; + + private ShaderCompileTaskCallback _action; + + /// + /// Check the completion status of the shader compile task, and run callbacks on step completion. + /// Calling this periodically is required to progress through steps of the compilation. + /// + /// True if the task is complete, false if it is in progress + public bool IsDone() + { + if (_compiling) + { + ProgramLinkStatus status = _program.CheckProgramLink(false); + + if (status != ProgramLinkStatus.Incomplete) + { + return _action(status == ProgramLinkStatus.Success, this); + } + } + else + { + // Waiting on the task. + + if (_programsTask.IsCompleted) + { + return _action(true, this); + } + } + + return false; + } + + /// + /// Run a callback when the specified task has completed. + /// + /// The task object that needs to complete + /// The action to perform when it is complete + public void OnTask(Task task, ShaderCompileTaskCallback action) + { + _compiling = false; + + _programsTask = task; + _action = action; + } + + /// + /// Run a callback when the specified program has been linked. + /// + /// The program that needs to be linked + /// The action to perform when linking is complete + public void OnCompiled(IProgram program, ShaderCompileTaskCallback action) + { + _compiling = true; + + _program = program; + _action = action; + + if (program == null) + { + action(false, this); + } + } + } +} -- cgit v1.2.3 From 20d560e3f925e387c9c28705a120202ca38abd8b Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 2 Apr 2021 22:47:14 +0100 Subject: The new host program needs to be saved even if it isn't valid. --- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'Ryujinx.Graphics.Gpu/Shader') diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 96b836c5..35908cb9 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -197,11 +197,6 @@ namespace Ryujinx.Graphics.Gpu.Shader task.OnCompiled(hostProgram, (bool isNewProgramValid, ShaderCompileTask task) => { - if (!isNewProgramValid) - { - return true; - } - // As the host program was invalidated, save the new entry in the cache. hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), new ShaderCodeHolder[] { shader }); @@ -365,11 +360,6 @@ namespace Ryujinx.Graphics.Gpu.Shader task.OnCompiled(hostProgram, (bool isNewProgramValid, ShaderCompileTask task) => { - if (!isNewProgramValid) - { - return true; - } - // As the host program was invalidated, save the new entry in the cache. hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), shaders); -- cgit v1.2.3 From a0aa09912cb8f35ae06834b08308a128886f207f Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 2 Apr 2021 23:05:55 +0100 Subject: Use event to wake the main thread on task completion --- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 12 +++++++++--- Ryujinx.Graphics.Gpu/Shader/ShaderCompileTask.cs | 13 +++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'Ryujinx.Graphics.Gpu/Shader') diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 35908cb9..5ee41cdf 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -110,6 +110,8 @@ namespace Ryujinx.Graphics.Gpu.Shader int programIndex = 0; List activeTasks = new List(); + AutoResetEvent taskDoneEvent = new AutoResetEvent(false); + // This thread dispatches tasks to do shader translation, and creates programs that OpenGL will link in the background. // The program link status is checked in a non-blocking manner so that multiple shaders can be compiled at once. @@ -158,7 +160,7 @@ namespace Ryujinx.Graphics.Gpu.Shader hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary); } - ShaderCompileTask task = new ShaderCompileTask(); + ShaderCompileTask task = new ShaderCompileTask(taskDoneEvent); activeTasks.Add(task); task.OnCompiled(hostProgram, (bool isHostProgramValid, ShaderCompileTask task) => @@ -261,7 +263,7 @@ namespace Ryujinx.Graphics.Gpu.Shader hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary); } - ShaderCompileTask task = new ShaderCompileTask(); + ShaderCompileTask task = new ShaderCompileTask(taskDoneEvent); activeTasks.Add(task); GuestShaderCacheEntry[] entries = cachedShaderEntries.ToArray(); @@ -412,7 +414,11 @@ namespace Ryujinx.Graphics.Gpu.Shader if (activeTasks.Count == maxTaskCount) { - Thread.Sleep(1); + // Wait for a task to be done, or for 1ms. + // Host shader compilation cannot signal when it is done, + // so the 1ms timeout is required to poll status. + + taskDoneEvent.WaitOne(1); } } diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCompileTask.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCompileTask.cs index cc1b322b..ff48fab0 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCompileTask.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCompileTask.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using System; +using System.Threading; using System.Threading.Tasks; namespace Ryujinx.Graphics.Gpu.Shader @@ -17,6 +18,16 @@ namespace Ryujinx.Graphics.Gpu.Shader private IProgram _program; private ShaderCompileTaskCallback _action; + private AutoResetEvent _taskDoneEvent; + + /// + /// Create a new shader compile task, with an event to signal whenever a subtask completes. + /// + /// Event to signal when a subtask completes + public ShaderCompileTask(AutoResetEvent taskDoneEvent) + { + _taskDoneEvent = taskDoneEvent; + } /// /// Check the completion status of the shader compile task, and run callbacks on step completion. @@ -58,6 +69,8 @@ namespace Ryujinx.Graphics.Gpu.Shader _programsTask = task; _action = action; + + task.ContinueWith(task => _taskDoneEvent.Set()); } /// -- cgit v1.2.3 From 35eac315ab6f5cfb089422794e695fda3e9cfd53 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 4 Apr 2021 14:01:33 +0100 Subject: The task isn't required for loading compute binary. --- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 53 +++++++++++++++--------------- 1 file changed, 26 insertions(+), 27 deletions(-) (limited to 'Ryujinx.Graphics.Gpu/Shader') diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 5ee41cdf..aae4d1c7 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -114,7 +114,7 @@ namespace Ryujinx.Graphics.Gpu.Shader // This thread dispatches tasks to do shader translation, and creates programs that OpenGL will link in the background. // The program link status is checked in a non-blocking manner so that multiple shaders can be compiled at once. - + while (programIndex < guestProgramList.Length || activeTasks.Count > 0) { if (activeTasks.Count < maxTaskCount && programIndex < guestProgramList.Length) @@ -168,29 +168,34 @@ namespace Ryujinx.Graphics.Gpu.Shader ShaderProgram program = null; ShaderProgramInfo shaderProgramInfo = null; - Task compileTask = Task.Run(() => + if (isHostProgramValid) { // Reconstruct code holder. - if (isHostProgramValid) - { - program = new ShaderProgram(entry.Header.Stage, ""); - shaderProgramInfo = hostShaderEntries[0].ToShaderProgramInfo(); - } - else - { - IGpuAccessor gpuAccessor = new CachedGpuAccessor(_context, entry.Code, entry.Header.GpuAccessorHeader, entry.TextureDescriptors); - program = Translator.CreateContext(0, gpuAccessor, DefaultFlags | TranslationFlags.Compute).Translate(out shaderProgramInfo); - } - }); + program = new ShaderProgram(entry.Header.Stage, ""); + shaderProgramInfo = hostShaderEntries[0].ToShaderProgramInfo(); - task.OnTask(compileTask, (bool _, ShaderCompileTask task) => - { ShaderCodeHolder shader = new ShaderCodeHolder(program, shaderProgramInfo, entry.Code); + _cpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shader)); + + return true; + } + else + { // If the host program was rejected by the gpu driver or isn't in cache, try to build from program sources again. - if (!isHostProgramValid) + + Task compileTask = Task.Run(() => + { + IGpuAccessor gpuAccessor = new CachedGpuAccessor(_context, entry.Code, entry.Header.GpuAccessorHeader, entry.TextureDescriptors); + + program = Translator.CreateContext(0, gpuAccessor, DefaultFlags | TranslationFlags.Compute).Translate(out shaderProgramInfo); + }); + + task.OnTask(compileTask, (bool _, ShaderCompileTask task) => { + ShaderCodeHolder shader = new ShaderCodeHolder(program, shaderProgramInfo, entry.Code); + Logger.Info?.Print(LogClass.Gpu, $"Host shader {key} got invalidated, rebuilding from guest..."); // Compile shader and create program as the shader program binary got invalidated. @@ -222,18 +227,12 @@ namespace Ryujinx.Graphics.Gpu.Shader }); return false; // Not finished: still need to compile the host program. - } - else - { - _cpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shader)); + }); - return true; - } - }); - - return false; // Not finished: translating the shaders. + return false; // Not finished: translating the program. + } }); - + } else { @@ -394,7 +393,7 @@ namespace Ryujinx.Graphics.Gpu.Shader } }); - return false; // Not finished: translating the shaders. + return false; // Not finished: translating the program. }); } -- cgit v1.2.3 From b1c3e01691507709bc4a6a23f02396f3b97803e5 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 4 Apr 2021 19:15:15 +0100 Subject: Nit --- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 1 - 1 file changed, 1 deletion(-) (limited to 'Ryujinx.Graphics.Gpu/Shader') diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index aae4d1c7..2aef2f25 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -232,7 +232,6 @@ namespace Ryujinx.Graphics.Gpu.Shader return false; // Not finished: translating the program. } }); - } else { -- cgit v1.2.3 From 9e68f5026ec9969922868ee018eb603ca63970bd Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 14 Apr 2021 23:15:25 +0100 Subject: Fix skipping missing shaders --- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Ryujinx.Graphics.Gpu/Shader') diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 2aef2f25..bfd46c82 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -137,6 +137,8 @@ namespace Ryujinx.Graphics.Gpu.Shader // Should not happen, but if someone messed with the cache it's better to catch it. invalidEntries?.Add(key); + _shaderCount = ++programIndex; + continue; } -- cgit v1.2.3