Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7373ec5792 | ||
|
de162a648b | ||
|
131baebe2a | ||
|
187372cbde | ||
|
022d495335 | ||
|
c1372ed775 | ||
|
a16682cfd3 |
@@ -1,15 +1,11 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
static class BitUtils
|
||||
{
|
||||
private static readonly sbyte[] HbsNibbleLut;
|
||||
|
||||
static BitUtils()
|
||||
{
|
||||
HbsNibbleLut = new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
|
||||
}
|
||||
private static ReadOnlySpan<sbyte> HbsNibbleLut => new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
|
||||
|
||||
public static long FillWithOnes(int bits)
|
||||
{
|
||||
|
@@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||
|
||||
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
|
||||
TextureManager.ReloadPools();
|
||||
MemoryManager.Physical.BufferCache.QueuePrune();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -77,6 +78,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||
private void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
|
||||
{
|
||||
TextureManager.ReloadPools();
|
||||
MemoryManager.Physical.BufferCache.QueuePrune();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache;
|
||||
private readonly Dictionary<ulong, BufferCacheEntry> _modifiedCache;
|
||||
private bool _pruneCaches;
|
||||
|
||||
public event Action NotifyBuffersModified;
|
||||
|
||||
@@ -136,6 +137,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// <param name="size">Size in bytes of the buffer</param>
|
||||
public void ForceDirty(MemoryManager memoryManager, ulong gpuVa, ulong size)
|
||||
{
|
||||
if (_pruneCaches)
|
||||
{
|
||||
Prune();
|
||||
}
|
||||
|
||||
if (!_dirtyCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
|
||||
result.EndGpuAddress < gpuVa + size ||
|
||||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
||||
@@ -158,17 +164,29 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// <returns>True if modified, false otherwise</returns>
|
||||
public bool CheckModified(MemoryManager memoryManager, ulong gpuVa, ulong size, out ulong outAddr)
|
||||
{
|
||||
if (!_modifiedCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
|
||||
result.EndGpuAddress < gpuVa + size ||
|
||||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
||||
if (_pruneCaches)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
|
||||
result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
|
||||
|
||||
_modifiedCache[gpuVa] = result;
|
||||
Prune();
|
||||
}
|
||||
|
||||
outAddr = result.Address;
|
||||
// Align the address to avoid creating too many entries on the quick lookup dictionary.
|
||||
ulong mask = BufferAlignmentMask;
|
||||
ulong alignedGpuVa = gpuVa & (~mask);
|
||||
ulong alignedEndGpuVa = (gpuVa + size + mask) & (~mask);
|
||||
|
||||
size = alignedEndGpuVa - alignedGpuVa;
|
||||
|
||||
if (!_modifiedCache.TryGetValue(alignedGpuVa, out BufferCacheEntry result) ||
|
||||
result.EndGpuAddress < alignedEndGpuVa ||
|
||||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(memoryManager, alignedGpuVa, size);
|
||||
result = new BufferCacheEntry(address, alignedGpuVa, GetBuffer(address, size));
|
||||
|
||||
_modifiedCache[alignedGpuVa] = result;
|
||||
}
|
||||
|
||||
outAddr = result.Address | (gpuVa & mask);
|
||||
|
||||
return result.Buffer.IsModified(result.Address, size);
|
||||
}
|
||||
@@ -435,6 +453,54 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prune any invalid entries from a quick access dictionary.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">Dictionary to prune</param>
|
||||
/// <param name="toDelete">List used to track entries to delete</param>
|
||||
private void Prune(Dictionary<ulong, BufferCacheEntry> dictionary, ref List<ulong> toDelete)
|
||||
{
|
||||
foreach (var entry in dictionary)
|
||||
{
|
||||
if (entry.Value.UnmappedSequence != entry.Value.Buffer.UnmappedSequence)
|
||||
{
|
||||
(toDelete ??= new()).Add(entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
if (toDelete != null)
|
||||
{
|
||||
foreach (ulong entry in toDelete)
|
||||
{
|
||||
dictionary.Remove(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prune any invalid entries from the quick access dictionaries.
|
||||
/// </summary>
|
||||
private void Prune()
|
||||
{
|
||||
List<ulong> toDelete = null;
|
||||
|
||||
Prune(_dirtyCache, ref toDelete);
|
||||
|
||||
toDelete?.Clear();
|
||||
|
||||
Prune(_modifiedCache, ref toDelete);
|
||||
|
||||
_pruneCaches = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues a prune of invalid entries the next time a dictionary cache is accessed.
|
||||
/// </summary>
|
||||
public void QueuePrune()
|
||||
{
|
||||
_pruneCaches = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all buffers in the cache.
|
||||
/// It's an error to use the buffer manager after disposal.
|
||||
|
@@ -325,13 +325,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
public void ReregisterRanges(Action<ulong, ulong> rangeAction)
|
||||
{
|
||||
ref var ranges = ref ThreadStaticArray<BufferModifiedRange>.Get();
|
||||
int count;
|
||||
|
||||
// Range list must be consistent for this operation.
|
||||
lock (_lock)
|
||||
{
|
||||
if (ranges.Length < Count)
|
||||
count = Count;
|
||||
if (ranges.Length < count)
|
||||
{
|
||||
Array.Resize(ref ranges, Count);
|
||||
Array.Resize(ref ranges, count);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
@@ -342,7 +344,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
}
|
||||
|
||||
ulong currentSync = _context.SyncNumber;
|
||||
for (int i = 0; i < Count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
BufferModifiedRange range = ranges[i];
|
||||
if (range.SyncNumber != currentSync)
|
||||
|
@@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
||||
short dqv = dq[0];
|
||||
ReadOnlySpan<byte> cat6Prob = (xd.Bd == 12)
|
||||
? Luts.Vp9Cat6ProbHigh12
|
||||
: (xd.Bd == 10) ? new ReadOnlySpan<byte>(Luts.Vp9Cat6ProbHigh12).Slice(2) : Luts.Vp9Cat6Prob;
|
||||
: (xd.Bd == 10) ? Luts.Vp9Cat6ProbHigh12.Slice(2) : Luts.Vp9Cat6Prob;
|
||||
int cat6Bits = (xd.Bd == 12) ? 18 : (xd.Bd == 10) ? 16 : 14;
|
||||
// Keep value, range, and count as locals. The compiler produces better
|
||||
// results with the locals than using r directly.
|
||||
|
@@ -1,14 +1,12 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.Nvdec.Vp9.Types;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Nvdec.Vp9
|
||||
{
|
||||
internal static class Luts
|
||||
{
|
||||
public static readonly byte[] SizeGroupLookup = new byte[]
|
||||
{
|
||||
0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3
|
||||
};
|
||||
public static ReadOnlySpan<byte> SizeGroupLookup => new byte[] { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3 };
|
||||
|
||||
public static readonly BlockSize[][] SubsizeLookup = new BlockSize[][]
|
||||
{
|
||||
@@ -1070,18 +1068,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
||||
-(sbyte)MvClassType.MvClass10,
|
||||
};
|
||||
|
||||
public static readonly sbyte[] Vp9MvFPTree = new sbyte[] { -0, 2, -1, 4, -2, -3 };
|
||||
public static ReadOnlySpan<sbyte> Vp9MvFPTree => new sbyte[] { -0, 2, -1, 4, -2, -3 };
|
||||
|
||||
// Entropy
|
||||
|
||||
public static readonly byte[] Vp9Cat1Prob = new byte[] { 159 };
|
||||
public static readonly byte[] Vp9Cat2Prob = new byte[] { 165, 145 };
|
||||
public static readonly byte[] Vp9Cat3Prob = new byte[] { 173, 148, 140 };
|
||||
public static readonly byte[] Vp9Cat4Prob = new byte[] { 176, 155, 140, 135 };
|
||||
public static readonly byte[] Vp9Cat5Prob = new byte[] { 180, 157, 141, 134, 130 };
|
||||
public static readonly byte[] Vp9Cat6Prob = new byte[] { 254, 254, 254, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 };
|
||||
public static ReadOnlySpan<byte> Vp9Cat1Prob => new byte[] { 159 };
|
||||
public static ReadOnlySpan<byte> Vp9Cat2Prob => new byte[] { 165, 145 };
|
||||
public static ReadOnlySpan<byte> Vp9Cat3Prob => new byte[] { 173, 148, 140 };
|
||||
public static ReadOnlySpan<byte> Vp9Cat4Prob => new byte[] { 176, 155, 140, 135 };
|
||||
public static ReadOnlySpan<byte> Vp9Cat5Prob => new byte[] { 180, 157, 141, 134, 130 };
|
||||
public static ReadOnlySpan<byte> Vp9Cat6Prob => new byte[] { 254, 254, 254, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129 };
|
||||
|
||||
public static readonly byte[] Vp9Cat6ProbHigh12 = new byte[]
|
||||
public static ReadOnlySpan<byte> Vp9Cat6ProbHigh12 => new byte[]
|
||||
{
|
||||
255, 255, 255, 255, 254, 254, 54, 252, 249, 243, 230, 196, 177, 153, 140, 133, 130, 129
|
||||
};
|
||||
@@ -1131,12 +1129,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
};
|
||||
|
||||
private static readonly byte[] Vp9CoefbandTrans4X4 = new byte[]
|
||||
private static ReadOnlySpan<byte> Vp9CoefbandTrans4X4 => new byte[]
|
||||
{
|
||||
0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5,
|
||||
};
|
||||
|
||||
public static byte[] get_band_translate(TxSize txSize)
|
||||
public static ReadOnlySpan<byte> get_band_translate(TxSize txSize)
|
||||
{
|
||||
return txSize == TxSize.Tx4x4 ? Vp9CoefbandTrans4X4 : Vp9CoefbandTrans8X8Plus;
|
||||
}
|
||||
|
@@ -210,7 +210,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
if (cbs != null && !(_buffer.HasCommandBufferDependency(cbs.Value) && _waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize)))
|
||||
if (cbs != null &&
|
||||
_gd.PipelineInternal.RenderPassActive &&
|
||||
!(_buffer.HasCommandBufferDependency(cbs.Value) &&
|
||||
_waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize)))
|
||||
{
|
||||
// If the buffer hasn't been used on the command buffer yet, try to preload the data.
|
||||
// This avoids ending and beginning render passes on each buffer data upload.
|
||||
|
@@ -130,6 +130,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
1f));
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
Span<byte> dummyTextureData = stackalloc byte[4];
|
||||
_dummyTexture.SetData(dummyTextureData);
|
||||
}
|
||||
|
||||
public void SetProgram(ShaderCollection program)
|
||||
{
|
||||
_program = program;
|
||||
|
@@ -49,7 +49,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private Auto<DisposableFramebuffer> _framebuffer;
|
||||
private Auto<DisposableRenderPass> _renderPass;
|
||||
private int _writtenAttachmentCount;
|
||||
private bool _renderPassActive;
|
||||
|
||||
private readonly DescriptorSetUpdater _descriptorSetUpdater;
|
||||
|
||||
@@ -73,6 +72,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private PipelineColorBlendAttachmentState[] _storedBlend;
|
||||
|
||||
public ulong DrawCount { get; private set; }
|
||||
public bool RenderPassActive { get; private set; }
|
||||
|
||||
public unsafe PipelineBase(VulkanRenderer gd, Device device)
|
||||
{
|
||||
@@ -114,6 +114,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_descriptorSetUpdater.Initialize();
|
||||
|
||||
SupportBufferUpdater = new SupportBufferUpdater(Gd);
|
||||
SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount);
|
||||
|
||||
@@ -838,6 +840,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
stages.CopyTo(_newState.Stages.AsSpan().Slice(0, stages.Length));
|
||||
|
||||
SignalStateChange();
|
||||
|
||||
if (_program.IsCompute)
|
||||
{
|
||||
EndRenderPass();
|
||||
}
|
||||
}
|
||||
|
||||
public void Specialize<T>(in T data) where T : unmanaged
|
||||
@@ -1451,7 +1458,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private unsafe void BeginRenderPass()
|
||||
{
|
||||
if (!_renderPassActive)
|
||||
if (!RenderPassActive)
|
||||
{
|
||||
var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height));
|
||||
var clearValue = new ClearValue();
|
||||
@@ -1467,18 +1474,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
};
|
||||
|
||||
Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
|
||||
_renderPassActive = true;
|
||||
RenderPassActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void EndRenderPass()
|
||||
{
|
||||
if (_renderPassActive)
|
||||
if (RenderPassActive)
|
||||
{
|
||||
PauseTransformFeedbackInternal();
|
||||
Gd.Api.CmdEndRenderPass(CommandBuffer);
|
||||
SignalRenderPassEnd();
|
||||
_renderPassActive = false;
|
||||
RenderPassActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public bool HasMinimalLayout { get; }
|
||||
public bool UsePushDescriptors { get; }
|
||||
public bool IsCompute { get; }
|
||||
|
||||
public uint Stages { get; }
|
||||
|
||||
@@ -47,7 +48,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private VulkanRenderer _gd;
|
||||
private Device _device;
|
||||
private bool _initialized;
|
||||
private bool _isCompute;
|
||||
|
||||
private ProgramPipelineState _state;
|
||||
private DisposableRenderPass _dummyRenderPass;
|
||||
@@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (shader.StageFlags == ShaderStageFlags.ShaderStageComputeBit)
|
||||
{
|
||||
_isCompute = true;
|
||||
IsCompute = true;
|
||||
}
|
||||
|
||||
internalShaders[i] = shader;
|
||||
@@ -163,7 +163,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
try
|
||||
{
|
||||
if (_isCompute)
|
||||
if (IsCompute)
|
||||
{
|
||||
CreateBackgroundComputePipeline();
|
||||
}
|
||||
|
@@ -26,8 +26,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
||||
{
|
||||
class IApplicationFunctions : IpcService
|
||||
{
|
||||
private ulong _defaultSaveDataSize = 200000000;
|
||||
private ulong _defaultJournalSaveDataSize = 200000000;
|
||||
private long _defaultSaveDataSize = 200000000;
|
||||
private long _defaultJournalSaveDataSize = 200000000;
|
||||
|
||||
private KEvent _gpuErrorDetectedSystemEvent;
|
||||
private KEvent _friendInvitationStorageChannelEvent;
|
||||
@@ -203,13 +203,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
||||
}
|
||||
|
||||
[CommandHipc(25)] // 3.0.0+
|
||||
// ExtendSaveData(u8 save_data_type, nn::account::Uid, u64 save_size, u64 journal_size) -> u64 result_code
|
||||
// ExtendSaveData(u8 save_data_type, nn::account::Uid, s64 save_size, s64 journal_size) -> u64 result_code
|
||||
public ResultCode ExtendSaveData(ServiceCtx context)
|
||||
{
|
||||
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64();
|
||||
Uid userId = context.RequestData.ReadStruct<Uid>();
|
||||
ulong saveDataSize = context.RequestData.ReadUInt64();
|
||||
ulong journalSize = context.RequestData.ReadUInt64();
|
||||
long saveDataSize = context.RequestData.ReadInt64();
|
||||
long journalSize = context.RequestData.ReadInt64();
|
||||
|
||||
// NOTE: Service calls nn::fs::ExtendApplicationSaveData.
|
||||
// Since LibHac currently doesn't support this method, we can stub it for now.
|
||||
@@ -225,7 +225,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
||||
}
|
||||
|
||||
[CommandHipc(26)] // 3.0.0+
|
||||
// GetSaveDataSize(u8 save_data_type, nn::account::Uid) -> (u64 save_size, u64 journal_size)
|
||||
// GetSaveDataSize(u8 save_data_type, nn::account::Uid) -> (s64 save_size, s64 journal_size)
|
||||
public ResultCode GetSaveDataSize(ServiceCtx context)
|
||||
{
|
||||
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadUInt64();
|
||||
@@ -268,6 +268,23 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(28)] // 11.0.0+
|
||||
// GetSaveDataSizeMax() -> (s64 save_size_max, s64 journal_size_max)
|
||||
public ResultCode GetSaveDataSizeMax(ServiceCtx context)
|
||||
{
|
||||
// NOTE: We are currently using a stub for GetSaveDataSize() which returns the default values.
|
||||
// For this method we shouldn't return anything lower than that, but since we aren't interacting
|
||||
// with fs to get the actual sizes, we return the default values here as well.
|
||||
// This also helps in case ExtendSaveData() has been executed and the default values were modified.
|
||||
|
||||
context.ResponseData.Write(_defaultSaveDataSize);
|
||||
context.ResponseData.Write(_defaultJournalSaveDataSize);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(30)]
|
||||
// BeginBlockingHomeButtonShortAndLongPressed()
|
||||
public ResultCode BeginBlockingHomeButtonShortAndLongPressed(ServiceCtx context)
|
||||
|
@@ -25,6 +25,7 @@ namespace Ryujinx.Memory.Tracking
|
||||
|
||||
private int _sequenceNumber;
|
||||
private BitMap _sequenceNumberBitmap;
|
||||
private BitMap _dirtyCheckedBitmap;
|
||||
private int _uncheckedHandles;
|
||||
|
||||
public bool Dirty { get; private set; } = true;
|
||||
@@ -36,6 +37,7 @@ namespace Ryujinx.Memory.Tracking
|
||||
|
||||
_dirtyBitmap = new ConcurrentBitmap(_handles.Length, true);
|
||||
_sequenceNumberBitmap = new BitMap(_handles.Length);
|
||||
_dirtyCheckedBitmap = new BitMap(_handles.Length);
|
||||
|
||||
int i = 0;
|
||||
|
||||
@@ -246,16 +248,18 @@ namespace Ryujinx.Memory.Tracking
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ParseDirtyBits(long dirtyBits, long mask, int index, long[] seqMasks, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action<ulong, ulong> modifiedAction)
|
||||
private void ParseDirtyBits(long dirtyBits, long mask, int index, long[] seqMasks, long[] checkMasks, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action<ulong, ulong> modifiedAction)
|
||||
{
|
||||
long seqMask = mask & ~seqMasks[index];
|
||||
long checkMask = (~dirtyBits) & seqMask;
|
||||
dirtyBits &= seqMask;
|
||||
|
||||
while (dirtyBits != 0)
|
||||
{
|
||||
int bit = BitOperations.TrailingZeroCount(dirtyBits);
|
||||
long bitValue = 1L << bit;
|
||||
|
||||
dirtyBits &= ~(1L << bit);
|
||||
dirtyBits &= ~bitValue;
|
||||
|
||||
int handleIndex = baseBit + bit;
|
||||
|
||||
@@ -273,11 +277,14 @@ namespace Ryujinx.Memory.Tracking
|
||||
}
|
||||
|
||||
rgSize += handle.Size;
|
||||
handle.Reprotect();
|
||||
handle.Reprotect(false, (checkMasks[index] & bitValue) == 0);
|
||||
|
||||
checkMasks[index] &= ~bitValue;
|
||||
|
||||
prevHandle = handleIndex;
|
||||
}
|
||||
|
||||
checkMasks[index] |= checkMask;
|
||||
seqMasks[index] |= mask;
|
||||
_uncheckedHandles -= BitOperations.PopCount((ulong)seqMask);
|
||||
|
||||
@@ -328,6 +335,7 @@ namespace Ryujinx.Memory.Tracking
|
||||
ulong rgSize = 0;
|
||||
|
||||
long[] seqMasks = _sequenceNumberBitmap.Masks;
|
||||
long[] checkedMasks = _dirtyCheckedBitmap.Masks;
|
||||
long[] masks = _dirtyBitmap.Masks;
|
||||
|
||||
int startIndex = startHandle >> ConcurrentBitmap.IntShift;
|
||||
@@ -345,20 +353,20 @@ namespace Ryujinx.Memory.Tracking
|
||||
|
||||
if (startIndex == endIndex)
|
||||
{
|
||||
ParseDirtyBits(startValue, startMask & endMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||
ParseDirtyBits(startValue, startMask & endMask, startIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseDirtyBits(startValue, startMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||
ParseDirtyBits(startValue, startMask, startIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||
|
||||
for (int i = startIndex + 1; i < endIndex; i++)
|
||||
{
|
||||
ParseDirtyBits(Volatile.Read(ref masks[i]), -1L, i, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||
ParseDirtyBits(Volatile.Read(ref masks[i]), -1L, i, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||
}
|
||||
|
||||
long endValue = Volatile.Read(ref masks[endIndex]);
|
||||
|
||||
ParseDirtyBits(endValue, endMask, endIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||
ParseDirtyBits(endValue, endMask, endIndex, seqMasks, checkedMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||
}
|
||||
|
||||
if (rgSize != 0)
|
||||
|
@@ -263,15 +263,15 @@ namespace Ryujinx.Memory.Tracking
|
||||
/// </summary>
|
||||
public void ForceDirty()
|
||||
{
|
||||
_checkCount++;
|
||||
|
||||
Dirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Consume the dirty flag for this handle, and reprotect so it can be set on the next write.
|
||||
/// </summary>
|
||||
public void Reprotect(bool asDirty = false)
|
||||
/// <param name="asDirty">True if the handle should be reprotected as dirty, rather than have it cleared</param>
|
||||
/// <param name="consecutiveCheck">True if this reprotect is the result of consecutive dirty checks</param>
|
||||
public void Reprotect(bool asDirty, bool consecutiveCheck = false)
|
||||
{
|
||||
if (_volatile) return;
|
||||
|
||||
@@ -296,7 +296,7 @@ namespace Ryujinx.Memory.Tracking
|
||||
}
|
||||
else if (!asDirty)
|
||||
{
|
||||
if (_checkCount > 0 && _checkCount < CheckCountForInfrequent)
|
||||
if (consecutiveCheck || (_checkCount > 0 && _checkCount < CheckCountForInfrequent))
|
||||
{
|
||||
if (++_volatileCount >= VolatileThreshold && _preAction == null)
|
||||
{
|
||||
@@ -313,6 +313,15 @@ namespace Ryujinx.Memory.Tracking
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Consume the dirty flag for this handle, and reprotect so it can be set on the next write.
|
||||
/// </summary>
|
||||
/// <param name="asDirty">True if the handle should be reprotected as dirty, rather than have it cleared</param>
|
||||
public void Reprotect(bool asDirty = false)
|
||||
{
|
||||
Reprotect(asDirty, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register an action to perform when the tracked region is read or written.
|
||||
/// The action is automatically removed after it runs.
|
||||
|
Reference in New Issue
Block a user