aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Image/Pool.cs
blob: 7cf06d0b01c5c376d3215879f9c6096cf172584b (plain)
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
using Ryujinx.Graphics.Gpu.Memory;
using System;

namespace Ryujinx.Graphics.Gpu.Image
{
    /// <summary>
    /// Represents a pool of GPU resources, such as samplers or textures.
    /// </summary>
    /// <typeparam name="T">Type of the GPU resource</typeparam>
    abstract class Pool<T> : IDisposable
    {
        protected const int DescriptorSize = 0x20;

        protected GpuContext Context;

        protected T[] Items;

        /// <summary>
        /// The maximum ID value of resources on the pool (inclusive).
        /// </summary>
        /// <remarks>
        /// The maximum amount of resources on the pool is equal to this value plus one.
        /// </remarks>
        public int MaximumId { get; }

        /// <summary>
        /// The address of the pool in guest memory.
        /// </summary>
        public ulong Address { get; }

        /// <summary>
        /// The size of the pool in bytes.
        /// </summary>
        public ulong Size { get; }

        public Pool(GpuContext context, ulong address, int maximumId)
        {
            Context   = context;
            MaximumId = maximumId;

            int count = maximumId + 1;

            ulong size = (ulong)(uint)count * DescriptorSize;;

            Items = new T[count];

            Address = address;
            Size    = size;
        }

        /// <summary>
        /// Gets the GPU resource with the given ID.
        /// </summary>
        /// <param name="id">ID of the resource. This is effectively a zero-based index</param>
        /// <returns>The GPU resource with the given ID</returns>
        public abstract T Get(int id);

        /// <summary>
        /// Synchronizes host memory with guest memory.
        /// This causes invalidation of pool entries,
        /// if a modification of entries by the CPU is detected.
        /// </summary>
        public void SynchronizeMemory()
        {
            (ulong, ulong)[] modifiedRanges = Context.PhysicalMemory.GetModifiedRanges(Address, Size, ResourceName.TexturePool);

            for (int index = 0; index < modifiedRanges.Length; index++)
            {
                (ulong mAddress, ulong mSize) = modifiedRanges[index];

                if (mAddress < Address)
                {
                    mAddress = Address;
                }

                ulong maxSize = Address + Size - mAddress;

                if (mSize > maxSize)
                {
                    mSize = maxSize;
                }

                InvalidateRangeImpl(mAddress, mSize);
            }
        }

        /// <summary>
        /// Invalidates a range of memory of the GPU resource pool.
        /// Entries that falls inside the speicified range will be invalidated,
        /// causing all the data to be reloaded from guest memory.
        /// </summary>
        /// <param name="address">The start address of the range to invalidate</param>
        /// <param name="size">The size of the range to invalidate</param>
        public void InvalidateRange(ulong address, ulong size)
        {
            ulong endAddress = address + size;

            ulong texturePoolEndAddress = Address + Size;

            // If the range being invalidated is not overlapping the texture pool range,
            // then we don't have anything to do, exit early.
            if (address >= texturePoolEndAddress || endAddress <= Address)
            {
                return;
            }

            if (address < Address)
            {
                address = Address;
            }

            if (endAddress > texturePoolEndAddress)
            {
                endAddress = texturePoolEndAddress;
            }

            size = endAddress - address;

            InvalidateRangeImpl(address, size);
        }

        protected abstract void InvalidateRangeImpl(ulong address, ulong size);

        protected abstract void Delete(T item);

        /// <summary>
        /// Performs the disposal of all resources stored on the pool.
        /// It's an error to try using the pool after disposal.
        /// </summary>
        public void Dispose()
        {
            if (Items != null)
            {
                for (int index = 0; index < Items.Length; index++)
                {
                    Delete(Items[index]);
                }

                Items = null;
            }
        }
    }
}