| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- using System;
- using System.Runtime.InteropServices;
- namespace Ryujinx.Graphics.Gpu.Engine.Threed
- {
- /// <summary>
- /// Constant buffer updater.
- /// </summary>
- class ConstantBufferUpdater
- {
- private const int UniformDataCacheSize = 512;
- private readonly GpuChannel _channel;
- private readonly DeviceStateWithShadow<ThreedClassState> _state;
- // State associated with direct uniform buffer updates.
- // This state is used to attempt to batch together consecutive updates.
- private ulong _ubBeginCpuAddress = 0;
- private ulong _ubFollowUpAddress = 0;
- private ulong _ubByteCount = 0;
- private int _ubIndex = 0;
- private int[] _ubData = new int[UniformDataCacheSize];
- /// <summary>
- /// Creates a new instance of the constant buffer updater.
- /// </summary>
- /// <param name="channel">GPU channel</param>
- /// <param name="state">Channel state</param>
- public ConstantBufferUpdater(GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state)
- {
- _channel = channel;
- _state = state;
- }
- /// <summary>
- /// Binds a uniform buffer for the vertex shader stage.
- /// </summary>
- /// <param name="argument">Method call argument</param>
- public void BindVertex(int argument)
- {
- Bind(argument, ShaderType.Vertex);
- }
- /// <summary>
- /// Binds a uniform buffer for the tessellation control shader stage.
- /// </summary>
- /// <param name="argument">Method call argument</param>
- public void BindTessControl(int argument)
- {
- Bind(argument, ShaderType.TessellationControl);
- }
- /// <summary>
- /// Binds a uniform buffer for the tessellation evaluation shader stage.
- /// </summary>
- /// <param name="argument">Method call argument</param>
- public void BindTessEvaluation(int argument)
- {
- Bind(argument, ShaderType.TessellationEvaluation);
- }
- /// <summary>
- /// Binds a uniform buffer for the geometry shader stage.
- /// </summary>
- /// <param name="argument">Method call argument</param>
- public void BindGeometry(int argument)
- {
- Bind(argument, ShaderType.Geometry);
- }
- /// <summary>
- /// Binds a uniform buffer for the fragment shader stage.
- /// </summary>
- /// <param name="argument">Method call argument</param>
- public void BindFragment(int argument)
- {
- Bind(argument, ShaderType.Fragment);
- }
- /// <summary>
- /// Binds a uniform buffer for the specified shader stage.
- /// </summary>
- /// <param name="argument">Method call argument</param>
- /// <param name="type">Shader stage that will access the uniform buffer</param>
- private void Bind(int argument, ShaderType type)
- {
- bool enable = (argument & 1) != 0;
- int index = (argument >> 4) & 0x1f;
- FlushUboDirty();
- if (enable)
- {
- var uniformBuffer = _state.State.UniformBufferState;
- ulong address = uniformBuffer.Address.Pack();
- _channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, address, (uint)uniformBuffer.Size);
- }
- else
- {
- _channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, 0, 0);
- }
- }
- /// <summary>
- /// Flushes any queued UBO updates.
- /// </summary>
- public void FlushUboDirty()
- {
- if (_ubFollowUpAddress != 0)
- {
- var memoryManager = _channel.MemoryManager;
- Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4)));
- if (memoryManager.Physical.WriteWithRedundancyCheck(_ubBeginCpuAddress, data))
- {
- memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
- }
- _ubFollowUpAddress = 0;
- _ubIndex = 0;
- }
- }
- /// <summary>
- /// Updates the uniform buffer data with inline data.
- /// </summary>
- /// <param name="argument">New uniform buffer data word</param>
- public void Update(int argument)
- {
- var uniformBuffer = _state.State.UniformBufferState;
- ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
- if (_ubFollowUpAddress != address || _ubIndex == _ubData.Length)
- {
- FlushUboDirty();
- _ubByteCount = 0;
- _ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
- }
- _ubData[_ubIndex++] = argument;
- _ubFollowUpAddress = address + 4;
- _ubByteCount += 4;
- _state.State.UniformBufferState.Offset += 4;
- }
- /// <summary>
- /// Updates the uniform buffer data with inline data.
- /// </summary>
- /// <param name="data">Data to be written to the uniform buffer</param>
- public void Update(ReadOnlySpan<int> data)
- {
- var uniformBuffer = _state.State.UniformBufferState;
- ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
- ulong size = (ulong)data.Length * 4;
- if (_ubFollowUpAddress != address || _ubIndex + data.Length > _ubData.Length)
- {
- FlushUboDirty();
- _ubByteCount = 0;
- _ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
- }
- data.CopyTo(_ubData.AsSpan(_ubIndex));
- _ubIndex += data.Length;
- _ubFollowUpAddress = address + size;
- _ubByteCount += size;
- _state.State.UniformBufferState.Offset += data.Length * 4;
- }
- }
- }
|