aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Vulkan/VertexBufferState.cs
blob: 7a022010809dc2482e481790624aba6038c41eae (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
using BufferHandle = Ryujinx.Graphics.GAL.BufferHandle;

namespace Ryujinx.Graphics.Vulkan
{
    internal struct VertexBufferState
    {
        public static VertexBufferState Null => new VertexBufferState(null, 0, 0, 0);

        private readonly int _offset;
        private readonly int _size;
        private readonly int _stride;

        private readonly BufferHandle _handle;
        private Auto<DisposableBuffer> _buffer;

        internal readonly int DescriptorIndex;
        internal int AttributeScalarAlignment;

        public VertexBufferState(Auto<DisposableBuffer> buffer, int descriptorIndex, int offset, int size, int stride = 0)
        {
            _buffer = buffer;
            _handle = BufferHandle.Null;

            _offset = offset;
            _size = size;
            _stride = stride;

            DescriptorIndex = descriptorIndex;
            AttributeScalarAlignment = 1;

            buffer?.IncrementReferenceCount();
        }

        public VertexBufferState(BufferHandle handle, int descriptorIndex, int offset, int size, int stride = 0)
        {
            // This buffer state may be rewritten at bind time, so it must be retrieved on bind.

            _buffer = null;
            _handle = handle;

            _offset = offset;
            _size = size;
            _stride = stride;

            DescriptorIndex = descriptorIndex;
            AttributeScalarAlignment = 1;
        }

        public void BindVertexBuffer(VulkanRenderer gd, CommandBufferScoped cbs, uint binding, ref PipelineState state)
        {
            var autoBuffer = _buffer;

            if (_handle != BufferHandle.Null)
            {
                // May need to restride the vertex buffer.

                if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0)
                {
                    autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment);

                    if (autoBuffer != null)
                    {
                        int stride = (_stride + (alignment - 1)) & -alignment;
                        int newSize = (_size / _stride) * stride;

                        var buffer = autoBuffer.Get(cbs, 0, newSize).Value;

                        if (gd.Capabilities.SupportsExtendedDynamicState)
                        {
                            gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
                                cbs.CommandBuffer,
                                binding,
                                1,
                                buffer,
                                0,
                                (ulong)newSize,
                                (ulong)stride);
                        }
                        else
                        {
                            gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, 0);
                        }

                        _buffer = autoBuffer;

                        state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
                    }

                    return;
                }
                else
                {
                    autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size);

                    // The original stride must be reapplied in case it was rewritten.
                    state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;

                    if (_offset >= size)
                    {
                        autoBuffer = null;
                    }
                }
            }

            if (autoBuffer != null)
            {
                var buffer = autoBuffer.Get(cbs, _offset, _size).Value;

                if (gd.Capabilities.SupportsExtendedDynamicState)
                {
                    gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
                        cbs.CommandBuffer,
                        binding,
                        1,
                        buffer,
                        (ulong)_offset,
                        (ulong)_size,
                        (ulong)_stride);
                }
                else
                {
                    gd.Api.CmdBindVertexBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset);
                }
            }
        }

        public bool BoundEquals(Auto<DisposableBuffer> buffer)
        {
            return _buffer == buffer;
        }

        public void Dispose()
        {
            // Only dispose if this buffer is not refetched on each bind.

            if (_handle == BufferHandle.Null)
            {
                _buffer?.DecrementReferenceCount();
            }
        }
    }
}