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);
}
}