ConstantBufferUpdater.cs
1 using System; 2 using System.Runtime.InteropServices; 3 4 namespace Ryujinx.Graphics.Gpu.Engine.Threed 5 { 6 /// <summary> 7 /// Constant buffer updater. 8 /// </summary> 9 class ConstantBufferUpdater 10 { 11 private const int UniformDataCacheSize = 512; 12 13 private readonly GpuChannel _channel; 14 private readonly DeviceStateWithShadow<ThreedClassState> _state; 15 16 // State associated with direct uniform buffer updates. 17 // This state is used to attempt to batch together consecutive updates. 18 private ulong _ubBeginCpuAddress = 0; 19 private ulong _ubFollowUpAddress = 0; 20 private ulong _ubByteCount = 0; 21 private int _ubIndex = 0; 22 private readonly int[] _ubData = new int[UniformDataCacheSize]; 23 24 /// <summary> 25 /// Creates a new instance of the constant buffer updater. 26 /// </summary> 27 /// <param name="channel">GPU channel</param> 28 /// <param name="state">Channel state</param> 29 public ConstantBufferUpdater(GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state) 30 { 31 _channel = channel; 32 _state = state; 33 } 34 35 /// <summary> 36 /// Binds a uniform buffer for the vertex shader stage. 37 /// </summary> 38 /// <param name="argument">Method call argument</param> 39 public void BindVertex(int argument) 40 { 41 Bind(argument, ShaderType.Vertex); 42 } 43 44 /// <summary> 45 /// Binds a uniform buffer for the tessellation control shader stage. 46 /// </summary> 47 /// <param name="argument">Method call argument</param> 48 public void BindTessControl(int argument) 49 { 50 Bind(argument, ShaderType.TessellationControl); 51 } 52 53 /// <summary> 54 /// Binds a uniform buffer for the tessellation evaluation shader stage. 55 /// </summary> 56 /// <param name="argument">Method call argument</param> 57 public void BindTessEvaluation(int argument) 58 { 59 Bind(argument, ShaderType.TessellationEvaluation); 60 } 61 62 /// <summary> 63 /// Binds a uniform buffer for the geometry shader stage. 64 /// </summary> 65 /// <param name="argument">Method call argument</param> 66 public void BindGeometry(int argument) 67 { 68 Bind(argument, ShaderType.Geometry); 69 } 70 71 /// <summary> 72 /// Binds a uniform buffer for the fragment shader stage. 73 /// </summary> 74 /// <param name="argument">Method call argument</param> 75 public void BindFragment(int argument) 76 { 77 Bind(argument, ShaderType.Fragment); 78 } 79 80 /// <summary> 81 /// Binds a uniform buffer for the specified shader stage. 82 /// </summary> 83 /// <param name="argument">Method call argument</param> 84 /// <param name="type">Shader stage that will access the uniform buffer</param> 85 private void Bind(int argument, ShaderType type) 86 { 87 bool enable = (argument & 1) != 0; 88 89 int index = (argument >> 4) & 0x1f; 90 91 FlushUboDirty(); 92 93 if (enable) 94 { 95 var uniformBuffer = _state.State.UniformBufferState; 96 97 ulong address = uniformBuffer.Address.Pack(); 98 99 _channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, address, (uint)uniformBuffer.Size); 100 } 101 else 102 { 103 _channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, 0, 0); 104 } 105 } 106 107 /// <summary> 108 /// Flushes any queued UBO updates. 109 /// </summary> 110 public void FlushUboDirty() 111 { 112 if (_ubFollowUpAddress != 0) 113 { 114 var memoryManager = _channel.MemoryManager; 115 116 Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4))); 117 118 if (memoryManager.Physical.WriteWithRedundancyCheck(_ubBeginCpuAddress, data)) 119 { 120 memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount); 121 } 122 123 _ubFollowUpAddress = 0; 124 _ubIndex = 0; 125 } 126 } 127 128 /// <summary> 129 /// Updates the uniform buffer data with inline data. 130 /// </summary> 131 /// <param name="argument">New uniform buffer data word</param> 132 public void Update(int argument) 133 { 134 var uniformBuffer = _state.State.UniformBufferState; 135 136 ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset; 137 138 if (_ubFollowUpAddress != address || _ubIndex == _ubData.Length) 139 { 140 FlushUboDirty(); 141 142 _ubByteCount = 0; 143 _ubBeginCpuAddress = _channel.MemoryManager.Translate(address); 144 } 145 146 _ubData[_ubIndex++] = argument; 147 148 _ubFollowUpAddress = address + 4; 149 _ubByteCount += 4; 150 151 _state.State.UniformBufferState.Offset += 4; 152 } 153 154 /// <summary> 155 /// Updates the uniform buffer data with inline data. 156 /// </summary> 157 /// <param name="data">Data to be written to the uniform buffer</param> 158 public void Update(ReadOnlySpan<int> data) 159 { 160 var uniformBuffer = _state.State.UniformBufferState; 161 162 ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset; 163 164 ulong size = (ulong)data.Length * 4; 165 166 if (_ubFollowUpAddress != address || _ubIndex + data.Length > _ubData.Length) 167 { 168 FlushUboDirty(); 169 170 _ubByteCount = 0; 171 _ubBeginCpuAddress = _channel.MemoryManager.Translate(address); 172 } 173 174 data.CopyTo(_ubData.AsSpan(_ubIndex)); 175 _ubIndex += data.Length; 176 177 _ubFollowUpAddress = address + size; 178 _ubByteCount += size; 179 180 _state.State.UniformBufferState.Offset += data.Length * 4; 181 } 182 } 183 }