| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- using Ryujinx.Common.Memory;
- using Ryujinx.Graphics.Gpu.Shader.DiskCache;
- using Ryujinx.Graphics.Shader;
- using System;
- using System.Collections.Generic;
- using System.Numerics;
- namespace Ryujinx.Graphics.Gpu.Shader
- {
- class ShaderSpecializationState
- {
- private const uint ComsMagic = (byte)'C' | ((byte)'O' << 8) | ((byte)'M' << 16) | ((byte)'S' << 24);
- private const uint GfxsMagic = (byte)'G' | ((byte)'F' << 8) | ((byte)'X' << 16) | ((byte)'S' << 24);
- private const uint TfbdMagic = (byte)'T' | ((byte)'F' << 8) | ((byte)'B' << 16) | ((byte)'D' << 24);
- private const uint TexkMagic = (byte)'T' | ((byte)'E' << 8) | ((byte)'X' << 16) | ((byte)'K' << 24);
- private const uint TexsMagic = (byte)'T' | ((byte)'E' << 8) | ((byte)'X' << 16) | ((byte)'S' << 24);
- /// <summary>
- /// Flags indicating GPU state that is used by the shader.
- /// </summary>
- [Flags]
- private enum QueriedStateFlags
- {
- EarlyZForce = 1 << 0,
- PrimitiveTopology = 1 << 1,
- TessellationMode = 1 << 2,
- TransformFeedback = 1 << 3
- }
- private QueriedStateFlags _queriedState;
- private bool _compute;
- private byte _constantBufferUsePerStage;
- /// <summary>
- /// Compute engine state.
- /// </summary>
- public GpuChannelComputeState ComputeState;
- /// <summary>
- /// 3D engine state.
- /// </summary>
- public GpuChannelGraphicsState GraphicsState;
- /// <summary>
- /// Contant buffers bound at the time the shader was compiled, per stage.
- /// </summary>
- public Array5<uint> ConstantBufferUse;
- /// <summary>
- /// Transform feedback buffers active at the time the shader was compiled.
- /// </summary>
- public TransformFeedbackDescriptor[] TransformFeedbackDescriptors;
- /// <summary>
- /// Flags indicating texture state that is used by the shader.
- /// </summary>
- [Flags]
- private enum QueriedTextureStateFlags
- {
- TextureFormat = 1 << 0,
- SamplerType = 1 << 1,
- CoordNormalized = 1 << 2
- }
- /// <summary>
- /// Reference type wrapping a value.
- /// </summary>
- private class Box<T>
- {
- /// <summary>
- /// Wrapped value.
- /// </summary>
- public T Value;
- }
- /// <summary>
- /// State of a texture or image that is accessed by the shader.
- /// </summary>
- private struct TextureSpecializationState
- {
- // New fields should be added to the end of the struct to keep disk shader cache compatibility.
- /// <summary>
- /// Flags indicating which state of the texture the shader depends on.
- /// </summary>
- public QueriedTextureStateFlags QueriedFlags;
- /// <summary>
- /// Encoded texture format value.
- /// </summary>
- public uint Format;
- /// <summary>
- /// True if the texture format is sRGB, false otherwise.
- /// </summary>
- public bool FormatSrgb;
- /// <summary>
- /// Texture target.
- /// </summary>
- public Image.TextureTarget TextureTarget;
- /// <summary>
- /// Indicates if the coordinates used to sample the texture are normalized or not (0.0..1.0 or 0..Width/Height).
- /// </summary>
- public bool CoordNormalized;
- }
- /// <summary>
- /// Texture binding information, used to identify each texture accessed by the shader.
- /// </summary>
- private struct TextureKey : IEquatable<TextureKey>
- {
- // New fields should be added to the end of the struct to keep disk shader cache compatibility.
- /// <summary>
- /// Shader stage where the texture is used.
- /// </summary>
- public readonly int StageIndex;
- /// <summary>
- /// Texture handle offset in words on the texture buffer.
- /// </summary>
- public readonly int Handle;
- /// <summary>
- /// Constant buffer slot of the texture buffer (-1 to use the texture buffer index GPU register).
- /// </summary>
- public readonly int CbufSlot;
- /// <summary>
- /// Creates a new texture key.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Texture handle offset in words on the texture buffer</param>
- /// <param name="cbufSlot">Constant buffer slot of the texture buffer (-1 to use the texture buffer index GPU register)</param>
- public TextureKey(int stageIndex, int handle, int cbufSlot)
- {
- StageIndex = stageIndex;
- Handle = handle;
- CbufSlot = cbufSlot;
- }
- public override bool Equals(object obj)
- {
- return obj is TextureKey textureKey && Equals(textureKey);
- }
- public bool Equals(TextureKey other)
- {
- return StageIndex == other.StageIndex && Handle == other.Handle && CbufSlot == other.CbufSlot;
- }
- public override int GetHashCode()
- {
- return HashCode.Combine(StageIndex, Handle, CbufSlot);
- }
- }
- private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization;
- /// <summary>
- /// Creates a new instance of the shader specialization state.
- /// </summary>
- private ShaderSpecializationState()
- {
- _textureSpecialization = new Dictionary<TextureKey, Box<TextureSpecializationState>>();
- }
- /// <summary>
- /// Creates a new instance of the shader specialization state.
- /// </summary>
- /// <param name="state">Current compute engine state</param>
- public ShaderSpecializationState(GpuChannelComputeState state) : this()
- {
- ComputeState = state;
- _compute = true;
- }
- /// <summary>
- /// Creates a new instance of the shader specialization state.
- /// </summary>
- /// <param name="state">Current 3D engine state</param>
- /// <param name="descriptors">Optional transform feedback buffers in use, if any</param>
- public ShaderSpecializationState(GpuChannelGraphicsState state, TransformFeedbackDescriptor[] descriptors) : this()
- {
- GraphicsState = state;
- _compute = false;
- if (descriptors != null)
- {
- TransformFeedbackDescriptors = descriptors;
- _queriedState |= QueriedStateFlags.TransformFeedback;
- }
- }
- /// <summary>
- /// Indicates that the shader accesses the early Z force state.
- /// </summary>
- public void RecordEarlyZForce()
- {
- _queriedState |= QueriedStateFlags.EarlyZForce;
- }
- /// <summary>
- /// Indicates that the shader accesses the primitive topology state.
- /// </summary>
- public void RecordPrimitiveTopology()
- {
- _queriedState |= QueriedStateFlags.PrimitiveTopology;
- }
- /// <summary>
- /// Indicates that the shader accesses the tessellation mode state.
- /// </summary>
- public void RecordTessellationMode()
- {
- _queriedState |= QueriedStateFlags.TessellationMode;
- }
- /// <summary>
- /// Indicates that the shader accesses the constant buffer use state.
- /// </summary>
- /// <param name="stageIndex">Shader stage index</param>
- /// <param name="useMask">Mask indicating the constant buffers bound at the time of the shader compilation</param>
- public void RecordConstantBufferUse(int stageIndex, uint useMask)
- {
- ConstantBufferUse[stageIndex] = useMask;
- _constantBufferUsePerStage |= (byte)(1 << stageIndex);
- }
- /// <summary>
- /// Indicates that a given texture is accessed by the shader.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
- /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
- /// <param name="descriptor">Descriptor of the texture</param>
- public void RegisterTexture(int stageIndex, int handle, int cbufSlot, Image.TextureDescriptor descriptor)
- {
- Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot);
- state.Value.Format = descriptor.UnpackFormat();
- state.Value.FormatSrgb = descriptor.UnpackSrgb();
- state.Value.TextureTarget = descriptor.UnpackTextureTarget();
- state.Value.CoordNormalized = descriptor.UnpackTextureCoordNormalized();
- }
- /// <summary>
- /// Indicates that a given texture is accessed by the shader.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
- /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
- /// <param name="format">Maxwell texture format value</param>
- /// <param name="formatSrgb">Whenever the texture format is a sRGB format</param>
- /// <param name="target">Texture target type</param>
- /// <param name="coordNormalized">Whenever the texture coordinates used on the shader are considered normalized</param>
- public void RegisterTexture(
- int stageIndex,
- int handle,
- int cbufSlot,
- uint format,
- bool formatSrgb,
- Image.TextureTarget target,
- bool coordNormalized)
- {
- Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot);
- state.Value.Format = format;
- state.Value.FormatSrgb = formatSrgb;
- state.Value.TextureTarget = target;
- state.Value.CoordNormalized = coordNormalized;
- }
- /// <summary>
- /// Indicates that the format of a given texture was used during the shader translation process.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
- /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
- public void RecordTextureFormat(int stageIndex, int handle, int cbufSlot)
- {
- Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot);
- state.Value.QueriedFlags |= QueriedTextureStateFlags.TextureFormat;
- }
- /// <summary>
- /// Indicates that the target of a given texture was used during the shader translation process.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
- /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
- public void RecordTextureSamplerType(int stageIndex, int handle, int cbufSlot)
- {
- Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot);
- state.Value.QueriedFlags |= QueriedTextureStateFlags.SamplerType;
- }
- /// <summary>
- /// Indicates that the coordinate normalization state of a given texture was used during the shader translation process.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
- /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
- public void RecordTextureCoordNormalized(int stageIndex, int handle, int cbufSlot)
- {
- Box<TextureSpecializationState> state = GetOrCreateTextureSpecState(stageIndex, handle, cbufSlot);
- state.Value.QueriedFlags |= QueriedTextureStateFlags.CoordNormalized;
- }
- /// <summary>
- /// Checks if a given texture was registerd on this specialization state.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
- /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
- public bool TextureRegistered(int stageIndex, int handle, int cbufSlot)
- {
- return GetTextureSpecState(stageIndex, handle, cbufSlot) != null;
- }
- /// <summary>
- /// Gets the recorded format of a given texture.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
- /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
- public (uint, bool) GetFormat(int stageIndex, int handle, int cbufSlot)
- {
- TextureSpecializationState state = GetTextureSpecState(stageIndex, handle, cbufSlot).Value;
- return (state.Format, state.FormatSrgb);
- }
- /// <summary>
- /// Gets the recorded target of a given texture.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
- /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
- public Image.TextureTarget GetTextureTarget(int stageIndex, int handle, int cbufSlot)
- {
- return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.TextureTarget;
- }
- /// <summary>
- /// Gets the recorded coordinate normalization state of a given texture.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
- /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
- public bool GetCoordNormalized(int stageIndex, int handle, int cbufSlot)
- {
- return GetTextureSpecState(stageIndex, handle, cbufSlot).Value.CoordNormalized;
- }
- /// <summary>
- /// Gets texture specialization state for a given texture, or create a new one if not present.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
- /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
- /// <returns>Texture specialization state</returns>
- private Box<TextureSpecializationState> GetOrCreateTextureSpecState(int stageIndex, int handle, int cbufSlot)
- {
- TextureKey key = new TextureKey(stageIndex, handle, cbufSlot);
- if (!_textureSpecialization.TryGetValue(key, out Box<TextureSpecializationState> state))
- {
- _textureSpecialization.Add(key, state = new Box<TextureSpecializationState>());
- }
- return state;
- }
- /// <summary>
- /// Gets texture specialization state for a given texture.
- /// </summary>
- /// <param name="stageIndex">Shader stage where the texture is used</param>
- /// <param name="handle">Offset in words of the texture handle on the texture buffer</param>
- /// <param name="cbufSlot">Slot of the texture buffer constant buffer</param>
- /// <returns>Texture specialization state</returns>
- private Box<TextureSpecializationState> GetTextureSpecState(int stageIndex, int handle, int cbufSlot)
- {
- TextureKey key = new TextureKey(stageIndex, handle, cbufSlot);
- if (_textureSpecialization.TryGetValue(key, out Box<TextureSpecializationState> state))
- {
- return state;
- }
- return null;
- }
- /// <summary>
- /// Checks if the recorded state matches the current GPU 3D engine state.
- /// </summary>
- /// <param name="channel">GPU channel</param>
- /// <param name="poolState">Texture pool state</param>
- /// <param name="graphicsState">Graphics state</param>
- /// <returns>True if the state matches, false otherwise</returns>
- public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelGraphicsState graphicsState)
- {
- if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable)
- {
- return false;
- }
- return Matches(channel, poolState, isCompute: false);
- }
- /// <summary>
- /// Checks if the recorded state matches the current GPU compute engine state.
- /// </summary>
- /// <param name="channel">GPU channel</param>
- /// <param name="poolState">Texture pool state</param>
- /// <returns>True if the state matches, false otherwise</returns>
- public bool MatchesCompute(GpuChannel channel, GpuChannelPoolState poolState)
- {
- return Matches(channel, poolState, isCompute: true);
- }
- /// <summary>
- /// Checks if the recorded state matches the current GPU state.
- /// </summary>
- /// <param name="channel">GPU channel</param>
- /// <param name="poolState">Texture pool state</param>
- /// <param name="isCompute">Indicates whenever the check is requested by the 3D or compute engine</param>
- /// <returns>True if the state matches, false otherwise</returns>
- private bool Matches(GpuChannel channel, GpuChannelPoolState poolState, bool isCompute)
- {
- int constantBufferUsePerStageMask = _constantBufferUsePerStage;
- while (constantBufferUsePerStageMask != 0)
- {
- int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask);
- uint useMask = isCompute
- ? channel.BufferManager.GetComputeUniformBufferUseMask()
- : channel.BufferManager.GetGraphicsUniformBufferUseMask(index);
- if (ConstantBufferUse[index] != useMask)
- {
- return false;
- }
- constantBufferUsePerStageMask &= ~(1 << index);
- }
- foreach (var kv in _textureSpecialization)
- {
- TextureKey textureKey = kv.Key;
- (int textureBufferIndex, int samplerBufferIndex) = TextureHandle.UnpackSlots(textureKey.CbufSlot, poolState.TextureBufferIndex);
- ulong textureCbAddress;
- ulong samplerCbAddress;
- if (isCompute)
- {
- textureCbAddress = channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex);
- samplerCbAddress = channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex);
- }
- else
- {
- textureCbAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(textureKey.StageIndex, textureBufferIndex);
- samplerCbAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(textureKey.StageIndex, samplerBufferIndex);
- }
- if (!channel.MemoryManager.Physical.IsMapped(textureCbAddress) || !channel.MemoryManager.Physical.IsMapped(samplerCbAddress))
- {
- continue;
- }
- Image.TextureDescriptor descriptor;
- if (isCompute)
- {
- descriptor = channel.TextureManager.GetComputeTextureDescriptor(
- poolState.TexturePoolGpuVa,
- poolState.TextureBufferIndex,
- poolState.TexturePoolMaximumId,
- textureKey.Handle,
- textureKey.CbufSlot);
- }
- else
- {
- descriptor = channel.TextureManager.GetGraphicsTextureDescriptor(
- poolState.TexturePoolGpuVa,
- poolState.TextureBufferIndex,
- poolState.TexturePoolMaximumId,
- textureKey.StageIndex,
- textureKey.Handle,
- textureKey.CbufSlot);
- }
- Box<TextureSpecializationState> specializationState = kv.Value;
- if (specializationState.Value.QueriedFlags.HasFlag(QueriedTextureStateFlags.CoordNormalized) &&
- specializationState.Value.CoordNormalized != descriptor.UnpackTextureCoordNormalized())
- {
- return false;
- }
- }
- return true;
- }
- /// <summary>
- /// Reads shader specialization state that has been serialized.
- /// </summary>
- /// <param name="dataReader">Data reader</param>
- /// <returns>Shader specialization state</returns>
- public static ShaderSpecializationState Read(ref BinarySerializer dataReader)
- {
- ShaderSpecializationState specState = new ShaderSpecializationState();
- dataReader.Read(ref specState._queriedState);
- dataReader.Read(ref specState._compute);
- if (specState._compute)
- {
- dataReader.ReadWithMagicAndSize(ref specState.ComputeState, ComsMagic);
- }
- else
- {
- dataReader.ReadWithMagicAndSize(ref specState.GraphicsState, GfxsMagic);
- }
- dataReader.Read(ref specState._constantBufferUsePerStage);
- int constantBufferUsePerStageMask = specState._constantBufferUsePerStage;
- while (constantBufferUsePerStageMask != 0)
- {
- int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask);
- dataReader.Read(ref specState.ConstantBufferUse[index]);
- constantBufferUsePerStageMask &= ~(1 << index);
- }
- if (specState._queriedState.HasFlag(QueriedStateFlags.TransformFeedback))
- {
- ushort tfCount = 0;
- dataReader.Read(ref tfCount);
- specState.TransformFeedbackDescriptors = new TransformFeedbackDescriptor[tfCount];
- for (int index = 0; index < tfCount; index++)
- {
- dataReader.ReadWithMagicAndSize(ref specState.TransformFeedbackDescriptors[index], TfbdMagic);
- }
- }
- ushort count = 0;
- dataReader.Read(ref count);
- for (int index = 0; index < count; index++)
- {
- TextureKey textureKey = default;
- Box<TextureSpecializationState> textureState = new Box<TextureSpecializationState>();
- dataReader.ReadWithMagicAndSize(ref textureKey, TexkMagic);
- dataReader.ReadWithMagicAndSize(ref textureState.Value, TexsMagic);
- specState._textureSpecialization[textureKey] = textureState;
- }
- return specState;
- }
- /// <summary>
- /// Serializes the shader specialization state.
- /// </summary>
- /// <param name="dataWriter">Data writer</param>
- public void Write(ref BinarySerializer dataWriter)
- {
- dataWriter.Write(ref _queriedState);
- dataWriter.Write(ref _compute);
- if (_compute)
- {
- dataWriter.WriteWithMagicAndSize(ref ComputeState, ComsMagic);
- }
- else
- {
- dataWriter.WriteWithMagicAndSize(ref GraphicsState, GfxsMagic);
- }
- dataWriter.Write(ref _constantBufferUsePerStage);
- int constantBufferUsePerStageMask = _constantBufferUsePerStage;
- while (constantBufferUsePerStageMask != 0)
- {
- int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask);
- dataWriter.Write(ref ConstantBufferUse[index]);
- constantBufferUsePerStageMask &= ~(1 << index);
- }
- if (_queriedState.HasFlag(QueriedStateFlags.TransformFeedback))
- {
- ushort tfCount = (ushort)TransformFeedbackDescriptors.Length;
- dataWriter.Write(ref tfCount);
- for (int index = 0; index < TransformFeedbackDescriptors.Length; index++)
- {
- dataWriter.WriteWithMagicAndSize(ref TransformFeedbackDescriptors[index], TfbdMagic);
- }
- }
- ushort count = (ushort)_textureSpecialization.Count;
- dataWriter.Write(ref count);
- foreach (var kv in _textureSpecialization)
- {
- var textureKey = kv.Key;
- var textureState = kv.Value;
- dataWriter.WriteWithMagicAndSize(ref textureKey, TexkMagic);
- dataWriter.WriteWithMagicAndSize(ref textureState.Value, TexsMagic);
- }
- }
- }
- }
|