using System; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.State { /// /// GPU state. /// class GpuState { private const int RegistersCount = 0xe00; public delegate void MethodCallback(GpuState state, int argument); private int[] _backingMemory; /// /// GPU register information. /// private struct Register { public MethodCallback Callback; public MethodOffset BaseOffset; public int Stride; public int Count; public bool Modified; } private Register[] _registers; /// /// Creates a new instance of the GPU state. /// public GpuState() { _backingMemory = new int[RegistersCount]; _registers = new Register[RegistersCount]; for (int index = 0; index < _registers.Length; index++) { _registers[index].BaseOffset = (MethodOffset)index; _registers[index].Stride = 1; _registers[index].Count = 1; _registers[index].Modified = true; } foreach (var item in GpuStateTable.Table) { int totalRegs = item.Size * item.Count; for (int regOffset = 0; regOffset < totalRegs; regOffset++) { int index = (int)item.Offset + regOffset; _registers[index].BaseOffset = item.Offset; _registers[index].Stride = item.Size; _registers[index].Count = item.Count; } } InitializeDefaultState(); } /// /// Calls a GPU method, using this state. /// /// The GPU method to be called public void CallMethod(MethodParams meth) { Register register = _registers[meth.Method]; if (_backingMemory[meth.Method] != meth.Argument) { _registers[(int)register.BaseOffset].Modified = true; } _backingMemory[meth.Method] = meth.Argument; register.Callback?.Invoke(this, meth.Argument); } /// /// Reads data from a GPU register at the given offset. /// /// Offset to be read /// Data at the register public int Read(int offset) { return _backingMemory[offset]; } /// /// Writes data to the GPU register at the given offset. /// /// Offset to be written /// Value to be written public void Write(int offset, int value) { _backingMemory[offset] = value; } /// /// Writes an offset value at the uniform buffer offset register. /// /// The offset to be written public void SetUniformBufferOffset(int offset) { _backingMemory[(int)MethodOffset.UniformBufferState + 3] = offset; } /// /// Initializes registers with the default state. /// private void InitializeDefaultState() { // Enable Rasterizer _backingMemory[(int)MethodOffset.RasterizeEnable] = 1; // Depth ranges. for (int index = 0; index < Constants.TotalViewports; index++) { _backingMemory[(int)MethodOffset.ViewportExtents + index * 4 + 2] = 0; _backingMemory[(int)MethodOffset.ViewportExtents + index * 4 + 3] = 0x3F800000; } // Viewport transform enable. _backingMemory[(int)MethodOffset.ViewportTransformEnable] = 1; // Default front stencil mask. _backingMemory[0x4e7] = 0xff; // Default color mask. for (int index = 0; index < Constants.TotalRenderTargets; index++) { _backingMemory[(int)MethodOffset.RtColorMask + index] = 0x1111; } } /// /// Registers a callback that is called every time a GPU method, or methods are called. /// /// Offset of the method /// Word count of the methods region /// Calllback to be called public void RegisterCallback(MethodOffset offset, int count, MethodCallback callback) { for (int index = 0; index < count; index++) { _registers[(int)offset + index].Callback = callback; } } /// /// Registers a callback that is called every time a GPU method is called. /// /// Offset of the method /// Calllback to be called public void RegisterCallback(MethodOffset offset, MethodCallback callback) { _registers[(int)offset].Callback = callback; } /// /// Checks if a given register has been modified since the last call to this method. /// /// Register offset /// True if modified, false otherwise public bool QueryModified(MethodOffset offset) { bool modified = _registers[(int)offset].Modified; _registers[(int)offset].Modified = false; return modified; } /// /// Checks if two registers have been modified since the last call to this method. /// /// First register offset /// Second register offset /// True if any register was modified, false otherwise public bool QueryModified(MethodOffset m1, MethodOffset m2) { bool modified = _registers[(int)m1].Modified || _registers[(int)m2].Modified; _registers[(int)m1].Modified = false; _registers[(int)m2].Modified = false; return modified; } /// /// Checks if two registers have been modified since the last call to this method. /// /// First register offset /// Second register offset /// Third register offset /// True if any register was modified, false otherwise public bool QueryModified(MethodOffset m1, MethodOffset m2, MethodOffset m3) { bool modified = _registers[(int)m1].Modified || _registers[(int)m2].Modified || _registers[(int)m3].Modified; _registers[(int)m1].Modified = false; _registers[(int)m2].Modified = false; _registers[(int)m3].Modified = false; return modified; } /// /// Checks if two registers have been modified since the last call to this method. /// /// First register offset /// Second register offset /// Third register offset /// Fourth register offset /// True if any register was modified, false otherwise public bool QueryModified(MethodOffset m1, MethodOffset m2, MethodOffset m3, MethodOffset m4) { bool modified = _registers[(int)m1].Modified || _registers[(int)m2].Modified || _registers[(int)m3].Modified || _registers[(int)m4].Modified; _registers[(int)m1].Modified = false; _registers[(int)m2].Modified = false; _registers[(int)m3].Modified = false; _registers[(int)m4].Modified = false; return modified; } /// /// Checks if two registers have been modified since the last call to this method. /// /// First register offset /// Second register offset /// Third register offset /// Fourth register offset /// Fifth register offset /// True if any register was modified, false otherwise public bool QueryModified( MethodOffset m1, MethodOffset m2, MethodOffset m3, MethodOffset m4, MethodOffset m5) { bool modified = _registers[(int)m1].Modified || _registers[(int)m2].Modified || _registers[(int)m3].Modified || _registers[(int)m4].Modified || _registers[(int)m5].Modified; _registers[(int)m1].Modified = false; _registers[(int)m2].Modified = false; _registers[(int)m3].Modified = false; _registers[(int)m4].Modified = false; _registers[(int)m5].Modified = false; return modified; } /// /// Gets indexed data from a given register offset. /// /// Type of the data /// Register offset /// Index for indexed data /// The data at the specified location public T Get(MethodOffset offset, int index) where T : struct { Register register = _registers[(int)offset]; if ((uint)index >= register.Count) { throw new ArgumentOutOfRangeException(nameof(index)); } return Get(offset + index * register.Stride); } /// /// Gets data from a given register offset. /// /// Type of the data /// Register offset /// The data at the specified location public T Get(MethodOffset offset) where T : struct { return MemoryMarshal.Cast(_backingMemory.AsSpan().Slice((int)offset))[0]; } } }