using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Shader;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
{
///
/// Vertex info buffer data updater.
///
class VertexInfoBufferUpdater : BufferUpdater
{
private VertexInfoBuffer _data;
///
/// Creates a new instance of the vertex info buffer updater.
///
/// Renderer that the vertex info buffer will be used with
public VertexInfoBufferUpdater(IRenderer renderer) : base(renderer)
{
}
///
/// Sets vertex data related counts.
///
/// Number of vertices used on the draw
/// Number of draw instances
/// Index of the first vertex on the vertex buffer
/// Index of the first instanced vertex on the vertex buffer
public void SetVertexCounts(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
{
if (_data.VertexCounts.X != vertexCount)
{
_data.VertexCounts.X = vertexCount;
MarkDirty(VertexInfoBuffer.VertexCountsOffset, sizeof(int));
}
if (_data.VertexCounts.Y != instanceCount)
{
_data.VertexCounts.Y = instanceCount;
MarkDirty(VertexInfoBuffer.VertexCountsOffset + sizeof(int), sizeof(int));
}
if (_data.VertexCounts.Z != firstVertex)
{
_data.VertexCounts.Z = firstVertex;
MarkDirty(VertexInfoBuffer.VertexCountsOffset + sizeof(int) * 2, sizeof(int));
}
if (_data.VertexCounts.W != firstInstance)
{
_data.VertexCounts.W = firstInstance;
MarkDirty(VertexInfoBuffer.VertexCountsOffset + sizeof(int) * 3, sizeof(int));
}
}
///
/// Sets vertex data related counts.
///
/// Number of primitives consumed by the geometry shader
public void SetGeometryCounts(int primitivesCount)
{
if (_data.GeometryCounts.X != primitivesCount)
{
_data.GeometryCounts.X = primitivesCount;
MarkDirty(VertexInfoBuffer.GeometryCountsOffset, sizeof(int));
}
}
///
/// Sets a vertex stride and related data.
///
/// Index of the vertex stride to be updated
/// Stride divided by the component or format size
/// Number of components that the format has
public void SetVertexStride(int index, int stride, int componentCount)
{
if (_data.VertexStrides[index].X != stride)
{
_data.VertexStrides[index].X = stride;
MarkDirty(VertexInfoBuffer.VertexStridesOffset + index * Unsafe.SizeOf>(), sizeof(int));
}
for (int c = 1; c < 4; c++)
{
int value = c < componentCount ? 1 : 0;
ref int currentValue = ref GetElementRef(ref _data.VertexStrides[index], c);
if (currentValue != value)
{
currentValue = value;
MarkDirty(VertexInfoBuffer.VertexStridesOffset + index * Unsafe.SizeOf>() + c * sizeof(int), sizeof(int));
}
}
}
///
/// Sets a vertex offset and related data.
///
/// Index of the vertex offset to be updated
/// Offset divided by the component or format size
/// If the draw is instanced, should have the vertex divisor value, otherwise should be zero
public void SetVertexOffset(int index, int offset, int divisor)
{
if (_data.VertexOffsets[index].X != offset)
{
_data.VertexOffsets[index].X = offset;
MarkDirty(VertexInfoBuffer.VertexOffsetsOffset + index * Unsafe.SizeOf>(), sizeof(int));
}
if (_data.VertexOffsets[index].Y != divisor)
{
_data.VertexOffsets[index].Y = divisor;
MarkDirty(VertexInfoBuffer.VertexOffsetsOffset + index * Unsafe.SizeOf>() + sizeof(int), sizeof(int));
}
}
///
/// Sets the offset of the index buffer.
///
/// Offset divided by the component size
public void SetIndexBufferOffset(int offset)
{
if (_data.GeometryCounts.W != offset)
{
_data.GeometryCounts.W = offset;
MarkDirty(VertexInfoBuffer.GeometryCountsOffset + sizeof(int) * 3, sizeof(int));
}
}
///
/// Submits all pending buffer updates to the GPU.
///
public void Commit()
{
Commit(MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref _data, 1)));
}
}
}