aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs
blob: 2f50463ded1f1e73921c9b8a1512c034fc7dbd46 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
using ChocolArm64.Events;
using ChocolArm64.Memory;
using System.Collections.Concurrent;

namespace Ryujinx.Graphics.Memory
{
    class NvGpuVmmCache
    {
        private const int PageBits = MemoryManager.PageBits;

        private const long PageSize = MemoryManager.PageSize;
        private const long PageMask = MemoryManager.PageMask;

        private ConcurrentDictionary<long, int>[] CachedPages;

        private MemoryManager _memory;

        public NvGpuVmmCache(MemoryManager memory)
        {
            _memory = memory;

            _memory.ObservedAccess += MemoryAccessHandler;

            CachedPages = new ConcurrentDictionary<long, int>[1 << 20];
        }

        private void MemoryAccessHandler(object sender, MemoryAccessEventArgs e)
        {
            long pa = _memory.GetPhysicalAddress(e.Position);

            CachedPages[pa >> PageBits]?.Clear();
        }

        public bool IsRegionModified(long position, long size, NvGpuBufferType bufferType)
        {
            long pa = _memory.GetPhysicalAddress(position);

            long addr = pa;

            long endAddr = (addr + size + PageMask) & ~PageMask;

            int newBuffMask = 1 << (int)bufferType;

            _memory.StartObservingRegion(position, size);

            long cachedPagesCount = 0;

            while (addr < endAddr)
            {
                long page = addr >> PageBits;

                ConcurrentDictionary<long, int> dictionary = CachedPages[page];

                if (dictionary == null)
                {
                    dictionary = new ConcurrentDictionary<long, int>();

                    CachedPages[page] = dictionary;
                }

                if (dictionary.TryGetValue(pa, out int currBuffMask))
                {
                    if ((currBuffMask & newBuffMask) != 0)
                    {
                        cachedPagesCount++;
                    }
                    else
                    {
                        dictionary[pa] |= newBuffMask;
                    }
                }
                else
                {
                    dictionary[pa] = newBuffMask;
                }

                addr += PageSize;
            }

            return cachedPagesCount != (endAddr - pa + PageMask) >> PageBits;
        }
    }
}