From e22116951f53b08a1f2c1671f1065ece06848dda Mon Sep 17 00:00:00 2001 From: gdkchan Date: Tue, 21 Jun 2022 21:11:11 -0300 Subject: [PATCH] Avoid lookup of invalid textures if pool did not change --- src/Ryujinx.Common/Collections/BitMap.cs | 226 ++++++++++++++++++ src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 11 + 2 files changed, 237 insertions(+) create mode 100644 src/Ryujinx.Common/Collections/BitMap.cs diff --git a/src/Ryujinx.Common/Collections/BitMap.cs b/src/Ryujinx.Common/Collections/BitMap.cs new file mode 100644 index 000000000..2c9211300 --- /dev/null +++ b/src/Ryujinx.Common/Collections/BitMap.cs @@ -0,0 +1,226 @@ +namespace Ryujinx.Common.Collections +{ + /// + /// Represents a collection that can store 1 bit values. + /// + public struct BitMap + { + /// + /// Size in bits of the integer used internally for the groups of bits. + /// + public const int IntSize = 64; + + private const int IntShift = 6; + private const int IntMask = IntSize - 1; + + private readonly long[] _masks; + + /// + /// Gets or sets the value of a bit. + /// + /// Bit to access + /// Bit value + public bool this[int bit] + { + get => IsSet(bit); + set + { + if (value) + { + Set(bit); + } + else + { + Clear(bit); + } + } + } + + /// + /// Creates a new bitmap. + /// + /// Total number of bits + public BitMap(int count) + { + _masks = new long[(count + IntMask) / IntSize]; + } + + /// + /// Checks if any bit is set. + /// + /// True if any bit is set, false otherwise + public bool AnySet() + { + for (int i = 0; i < _masks.Length; i++) + { + if (_masks[i] != 0) + { + return true; + } + } + + return false; + } + + /// + /// Checks if a specific bit is set. + /// + /// Bit to be checked + /// True if set, false otherwise + public bool IsSet(int bit) + { + int wordIndex = bit >> IntShift; + int wordBit = bit & IntMask; + + long wordMask = 1L << wordBit; + + return (_masks[wordIndex] & wordMask) != 0; + } + + /// + /// Checks if any bit inside a given range of bits is set. + /// + /// Start bit of the range + /// End bit of the range (inclusive) + /// True if any bit is set, false otherwise + public bool IsSet(int start, int end) + { + if (start == end) + { + return IsSet(start); + } + + int startIndex = start >> IntShift; + int startBit = start & IntMask; + long startMask = -1L << startBit; + + int endIndex = end >> IntShift; + int endBit = end & IntMask; + long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); + + if (startIndex == endIndex) + { + return (_masks[startIndex] & startMask & endMask) != 0; + } + + if ((_masks[startIndex] & startMask) != 0) + { + return true; + } + + for (int i = startIndex + 1; i < endIndex; i++) + { + if (_masks[i] != 0) + { + return true; + } + } + + if ((_masks[endIndex] & endMask) != 0) + { + return true; + } + + return false; + } + + /// + /// Sets the value of a bit to 1. + /// + /// Bit to be set + /// True if the bit was 0 and then changed to 1, false if it was already 1 + public bool Set(int bit) + { + int wordIndex = bit >> IntShift; + int wordBit = bit & IntMask; + + long wordMask = 1L << wordBit; + + if ((_masks[wordIndex] & wordMask) != 0) + { + return false; + } + + _masks[wordIndex] |= wordMask; + + return true; + } + + /// + /// Sets a given range of bits to 1. + /// + /// Start bit of the range + /// End bit of the range (inclusive) + public void SetRange(int start, int end) + { + if (start == end) + { + Set(start); + return; + } + + int startIndex = start >> IntShift; + int startBit = start & IntMask; + long startMask = -1L << startBit; + + int endIndex = end >> IntShift; + int endBit = end & IntMask; + long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); + + if (startIndex == endIndex) + { + _masks[startIndex] |= startMask & endMask; + } + else + { + _masks[startIndex] |= startMask; + + for (int i = startIndex + 1; i < endIndex; i++) + { + _masks[i] |= -1; + } + + _masks[endIndex] |= endMask; + } + } + + /// + /// Sets a given bit to 0. + /// + /// Bit to be cleared + public void Clear(int bit) + { + int wordIndex = bit >> IntShift; + int wordBit = bit & IntMask; + + long wordMask = 1L << wordBit; + + _masks[wordIndex] &= ~wordMask; + } + + /// + /// Sets all bits to 0. + /// + public void Clear() + { + for (int i = 0; i < _masks.Length; i++) + { + _masks[i] = 0; + } + } + + /// + /// Sets one or more groups of bits to 0. + /// See for how many bits are inside each group. + /// + /// Start index of the group + /// End index of the group (inclusive) + public void ClearInt(int start, int end) + { + for (int i = start; i <= end; i++) + { + _masks[i] = 0; + } + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index fd1609c23..c2a503840 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Collections; using Ryujinx.Common.Logging; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Memory; @@ -72,6 +73,7 @@ namespace Ryujinx.Graphics.Gpu.Image } private readonly GpuChannel _channel; + private readonly BitMap _invalidMap; private readonly ConcurrentQueue _dereferenceQueue = new(); private TextureDescriptor _defaultDescriptor; @@ -166,6 +168,7 @@ namespace Ryujinx.Graphics.Gpu.Image { _channel = channel; _aliasLists = new Dictionary(); + _invalidMap = new BitMap(maximumId + 1); } /// @@ -182,6 +185,11 @@ namespace Ryujinx.Graphics.Gpu.Image if (texture == null) { + if (_invalidMap.IsSet(id)) + { + return ref descriptor; + } + texture = PhysicalMemory.TextureCache.FindShortCache(descriptor); if (texture == null) @@ -198,6 +206,7 @@ namespace Ryujinx.Graphics.Gpu.Image // If this happens, then the texture address is invalid, we can't add it to the cache. if (texture == null) { + _invalidMap.Set(id); return ref descriptor; } } @@ -515,6 +524,8 @@ namespace Ryujinx.Graphics.Gpu.Image RemoveAliasList(texture); } } + + _invalidMap.Clear(id); } }