diff options
author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
---|---|---|
committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs | |
parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) |
Move solution and projects to src
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs')
-rw-r--r-- | Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs | 725 |
1 files changed, 0 insertions, 725 deletions
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs deleted file mode 100644 index 77fb3ca4..00000000 --- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs +++ /dev/null @@ -1,725 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.Shader; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using static Ryujinx.Graphics.Gpu.Shader.ShaderCache; - -namespace Ryujinx.Graphics.Gpu.Shader.DiskCache -{ - class ParallelDiskCacheLoader - { - private const int ThreadCount = 8; - - private readonly GpuContext _context; - private readonly ShaderCacheHashTable _graphicsCache; - private readonly ComputeShaderCacheHashTable _computeCache; - private readonly DiskCacheHostStorage _hostStorage; - private readonly CancellationToken _cancellationToken; - private readonly Action<ShaderCacheState, int, int> _stateChangeCallback; - - /// <summary> - /// Indicates if the cache should be loaded. - /// </summary> - public bool Active => !_cancellationToken.IsCancellationRequested; - - private bool _needsHostRegen; - - /// <summary> - /// Number of shaders that failed to compile from the cache. - /// </summary> - public int ErrorCount { get; private set; } - - /// <summary> - /// Program validation entry. - /// </summary> - private readonly struct ProgramEntry - { - /// <summary> - /// Cached shader program. - /// </summary> - public readonly CachedShaderProgram CachedProgram; - - /// <summary> - /// Optional binary code. If not null, it is used instead of the backend host binary. - /// </summary> - public readonly byte[] BinaryCode; - - /// <summary> - /// Program index. - /// </summary> - public readonly int ProgramIndex; - - /// <summary> - /// Indicates if the program is a compute shader. - /// </summary> - public readonly bool IsCompute; - - /// <summary> - /// Indicates if the program is a host binary shader. - /// </summary> - public readonly bool IsBinary; - - /// <summary> - /// Creates a new program validation entry. - /// </summary> - /// <param name="cachedProgram">Cached shader program</param> - /// <param name="binaryCode">Optional binary code. If not null, it is used instead of the backend host binary</param> - /// <param name="programIndex">Program index</param> - /// <param name="isCompute">Indicates if the program is a compute shader</param> - /// <param name="isBinary">Indicates if the program is a host binary shader</param> - public ProgramEntry( - CachedShaderProgram cachedProgram, - byte[] binaryCode, - int programIndex, - bool isCompute, - bool isBinary) - { - CachedProgram = cachedProgram; - BinaryCode = binaryCode; - ProgramIndex = programIndex; - IsCompute = isCompute; - IsBinary = isBinary; - } - } - - /// <summary> - /// Translated shader compilation entry. - /// </summary> - private readonly struct ProgramCompilation - { - /// <summary> - /// Translated shader stages. - /// </summary> - public readonly ShaderProgram[] TranslatedStages; - - /// <summary> - /// Cached shaders. - /// </summary> - public readonly CachedShaderStage[] Shaders; - - /// <summary> - /// Specialization state. - /// </summary> - public readonly ShaderSpecializationState SpecializationState; - - /// <summary> - /// Program index. - /// </summary> - public readonly int ProgramIndex; - - /// <summary> - /// Indicates if the program is a compute shader. - /// </summary> - public readonly bool IsCompute; - - /// <summary> - /// Creates a new translated shader compilation entry. - /// </summary> - /// <param name="translatedStages">Translated shader stages</param> - /// <param name="shaders">Cached shaders</param> - /// <param name="specState">Specialization state</param> - /// <param name="programIndex">Program index</param> - /// <param name="isCompute">Indicates if the program is a compute shader</param> - public ProgramCompilation( - ShaderProgram[] translatedStages, - CachedShaderStage[] shaders, - ShaderSpecializationState specState, - int programIndex, - bool isCompute) - { - TranslatedStages = translatedStages; - Shaders = shaders; - SpecializationState = specState; - ProgramIndex = programIndex; - IsCompute = isCompute; - } - } - - /// <summary> - /// Program translation entry. - /// </summary> - private readonly struct AsyncProgramTranslation - { - /// <summary> - /// Guest code for each active stage. - /// </summary> - public readonly GuestCodeAndCbData?[] GuestShaders; - - /// <summary> - /// Specialization state. - /// </summary> - public readonly ShaderSpecializationState SpecializationState; - - /// <summary> - /// Program index. - /// </summary> - public readonly int ProgramIndex; - - /// <summary> - /// Indicates if the program is a compute shader. - /// </summary> - public readonly bool IsCompute; - - /// <summary> - /// Creates a new program translation entry. - /// </summary> - /// <param name="guestShaders">Guest code for each active stage</param> - /// <param name="specState">Specialization state</param> - /// <param name="programIndex">Program index</param> - /// <param name="isCompute">Indicates if the program is a compute shader</param> - public AsyncProgramTranslation( - GuestCodeAndCbData?[] guestShaders, - ShaderSpecializationState specState, - int programIndex, - bool isCompute) - { - GuestShaders = guestShaders; - SpecializationState = specState; - ProgramIndex = programIndex; - IsCompute = isCompute; - } - } - - private readonly Queue<ProgramEntry> _validationQueue; - private readonly ConcurrentQueue<ProgramCompilation> _compilationQueue; - private readonly BlockingCollection<AsyncProgramTranslation> _asyncTranslationQueue; - private readonly SortedList<int, (CachedShaderProgram, byte[])> _programList; - - private int _backendParallelCompileThreads; - private int _compiledCount; - private int _totalCount; - - /// <summary> - /// Creates a new parallel disk cache loader. - /// </summary> - /// <param name="context">GPU context</param> - /// <param name="graphicsCache">Graphics shader cache</param> - /// <param name="computeCache">Compute shader cache</param> - /// <param name="hostStorage">Disk cache host storage</param> - /// <param name="cancellationToken">Cancellation token</param> - /// <param name="stateChangeCallback">Function to be called when there is a state change, reporting state, compiled and total shaders count</param> - public ParallelDiskCacheLoader( - GpuContext context, - ShaderCacheHashTable graphicsCache, - ComputeShaderCacheHashTable computeCache, - DiskCacheHostStorage hostStorage, - CancellationToken cancellationToken, - Action<ShaderCacheState, int, int> stateChangeCallback) - { - _context = context; - _graphicsCache = graphicsCache; - _computeCache = computeCache; - _hostStorage = hostStorage; - _cancellationToken = cancellationToken; - _stateChangeCallback = stateChangeCallback; - _validationQueue = new Queue<ProgramEntry>(); - _compilationQueue = new ConcurrentQueue<ProgramCompilation>(); - _asyncTranslationQueue = new BlockingCollection<AsyncProgramTranslation>(ThreadCount); - _programList = new SortedList<int, (CachedShaderProgram, byte[])>(); - _backendParallelCompileThreads = Math.Min(Environment.ProcessorCount, 8); // Must be kept in sync with the backend code. - } - - /// <summary> - /// Loads all shaders from the cache. - /// </summary> - public void LoadShaders() - { - Thread[] workThreads = new Thread[ThreadCount]; - - for (int index = 0; index < ThreadCount; index++) - { - workThreads[index] = new Thread(ProcessAsyncQueue) - { - Name = $"GPU.AsyncTranslationThread.{index}" - }; - } - - int programCount = _hostStorage.GetProgramCount(); - - _compiledCount = 0; - _totalCount = programCount; - - _stateChangeCallback(ShaderCacheState.Start, 0, programCount); - - Logger.Info?.Print(LogClass.Gpu, $"Loading {programCount} shaders from the cache..."); - - for (int index = 0; index < ThreadCount; index++) - { - workThreads[index].Start(_cancellationToken); - } - - try - { - _hostStorage.LoadShaders(_context, this); - } - catch (DiskCacheLoadException diskCacheLoadException) - { - Logger.Warning?.Print(LogClass.Gpu, $"Error loading the shader cache. {diskCacheLoadException.Message}"); - - // If we can't even access the file, then we also can't rebuild. - if (diskCacheLoadException.Result != DiskCacheLoadResult.NoAccess) - { - _needsHostRegen = true; - } - } - catch (InvalidDataException invalidDataException) - { - Logger.Warning?.Print(LogClass.Gpu, $"Error decompressing the shader cache file. {invalidDataException.Message}"); - _needsHostRegen = true; - } - catch (IOException ioException) - { - Logger.Warning?.Print(LogClass.Gpu, $"Error reading the shader cache file. {ioException.Message}"); - _needsHostRegen = true; - } - - _asyncTranslationQueue.CompleteAdding(); - - for (int index = 0; index < ThreadCount; index++) - { - workThreads[index].Join(); - } - - CheckCompilationBlocking(); - - if (_needsHostRegen && Active) - { - // Rebuild both shared and host cache files. - // Rebuilding shared is required because the shader information returned by the translator - // might have changed, and so we have to reconstruct the file with the new information. - try - { - _hostStorage.ClearSharedCache(); - _hostStorage.ClearHostCache(_context); - - if (_programList.Count != 0) - { - Logger.Info?.Print(LogClass.Gpu, $"Rebuilding {_programList.Count} shaders..."); - - using var streams = _hostStorage.GetOutputStreams(_context); - - foreach (var kv in _programList) - { - if (!Active) - { - break; - } - - (CachedShaderProgram program, byte[] binaryCode) = kv.Value; - _hostStorage.AddShader(_context, program, binaryCode, streams); - } - - Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully."); - } - else - { - _hostStorage.ClearGuestCache(); - - Logger.Info?.Print(LogClass.Gpu, "Shader cache deleted due to corruption."); - } - } - catch (DiskCacheLoadException diskCacheLoadException) - { - Logger.Warning?.Print(LogClass.Gpu, $"Error deleting the shader cache. {diskCacheLoadException.Message}"); - } - catch (IOException ioException) - { - Logger.Warning?.Print(LogClass.Gpu, $"Error deleting the shader cache file. {ioException.Message}"); - } - } - - Logger.Info?.Print(LogClass.Gpu, "Shader cache loaded."); - - _stateChangeCallback(ShaderCacheState.Loaded, programCount, programCount); - } - - /// <summary> - /// Enqueues a host program for compilation. - /// </summary> - /// <param name="cachedProgram">Cached program</param> - /// <param name="binaryCode">Host binary code</param> - /// <param name="programIndex">Program index</param> - /// <param name="isCompute">Indicates if the program is a compute shader</param> - public void QueueHostProgram(CachedShaderProgram cachedProgram, byte[] binaryCode, int programIndex, bool isCompute) - { - EnqueueForValidation(new ProgramEntry(cachedProgram, binaryCode, programIndex, isCompute, isBinary: true)); - } - - /// <summary> - /// Enqueues a guest program for compilation. - /// </summary> - /// <param name="guestShaders">Guest code for each active stage</param> - /// <param name="specState">Specialization state</param> - /// <param name="programIndex">Program index</param> - /// <param name="isCompute">Indicates if the program is a compute shader</param> - public void QueueGuestProgram(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex, bool isCompute) - { - try - { - AsyncProgramTranslation asyncTranslation = new AsyncProgramTranslation(guestShaders, specState, programIndex, isCompute); - _asyncTranslationQueue.Add(asyncTranslation, _cancellationToken); - } - catch (OperationCanceledException) - { - } - } - - /// <summary> - /// Check the state of programs that have already been compiled, - /// and add to the cache if the compilation was successful. - /// </summary> - public void CheckCompilation() - { - ProcessCompilationQueue(); - - // Process programs that already finished compiling. - // If not yet compiled, do nothing. This avoids blocking to wait for shader compilation. - while (_validationQueue.TryPeek(out ProgramEntry entry)) - { - ProgramLinkStatus result = entry.CachedProgram.HostProgram.CheckProgramLink(false); - - if (result != ProgramLinkStatus.Incomplete) - { - ProcessCompiledProgram(ref entry, result); - _validationQueue.Dequeue(); - } - else - { - break; - } - } - } - - /// <summary> - /// Waits until all programs finishes compiling, then adds the ones - /// with successful compilation to the cache. - /// </summary> - private void CheckCompilationBlocking() - { - ProcessCompilationQueue(); - - while (_validationQueue.TryDequeue(out ProgramEntry entry) && Active) - { - ProcessCompiledProgram(ref entry, entry.CachedProgram.HostProgram.CheckProgramLink(true), asyncCompile: false); - } - } - - /// <summary> - /// Process a compiled program result. - /// </summary> - /// <param name="entry">Compiled program entry</param> - /// <param name="result">Compilation result</param> - /// <param name="asyncCompile">For failed host compilations, indicates if a guest compilation should be done asynchronously</param> - private void ProcessCompiledProgram(ref ProgramEntry entry, ProgramLinkStatus result, bool asyncCompile = true) - { - if (result == ProgramLinkStatus.Success) - { - // Compilation successful, add to memory cache. - if (entry.IsCompute) - { - _computeCache.Add(entry.CachedProgram); - } - else - { - _graphicsCache.Add(entry.CachedProgram); - } - - if (!entry.IsBinary) - { - _needsHostRegen = true; - } - - // Fetch the binary code from the backend if it isn't already present. - byte[] binaryCode = entry.BinaryCode ?? entry.CachedProgram.HostProgram.GetBinary(); - - _programList.Add(entry.ProgramIndex, (entry.CachedProgram, binaryCode)); - SignalCompiled(); - } - else if (entry.IsBinary) - { - // If this is a host binary and compilation failed, - // we still have a chance to recompile from the guest binary. - CachedShaderProgram program = entry.CachedProgram; - - GuestCodeAndCbData?[] guestShaders = new GuestCodeAndCbData?[program.Shaders.Length]; - - for (int index = 0; index < program.Shaders.Length; index++) - { - CachedShaderStage shader = program.Shaders[index]; - - if (shader != null) - { - guestShaders[index] = new GuestCodeAndCbData(shader.Code, shader.Cb1Data); - } - } - - if (asyncCompile) - { - QueueGuestProgram(guestShaders, program.SpecializationState, entry.ProgramIndex, entry.IsCompute); - } - else - { - RecompileFromGuestCode(guestShaders, program.SpecializationState, entry.ProgramIndex, entry.IsCompute); - ProcessCompilationQueue(); - } - } - else - { - // Failed to compile from both host and guest binary. - ErrorCount++; - SignalCompiled(); - } - } - - /// <summary> - /// Processes the queue of translated guest programs that should be compiled on the host. - /// </summary> - private void ProcessCompilationQueue() - { - while (_compilationQueue.TryDequeue(out ProgramCompilation compilation) && Active) - { - ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length]; - - int fragmentOutputMap = -1; - - for (int index = 0; index < compilation.TranslatedStages.Length; index++) - { - ShaderProgram shader = compilation.TranslatedStages[index]; - shaderSources[index] = CreateShaderSource(shader); - - if (shader.Info.Stage == ShaderStage.Fragment) - { - fragmentOutputMap = shader.Info.FragmentOutputMap; - } - } - - ShaderInfo shaderInfo = compilation.SpecializationState.PipelineState.HasValue - ? new ShaderInfo(fragmentOutputMap, compilation.SpecializationState.PipelineState.Value, fromCache: true) - : new ShaderInfo(fragmentOutputMap, fromCache: true); - - IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, shaderInfo); - CachedShaderProgram program = new CachedShaderProgram(hostProgram, compilation.SpecializationState, compilation.Shaders); - - // Vulkan's binary code is the SPIR-V used for compilation, so it is ready immediately. Other APIs get this after compilation. - byte[] binaryCode = _context.Capabilities.Api == TargetApi.Vulkan ? ShaderBinarySerializer.Pack(shaderSources) : null; - - EnqueueForValidation(new ProgramEntry(program, binaryCode, compilation.ProgramIndex, compilation.IsCompute, isBinary: false)); - } - } - - /// <summary> - /// Enqueues a program for validation, which will check if the program was compiled successfully. - /// </summary> - /// <param name="newEntry">Program entry to be validated</param> - private void EnqueueForValidation(ProgramEntry newEntry) - { - _validationQueue.Enqueue(newEntry); - - // Do not allow more than N shader compilation in-flight, where N is the maximum number of threads - // the driver will be using for parallel compilation. - // Submitting more seems to cause NVIDIA OpenGL driver to crash. - if (_validationQueue.Count >= _backendParallelCompileThreads && _validationQueue.TryDequeue(out ProgramEntry entry)) - { - ProcessCompiledProgram(ref entry, entry.CachedProgram.HostProgram.CheckProgramLink(true), asyncCompile: false); - } - } - - /// <summary> - /// Processses the queue of programs that should be translated from guest code. - /// </summary> - /// <param name="state">Cancellation token</param> - private void ProcessAsyncQueue(object state) - { - CancellationToken ct = (CancellationToken)state; - - try - { - foreach (AsyncProgramTranslation asyncCompilation in _asyncTranslationQueue.GetConsumingEnumerable(ct)) - { - RecompileFromGuestCode( - asyncCompilation.GuestShaders, - asyncCompilation.SpecializationState, - asyncCompilation.ProgramIndex, - asyncCompilation.IsCompute); - } - } - catch (OperationCanceledException) - { - } - } - - /// <summary> - /// Recompiles a program from guest code. - /// </summary> - /// <param name="guestShaders">Guest code for each active stage</param> - /// <param name="specState">Specialization state</param> - /// <param name="programIndex">Program index</param> - /// <param name="isCompute">Indicates if the program is a compute shader</param> - private void RecompileFromGuestCode(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex, bool isCompute) - { - try - { - if (isCompute) - { - RecompileComputeFromGuestCode(guestShaders, specState, programIndex); - } - else - { - RecompileGraphicsFromGuestCode(guestShaders, specState, programIndex); - } - } - catch (Exception exception) - { - Logger.Error?.Print(LogClass.Gpu, $"Error translating guest shader. {exception.Message}"); - - ErrorCount++; - SignalCompiled(); - } - } - - /// <summary> - /// Recompiles a graphics program from guest code. - /// </summary> - /// <param name="guestShaders">Guest code for each active stage</param> - /// <param name="specState">Specialization state</param> - /// <param name="programIndex">Program index</param> - private void RecompileGraphicsFromGuestCode(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex) - { - ShaderSpecializationState newSpecState = new ShaderSpecializationState( - ref specState.GraphicsState, - specState.PipelineState, - specState.TransformFeedbackDescriptors); - - ResourceCounts counts = new ResourceCounts(); - - TranslatorContext[] translatorContexts = new TranslatorContext[Constants.ShaderStages + 1]; - TranslatorContext nextStage = null; - - TargetApi api = _context.Capabilities.Api; - - for (int stageIndex = Constants.ShaderStages - 1; stageIndex >= 0; stageIndex--) - { - if (guestShaders[stageIndex + 1].HasValue) - { - GuestCodeAndCbData shader = guestShaders[stageIndex + 1].Value; - - byte[] guestCode = shader.Code; - byte[] cb1Data = shader.Cb1Data; - - DiskCacheGpuAccessor gpuAccessor = new DiskCacheGpuAccessor(_context, guestCode, cb1Data, specState, newSpecState, counts, stageIndex); - TranslatorContext currentStage = DecodeGraphicsShader(gpuAccessor, api, DefaultFlags, 0); - - if (nextStage != null) - { - currentStage.SetNextStage(nextStage); - } - - if (stageIndex == 0 && guestShaders[0].HasValue) - { - byte[] guestCodeA = guestShaders[0].Value.Code; - byte[] cb1DataA = guestShaders[0].Value.Cb1Data; - - DiskCacheGpuAccessor gpuAccessorA = new DiskCacheGpuAccessor(_context, guestCodeA, cb1DataA, specState, newSpecState, counts, 0); - translatorContexts[0] = DecodeGraphicsShader(gpuAccessorA, api, DefaultFlags | TranslationFlags.VertexA, 0); - } - - translatorContexts[stageIndex + 1] = currentStage; - nextStage = currentStage; - } - } - - if (!_context.Capabilities.SupportsGeometryShader) - { - ShaderCache.TryRemoveGeometryStage(translatorContexts); - } - - CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length]; - List<ShaderProgram> translatedStages = new List<ShaderProgram>(); - - TranslatorContext previousStage = null; - - for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++) - { - TranslatorContext currentStage = translatorContexts[stageIndex + 1]; - - if (currentStage != null) - { - ShaderProgram program; - - byte[] guestCode = guestShaders[stageIndex + 1].Value.Code; - byte[] cb1Data = guestShaders[stageIndex + 1].Value.Cb1Data; - - if (stageIndex == 0 && guestShaders[0].HasValue) - { - program = currentStage.Translate(translatorContexts[0]); - - byte[] guestCodeA = guestShaders[0].Value.Code; - byte[] cb1DataA = guestShaders[0].Value.Cb1Data; - - shaders[0] = new CachedShaderStage(null, guestCodeA, cb1DataA); - shaders[1] = new CachedShaderStage(program.Info, guestCode, cb1Data); - } - else - { - program = currentStage.Translate(); - - shaders[stageIndex + 1] = new CachedShaderStage(program.Info, guestCode, cb1Data); - } - - if (program != null) - { - translatedStages.Add(program); - } - - previousStage = currentStage; - } - else if ( - previousStage != null && - previousStage.LayerOutputWritten && - stageIndex == 3 && - !_context.Capabilities.SupportsLayerVertexTessellation) - { - translatedStages.Add(previousStage.GenerateGeometryPassthrough()); - } - } - - _compilationQueue.Enqueue(new ProgramCompilation(translatedStages.ToArray(), shaders, newSpecState, programIndex, isCompute: false)); - } - - /// <summary> - /// Recompiles a compute program from guest code. - /// </summary> - /// <param name="guestShaders">Guest code for each active stage</param> - /// <param name="specState">Specialization state</param> - /// <param name="programIndex">Program index</param> - private void RecompileComputeFromGuestCode(GuestCodeAndCbData?[] guestShaders, ShaderSpecializationState specState, int programIndex) - { - GuestCodeAndCbData shader = guestShaders[0].Value; - ResourceCounts counts = new ResourceCounts(); - ShaderSpecializationState newSpecState = new ShaderSpecializationState(ref specState.ComputeState); - DiskCacheGpuAccessor gpuAccessor = new DiskCacheGpuAccessor(_context, shader.Code, shader.Cb1Data, specState, newSpecState, counts, 0); - - TranslatorContext translatorContext = DecodeComputeShader(gpuAccessor, _context.Capabilities.Api, 0); - - ShaderProgram program = translatorContext.Translate(); - - CachedShaderStage[] shaders = new[] { new CachedShaderStage(program.Info, shader.Code, shader.Cb1Data) }; - - _compilationQueue.Enqueue(new ProgramCompilation(new[] { program }, shaders, newSpecState, programIndex, isCompute: true)); - } - - /// <summary> - /// Signals that compilation of a program has been finished successfully, - /// or that it failed and guest recompilation has also been attempted. - /// </summary> - private void SignalCompiled() - { - _stateChangeCallback(ShaderCacheState.Loading, ++_compiledCount, _totalCount); - } - } -}
\ No newline at end of file |