|
|
@@ -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<ShaderCompileTask> activeTasks = new List<ShaderCompileTask>();
|
|
|
|
|
|
- // 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<byte> guestProgramReadOnlySpan = guestProgram;
|
|
|
+ byte[] hostProgramBinary = _cacheManager.GetHostProgramByHash(ref key);
|
|
|
+ bool hasHostCache = hostProgramBinary != null;
|
|
|
|
|
|
- ReadOnlySpan<GuestShaderCacheEntry> 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<byte> 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<byte> guestProgramReadOnlySpan = guestProgram;
|
|
|
|
|
|
- ShaderCodeHolder shader = new ShaderCodeHolder(program, shaderProgramInfo, entry.Code);
|
|
|
+ ReadOnlySpan<GuestShaderCacheEntry> 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<byte> 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<ShaderProgram> shaderPrograms = new List<ShaderProgram>();
|
|
|
+ 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<ShaderProgram> shaderPrograms = new List<ShaderProgram>();
|
|
|
|
|
|
- HostShaderCacheEntry[] hostShaderEntries = null;
|
|
|
+ TransformFeedbackDescriptor[] tfd = CacheHelper.ReadTransformFeedbackInformation(ref guestProgramReadOnlySpan, fileHeader);
|
|
|
|
|
|
- // Try loading host shader binary.
|
|
|
- if (hasHostCache)
|
|
|
- {
|
|
|
- hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan<byte> 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<byte> 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<IShader> hostShaders = new List<IShader>();
|
|
|
|
|
|
- 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<IShader> hostShaders = new List<IShader>();
|
|
|
+ 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);
|