aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Vulkan/IndexBufferPattern.cs
blob: 90774293169c96e74b2f59b9de87a5483e04a8b0 (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
using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Ryujinx.Graphics.Vulkan
{
    internal class IndexBufferPattern : IDisposable
    {
        public int PrimitiveVertices { get; }
        public int PrimitiveVerticesOut { get; }
        public int BaseIndex { get; }
        public int[] OffsetIndex { get; }
        public int IndexStride { get; }
        public bool RepeatStart { get; }

        private VulkanRenderer _gd;
        private int _currentSize;
        private BufferHandle _repeatingBuffer;

        public IndexBufferPattern(VulkanRenderer gd,
            int primitiveVertices,
            int primitiveVerticesOut,
            int baseIndex,
            int[] offsetIndex,
            int indexStride,
            bool repeatStart)
        {
            PrimitiveVertices = primitiveVertices;
            PrimitiveVerticesOut = primitiveVerticesOut;
            BaseIndex = baseIndex;
            OffsetIndex = offsetIndex;
            IndexStride = indexStride;
            RepeatStart = repeatStart;

            _gd = gd;
        }

        public int GetPrimitiveCount(int vertexCount)
        {
            return Math.Max(0, (vertexCount - BaseIndex) / IndexStride);
        }

        public int GetConvertedCount(int indexCount)
        {
            int primitiveCount = GetPrimitiveCount(indexCount);
            return primitiveCount * OffsetIndex.Length;
        }

        public IEnumerable<int> GetIndexMapping(int indexCount)
        {
            int primitiveCount = GetPrimitiveCount(indexCount);
            int index = BaseIndex;

            for (int i = 0; i < primitiveCount; i++)
            {
                if (RepeatStart)
                {
                    // Used for triangle fan
                    yield return 0;
                }

                for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
                {
                    yield return index + OffsetIndex[j];
                }

                index += IndexStride;
            }
        }

        public BufferHandle GetRepeatingBuffer(int vertexCount, out int indexCount)
        {
            int primitiveCount = GetPrimitiveCount(vertexCount);
            indexCount = primitiveCount * PrimitiveVerticesOut;

            int expectedSize = primitiveCount * OffsetIndex.Length;

            if (expectedSize <= _currentSize && _repeatingBuffer != BufferHandle.Null)
            {
                return _repeatingBuffer;
            }

            // Expand the repeating pattern to the number of requested primitives.
            BufferHandle newBuffer = _gd.CreateBuffer(expectedSize * sizeof(int));

            // Copy the old data to the new one.
            if (_repeatingBuffer != BufferHandle.Null)
            {
                _gd.Pipeline.CopyBuffer(_repeatingBuffer, newBuffer, 0, 0, _currentSize * sizeof(int));
                _gd.DeleteBuffer(_repeatingBuffer);
            }

            _repeatingBuffer = newBuffer;

            // Add the additional repeats on top.
            int newPrimitives = primitiveCount;
            int oldPrimitives = (_currentSize) / OffsetIndex.Length;

            int[] newData;

            newPrimitives -= oldPrimitives;
            newData = new int[expectedSize - _currentSize];

            int outOffset = 0;
            int index = oldPrimitives * IndexStride + BaseIndex;

            for (int i = 0; i < newPrimitives; i++)
            {
                if (RepeatStart)
                {
                    // Used for triangle fan
                    newData[outOffset++] = 0;
                }

                for (int j = RepeatStart ? 1 : 0; j < OffsetIndex.Length; j++)
                {
                    newData[outOffset++] = index + OffsetIndex[j];
                }

                index += IndexStride;
            }

            _gd.SetBufferData(newBuffer, _currentSize * sizeof(int), MemoryMarshal.Cast<int, byte>(newData));
            _currentSize = expectedSize;

            return newBuffer;
        }

        public void Dispose()
        {
            if (_repeatingBuffer != BufferHandle.Null)
            {
                _gd.DeleteBuffer(_repeatingBuffer);
                _repeatingBuffer = BufferHandle.Null;
            }
        }
    }
}