| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- using Ryujinx.Graphics.Gpu.Memory;
- using Ryujinx.Graphics.Gpu.Shader.HashTable;
- using Ryujinx.Graphics.Shader;
- using System;
- using System.Collections.Generic;
- namespace Ryujinx.Graphics.Gpu.Shader
- {
- /// <summary>
- /// Holds already cached code for a guest shader.
- /// </summary>
- struct CachedGraphicsGuestCode
- {
- public byte[] VertexACode;
- public byte[] VertexBCode;
- public byte[] TessControlCode;
- public byte[] TessEvaluationCode;
- public byte[] GeometryCode;
- public byte[] FragmentCode;
- /// <summary>
- /// Gets the guest code of a shader stage by its index.
- /// </summary>
- /// <param name="stageIndex">Index of the shader stage</param>
- /// <returns>Guest code, or null if not present</returns>
- public byte[] GetByIndex(int stageIndex)
- {
- return stageIndex switch
- {
- 1 => TessControlCode,
- 2 => TessEvaluationCode,
- 3 => GeometryCode,
- 4 => FragmentCode,
- _ => VertexBCode
- };
- }
- }
- /// <summary>
- /// Graphics shader cache hash table.
- /// </summary>
- class ShaderCacheHashTable
- {
- /// <summary>
- /// Shader ID cache.
- /// </summary>
- private struct IdCache
- {
- private PartitionedHashTable<int> _cache;
- private int _id;
- /// <summary>
- /// Initializes the state.
- /// </summary>
- public void Initialize()
- {
- _cache = new PartitionedHashTable<int>();
- _id = 0;
- }
- /// <summary>
- /// Adds guest code to the cache.
- /// </summary>
- /// <remarks>
- /// If the code was already cached, it will just return the existing ID.
- /// </remarks>
- /// <param name="code">Code to add</param>
- /// <returns>Unique ID for the guest code</returns>
- public int Add(byte[] code)
- {
- int id = ++_id;
- int cachedId = _cache.GetOrAdd(code, id);
- if (cachedId != id)
- {
- --_id;
- }
- return cachedId;
- }
- /// <summary>
- /// Tries to find cached guest code.
- /// </summary>
- /// <param name="dataAccessor">Code accessor used to read guest code to find a match on the hash table</param>
- /// <param name="id">ID of the guest code, if found</param>
- /// <param name="data">Cached guest code, if found</param>
- /// <returns>True if found, false otherwise</returns>
- public bool TryFind(IDataAccessor dataAccessor, out int id, out byte[] data)
- {
- return _cache.TryFindItem(dataAccessor, out id, out data);
- }
- }
- /// <summary>
- /// Guest code IDs of the guest shaders that when combined forms a single host program.
- /// </summary>
- private struct IdTable : IEquatable<IdTable>
- {
- public int VertexAId;
- public int VertexBId;
- public int TessControlId;
- public int TessEvaluationId;
- public int GeometryId;
- public int FragmentId;
- public override bool Equals(object obj)
- {
- return obj is IdTable other && Equals(other);
- }
- public bool Equals(IdTable other)
- {
- return other.VertexAId == VertexAId &&
- other.VertexBId == VertexBId &&
- other.TessControlId == TessControlId &&
- other.TessEvaluationId == TessEvaluationId &&
- other.GeometryId == GeometryId &&
- other.FragmentId == FragmentId;
- }
- public override int GetHashCode()
- {
- return HashCode.Combine(VertexAId, VertexBId, TessControlId, TessEvaluationId, GeometryId, FragmentId);
- }
- }
- private IdCache _vertexACache;
- private IdCache _vertexBCache;
- private IdCache _tessControlCache;
- private IdCache _tessEvaluationCache;
- private IdCache _geometryCache;
- private IdCache _fragmentCache;
- private readonly Dictionary<IdTable, ShaderSpecializationList> _shaderPrograms;
- /// <summary>
- /// Creates a new graphics shader cache hash table.
- /// </summary>
- public ShaderCacheHashTable()
- {
- _vertexACache.Initialize();
- _vertexBCache.Initialize();
- _tessControlCache.Initialize();
- _tessEvaluationCache.Initialize();
- _geometryCache.Initialize();
- _fragmentCache.Initialize();
- _shaderPrograms = new Dictionary<IdTable, ShaderSpecializationList>();
- }
- /// <summary>
- /// Adds a program to the cache.
- /// </summary>
- /// <param name="program">Program to be added</param>
- public void Add(CachedShaderProgram program)
- {
- IdTable idTable = new IdTable();
- foreach (var shader in program.Shaders)
- {
- if (shader == null)
- {
- continue;
- }
- if (shader.Info != null)
- {
- switch (shader.Info.Stage)
- {
- case ShaderStage.Vertex:
- idTable.VertexBId = _vertexBCache.Add(shader.Code);
- break;
- case ShaderStage.TessellationControl:
- idTable.TessControlId = _tessControlCache.Add(shader.Code);
- break;
- case ShaderStage.TessellationEvaluation:
- idTable.TessEvaluationId = _tessEvaluationCache.Add(shader.Code);
- break;
- case ShaderStage.Geometry:
- idTable.GeometryId = _geometryCache.Add(shader.Code);
- break;
- case ShaderStage.Fragment:
- idTable.FragmentId = _fragmentCache.Add(shader.Code);
- break;
- }
- }
- else
- {
- idTable.VertexAId = _vertexACache.Add(shader.Code);
- }
- }
- if (!_shaderPrograms.TryGetValue(idTable, out ShaderSpecializationList specList))
- {
- specList = new ShaderSpecializationList();
- _shaderPrograms.Add(idTable, specList);
- }
- specList.Add(program);
- }
- /// <summary>
- /// Tries to find a cached program.
- /// </summary>
- /// <remarks>
- /// Even if false is returned, <paramref name="guestCode"/> might still contain cached guest code.
- /// This can be used to avoid additional allocations for guest code that was already cached.
- /// </remarks>
- /// <param name="channel">GPU channel</param>
- /// <param name="poolState">Texture pool state</param>
- /// <param name="graphicsState">Graphics state</param>
- /// <param name="addresses">Guest addresses of the shaders to find</param>
- /// <param name="program">Cached host program for the given state, if found</param>
- /// <param name="guestCode">Cached guest code, if any found</param>
- /// <returns>True if a cached host program was found, false otherwise</returns>
- public bool TryFind(
- GpuChannel channel,
- ref GpuChannelPoolState poolState,
- ref GpuChannelGraphicsState graphicsState,
- ShaderAddresses addresses,
- out CachedShaderProgram program,
- out CachedGraphicsGuestCode guestCode)
- {
- var memoryManager = channel.MemoryManager;
- IdTable idTable = new IdTable();
- guestCode = new CachedGraphicsGuestCode();
- program = null;
- bool found = TryGetId(_vertexACache, memoryManager, addresses.VertexA, out idTable.VertexAId, out guestCode.VertexACode);
- found &= TryGetId(_vertexBCache, memoryManager, addresses.VertexB, out idTable.VertexBId, out guestCode.VertexBCode);
- found &= TryGetId(_tessControlCache, memoryManager, addresses.TessControl, out idTable.TessControlId, out guestCode.TessControlCode);
- found &= TryGetId(_tessEvaluationCache, memoryManager, addresses.TessEvaluation, out idTable.TessEvaluationId, out guestCode.TessEvaluationCode);
- found &= TryGetId(_geometryCache, memoryManager, addresses.Geometry, out idTable.GeometryId, out guestCode.GeometryCode);
- found &= TryGetId(_fragmentCache, memoryManager, addresses.Fragment, out idTable.FragmentId, out guestCode.FragmentCode);
- if (found && _shaderPrograms.TryGetValue(idTable, out ShaderSpecializationList specList))
- {
- return specList.TryFindForGraphics(channel, ref poolState, ref graphicsState, out program);
- }
- return false;
- }
- /// <summary>
- /// Tries to get the ID of a single cached shader stage.
- /// </summary>
- /// <param name="idCache">ID cache of the stage</param>
- /// <param name="memoryManager">GPU memory manager</param>
- /// <param name="baseAddress">Base address of the shader</param>
- /// <param name="id">ID, if found</param>
- /// <param name="data">Cached guest code, if found</param>
- /// <returns>True if a cached shader is found, false otherwise</returns>
- private static bool TryGetId(IdCache idCache, MemoryManager memoryManager, ulong baseAddress, out int id, out byte[] data)
- {
- if (baseAddress == 0)
- {
- id = 0;
- data = null;
- return true;
- }
- ShaderCodeAccessor codeAccessor = new ShaderCodeAccessor(memoryManager, baseAddress);
- return idCache.TryFind(codeAccessor, out id, out data);
- }
- /// <summary>
- /// Gets all programs that have been added to the table.
- /// </summary>
- /// <returns>Programs added to the table</returns>
- public IEnumerable<CachedShaderProgram> GetPrograms()
- {
- foreach (var specList in _shaderPrograms.Values)
- {
- foreach (var program in specList)
- {
- yield return program;
- }
- }
- }
- }
- }
|