/ src / Ryujinx.Graphics.Gpu / Engine / Threed / ConstantBufferUpdater.cs
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  }