using System; using System.Collections.Generic; using System.Numerics; namespace Ryujinx.Graphics.Shader.Translation { class AttributeUsage { public bool NextUsesFixedFuncAttributes { get; private set; } public int UsedInputAttributes { get; private set; } public int UsedOutputAttributes { get; private set; } public HashSet UsedInputAttributesPerPatch { get; } public HashSet UsedOutputAttributesPerPatch { get; } public HashSet NextUsedInputAttributesPerPatch { get; private set; } public int PassthroughAttributes { get; private set; } private int _nextUsedInputAttributes; private int _thisUsedInputAttributes; private Dictionary _perPatchAttributeLocations; private readonly IGpuAccessor _gpuAccessor; public UInt128 NextInputAttributesComponents { get; private set; } public UInt128 ThisInputAttributesComponents { get; private set; } public AttributeUsage(IGpuAccessor gpuAccessor) { _gpuAccessor = gpuAccessor; UsedInputAttributesPerPatch = new(); UsedOutputAttributesPerPatch = new(); } public void SetInputUserAttribute(int index, int component) { int mask = 1 << index; UsedInputAttributes |= mask; _thisUsedInputAttributes |= mask; ThisInputAttributesComponents |= UInt128.One << (index * 4 + component); } public void SetInputUserAttributePerPatch(int index) { UsedInputAttributesPerPatch.Add(index); } public void SetOutputUserAttribute(int index) { UsedOutputAttributes |= 1 << index; } public void SetOutputUserAttributePerPatch(int index) { UsedOutputAttributesPerPatch.Add(index); } public void MergeFromtNextStage(bool gpPassthrough, bool nextUsesFixedFunctionAttributes, AttributeUsage nextStage) { NextInputAttributesComponents = nextStage.ThisInputAttributesComponents; NextUsedInputAttributesPerPatch = nextStage.UsedInputAttributesPerPatch; NextUsesFixedFuncAttributes = nextUsesFixedFunctionAttributes; MergeOutputUserAttributes(gpPassthrough, nextStage.UsedInputAttributes, nextStage.UsedInputAttributesPerPatch); if (UsedOutputAttributesPerPatch.Count != 0) { // Regular and per-patch input/output locations can't overlap, // so we must assign on our location using unused regular input/output locations. Dictionary locationsMap = new(); int freeMask = ~UsedOutputAttributes; foreach (int attr in UsedOutputAttributesPerPatch) { int location = BitOperations.TrailingZeroCount(freeMask); if (location == 32) { _gpuAccessor.Log($"No enough free locations for patch input/output 0x{attr:X}."); break; } locationsMap.Add(attr, location); freeMask &= ~(1 << location); } // Both stages must agree on the locations, so use the same "map" for both. _perPatchAttributeLocations = locationsMap; nextStage._perPatchAttributeLocations = locationsMap; } } private void MergeOutputUserAttributes(bool gpPassthrough, int mask, IEnumerable perPatch) { _nextUsedInputAttributes = mask; if (gpPassthrough) { PassthroughAttributes = mask & ~UsedOutputAttributes; } else { UsedOutputAttributes |= mask; UsedOutputAttributesPerPatch.UnionWith(perPatch); } } public int GetPerPatchAttributeLocation(int index) { if (_perPatchAttributeLocations == null || !_perPatchAttributeLocations.TryGetValue(index, out int location)) { return index; } return location; } public bool IsUsedOutputAttribute(int attr) { // The check for fixed function attributes on the next stage is conservative, // returning false if the output is just not used by the next stage is also valid. if (NextUsesFixedFuncAttributes && attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) { int index = (attr - AttributeConsts.UserAttributeBase) >> 4; return (_nextUsedInputAttributes & (1 << index)) != 0; } return true; } public int GetFreeUserAttribute(bool isOutput, int index) { int useMask = isOutput ? _nextUsedInputAttributes : _thisUsedInputAttributes; int bit = -1; while (useMask != -1) { bit = BitOperations.TrailingZeroCount(~useMask); if (bit == 32) { bit = -1; break; } else if (index < 1) { break; } useMask |= 1 << bit; index--; } return bit; } public void SetAllInputUserAttributes() { UsedInputAttributes |= Constants.AllAttributesMask; ThisInputAttributesComponents |= ~UInt128.Zero >> (128 - Constants.MaxAttributes * 4); } public void SetAllOutputUserAttributes() { UsedOutputAttributes |= Constants.AllAttributesMask; } } }