aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Memory/BufferMigration.cs
blob: e020c0c39b1bcb3ee4b6f94b85aa083438b43ec9 (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
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
using System;

namespace Ryujinx.Graphics.Gpu.Memory
{
    /// <summary>
    /// A record of when buffer data was copied from one buffer to another, along with the SyncNumber when the migration will be complete.
    /// Keeps the source buffer alive for data flushes until the migration is complete.
    /// </summary>
    internal class BufferMigration : IDisposable
    {
        /// <summary>
        /// The offset for the migrated region.
        /// </summary>
        private readonly ulong _offset;

        /// <summary>
        /// The size for the migrated region.
        /// </summary>
        private readonly ulong _size;

        /// <summary>
        /// The buffer that was migrated from.
        /// </summary>
        private readonly Buffer _buffer;

        /// <summary>
        /// The source range action, to be called on overlap with an unreached sync number.
        /// </summary>
        private readonly Action<ulong, ulong> _sourceRangeAction;

        /// <summary>
        /// The source range list.
        /// </summary>
        private readonly BufferModifiedRangeList _source;

        /// <summary>
        /// The destination range list. This range list must be updated when flushing the source.
        /// </summary>
        public readonly BufferModifiedRangeList Destination;

        /// <summary>
        /// The sync number that needs to be reached for this migration to be removed. This is set to the pending sync number on creation.
        /// </summary>
        public readonly ulong SyncNumber;

        /// <summary>
        /// Creates a record for a buffer migration.
        /// </summary>
        /// <param name="buffer">The source buffer for this migration</param>
        /// <param name="sourceRangeAction">The flush action for the source buffer</param>
        /// <param name="source">The modified range list for the source buffer</param>
        /// <param name="dest">The modified range list for the destination buffer</param>
        /// <param name="syncNumber">The sync number for when the migration is complete</param>
        public BufferMigration(
            Buffer buffer,
            Action<ulong, ulong> sourceRangeAction,
            BufferModifiedRangeList source,
            BufferModifiedRangeList dest,
            ulong syncNumber)
        {
            _offset = buffer.Address;
            _size = buffer.Size;
            _buffer = buffer;
            _sourceRangeAction = sourceRangeAction;
            _source = source;
            Destination = dest;
            SyncNumber = syncNumber;
        }

        /// <summary>
        /// Determine if the given range overlaps this migration, and has not been completed yet.
        /// </summary>
        /// <param name="offset">Start offset</param>
        /// <param name="size">Range size</param>
        /// <param name="syncNumber">The sync number that was waited on</param>
        /// <returns>True if overlapping and in progress, false otherwise</returns>
        public bool Overlaps(ulong offset, ulong size, ulong syncNumber)
        {
            ulong end = offset + size;
            ulong destEnd = _offset + _size;
            long syncDiff = (long)(syncNumber - SyncNumber); // syncNumber is less if the copy has not completed.

            return !(end <= _offset || offset >= destEnd) && syncDiff < 0;
        }

        /// <summary>
        /// Determine if the given range matches this migration.
        /// </summary>
        /// <param name="offset">Start offset</param>
        /// <param name="size">Range size</param>
        /// <returns>True if the range exactly matches, false otherwise</returns>
        public bool FullyMatches(ulong offset, ulong size)
        {
            return _offset == offset && _size == size;
        }

        /// <summary>
        /// Perform the migration source's range action on the range provided, clamped to the bounds of the source buffer.
        /// </summary>
        /// <param name="offset">Start offset</param>
        /// <param name="size">Range size</param>
        /// <param name="syncNumber">Current sync number</param>
        /// <param name="parent">The modified range list that originally owned this range</param>
        public void RangeActionWithMigration(ulong offset, ulong size, ulong syncNumber, BufferModifiedRangeList parent)
        {
            ulong end = offset + size;
            end = Math.Min(_offset + _size, end);
            offset = Math.Max(_offset, offset);

            size = end - offset;

            _source.RangeActionWithMigration(offset, size, syncNumber, parent, _sourceRangeAction);
        }

        /// <summary>
        /// Removes this reference to the range list, potentially allowing for the source buffer to be disposed.
        /// </summary>
        public void Dispose()
        {
            Destination.RemoveMigration(this);

            _buffer.DecrementReferenceCount();
        }
    }
}