using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Image; using System; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.State { class GpuState { private const int RegistersCount = 0xe00; public delegate void MethodCallback(int argument); private int[] _backingMemory; private struct Register { public MethodCallback Callback; public StateWriteFlags WriteFlag; } private Register[] _registers; public StateWriteFlags StateWriteFlags { get; set; } public GpuState() { _backingMemory = new int[RegistersCount]; _registers = new Register[RegistersCount]; StateWriteFlags = StateWriteFlags.Any; InitializeDefaultState(); InitializeStateWatchers(); } public bool ExitEarly; public void CallMethod(MethodParams meth) { if (ExitEarly) { return; } Register register = _registers[meth.Method]; if (_backingMemory[meth.Method] != meth.Argument) { StateWriteFlags |= register.WriteFlag; } _backingMemory[meth.Method] = meth.Argument; MethodCallback callback = register.Callback; if (callback != null) { callback(meth.Argument); } } public int Read(int offset) { return _backingMemory[offset]; } public void RegisterCopyBufferCallback(MethodCallback callback) { RegisterCallback(0xc0, callback); } public void RegisterCopyTextureCallback(MethodCallback callback) { RegisterCallback(0x237, callback); } public void RegisterDrawEndCallback(MethodCallback callback) { RegisterCallback(0x585, callback); } public void RegisterDrawBeginCallback(MethodCallback callback) { RegisterCallback(0x586, callback); } public void RegisterSetIndexCountCallback(MethodCallback callback) { RegisterCallback(0x5f8, callback); } public void RegisterClearCallback(MethodCallback callback) { RegisterCallback(0x674, callback); } public void RegisterReportCallback(MethodCallback callback) { RegisterCallback(0x6c3, callback); } public void RegisterUniformBufferUpdateCallback(MethodCallback callback) { for (int index = 0; index < 16; index++) { RegisterCallback(0x8e4 + index, callback); } } public void RegisterUniformBufferBind0Callback(MethodCallback callback) { RegisterCallback(0x904, callback); } public void RegisterUniformBufferBind1Callback(MethodCallback callback) { RegisterCallback(0x90c, callback); } public void RegisterUniformBufferBind2Callback(MethodCallback callback) { RegisterCallback(0x914, callback); } public void RegisterUniformBufferBind3Callback(MethodCallback callback) { RegisterCallback(0x91c, callback); } public void RegisterUniformBufferBind4Callback(MethodCallback callback) { RegisterCallback(0x924, callback); } public CopyTexture GetCopyDstTexture() { return Get(MethodOffset.CopyDstTexture); } public CopyTexture GetCopySrcTexture() { return Get(MethodOffset.CopySrcTexture); } public RtColorState GetRtColorState(int index) { return Get(MethodOffset.RtColorState + 16 * index); } public CopyTextureControl GetCopyTextureControl() { return Get(MethodOffset.CopyTextureControl); } public CopyRegion GetCopyRegion() { return Get(MethodOffset.CopyRegion); } public ViewportTransform GetViewportTransform(int index) { return Get(MethodOffset.ViewportTransform + 8 * index); } public ViewportExtents GetViewportExtents(int index) { return Get(MethodOffset.ViewportExtents + 4 * index); } public VertexBufferDrawState GetVertexBufferDrawState() { return Get(MethodOffset.VertexBufferDrawState); } public ClearColors GetClearColors() { return Get(MethodOffset.ClearColors); } public float GetClearDepthValue() { return Get(MethodOffset.ClearDepthValue); } public int GetClearStencilValue() { return _backingMemory[(int)MethodOffset.ClearStencilValue]; } public StencilBackMasks GetStencilBackMasks() { return Get(MethodOffset.StencilBackMasks); } public RtDepthStencilState GetRtDepthStencilState() { return Get(MethodOffset.RtDepthStencilState); } public VertexAttribState GetVertexAttribState(int index) { return Get(MethodOffset.VertexAttribState + index); } public Size3D GetRtDepthStencilSize() { return Get(MethodOffset.RtDepthStencilSize); } public Bool GetDepthTestEnable() { return Get(MethodOffset.DepthTestEnable); } public CompareOp GetDepthTestFunc() { return Get(MethodOffset.DepthTestFunc); } public Bool GetDepthWriteEnable() { return Get(MethodOffset.DepthWriteEnable); } public Bool GetBlendEnable(int index) { return Get(MethodOffset.BlendEnable + index); } public StencilTestState GetStencilTestState() { return Get(MethodOffset.StencilTestState); } public int GetBaseVertex() { return _backingMemory[(int)MethodOffset.FirstVertex]; } public int GetBaseInstance() { return _backingMemory[(int)MethodOffset.FirstInstance]; } public PoolState GetSamplerPoolState() { return Get(MethodOffset.SamplerPoolState); } public PoolState GetTexturePoolState() { return Get(MethodOffset.TexturePoolState); } public StencilBackTestState GetStencilBackTestState() { return Get(MethodOffset.StencilBackTestState); } public TextureMsaaMode GetRtMsaaMode() { return Get(MethodOffset.RtMsaaMode); } public GpuVa GetShaderBaseAddress() { return Get(MethodOffset.ShaderBaseAddress); } public PrimitiveRestartState GetPrimitiveRestartState() { return Get(MethodOffset.PrimitiveRestartState); } public IndexBufferState GetIndexBufferState() { return Get(MethodOffset.IndexBufferState); } public FaceState GetFaceState() { return Get(MethodOffset.FaceState); } public ReportState GetReportState() { return Get(MethodOffset.ReportState); } public VertexBufferState GetVertexBufferState(int index) { return Get(MethodOffset.VertexBufferState + 4 * index); } public BlendState GetBlendState(int index) { return Get(MethodOffset.BlendState + 8 * index); } public GpuVa GetVertexBufferEndAddress(int index) { return Get(MethodOffset.VertexBufferEndAddress + 2 * index); } public ShaderState GetShaderState(int index) { return Get(MethodOffset.ShaderState + 16 * index); } public UniformBufferState GetUniformBufferState() { return Get(MethodOffset.UniformBufferState); } public void SetUniformBufferOffset(int offset) { _backingMemory[(int)MethodOffset.UniformBufferState + 3] = offset; } public int GetTextureBufferIndex() { return _backingMemory[(int)MethodOffset.TextureBufferIndex]; } private void InitializeDefaultState() { // Depth ranges. for (int index = 0; index < 8; index++) { _backingMemory[(int)MethodOffset.ViewportExtents + index * 4 + 2] = 0; _backingMemory[(int)MethodOffset.ViewportExtents + index * 4 + 3] = 0x3F800000; } // Default front stencil mask. _backingMemory[0x4e7] = 0xff; // Default color mask. _backingMemory[(int)MethodOffset.RtColorMask] = 0x1111; } private void InitializeStateWatchers() { SetWriteStateFlag(MethodOffset.RtColorState, StateWriteFlags.RtColorState, 16 * 8); SetWriteStateFlag(MethodOffset.ViewportTransform, StateWriteFlags.ViewportTransform, 8 * 8); SetWriteStateFlag(MethodOffset.ViewportExtents, StateWriteFlags.ViewportTransform, 4 * 8); SetWriteStateFlag(MethodOffset.VertexBufferDrawState, StateWriteFlags.VertexBufferState); SetWriteStateFlag(MethodOffset.DepthBiasState, StateWriteFlags.DepthBiasState); SetWriteStateFlag(MethodOffset.DepthBiasFactor, StateWriteFlags.DepthBiasState, 1); SetWriteStateFlag(MethodOffset.DepthBiasUnits, StateWriteFlags.DepthBiasState, 1); SetWriteStateFlag(MethodOffset.DepthBiasClamp, StateWriteFlags.DepthBiasState, 1); SetWriteStateFlag(MethodOffset.RtDepthStencilState, StateWriteFlags.RtDepthStencilState); SetWriteStateFlag (MethodOffset.RtDepthStencilSize, StateWriteFlags.RtDepthStencilState); SetWriteStateFlag(MethodOffset.DepthTestEnable, StateWriteFlags.DepthTestState, 1); SetWriteStateFlag(MethodOffset.DepthWriteEnable, StateWriteFlags.DepthTestState, 1); SetWriteStateFlag(MethodOffset.DepthTestFunc, StateWriteFlags.DepthTestState, 1); SetWriteStateFlag(MethodOffset.VertexAttribState, StateWriteFlags.VertexAttribState, 16); SetWriteStateFlag (MethodOffset.StencilBackMasks, StateWriteFlags.StencilTestState); SetWriteStateFlag (MethodOffset.StencilTestState, StateWriteFlags.StencilTestState); SetWriteStateFlag(MethodOffset.StencilBackTestState, StateWriteFlags.StencilTestState); SetWriteStateFlag(MethodOffset.SamplerPoolState, StateWriteFlags.SamplerPoolState); SetWriteStateFlag(MethodOffset.TexturePoolState, StateWriteFlags.TexturePoolState); SetWriteStateFlag(MethodOffset.ShaderBaseAddress, StateWriteFlags.ShaderState); SetWriteStateFlag(MethodOffset.PrimitiveRestartState, StateWriteFlags.PrimitiveRestartState); SetWriteStateFlag(MethodOffset.IndexBufferState, StateWriteFlags.IndexBufferState); SetWriteStateFlag(MethodOffset.FaceState, StateWriteFlags.FaceState); SetWriteStateFlag(MethodOffset.RtColorMask, StateWriteFlags.RtColorMask); SetWriteStateFlag(MethodOffset.VertexBufferInstanced, StateWriteFlags.VertexBufferState, 16); SetWriteStateFlag(MethodOffset.VertexBufferState, StateWriteFlags.VertexBufferState, 4 * 16); SetWriteStateFlag(MethodOffset.VertexBufferEndAddress, StateWriteFlags.VertexBufferState, 2 * 16); SetWriteStateFlag(MethodOffset.BlendEnable, StateWriteFlags.BlendState, 8); SetWriteStateFlag(MethodOffset.BlendState, StateWriteFlags.BlendState, 8 * 8); SetWriteStateFlag(MethodOffset.ShaderState, StateWriteFlags.ShaderState, 16 * 6); SetWriteStateFlag(MethodOffset.TextureBufferIndex, StateWriteFlags.TexturePoolState, 1); } private void SetWriteStateFlag(MethodOffset offset, StateWriteFlags flag) { SetWriteStateFlag(offset, flag, Marshal.SizeOf()); } private void SetWriteStateFlag(MethodOffset offset, StateWriteFlags flag, int size) { for (int index = 0; index < size; index++) { _registers[(int)offset + index].WriteFlag = flag; } } public void RegisterCallback(MethodOffset offset, MethodCallback callback) { _registers[(int)offset].Callback = callback; } private void RegisterCallback(int offset, MethodCallback callback) { _registers[offset].Callback = callback; } public T Get(MethodOffset offset) where T : struct { return MemoryMarshal.Cast(_backingMemory.AsSpan().Slice((int)offset))[0]; } } }