aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs
blob: 4862bca1834d4aaa0452d9d805b41392e8c25dad (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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using System;
using System.Runtime.InteropServices;

namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
    /// <summary>
    /// Holds inline index buffer state.
    /// The inline index buffer data is sent to the GPU through the command buffer.
    /// </summary>
    struct IbStreamer
    {
        private BufferHandle _inlineIndexBuffer;
        private int _inlineIndexBufferSize;
        private int _inlineIndexCount;

        /// <summary>
        /// Indicates if any index buffer data has been pushed.
        /// </summary>
        public bool HasInlineIndexData => _inlineIndexCount != 0;

        /// <summary>
        /// Total numbers of indices that have been pushed.
        /// </summary>
        public int InlineIndexCount => _inlineIndexCount;

        /// <summary>
        /// Gets the handle for the host buffer currently holding the inline index buffer data.
        /// </summary>
        /// <returns>Host buffer handle</returns>
        public BufferHandle GetInlineIndexBuffer()
        {
            return _inlineIndexBuffer;
        }

        /// <summary>
        /// Gets the number of elements on the current inline index buffer,
        /// while also reseting it to zero for the next draw.
        /// </summary>
        /// <returns>Inline index bufffer count</returns>
        public int GetAndResetInlineIndexCount()
        {
            int temp = _inlineIndexCount;
            _inlineIndexCount = 0;
            return temp;
        }

        /// <summary>
        /// Pushes four 8-bit index buffer elements.
        /// </summary>
        /// <param name="renderer">Host renderer</param>
        /// <param name="argument">Method call argument</param>
        public void VbElementU8(IRenderer renderer, int argument)
        {
            byte i0 = (byte)argument;
            byte i1 = (byte)(argument >> 8);
            byte i2 = (byte)(argument >> 16);
            byte i3 = (byte)(argument >> 24);

            Span<uint> data = stackalloc uint[4];

            data[0] = i0;
            data[1] = i1;
            data[2] = i2;
            data[3] = i3;

            int offset = _inlineIndexCount * 4;

            renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data));

            _inlineIndexCount += 4;
        }

        /// <summary>
        /// Pushes two 16-bit index buffer elements.
        /// </summary>
        /// <param name="renderer">Host renderer</param>
        /// <param name="argument">Method call argument</param>
        public void VbElementU16(IRenderer renderer, int argument)
        {
            ushort i0 = (ushort)argument;
            ushort i1 = (ushort)(argument >> 16);

            Span<uint> data = stackalloc uint[2];

            data[0] = i0;
            data[1] = i1;

            int offset = _inlineIndexCount * 4;

            renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data));

            _inlineIndexCount += 2;
        }

        /// <summary>
        /// Pushes one 32-bit index buffer element.
        /// </summary>
        /// <param name="renderer">Host renderer</param>
        /// <param name="argument">Method call argument</param>
        public void VbElementU32(IRenderer renderer, int argument)
        {
            uint i0 = (uint)argument;

            Span<uint> data = stackalloc uint[1];

            data[0] = i0;

            int offset = _inlineIndexCount++ * 4;

            renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data));
        }

        /// <summary>
        /// Gets the handle of a buffer large enough to hold the data that will be written to <paramref name="offset"/>.
        /// </summary>
        /// <param name="renderer">Host renderer</param>
        /// <param name="offset">Offset where the data will be written</param>
        /// <returns>Buffer handle</returns>
        private BufferHandle GetInlineIndexBuffer(IRenderer renderer, int offset)
        {
            // Calculate a reasonable size for the buffer that can fit all the data,
            // and that also won't require frequent resizes if we need to push more data.
            int size = BitUtils.AlignUp(offset + 0x10, 0x200);

            if (_inlineIndexBuffer == BufferHandle.Null)
            {
                _inlineIndexBuffer = renderer.CreateBuffer(size);
                _inlineIndexBufferSize = size;
            }
            else if (_inlineIndexBufferSize < size)
            {
                BufferHandle oldBuffer = _inlineIndexBuffer;
                int oldSize = _inlineIndexBufferSize;

                _inlineIndexBuffer = renderer.CreateBuffer(size);
                _inlineIndexBufferSize = size;

                renderer.Pipeline.CopyBuffer(oldBuffer, _inlineIndexBuffer, 0, 0, oldSize);
                renderer.DeleteBuffer(oldBuffer);
            }

            return _inlineIndexBuffer;
        }
    }
}