diff --git a/Ryujinx.Graphics.Device/AccessControl.cs b/Ryujinx.Graphics.Device/AccessControl.cs
deleted file mode 100644
index 02203783ac..0000000000
--- a/Ryujinx.Graphics.Device/AccessControl.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Ryujinx.Graphics.Device
-{
-    public enum AccessControl
-    {
-        None      = 0,
-        ReadOnly  = 1 << 0,
-        WriteOnly = 1 << 1,
-        ReadWrite = ReadOnly | WriteOnly
-    }
-}
diff --git a/Ryujinx.Graphics.Device/DeviceState.cs b/Ryujinx.Graphics.Device/DeviceState.cs
index 1001d29500..1810057159 100644
--- a/Ryujinx.Graphics.Device/DeviceState.cs
+++ b/Ryujinx.Graphics.Device/DeviceState.cs
@@ -1,10 +1,8 @@
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
-using System.Linq;
-using System.Reflection;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 
 namespace Ryujinx.Graphics.Device
 {
@@ -14,28 +12,22 @@ namespace Ryujinx.Graphics.Device
 
         public TState State;
 
-        private readonly BitArray _readableRegisters;
-        private readonly BitArray _writableRegisters;
+        private uint Size => (uint)(Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize;
 
-        private readonly Dictionary<int, Func<int>> _readCallbacks;
-        private readonly Dictionary<int, Action<int>> _writeCallbacks;
+        private readonly Func<int>[] _readCallbacks;
+        private readonly Action<int>[] _writeCallbacks;
 
-        private readonly Dictionary<int, string> _fieldNamesForDebug;
+        private readonly Dictionary<uint, string> _fieldNamesForDebug;
         private readonly Action<string> _debugLogCallback;
 
         public DeviceState(IReadOnlyDictionary<string, RwCallback> callbacks = null, Action<string> debugLogCallback = null)
         {
-            int size = (Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize;
-
-            _readableRegisters = new BitArray(size);
-            _writableRegisters = new BitArray(size);
-
-            _readCallbacks = new Dictionary<int, Func<int>>();
-            _writeCallbacks = new Dictionary<int, Action<int>>();
+            _readCallbacks = new Func<int>[Size];
+            _writeCallbacks = new Action<int>[Size];
 
             if (debugLogCallback != null)
             {
-                _fieldNamesForDebug = new Dictionary<int, string>();
+                _fieldNamesForDebug = new Dictionary<uint, string>();
                 _debugLogCallback = debugLogCallback;
             }
 
@@ -45,32 +37,30 @@ namespace Ryujinx.Graphics.Device
             for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
             {
                 var field = fields[fieldIndex];
-                var regAttr = field.GetCustomAttributes<RegisterAttribute>(false).FirstOrDefault();
 
                 int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
 
                 for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
                 {
-                    _readableRegisters[(offset + i) / RegisterSize] = regAttr?.AccessControl.HasFlag(AccessControl.ReadOnly)  ?? true;
-                    _writableRegisters[(offset + i) / RegisterSize] = regAttr?.AccessControl.HasFlag(AccessControl.WriteOnly) ?? true;
-                }
+                    int index = (offset + i) / RegisterSize;
 
-                if (callbacks != null && callbacks.TryGetValue(field.Name, out var cb))
-                {
-                    if (cb.Read != null)
+                    if (callbacks != null && callbacks.TryGetValue(field.Name, out var cb))
                     {
-                        _readCallbacks.Add(offset, cb.Read);
-                    }
+                        if (cb.Read != null)
+                        {
+                            _readCallbacks[index] = cb.Read;
+                        }
 
-                    if (cb.Write != null)
-                    {
-                        _writeCallbacks.Add(offset, cb.Write);
+                        if (cb.Write != null)
+                        {
+                            _writeCallbacks[index] = cb.Write;
+                        }
                     }
                 }
 
                 if (debugLogCallback != null)
                 {
-                    _fieldNamesForDebug.Add(offset, field.Name);
+                    _fieldNamesForDebug.Add((uint)offset, field.Name);
                 }
 
                 offset += sizeOfField;
@@ -79,48 +69,71 @@ namespace Ryujinx.Graphics.Device
             Debug.Assert(offset == Unsafe.SizeOf<TState>());
         }
 
-        public virtual int Read(int offset)
+        public int Read(int offset)
         {
-            if (Check(offset) && _readableRegisters[offset / RegisterSize])
-            {
-                int alignedOffset = Align(offset);
+            uint index = (uint)offset / RegisterSize;
 
-                if (_readCallbacks.TryGetValue(alignedOffset, out Func<int> read))
+            if (index < Size)
+            {
+                uint alignedOffset = index * RegisterSize;
+
+                var readCallback = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_readCallbacks), (IntPtr)index);
+                if (readCallback != null)
                 {
-                    return read();
+                    return readCallback();
                 }
                 else
                 {
-                    return GetRef<int>(alignedOffset);
+                    return GetRefUnchecked<int>(alignedOffset);
                 }
             }
 
             return 0;
         }
 
-        public virtual void Write(int offset, int data)
+        public void Write(int offset, int data)
         {
-            if (Check(offset) && _writableRegisters[offset / RegisterSize])
+            uint index = (uint)offset / RegisterSize;
+
+            if (index < Size)
             {
-                int alignedOffset = Align(offset);
+                uint alignedOffset = index * RegisterSize;
+                DebugWrite(alignedOffset, data);
 
-                if (_fieldNamesForDebug != null && _fieldNamesForDebug.TryGetValue(alignedOffset, out string fieldName))
-                {
-                    _debugLogCallback($"{typeof(TState).Name}.{fieldName} = 0x{data:X}");
-                }
+                GetRefIntAlignedUncheck(index) = data;
 
-                GetRef<int>(alignedOffset) = data;
-
-                if (_writeCallbacks.TryGetValue(alignedOffset, out Action<int> write))
-                {
-                    write(data);
-                }
+                Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data);
             }
         }
 
-        private bool Check(int offset)
+        public void WriteWithRedundancyCheck(int offset, int data, out bool changed)
         {
-            return (uint)Align(offset) < Unsafe.SizeOf<TState>();
+            uint index = (uint)offset / RegisterSize;
+
+            if (index < Size)
+            {
+                uint alignedOffset = index * RegisterSize;
+                DebugWrite(alignedOffset, data);
+
+                ref var storage = ref GetRefIntAlignedUncheck(index);
+                changed = storage != data;
+                storage = data;
+
+                Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data);
+            }
+            else
+            {
+                changed = false;
+            }
+        }
+
+        [Conditional("DEBUG")]
+        private void DebugWrite(uint alignedOffset, int data)
+        {
+            if (_fieldNamesForDebug != null && _fieldNamesForDebug.TryGetValue(alignedOffset, out string fieldName))
+            {
+                _debugLogCallback($"{typeof(TState).Name}.{fieldName} = 0x{data:X}");
+            }
         }
 
         public ref T GetRef<T>(int offset) where T : unmanaged
@@ -130,12 +143,19 @@ namespace Ryujinx.Graphics.Device
                 throw new ArgumentOutOfRangeException(nameof(offset));
             }
 
+            return ref GetRefUnchecked<T>((uint)offset);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private ref T GetRefUnchecked<T>(uint offset) where T : unmanaged
+        {
             return ref Unsafe.As<TState, T>(ref Unsafe.AddByteOffset(ref State, (IntPtr)offset));
         }
 
-        private static int Align(int offset)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private ref int GetRefIntAlignedUncheck(ulong index)
         {
-            return offset & ~(RegisterSize - 1);
+            return ref Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (IntPtr)index);
         }
     }
 }
diff --git a/Ryujinx.Graphics.Device/RegisterAttribute.cs b/Ryujinx.Graphics.Device/RegisterAttribute.cs
deleted file mode 100644
index 6e19896342..0000000000
--- a/Ryujinx.Graphics.Device/RegisterAttribute.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-
-namespace Ryujinx.Graphics.Device
-{
-    [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
-    public sealed class RegisterAttribute : Attribute
-    {
-        public AccessControl AccessControl { get; }
-
-        public RegisterAttribute(AccessControl ac)
-        {
-            AccessControl = ac;
-        }
-    }
-}
diff --git a/Ryujinx.Graphics.Device/SizeCalculator.cs b/Ryujinx.Graphics.Device/SizeCalculator.cs
index 7cc4891501..7409c04185 100644
--- a/Ryujinx.Graphics.Device/SizeCalculator.cs
+++ b/Ryujinx.Graphics.Device/SizeCalculator.cs
@@ -3,7 +3,7 @@ using System.Reflection;
 
 namespace Ryujinx.Graphics.Device
 {
-    static class SizeCalculator
+    public static class SizeCalculator
     {
         public static int SizeOf(Type type)
         {
diff --git a/Ryujinx.Graphics.Gpu/ClassId.cs b/Ryujinx.Graphics.Gpu/ClassId.cs
index 97f1e32b09..4e475a24d0 100644
--- a/Ryujinx.Graphics.Gpu/ClassId.cs
+++ b/Ryujinx.Graphics.Gpu/ClassId.cs
@@ -3,13 +3,13 @@ namespace Ryujinx.Graphics.Gpu
     /// <summary>
     /// GPU engine class ID.
     /// </summary>
-    enum ClassId
+    public enum ClassId
     {
-        Engine2D            = 0x902d,
-        Engine3D            = 0xb197,
-        EngineCompute       = 0xb1c0,
-        EngineInline2Memory = 0xa140,
-        EngineDma           = 0xb0b5,
-        EngineGpfifo        = 0xb06f
+        Twod = 0x902d,
+        Threed = 0xb197,
+        Compute = 0xb1c0,
+        InlineToMemory = 0xa140,
+        Dma = 0xb0b5,
+        GPFifo = 0xb06f
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
index 5d1fd2c0d3..ac8b1186bd 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
@@ -1,9 +1,10 @@
 using Ryujinx.Graphics.Device;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
+using Ryujinx.Graphics.Gpu.Engine.Threed;
+using Ryujinx.Graphics.Gpu.Engine.Types;
 using Ryujinx.Graphics.Gpu.Image;
 using Ryujinx.Graphics.Gpu.Shader;
-using Ryujinx.Graphics.Gpu.State;
 using Ryujinx.Graphics.Shader;
 using System;
 using System.Collections.Generic;
@@ -14,27 +15,34 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
     /// <summary>
     /// Represents a compute engine class.
     /// </summary>
-    class ComputeClass : InlineToMemoryClass, IDeviceState
+    class ComputeClass : IDeviceState
     {
         private readonly GpuContext _context;
         private readonly GpuChannel _channel;
+        private readonly ThreedClass _3dEngine;
         private readonly DeviceState<ComputeClassState> _state;
 
+        private readonly InlineToMemoryClass _i2mClass;
+
         /// <summary>
         /// Creates a new instance of the compute engine class.
         /// </summary>
         /// <param name="context">GPU context</param>
         /// <param name="channel">GPU channel</param>
-        public ComputeClass(GpuContext context, GpuChannel channel) : base(context, channel, false)
+        /// <param name="threedEngine">3D engine</param>
+        public ComputeClass(GpuContext context, GpuChannel channel, ThreedClass threedEngine)
         {
             _context = context;
             _channel = channel;
+            _3dEngine = threedEngine;
             _state = new DeviceState<ComputeClassState>(new Dictionary<string, RwCallback>
             {
                 { nameof(ComputeClassState.LaunchDma), new RwCallback(LaunchDma, null) },
                 { nameof(ComputeClassState.LoadInlineData), new RwCallback(LoadInlineData, null) },
                 { nameof(ComputeClassState.SendSignalingPcasB), new RwCallback(SendSignalingPcasB, null) }
             });
+
+            _i2mClass = new InlineToMemoryClass(context, channel, initializeState: false);
         }
 
         /// <summary>
@@ -42,22 +50,31 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
         /// </summary>
         /// <param name="offset">Register byte offset</param>
         /// <returns>Data at the specified offset</returns>
-        public override int Read(int offset) => _state.Read(offset);
+        public int Read(int offset) => _state.Read(offset);
 
         /// <summary>
         /// Writes data to the class registers.
         /// </summary>
         /// <param name="offset">Register byte offset</param>
         /// <param name="data">Data to be written</param>
-        public override void Write(int offset, int data) => _state.Write(offset, data);
+        public void Write(int offset, int data) => _state.Write(offset, data);
 
         /// <summary>
         /// Launches the Inline-to-Memory DMA copy operation.
         /// </summary>
         /// <param name="argument">Method call argument</param>
-        protected override void LaunchDma(int argument)
+        private void LaunchDma(int argument)
         {
-            LaunchDma(ref Unsafe.As<ComputeClassState, InlineToMemoryClassState>(ref _state.State), argument);
+            _i2mClass.LaunchDma(ref Unsafe.As<ComputeClassState, InlineToMemoryClassState>(ref _state.State), argument);
+        }
+
+        /// <summary>
+        /// Pushes a word of data to the Inline-to-Memory engine.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void LoadInlineData(int argument)
+        {
+            _i2mClass.LoadInlineData(argument);
         }
 
         /// <summary>
@@ -68,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
         {
             var memoryManager = _channel.MemoryManager;
 
-            _context.Methods.FlushUboDirty(memoryManager);
+            _3dEngine.FlushUboDirty();
 
             uint qmdAddress = _state.State.SendPcasA;
 
@@ -102,7 +119,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
                 texturePoolGpuVa,
                 _state.State.SetTexHeaderPoolCMaximumIndex,
                 _state.State.SetBindlessTextureConstantBufferSlotSelect,
-                false);
+                false,
+                PrimitiveTopology.Points);
 
             ShaderBundle cs = memoryManager.Physical.ShaderCache.GetComputeShader(
                 _channel,
@@ -207,7 +225,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
 
             _context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth);
 
-            _context.Methods.ForceShaderUpdate();
+            _3dEngine.ForceShaderUpdate();
         }
     }
 }
diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs
index a1116074a2..1b20e41c29 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Gpu.Engine.Types;
 using System;
 using System.Runtime.CompilerServices;
 
diff --git a/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs b/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs
index 451f496364..5581b5cc1b 100644
--- a/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs
@@ -1,5 +1,8 @@
 namespace Ryujinx.Graphics.Gpu.Engine
 {
+    /// <summary>
+    /// Conditional rendering enable.
+    /// </summary>
     enum ConditionalRenderEnabled
     {
         False,
diff --git a/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs b/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs
new file mode 100644
index 0000000000..3a06bc2a6a
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs
@@ -0,0 +1,95 @@
+using Ryujinx.Graphics.Device;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Graphics.Gpu.Engine
+{
+    /// <summary>
+    /// State interface with a shadow memory control register.
+    /// </summary>
+    interface IShadowState
+    {
+        /// <summary>
+        /// MME shadow ram control mode.
+        /// </summary>
+        SetMmeShadowRamControlMode SetMmeShadowRamControlMode { get; }
+    }
+
+    /// <summary>
+    /// Represents a device's state, with a additional shadow state.
+    /// </summary>
+    /// <typeparam name="TState">Type of the state</typeparam>
+    class DeviceStateWithShadow<TState> : IDeviceState where TState : unmanaged, IShadowState
+    {
+        private readonly DeviceState<TState> _state;
+        private readonly DeviceState<TState> _shadowState;
+
+        /// <summary>
+        /// Current device state.
+        /// </summary>
+        public ref TState State => ref _state.State;
+
+        /// <summary>
+        /// Creates a new instance of the device state, with shadow state.
+        /// </summary>
+        /// <param name="callbacks">Optional that will be called if a register specified by name is read or written</param>
+        /// <param name="debugLogCallback">Optional callback to be used for debug log messages</param>
+        public DeviceStateWithShadow(IReadOnlyDictionary<string, RwCallback> callbacks = null, Action<string> debugLogCallback = null)
+        {
+            _state = new DeviceState<TState>(callbacks, debugLogCallback);
+            _shadowState = new DeviceState<TState>();
+        }
+
+        /// <summary>
+        /// Reads a value from a register.
+        /// </summary>
+        /// <param name="offset">Register offset in bytes</param>
+        /// <returns>Value stored on the register</returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public int Read(int offset)
+        {
+            return _state.Read(offset);
+        }
+
+        /// <summary>
+        /// Writes a value to a register.
+        /// </summary>
+        /// <param name="offset">Register offset in bytes</param>
+        /// <param name="value">Value to be written</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Write(int offset, int value)
+        {
+            WriteWithRedundancyCheck(offset, value, out _);
+        }
+
+        /// <summary>
+        /// Writes a value to a register, returning a value indicating if <paramref name="value"/>
+        /// is different from the current value on the register.
+        /// </summary>
+        /// <param name="offset">Register offset in bytes</param>
+        /// <param name="value">Value to be written</param>
+        /// <param name="changed">True if the value was changed, false otherwise</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void WriteWithRedundancyCheck(int offset, int value, out bool changed)
+        {
+            var shadowRamControl = _state.State.SetMmeShadowRamControlMode;
+            if (shadowRamControl == SetMmeShadowRamControlMode.MethodPassthrough || offset < 0x200)
+            {
+                _state.WriteWithRedundancyCheck(offset, value, out changed);
+            }
+            else if (shadowRamControl == SetMmeShadowRamControlMode.MethodTrack ||
+                     shadowRamControl == SetMmeShadowRamControlMode.MethodTrackWithFilter)
+            {
+                _shadowState.Write(offset, value);
+                _state.WriteWithRedundancyCheck(offset, value, out changed);
+            }
+            else /* if (shadowRamControl == SetMmeShadowRamControlMode.MethodReplay) */
+            {
+                Debug.Assert(shadowRamControl == SetMmeShadowRamControlMode.MethodReplay);
+                _state.WriteWithRedundancyCheck(offset, _shadowState.Read(offset), out changed);
+            }
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs
index 58fa23266b..70909168c4 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs
@@ -1,6 +1,6 @@
 using Ryujinx.Common;
 using Ryujinx.Graphics.Device;
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Gpu.Engine.Threed;
 using Ryujinx.Graphics.Texture;
 using System;
 using System.Collections.Generic;
@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
     {
         private readonly GpuContext _context;
         private readonly GpuChannel _channel;
+        private readonly ThreedClass _3dEngine;
         private readonly DeviceState<DmaClassState> _state;
 
         /// <summary>
@@ -35,10 +36,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
         /// </summary>
         /// <param name="context">GPU context</param>
         /// <param name="channel">GPU channel</param>
-        public DmaClass(GpuContext context, GpuChannel channel)
+        /// <param name="threedEngine">3D engine</param>
+        public DmaClass(GpuContext context, GpuChannel channel, ThreedClass threedEngine)
         {
             _context = context;
             _channel = channel;
+            _3dEngine = threedEngine;
             _state = new DeviceState<DmaClassState>(new Dictionary<string, RwCallback>
             {
                 { nameof(DmaClassState.LaunchDma), new RwCallback(LaunchDma, null) }
@@ -69,7 +72,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
         /// <param name="xCount">Number of pixels to be copied</param>
         /// <param name="yCount">Number of lines to be copied</param>
         /// <returns></returns>
-        private static bool IsTextureCopyComplete(CopyBufferTexture tex, bool linear, int bpp, int stride, int xCount, int yCount)
+        private static bool IsTextureCopyComplete(DmaTexture tex, bool linear, int bpp, int stride, int xCount, int yCount)
         {
             if (linear)
             {
@@ -116,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
             int xCount = (int)_state.State.LineLengthIn;
             int yCount = (int)_state.State.LineCount;
 
-            _context.Methods.FlushUboDirty(memoryManager);
+            _3dEngine.FlushUboDirty();
 
             if (copy2D)
             {
@@ -125,8 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
                 int srcBpp = remap ? ((int)_state.State.SetRemapComponentsNumSrcComponents + 1) * componentSize : 1;
                 int dstBpp = remap ? ((int)_state.State.SetRemapComponentsNumDstComponents + 1) * componentSize : 1;
 
-                var dst = Unsafe.As<uint, CopyBufferTexture>(ref _state.State.SetDstBlockSize);
-                var src = Unsafe.As<uint, CopyBufferTexture>(ref _state.State.SetSrcBlockSize);
+                var dst = Unsafe.As<uint, DmaTexture>(ref _state.State.SetDstBlockSize);
+                var src = Unsafe.As<uint, DmaTexture>(ref _state.State.SetSrcBlockSize);
 
                 int srcStride = (int)_state.State.PitchIn;
                 int dstStride = (int)_state.State.PitchOut;
diff --git a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs
new file mode 100644
index 0000000000..6873ff40f3
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Graphics.Gpu.Engine.Types;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Dma
+{
+    /// <summary>
+    /// Buffer to texture copy parameters.
+    /// </summary>
+    struct DmaTexture
+    {
+#pragma warning disable CS0649
+        public MemoryLayout MemoryLayout;
+        public int Width;
+        public int Height;
+        public int Depth;
+        public int RegionZ;
+        public ushort RegionX;
+        public ushort RegionY;
+#pragma warning restore CS0649
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs
index 75b19c372c..28822f4e11 100644
--- a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs
@@ -1,6 +1,5 @@
 using Ryujinx.Graphics.Device;
 using Ryujinx.Graphics.Gpu.Engine.MME;
-using Ryujinx.Graphics.Gpu.State;
 using System;
 using System.Collections.Generic;
 using System.Threading;
@@ -150,7 +149,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
         /// <param name="argument">Method call argument</param>
         public void WaitForIdle(int argument)
         {
-            _context.Methods.PerformDeferredDraws();
+            _parent.PerformDeferredDraws();
             _context.Renderer.Pipeline.Barrier();
 
             _context.CreateHostSyncIfNeeded();
@@ -189,7 +188,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
         /// <param name="argument">Method call argument</param>
         public void SetMmeShadowRamControl(int argument)
         {
-            _parent.SetShadowRamControl((ShadowRamControl)argument);
+            _parent.SetShadowRamControl(argument);
         }
 
         /// <summary>
@@ -217,7 +216,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
         /// </summary>
         /// <param name="index">Index of the macro</param>
         /// <param name="state">Current GPU state</param>
-        public void CallMme(int index, GpuState state)
+        public void CallMme(int index, IDeviceState state)
         {
             _macros[index].Execute(_macroCode, state);
         }
diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs
index c2727f4887..dd5e6fe57f 100644
--- a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs
@@ -2,9 +2,9 @@
 using Ryujinx.Graphics.Gpu.Engine.Compute;
 using Ryujinx.Graphics.Gpu.Engine.Dma;
 using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
+using Ryujinx.Graphics.Gpu.Engine.Threed;
 using Ryujinx.Graphics.Gpu.Engine.Twod;
 using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.State;
 using System;
 using System.Runtime.CompilerServices;
 
@@ -18,9 +18,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
         private const int MacrosCount = 0x80;
         private const int MacroIndexMask = MacrosCount - 1;
 
-        private readonly GpuContext _context;
+        private const int UniformBufferUpdateDataMethodOffset = 0x8e4;
+
         private readonly GpuChannel _channel;
 
+        /// <summary>
+        /// Channel memory manager.
+        /// </summary>
         public MemoryManager MemoryManager => _channel.MemoryManager;
 
         /// <summary>
@@ -37,8 +41,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
 
         private DmaState _state;
 
-        private readonly GpuState[] _subChannels;
-        private readonly IDeviceState[] _subChannels2;
+        private readonly ThreedClass _3dClass;
+        private readonly ComputeClass _computeClass;
+        private readonly InlineToMemoryClass _i2mClass;
+        private readonly TwodClass _2dClass;
+        private readonly DmaClass _dmaClass;
+
         private readonly GPFifoClass _fifoClass;
 
         /// <summary>
@@ -48,29 +56,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
         /// <param name="channel">Channel that the GPFIFO processor belongs to</param>
         public GPFifoProcessor(GpuContext context, GpuChannel channel)
         {
-            _context = context;
             _channel = channel;
 
             _fifoClass = new GPFifoClass(context, this);
-            _subChannels = new GpuState[8];
-            _subChannels2 = new IDeviceState[8]
-            {
-                null,
-                new ComputeClass(context, channel),
-                new InlineToMemoryClass(context, channel),
-                new TwodClass(channel),
-                new DmaClass(context, channel),
-                null,
-                null,
-                null
-            };
-
-            for (int index = 0; index < _subChannels.Length; index++)
-            {
-                _subChannels[index] = new GpuState(channel, _subChannels2[index]);
-
-                _context.Methods.RegisterCallbacks(_subChannels[index]);
-            }
+            _3dClass = new ThreedClass(context, channel);
+            _computeClass = new ComputeClass(context, channel, _3dClass);
+            _i2mClass = new InlineToMemoryClass(context, channel);
+            _2dClass = new TwodClass(channel);
+            _dmaClass = new DmaClass(context, channel, _3dClass);
         }
 
         /// <summary>
@@ -85,7 +78,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
 
                 if (_state.MethodCount != 0)
                 {
-                    Send(new MethodParams(_state.Method, command, _state.SubChannel, _state.MethodCount));
+                    Send(_state.Method, command, _state.SubChannel, _state.MethodCount <= 1);
 
                     if (!_state.NonIncrementing)
                     {
@@ -121,13 +114,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
                             _state.NonIncrementing = meth.SecOp == SecOp.NonIncMethod;
                             break;
                         case SecOp.ImmdDataMethod:
-                            Send(new MethodParams(meth.MethodAddress, meth.ImmdData, meth.MethodSubchannel, 1));
+                            Send(meth.MethodAddress, meth.ImmdData, meth.MethodSubchannel, true);
                             break;
                     }
                 }
             }
 
-            _context.Methods.FlushUboDirty(MemoryManager);
+            _3dClass.FlushUboDirty();
         }
 
         /// <summary>
@@ -145,11 +138,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
 
             if (meth.MethodCount < availableCount &&
                 meth.SecOp == SecOp.NonIncMethod &&
-                meth.MethodAddress == (int)MethodOffset.UniformBufferUpdateData)
+                meth.MethodAddress == UniformBufferUpdateDataMethodOffset)
             {
-                GpuState state = _subChannels[meth.MethodSubchannel];
-
-                _context.Methods.UniformBufferUpdate(state, commandBuffer.Slice(offset + 1, meth.MethodCount));
+                _3dClass.ConstantBufferUpdate(commandBuffer.Slice(offset + 1, meth.MethodCount));
 
                 return true;
             }
@@ -161,55 +152,105 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
         /// Sends a uncompressed method for processing by the graphics pipeline.
         /// </summary>
         /// <param name="meth">Method to be processed</param>
-        private void Send(MethodParams meth)
+        private void Send(int offset, int argument, int subChannel, bool isLastCall)
         {
-            if ((MethodOffset)meth.Method == MethodOffset.BindChannel)
+            if (offset < 0x60)
             {
-                _subChannels[meth.SubChannel].ClearCallbacks();
+                _fifoClass.Write(offset * 4, argument);
+            }
+            else if (offset < 0xe00)
+            {
+                offset *= 4;
 
-                _context.Methods.RegisterCallbacks(_subChannels[meth.SubChannel]);
-            }
-            else if (meth.Method < 0x60)
-            {
-                // TODO: check if macros are shared between subchannels or not. For now let's assume they are.
-                _fifoClass.Write(meth.Method * 4, meth.Argument);
-            }
-            else if (meth.Method < 0xe00)
-            {
-                _subChannels[meth.SubChannel].CallMethod(meth);
+                switch (subChannel)
+                {
+                    case 0:
+                        _3dClass.Write(offset, argument);
+                        break;
+                    case 1:
+                        _computeClass.Write(offset, argument);
+                        break;
+                    case 2:
+                        _i2mClass.Write(offset, argument);
+                        break;
+                    case 3:
+                        _2dClass.Write(offset, argument);
+                        break;
+                    case 4:
+                        _dmaClass.Write(offset, argument);
+                        break;
+                }
             }
             else
             {
-                int macroIndex = (meth.Method >> 1) & MacroIndexMask;
-
-                if ((meth.Method & 1) != 0)
+                IDeviceState state = subChannel switch
                 {
-                    _fifoClass.MmePushArgument(macroIndex, meth.Argument);
-                }
-                else
-                {
-                    _fifoClass.MmeStart(macroIndex, meth.Argument);
-                }
+                    0 => _3dClass,
+                    3 => _2dClass,
+                    _ => null
+                };
 
-                if (meth.IsLastCall)
+                if (state != null)
                 {
-                    _fifoClass.CallMme(macroIndex, _subChannels[meth.SubChannel]);
+                    int macroIndex = (offset >> 1) & MacroIndexMask;
 
-                    _context.Methods.PerformDeferredDraws();
+                    if ((offset & 1) != 0)
+                    {
+                        _fifoClass.MmePushArgument(macroIndex, argument);
+                    }
+                    else
+                    {
+                        _fifoClass.MmeStart(macroIndex, argument);
+                    }
+
+                    if (isLastCall)
+                    {
+                        _fifoClass.CallMme(macroIndex, state);
+
+                        _3dClass.PerformDeferredDraws();
+                    }
                 }
             }
         }
 
+        /// <summary>
+        /// Writes data directly to the state of the specified class.
+        /// </summary>
+        /// <param name="classId">ID of the class to write the data into</param>
+        /// <param name="offset">State offset in bytes</param>
+        /// <param name="value">Value to be written</param>
+        public void Write(ClassId classId, int offset, int value)
+        {
+            switch (classId)
+            {
+                case ClassId.Threed:
+                    _3dClass.Write(offset, value);
+                    break;
+                case ClassId.Compute:
+                    _computeClass.Write(offset, value);
+                    break;
+                case ClassId.InlineToMemory:
+                    _i2mClass.Write(offset, value);
+                    break;
+                case ClassId.Twod:
+                    _2dClass.Write(offset, value);
+                    break;
+                case ClassId.Dma:
+                    _dmaClass.Write(offset, value);
+                    break;
+                case ClassId.GPFifo:
+                    _fifoClass.Write(offset, value);
+                    break;
+            }
+        }
+
         /// <summary>
         /// Sets the shadow ram control value of all sub-channels.
         /// </summary>
         /// <param name="control">New shadow ram control value</param>
-        public void SetShadowRamControl(ShadowRamControl control)
+        public void SetShadowRamControl(int control)
         {
-            for (int i = 0; i < _subChannels.Length; i++)
-            {
-                _subChannels[i].ShadowRamControl = control;
-            }
+            _3dClass.SetShadowRamControl(control);
         }
 
         /// <summary>
@@ -218,10 +259,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
         /// </summary>
         public void ForceAllDirty()
         {
-            for (int index = 0; index < _subChannels.Length; index++)
-            {
-                _subChannels[index].ForceAllDirty();
-            }
+            _3dClass.ForceStateDirty();
+            _channel.BufferManager.Rebind();
+            _channel.TextureManager.Rebind();
+        }
+
+        /// <summary>
+        /// Perform any deferred draws.
+        /// </summary>
+        public void PerformDeferredDraws()
+        {
+            _3dClass.PerformDeferredDraws();
         }
     }
 }
diff --git a/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs b/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
deleted file mode 100644
index 3d23b785c2..0000000000
--- a/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.Graphics.Gpu.State;
-using Ryujinx.Graphics.Texture;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
-    partial class Methods
-    {
-        private Inline2MemoryParams _params;
-
-        private bool _isLinear;
-
-        private int _offset;
-        private int _size;
-
-        private bool _finished;
-
-        private int[] _buffer;
-
-        /// <summary>
-        /// Launches Inline-to-Memory engine DMA copy.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        public void LaunchDma(GpuState state, int argument)
-        {
-            _params = state.Get<Inline2MemoryParams>(MethodOffset.I2mParams);
-
-            _isLinear = (argument & 1) != 0;
-
-            _offset = 0;
-            _size   = _params.LineLengthIn * _params.LineCount;
-
-            int count = BitUtils.DivRoundUp(_size, 4);
-
-            if (_buffer == null || _buffer.Length < count)
-            {
-                _buffer = new int[count];
-            }
-
-            ulong dstBaseAddress = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
-
-            // Trigger read tracking, to flush any managed resources in the destination region.
-            state.Channel.MemoryManager.Physical.GetSpan(dstBaseAddress, _size, true);
-
-            _finished = false;
-        }
-
-        /// <summary>
-        /// Pushes a word of data to the Inline-to-Memory engine.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        public void LoadInlineData(GpuState state, int argument)
-        {
-            if (!_finished)
-            {
-                _buffer[_offset++] = argument;
-
-                if (_offset * 4 >= _size)
-                {
-                    FinishTransfer(state);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Performs actual copy of the inline data after the transfer is finished.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void FinishTransfer(GpuState state)
-        {
-            Span<byte> data = MemoryMarshal.Cast<int, byte>(_buffer).Slice(0, _size);
-
-            if (_isLinear && _params.LineCount == 1)
-            {
-                ulong address = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
-
-                state.Channel.MemoryManager.Physical.Write(address, data);
-            }
-            else
-            {
-                var dstCalculator = new OffsetCalculator(
-                    _params.DstWidth,
-                    _params.DstHeight,
-                    _params.DstStride,
-                    _isLinear,
-                    _params.DstMemoryLayout.UnpackGobBlocksInY(),
-                    1);
-
-                int srcOffset = 0;
-
-                ulong dstBaseAddress = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
-
-                for (int y = _params.DstY; y < _params.DstY + _params.LineCount; y++)
-                {
-                    int x1      = _params.DstX;
-                    int x2      = _params.DstX + _params.LineLengthIn;
-                    int x2Trunc = _params.DstX + BitUtils.AlignDown(_params.LineLengthIn, 16);
-
-                    int x;
-
-                    for (x = x1; x < x2Trunc; x += 16, srcOffset += 16)
-                    {
-                        int dstOffset = dstCalculator.GetOffset(x, y);
-
-                        ulong dstAddress = dstBaseAddress + (ulong)dstOffset;
-
-                        Span<byte> pixel = data.Slice(srcOffset, 16);
-
-                        state.Channel.MemoryManager.Physical.Write(dstAddress, pixel);
-                    }
-
-                    for (; x < x2; x++, srcOffset++)
-                    {
-                        int dstOffset = dstCalculator.GetOffset(x, y);
-
-                        ulong dstAddress = dstBaseAddress + (ulong)dstOffset;
-
-                        Span<byte> pixel = data.Slice(srcOffset, 1);
-
-                        state.Channel.MemoryManager.Physical.Write(dstAddress, pixel);
-                    }
-                }
-            }
-
-            _finished = true;
-
-            _context.AdvanceSequence();
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs b/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs
index 0e7d6fb076..cb4133ec5d 100644
--- a/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs
@@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
         /// <param name="context">GPU context</param>
         /// <param name="channel">GPU channel</param>
         /// <param name="initializeState">Indicates if the internal state should be initialized. Set to false if part of another engine</param>
-        protected InlineToMemoryClass(GpuContext context, GpuChannel channel, bool initializeState)
+        public InlineToMemoryClass(GpuContext context, GpuChannel channel, bool initializeState)
         {
             _context = context;
             _channel = channel;
@@ -70,20 +70,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
         /// </summary>
         /// <param name="offset">Register byte offset</param>
         /// <returns>Data at the specified offset</returns>
-        public virtual int Read(int offset) => _state.Read(offset);
+        public int Read(int offset) => _state.Read(offset);
 
         /// <summary>
         /// Writes data to the class registers.
         /// </summary>
         /// <param name="offset">Register byte offset</param>
         /// <param name="data">Data to be written</param>
-        public virtual void Write(int offset, int data) => _state.Write(offset, data);
+        public void Write(int offset, int data) => _state.Write(offset, data);
 
         /// <summary>
         /// Launches Inline-to-Memory engine DMA copy.
         /// </summary>
         /// <param name="argument">Method call argument</param>
-        protected virtual void LaunchDma(int argument)
+        private void LaunchDma(int argument)
         {
             LaunchDma(ref _state.State, argument);
         }
@@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
         /// </summary>
         /// <param name="state">Current class state</param>
         /// <param name="argument">Method call argument</param>
-        protected void LaunchDma(ref InlineToMemoryClassState state, int argument)
+        public void LaunchDma(ref InlineToMemoryClassState state, int argument)
         {
             _isLinear = (argument & 1) != 0;
 
@@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
         /// Pushes a word of data to the Inline-to-Memory engine.
         /// </summary>
         /// <param name="argument">Method call argument</param>
-        protected void LoadInlineData(int argument)
+        public void LoadInlineData(int argument)
         {
             if (!_finished)
             {
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs b/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs
index b056ecc89f..b957de08d1 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
 using System;
 using System.Collections.Generic;
 
@@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// <summary>
         /// Arguments FIFO.
         /// </summary>
-        public Queue<int> Fifo { get; }
+        Queue<int> Fifo { get; }
 
         /// <summary>
         /// Should execute the GPU Macro code being passed.
@@ -20,6 +20,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// <param name="code">Code to be executed</param>
         /// <param name="state">GPU state at the time of the call</param>
         /// <param name="arg0">First argument to be passed to the GPU Macro</param>
-        void Execute(ReadOnlySpan<int> code, GpuState state, int arg0);
+        void Execute(ReadOnlySpan<int> code, IDeviceState state, int arg0);
     }
 }
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
index 9847f4c046..1a79afb934 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
 using System;
 
 namespace Ryujinx.Graphics.Gpu.Engine.MME
@@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// </summary>
         /// <param name="code">Program code</param>
         /// <param name="state">Current GPU state</param>
-        public void Execute(ReadOnlySpan<int> code, GpuState state)
+        public void Execute(ReadOnlySpan<int> code, IDeviceState state)
         {
             if (_executionPending)
             {
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs
index 9a834ca69f..0173a7fb39 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs
@@ -1,5 +1,5 @@
 using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
 using System;
 using System.Collections.Generic;
 
@@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// <param name="code">Code of the program to execute</param>
         /// <param name="state">Current GPU state</param>
         /// <param name="arg0">Optional argument passed to the program, 0 if not used</param>
-        public void Execute(ReadOnlySpan<int> code, GpuState state, int arg0)
+        public void Execute(ReadOnlySpan<int> code, IDeviceState state, int arg0)
         {
             Reset();
 
@@ -55,7 +55,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
 
             FetchOpCode(code);
 
-            while (Step(code, state)) ;
+            while (Step(code, state))
+            {
+            }
 
             // Due to the delay slot, we still need to execute
             // one more instruction before we actually exit.
@@ -85,7 +87,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// <param name="code">Program code to execute</param>
         /// <param name="state">Current GPU state</param>
         /// <returns>True to continue execution, false if the program exited</returns>
-        private bool Step(ReadOnlySpan<int> code, GpuState state)
+        private bool Step(ReadOnlySpan<int> code, IDeviceState state)
         {
             int baseAddr = _pc - 1;
 
@@ -193,7 +195,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// </summary>
         /// <param name="state">Current GPU state</param>
         /// <returns>Operation result</returns>
-        private int GetAluResult(GpuState state)
+        private int GetAluResult(IDeviceState state)
         {
             AluOperation op = (AluOperation)(_opCode & 7);
 
@@ -378,9 +380,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// <param name="state">Current GPU state</param>
         /// <param name="reg">Register offset to read</param>
         /// <returns>GPU register value</returns>
-        private int Read(GpuState state, int reg)
+        private int Read(IDeviceState state, int reg)
         {
-            return state.Read(reg);
+            return state.Read(reg * 4);
         }
 
         /// <summary>
@@ -388,11 +390,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// </summary>
         /// <param name="state">Current GPU state</param>
         /// <param name="value">Call argument</param>
-        private void Send(GpuState state, int value)
+        private void Send(IDeviceState state, int value)
         {
-            MethodParams meth = new MethodParams(_methAddr, value);
-
-            state.CallMethod(meth);
+            state.Write(_methAddr * 4, value);
 
             _methAddr += _methIncr;
         }
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs
index 346ae6cf1b..f0393dd149 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
 using System;
 using System.Collections.Generic;
 
@@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// <param name="code">Code of the program to execute</param>
         /// <param name="state">Current GPU state</param>
         /// <param name="arg0">Optional argument passed to the program, 0 if not used</param>
-        public void Execute(ReadOnlySpan<int> code, GpuState state, int arg0)
+        public void Execute(ReadOnlySpan<int> code, IDeviceState state, int arg0)
         {
             if (_execute == null)
             {
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs
index e752b9dc13..d281a75a82 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
 using System;
 using System.Collections.Generic;
 using System.Reflection.Emit;
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// </summary>
         public MacroJitCompiler()
         {
-            _meth = new DynamicMethod("Macro", typeof(void), new Type[] { typeof(MacroJitContext), typeof(GpuState), typeof(int) });
+            _meth = new DynamicMethod("Macro", typeof(void), new Type[] { typeof(MacroJitContext), typeof(IDeviceState), typeof(int) });
             _ilGen = _meth.GetILGenerator();
             _gprs = new LocalBuilder[8];
 
@@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
             _ilGen.Emit(OpCodes.Stloc, _gprs[1]);
         }
 
-        public delegate void MacroExecute(MacroJitContext context, GpuState state, int arg0);
+        public delegate void MacroExecute(MacroJitContext context, IDeviceState state, int arg0);
 
         /// <summary>
         /// Translates a new piece of GPU Macro code into host executable code.
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs
index ba426dc0fa..aa31c9ee4f 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs
@@ -1,5 +1,5 @@
 using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
 using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gpu.Engine.MME
@@ -36,9 +36,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// <param name="state">Current GPU state</param>
         /// <param name="reg">Register offset to read</param>
         /// <returns>GPU register value</returns>
-        public static int Read(GpuState state, int reg)
+        public static int Read(IDeviceState state, int reg)
         {
-            return state.Read(reg);
+            return state.Read(reg * 4);
         }
 
         /// <summary>
@@ -47,11 +47,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// <param name="value">Call argument</param>
         /// <param name="state">Current GPU state</param>
         /// <param name="methAddr">Address, in words, of the method</param>
-        public static void Send(int value, GpuState state, int methAddr)
+        public static void Send(int value, IDeviceState state, int methAddr)
         {
-            MethodParams meth = new MethodParams(methAddr, value);
-
-            state.CallMethod(meth);
+            state.Write(methAddr * 4, value);
         }
     }
 }
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs b/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs
deleted file mode 100644
index 5f6316dcb3..0000000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
-    partial class Methods
-    {
-        /// <summary>
-        /// Clears the current color and depth-stencil buffers.
-        /// Which buffers should be cleared is also specified on the argument.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void Clear(GpuState state, int argument)
-        {
-            ConditionalRenderEnabled renderEnable = GetRenderEnable(state);
-
-            if (renderEnable == ConditionalRenderEnabled.False)
-            {
-                return;
-            }
-
-            // Scissor and rasterizer discard also affect clears.
-            if (state.QueryModified(MethodOffset.ScissorState))
-            {
-                UpdateScissorState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.RasterizeEnable))
-            {
-                UpdateRasterizerState(state);
-            }
-
-            int index = (argument >> 6) & 0xf;
-
-            UpdateRenderTargetState(state, useControl: false, singleUse: index);
-
-            state.Channel.TextureManager.UpdateRenderTargets();
-
-            bool clearDepth   = (argument & 1) != 0;
-            bool clearStencil = (argument & 2) != 0;
-
-            uint componentMask = (uint)((argument >> 2) & 0xf);
-
-            if (componentMask != 0)
-            {
-                var clearColor = state.Get<ClearColors>(MethodOffset.ClearColors);
-
-                ColorF color = new ColorF(
-                    clearColor.Red,
-                    clearColor.Green,
-                    clearColor.Blue,
-                    clearColor.Alpha);
-
-                _context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color);
-            }
-
-            if (clearDepth || clearStencil)
-            {
-                float depthValue   = state.Get<float>(MethodOffset.ClearDepthValue);
-                int   stencilValue = state.Get<int>  (MethodOffset.ClearStencilValue);
-
-                int stencilMask = 0;
-
-                if (clearStencil)
-                {
-                    stencilMask = state.Get<StencilTestState>(MethodOffset.StencilTestState).FrontMask;
-                }
-
-                _context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
-                    depthValue,
-                    clearDepth,
-                    stencilValue,
-                    stencilMask);
-            }
-
-            UpdateRenderTargetState(state, useControl: true);
-
-            if (renderEnable == ConditionalRenderEnabled.Host)
-            {
-                _context.Renderer.Pipeline.EndHostConditionalRendering();
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs b/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs
deleted file mode 100644
index fec1cc4657..0000000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs
+++ /dev/null
@@ -1,343 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Image;
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
-    partial class Methods
-    {
-        private bool _drawIndexed;
-
-        private bool _instancedDrawPending;
-        private bool _instancedIndexed;
-
-        private int _instancedFirstIndex;
-        private int _instancedFirstVertex;
-        private int _instancedFirstInstance;
-        private int _instancedIndexCount;
-        private int _instancedDrawStateFirst;
-        private int _instancedDrawStateCount;
-
-        private int _instanceIndex;
-
-        private IbStreamer _ibStreamer;
-
-        /// <summary>
-        /// Primitive topology of the current draw.
-        /// </summary>
-        public PrimitiveTopology Topology { get; private set; }
-
-        /// <summary>
-        /// Finishes the draw call.
-        /// This draws geometry on the bound buffers based on the current GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void DrawEnd(GpuState state, int argument)
-        {
-            var indexBuffer = state.Get<IndexBufferState>(MethodOffset.IndexBufferState);
-
-            DrawEnd(state, indexBuffer.First, indexBuffer.Count);
-        }
-
-        /// <summary>
-        /// Finishes the draw call.
-        /// This draws geometry on the bound buffers based on the current GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
-        /// <param name="indexCount">Number of index buffer elements used on the draw</param>
-        private void DrawEnd(GpuState state, int firstIndex, int indexCount)
-        {
-            ConditionalRenderEnabled renderEnable = GetRenderEnable(state);
-
-            if (renderEnable == ConditionalRenderEnabled.False || _instancedDrawPending)
-            {
-                if (renderEnable == ConditionalRenderEnabled.False)
-                {
-                    PerformDeferredDraws();
-                }
-
-                _drawIndexed = false;
-
-                if (renderEnable == ConditionalRenderEnabled.Host)
-                {
-                    _context.Renderer.Pipeline.EndHostConditionalRendering();
-                }
-
-                return;
-            }
-
-            UpdateState(state, firstIndex, indexCount);
-
-            bool instanced = _vsUsesInstanceId || _isAnyVbInstanced;
-
-            if (instanced)
-            {
-                _instancedDrawPending = true;
-
-                _instancedIndexed = _drawIndexed;
-
-                _instancedFirstIndex = firstIndex;
-                _instancedFirstVertex = state.Get<int>(MethodOffset.FirstVertex);
-                _instancedFirstInstance = state.Get<int>(MethodOffset.FirstInstance);
-
-                _instancedIndexCount = indexCount;
-
-                var drawState = state.Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);
-
-                _instancedDrawStateFirst = drawState.First;
-                _instancedDrawStateCount = drawState.Count;
-
-                _drawIndexed = false;
-
-                if (renderEnable == ConditionalRenderEnabled.Host)
-                {
-                    _context.Renderer.Pipeline.EndHostConditionalRendering();
-                }
-
-                return;
-            }
-
-            int firstInstance = state.Get<int>(MethodOffset.FirstInstance);
-
-            int inlineIndexCount = _ibStreamer.GetAndResetInlineIndexCount();
-
-            if (inlineIndexCount != 0)
-            {
-                int firstVertex = state.Get<int>(MethodOffset.FirstVertex);
-
-                BufferRange br = new BufferRange(_ibStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
-
-                state.Channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
-
-                _context.Renderer.Pipeline.DrawIndexed(
-                    inlineIndexCount,
-                    1,
-                    firstIndex,
-                    firstVertex,
-                    firstInstance);
-            }
-            else if (_drawIndexed)
-            {
-                int firstVertex = state.Get<int>(MethodOffset.FirstVertex);
-
-                _context.Renderer.Pipeline.DrawIndexed(
-                    indexCount,
-                    1,
-                    firstIndex,
-                    firstVertex,
-                    firstInstance);
-            }
-            else
-            {
-                var drawState = state.Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);
-
-                _context.Renderer.Pipeline.Draw(
-                    drawState.Count,
-                    1,
-                    drawState.First,
-                    firstInstance);
-            }
-
-            _drawIndexed = false;
-
-            if (renderEnable == ConditionalRenderEnabled.Host)
-            {
-                _context.Renderer.Pipeline.EndHostConditionalRendering();
-            }
-        }
-
-        /// <summary>
-        /// Starts draw.
-        /// This sets primitive type and instanced draw parameters.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void DrawBegin(GpuState state, int argument)
-        {
-            bool incrementInstance = (argument & (1 << 26)) != 0;
-            bool resetInstance     = (argument & (1 << 27)) == 0;
-
-            PrimitiveType type = (PrimitiveType)(argument & 0xffff);
-
-            PrimitiveTypeOverride typeOverride = state.Get<PrimitiveTypeOverride>(MethodOffset.PrimitiveTypeOverride);
-
-            if (typeOverride != PrimitiveTypeOverride.Invalid)
-            {
-                DrawBegin(incrementInstance, resetInstance, typeOverride.Convert());
-            }
-            else
-            {
-                DrawBegin(incrementInstance, resetInstance, type.Convert());
-            }
-        }
-
-        /// <summary>
-        /// Starts draw.
-        /// This sets primitive type and instanced draw parameters.
-        /// </summary>
-        /// <param name="incrementInstance">Indicates if the current instance should be incremented</param>
-        /// <param name="resetInstance">Indicates if the current instance should be set to zero</param>
-        /// <param name="topology">Primitive topology</param>
-        private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveTopology topology)
-        {
-            if (incrementInstance)
-            {
-                _instanceIndex++;
-            }
-            else if (resetInstance)
-            {
-                PerformDeferredDraws();
-
-                _instanceIndex = 0;
-            }
-
-            _context.Renderer.Pipeline.SetPrimitiveTopology(topology);
-
-            Topology = topology;
-        }
-
-        /// <summary>
-        /// Sets the index buffer count.
-        /// This also sets internal state that indicates that the next draw is an indexed draw.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void SetIndexBufferCount(GpuState state, int argument)
-        {
-            _drawIndexed = true;
-        }
-
-        /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void DrawIndexedSmall(GpuState state, int argument)
-        {
-            DrawIndexedSmall(state, argument, false);
-        }
-
-        /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void DrawIndexedSmall2(GpuState state, int argument)
-        {
-            DrawIndexedSmall(state, argument);
-        }
-
-        /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements,
-        /// while also pre-incrementing the current instance value.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void DrawIndexedSmallIncInstance(GpuState state, int argument)
-        {
-            DrawIndexedSmall(state, argument, true);
-        }
-
-        /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements,
-        /// while also pre-incrementing the current instance value.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void DrawIndexedSmallIncInstance2(GpuState state, int argument)
-        {
-            DrawIndexedSmallIncInstance(state, argument);
-        }
-
-        /// <summary>
-        /// Performs a indexed draw with a low number of index buffer elements,
-        /// while optionally also pre-incrementing the current instance value.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        /// <param name="instanced">True to increment the current instance value, false otherwise</param>
-        private void DrawIndexedSmall(GpuState state, int argument, bool instanced)
-        {
-            PrimitiveTypeOverride typeOverride = state.Get<PrimitiveTypeOverride>(MethodOffset.PrimitiveTypeOverride);
-
-            DrawBegin(instanced, !instanced, typeOverride.Convert());
-
-            int firstIndex = argument & 0xffff;
-            int indexCount = (argument >> 16) & 0xfff;
-
-            bool oldDrawIndexed = _drawIndexed;
-
-            _drawIndexed = true;
-
-            DrawEnd(state, firstIndex, indexCount);
-
-            _drawIndexed = oldDrawIndexed;
-        }
-
-        /// <summary>
-        /// Pushes four 8-bit index buffer elements.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void VbElementU8(GpuState state, int argument)
-        {
-            _ibStreamer.VbElementU8(_context.Renderer, argument);
-        }
-
-        /// <summary>
-        /// Pushes two 16-bit index buffer elements.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void VbElementU16(GpuState state, int argument)
-        {
-            _ibStreamer.VbElementU16(_context.Renderer, argument);
-        }
-
-        /// <summary>
-        /// Pushes one 32-bit index buffer element.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void VbElementU32(GpuState state, int argument)
-        {
-            _ibStreamer.VbElementU32(_context.Renderer, argument);
-        }
-
-        /// <summary>
-        /// Perform any deferred draws.
-        /// This is used for instanced draws.
-        /// Since each instance is a separate draw, we defer the draw and accumulate the instance count.
-        /// Once we detect the last instanced draw, then we perform the host instanced draw,
-        /// with the accumulated instance count.
-        /// </summary>
-        public void PerformDeferredDraws()
-        {
-            // Perform any pending instanced draw.
-            if (_instancedDrawPending)
-            {
-                _instancedDrawPending = false;
-
-                if (_instancedIndexed)
-                {
-                    _context.Renderer.Pipeline.DrawIndexed(
-                        _instancedIndexCount,
-                        _instanceIndex + 1,
-                        _instancedFirstIndex,
-                        _instancedFirstVertex,
-                        _instancedFirstInstance);
-                }
-                else
-                {
-                    _context.Renderer.Pipeline.Draw(
-                        _instancedDrawStateCount,
-                        _instanceIndex + 1,
-                        _instancedDrawStateFirst,
-                        _instancedFirstInstance);
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodFirmware.cs b/Ryujinx.Graphics.Gpu/Engine/MethodFirmware.cs
deleted file mode 100644
index 25a48af9d4..0000000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodFirmware.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
-    partial class Methods
-    {
-        private void FirmwareCall4(GpuState state, int argument)
-        {
-            state.Write(0xd00, 1);
-        }
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodIncrementSyncpoint.cs b/Ryujinx.Graphics.Gpu/Engine/MethodIncrementSyncpoint.cs
deleted file mode 100644
index 9c22275d55..0000000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodIncrementSyncpoint.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
-    partial class Methods
-    {
-        /// <summary>
-        /// Performs an incrementation on a syncpoint.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        public void IncrementSyncpoint(GpuState state, int argument)
-        {
-            uint syncpointId = (uint)(argument) & 0xFFFF;
-
-            _context.CreateHostSyncIfNeeded();
-            _context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result.
-            _context.Synchronization.IncrementSyncpoint(syncpointId);
-        }
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs b/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
deleted file mode 100644
index 2dd0bbfa34..0000000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.State;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
-    partial class Methods
-    {
-        private const int NsToTicksFractionNumerator   = 384;
-        private const int NsToTicksFractionDenominator = 625;
-
-        /// <summary>
-        /// Writes a GPU counter to guest memory.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void Report(GpuState state, int argument)
-        {
-            SemaphoreOperation op = (SemaphoreOperation)(argument & 3);
-            ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f);
-
-            switch (op)
-            {
-                case SemaphoreOperation.Release: ReleaseSemaphore(state);    break;
-                case SemaphoreOperation.Counter: ReportCounter(state, type); break;
-            }
-        }
-
-        /// <summary>
-        /// Writes (or Releases) a GPU semaphore value to guest memory.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void ReleaseSemaphore(GpuState state)
-        {
-            var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
-
-            state.Channel.MemoryManager.Write(rs.Address.Pack(), rs.Payload);
-
-            _context.AdvanceSequence();
-        }
-
-        /// <summary>
-        /// Packed GPU counter data (including GPU timestamp) in memory.
-        /// </summary>
-        private struct CounterData
-        {
-            public ulong Counter;
-            public ulong Timestamp;
-        }
-
-        /// <summary>
-        /// Writes a GPU counter to guest memory.
-        /// This also writes the current timestamp value.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="type">Counter to be written to memory</param>
-        private void ReportCounter(GpuState state, ReportCounterType type)
-        {
-            var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
-
-            ulong gpuVa = rs.Address.Pack();
-
-            ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
-
-            if (GraphicsConfig.FastGpuTime)
-            {
-                // Divide by some amount to report time as if operations were performed faster than they really are.
-                // This can prevent some games from switching to a lower resolution because rendering is too slow.
-                ticks /= 256;
-            }
-
-            ICounterEvent counter = null;
-
-            EventHandler<ulong> resultHandler = (object evt, ulong result) =>
-            {
-                CounterData counterData = new CounterData();
-
-                counterData.Counter = result;
-                counterData.Timestamp = ticks;
-
-                if (counter?.Invalid != true)
-                {
-                    state.Channel.MemoryManager.Write(gpuVa, counterData);
-                }
-            };
-
-            switch (type)
-            {
-                case ReportCounterType.Zero:
-                    resultHandler(null, 0);
-                    break;
-                case ReportCounterType.SamplesPassed:
-                    counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler);
-                    break;
-                case ReportCounterType.PrimitivesGenerated:
-                    counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler);
-                    break;
-                case ReportCounterType.TransformFeedbackPrimitivesWritten:
-                    counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler);
-                    break;
-            }
-
-            state.Channel.MemoryManager.CounterCache.AddOrUpdate(gpuVa, counter);
-        }
-
-        /// <summary>
-        /// Converts a nanoseconds timestamp value to Maxwell time ticks.
-        /// </summary>
-        /// <remarks>
-        /// The frequency is 614400000 Hz.
-        /// </remarks>
-        /// <param name="nanoseconds">Timestamp in nanoseconds</param>
-        /// <returns>Maxwell ticks</returns>
-        private static ulong ConvertNanosecondsToTicks(ulong nanoseconds)
-        {
-            // We need to divide first to avoid overflows.
-            // We fix up the result later by calculating the difference and adding
-            // that to the result.
-            ulong divided = nanoseconds / NsToTicksFractionDenominator;
-
-            ulong rounded = divided * NsToTicksFractionDenominator;
-
-            ulong errorBias = (nanoseconds - rounded) * NsToTicksFractionNumerator / NsToTicksFractionDenominator;
-
-            return divided * NsToTicksFractionNumerator + errorBias;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs b/Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs
deleted file mode 100644
index 79f8e1e8ae..0000000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
-    partial class Methods
-    {
-        /// <summary>
-        /// Resets the value of an internal GPU counter back to zero.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void ResetCounter(GpuState state, int argument)
-        {
-            ResetCounterType type = (ResetCounterType)argument;
-
-            switch (type)
-            {
-                case ResetCounterType.SamplesPassed:
-                    _context.Renderer.ResetCounter(CounterType.SamplesPassed);
-                    break;
-                case ResetCounterType.PrimitivesGenerated:
-                    _context.Renderer.ResetCounter(CounterType.PrimitivesGenerated);
-                    break;
-                case ResetCounterType.TransformFeedbackPrimitivesWritten:
-                    _context.Renderer.ResetCounter(CounterType.TransformFeedbackPrimitivesWritten);
-                    break;
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
deleted file mode 100644
index 0746efa550..0000000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
-    partial class Methods
-    {
-        /// <summary>
-        /// Binds a uniform buffer for the vertex shader stage.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void UniformBufferBindVertex(GpuState state, int argument)
-        {
-            UniformBufferBind(state, argument, ShaderType.Vertex);
-        }
-
-        /// <summary>
-        /// Binds a uniform buffer for the tessellation control shader stage.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void UniformBufferBindTessControl(GpuState state, int argument)
-        {
-            UniformBufferBind(state, argument, ShaderType.TessellationControl);
-        }
-
-        /// <summary>
-        /// Binds a uniform buffer for the tessellation evaluation shader stage.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void UniformBufferBindTessEvaluation(GpuState state, int argument)
-        {
-            UniformBufferBind(state, argument, ShaderType.TessellationEvaluation);
-        }
-
-        /// <summary>
-        /// Binds a uniform buffer for the geometry shader stage.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void UniformBufferBindGeometry(GpuState state, int argument)
-        {
-            UniformBufferBind(state, argument, ShaderType.Geometry);
-        }
-
-        /// <summary>
-        /// Binds a uniform buffer for the fragment shader stage.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        private void UniformBufferBindFragment(GpuState state, int argument)
-        {
-            UniformBufferBind(state, argument, ShaderType.Fragment);
-        }
-
-        /// <summary>
-        ///Binds a uniform buffer for the specified shader stage.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">Method call argument</param>
-        /// <param name="type">Shader stage that will access the uniform buffer</param>
-        private void UniformBufferBind(GpuState state, int argument, ShaderType type)
-        {
-            bool enable = (argument & 1) != 0;
-
-            int index = (argument >> 4) & 0x1f;
-
-            FlushUboDirty(state.Channel.MemoryManager);
-
-            if (enable)
-            {
-                var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
-
-                ulong address = uniformBuffer.Address.Pack();
-
-                state.Channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, address, (uint)uniformBuffer.Size);
-            }
-            else
-            {
-                state.Channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, 0, 0);
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
deleted file mode 100644
index 49c8cda445..0000000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.State;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
-    partial class Methods
-    {
-        // 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;
-
-        /// <summary>
-        /// Flushes any queued ubo updates.
-        /// </summary>
-        /// <param name="memoryManager">GPU memory manager where the uniform buffer is mapped</param>
-        public void FlushUboDirty(MemoryManager memoryManager)
-        {
-            if (_ubFollowUpAddress != 0)
-            {
-                memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
-
-                _ubFollowUpAddress = 0;
-            }
-        }
-
-        /// <summary>
-        /// Updates the uniform buffer data with inline data.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="argument">New uniform buffer data word</param>
-        private void UniformBufferUpdate(GpuState state, int argument)
-        {
-            var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
-
-            ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
-
-            if (_ubFollowUpAddress != address)
-            {
-                FlushUboDirty(state.Channel.MemoryManager);
-
-                _ubByteCount = 0;
-                _ubBeginCpuAddress = state.Channel.MemoryManager.Translate(address);
-            }
-
-            var byteData = MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1));
-            state.Channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
-
-            _ubFollowUpAddress = address + 4;
-            _ubByteCount += 4;
-
-            state.SetUniformBufferOffset(uniformBuffer.Offset + 4);
-        }
-
-        /// <summary>
-        /// Updates the uniform buffer data with inline data.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="data">Data to be written to the uniform buffer</param>
-        public void UniformBufferUpdate(GpuState state, ReadOnlySpan<int> data)
-        {
-            var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
-
-            ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
-
-            ulong size = (ulong)data.Length * 4;
-
-            if (_ubFollowUpAddress != address)
-            {
-                FlushUboDirty(state.Channel.MemoryManager);
-
-                _ubByteCount = 0;
-                _ubBeginCpuAddress = state.Channel.MemoryManager.Translate(address);
-            }
-
-            var byteData = MemoryMarshal.Cast<int, byte>(data);
-            state.Channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
-
-            _ubFollowUpAddress = address + size;
-            _ubByteCount += size;
-
-            state.SetUniformBufferOffset(uniformBuffer.Offset + data.Length * 4);
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
deleted file mode 100644
index 756d56d96f..0000000000
--- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs
+++ /dev/null
@@ -1,1138 +0,0 @@
-using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Image;
-using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.Shader;
-using Ryujinx.Graphics.Gpu.State;
-using Ryujinx.Graphics.Shader;
-using Ryujinx.Graphics.Texture;
-using System;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
-    using Texture = Image.Texture;
-
-    /// <summary>
-    /// GPU method implementations.
-    /// </summary>
-    partial class Methods
-    {
-        private readonly GpuContext _context;
-        private readonly ShaderProgramInfo[] _currentProgramInfo;
-
-        private bool _isAnyVbInstanced;
-        private bool _vsUsesInstanceId;
-        private byte _vsClipDistancesWritten;
-
-        private bool _forceShaderUpdate;
-
-        private bool _prevTfEnable;
-
-        /// <summary>
-        /// Creates a new instance of the GPU methods class.
-        /// </summary>
-        /// <param name="context">GPU context</param>
-        public Methods(GpuContext context)
-        {
-            _context = context;
-
-            _currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
-        }
-
-        /// <summary>
-        /// Register callback for GPU method calls that triggers an action on the GPU.
-        /// </summary>
-        /// <param name="state">GPU state where the triggers will be registered</param>
-        public void RegisterCallbacks(GpuState state)
-        {
-            state.RegisterCallback(MethodOffset.LaunchDma,      LaunchDma);
-            state.RegisterCallback(MethodOffset.LoadInlineData, LoadInlineData);
-
-            state.RegisterCallback(MethodOffset.SyncpointAction, IncrementSyncpoint);
-
-            state.RegisterCallback(MethodOffset.TextureBarrier,      TextureBarrier);
-            state.RegisterCallback(MethodOffset.TextureBarrierTiled, TextureBarrierTiled);
-
-            state.RegisterCallback(MethodOffset.VbElementU8,  VbElementU8);
-            state.RegisterCallback(MethodOffset.VbElementU16, VbElementU16);
-            state.RegisterCallback(MethodOffset.VbElementU32, VbElementU32);
-
-            state.RegisterCallback(MethodOffset.ResetCounter, ResetCounter);
-
-            state.RegisterCallback(MethodOffset.DrawEnd,                      DrawEnd);
-            state.RegisterCallback(MethodOffset.DrawBegin,                    DrawBegin);
-            state.RegisterCallback(MethodOffset.DrawIndexedSmall,             DrawIndexedSmall);
-            state.RegisterCallback(MethodOffset.DrawIndexedSmall2,            DrawIndexedSmall2);
-            state.RegisterCallback(MethodOffset.DrawIndexedSmallIncInstance,  DrawIndexedSmallIncInstance);
-            state.RegisterCallback(MethodOffset.DrawIndexedSmallIncInstance2, DrawIndexedSmallIncInstance2);
-
-            state.RegisterCallback(MethodOffset.IndexBufferCount, SetIndexBufferCount);
-
-            state.RegisterCallback(MethodOffset.Clear, Clear);
-
-            state.RegisterCallback(MethodOffset.Report, Report);
-
-            state.RegisterCallback(MethodOffset.FirmwareCall4, FirmwareCall4);
-
-            state.RegisterCallback(MethodOffset.UniformBufferUpdateData, 16, UniformBufferUpdate);
-
-            state.RegisterCallback(MethodOffset.UniformBufferBindVertex,         UniformBufferBindVertex);
-            state.RegisterCallback(MethodOffset.UniformBufferBindTessControl,    UniformBufferBindTessControl);
-            state.RegisterCallback(MethodOffset.UniformBufferBindTessEvaluation, UniformBufferBindTessEvaluation);
-            state.RegisterCallback(MethodOffset.UniformBufferBindGeometry,       UniformBufferBindGeometry);
-            state.RegisterCallback(MethodOffset.UniformBufferBindFragment,       UniformBufferBindFragment);
-        }
-
-        /// <summary>
-        /// Updates host state based on the current guest GPU state.
-        /// </summary>
-        /// <param name="state">Guest GPU state</param>
-        /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
-        /// <param name="indexCount">Number of index buffer elements used on the draw</param>
-        private void UpdateState(GpuState state, int firstIndex, int indexCount)
-        {
-            bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable);
-
-            if (!tfEnable && _prevTfEnable)
-            {
-                _context.Renderer.Pipeline.EndTransformFeedback();
-                _prevTfEnable = false;
-            }
-
-            FlushUboDirty(state.Channel.MemoryManager);
-
-            // Shaders must be the first one to be updated if modified, because
-            // some of the other state depends on information from the currently
-            // bound shaders.
-            if (state.QueryModified(MethodOffset.ShaderBaseAddress, MethodOffset.ShaderState) || _forceShaderUpdate)
-            {
-                _forceShaderUpdate = false;
-
-                UpdateShaderState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.TfBufferState))
-            {
-                UpdateTfBufferState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.ClipDistanceEnable))
-            {
-                UpdateUserClipState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.RasterizeEnable))
-            {
-                UpdateRasterizerState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.RtColorState,
-                                    MethodOffset.RtDepthStencilState,
-                                    MethodOffset.RtControl,
-                                    MethodOffset.RtDepthStencilSize,
-                                    MethodOffset.RtDepthStencilEnable))
-            {
-                UpdateRenderTargetState(state, useControl: true);
-            }
-
-            if (state.QueryModified(MethodOffset.ScissorState))
-            {
-                UpdateScissorState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.ViewVolumeClipControl))
-            {
-                UpdateDepthClampState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.AlphaTestEnable,
-                                    MethodOffset.AlphaTestRef,
-                                    MethodOffset.AlphaTestFunc))
-            {
-                UpdateAlphaTestState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.DepthTestEnable,
-                                    MethodOffset.DepthWriteEnable,
-                                    MethodOffset.DepthTestFunc))
-            {
-                UpdateDepthTestState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.DepthMode,
-                                    MethodOffset.ViewportTransform,
-                                    MethodOffset.ViewportExtents))
-            {
-                UpdateViewportTransform(state);
-            }
-
-            if (state.QueryModified(MethodOffset.DepthBiasState,
-                                    MethodOffset.DepthBiasFactor,
-                                    MethodOffset.DepthBiasUnits,
-                                    MethodOffset.DepthBiasClamp))
-            {
-                UpdateDepthBiasState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.StencilBackMasks,
-                                    MethodOffset.StencilTestState,
-                                    MethodOffset.StencilBackTestState))
-            {
-                UpdateStencilTestState(state);
-            }
-
-            // Pools.
-            if (state.QueryModified(MethodOffset.SamplerPoolState, MethodOffset.SamplerIndex))
-            {
-                UpdateSamplerPoolState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.TexturePoolState))
-            {
-                UpdateTexturePoolState(state);
-            }
-
-            // Rasterizer state.
-            if (state.QueryModified(MethodOffset.VertexAttribState))
-            {
-                UpdateVertexAttribState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.LineWidthSmooth, MethodOffset.LineSmoothEnable))
-            {
-                UpdateLineState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.PointSize,
-                                    MethodOffset.VertexProgramPointSize,
-                                    MethodOffset.PointSpriteEnable,
-                                    MethodOffset.PointCoordReplace))
-            {
-                UpdatePointState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.PrimitiveRestartState))
-            {
-                UpdatePrimitiveRestartState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.IndexBufferState))
-            {
-                UpdateIndexBufferState(state, firstIndex, indexCount);
-            }
-
-            if (state.QueryModified(MethodOffset.VertexBufferDrawState,
-                                    MethodOffset.VertexBufferInstanced,
-                                    MethodOffset.VertexBufferState,
-                                    MethodOffset.VertexBufferEndAddress))
-            {
-                UpdateVertexBufferState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.FaceState))
-            {
-                UpdateFaceState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.RtColorMaskShared, MethodOffset.RtColorMask))
-            {
-                UpdateRtColorMask(state);
-            }
-
-            if (state.QueryModified(MethodOffset.BlendIndependent,
-                                    MethodOffset.BlendConstant,
-                                    MethodOffset.BlendStateCommon,
-                                    MethodOffset.BlendEnableCommon,
-                                    MethodOffset.BlendEnable,
-                                    MethodOffset.BlendState))
-            {
-                UpdateBlendState(state);
-            }
-
-            if (state.QueryModified(MethodOffset.LogicOpState))
-            {
-                UpdateLogicOpState(state);
-            }
-
-            CommitBindings(state);
-
-            if (tfEnable && !_prevTfEnable)
-            {
-                _context.Renderer.Pipeline.BeginTransformFeedback(Topology);
-                _prevTfEnable = true;
-            }
-        }
-
-        /// <summary>
-        /// Updates Rasterizer primitive discard state based on guest gpu state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateRasterizerState(GpuState state)
-        {
-            Boolean32 enable = state.Get<Boolean32>(MethodOffset.RasterizeEnable);
-            _context.Renderer.Pipeline.SetRasterizerDiscard(!enable);
-        }
-
-        /// <summary>
-        /// Ensures that the bindings are visible to the host GPU.
-        /// Note: this actually performs the binding using the host graphics API.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void CommitBindings(GpuState state)
-        {
-            UpdateStorageBuffers(state);
-
-            state.Channel.TextureManager.CommitGraphicsBindings();
-            state.Channel.BufferManager.CommitGraphicsBindings();
-        }
-
-        /// <summary>
-        /// Updates storage buffer bindings.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateStorageBuffers(GpuState state)
-        {
-            for (int stage = 0; stage < _currentProgramInfo.Length; stage++)
-            {
-                ShaderProgramInfo info = _currentProgramInfo[stage];
-
-                if (info == null)
-                {
-                    continue;
-                }
-
-                for (int index = 0; index < info.SBuffers.Count; index++)
-                {
-                    BufferDescriptor sb = info.SBuffers[index];
-
-                    ulong sbDescAddress = state.Channel.BufferManager.GetGraphicsUniformBufferAddress(stage, 0);
-
-                    int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10;
-
-                    sbDescAddress += (ulong)sbDescOffset;
-
-                    SbDescriptor sbDescriptor = state.Channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
-
-                    state.Channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Updates render targets (color and depth-stencil buffers) based on current render target state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="useControl">Use draw buffers information from render target control register</param>
-        /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
-        private void UpdateRenderTargetState(GpuState state, bool useControl, int singleUse = -1)
-        {
-            var memoryManager = state.Channel.MemoryManager;
-            var rtControl = state.Get<RtControl>(MethodOffset.RtControl);
-
-            int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
-
-            var msaaMode = state.Get<TextureMsaaMode>(MethodOffset.RtMsaaMode);
-
-            int samplesInX = msaaMode.SamplesInX();
-            int samplesInY = msaaMode.SamplesInY();
-
-            var scissor = state.Get<ScreenScissorState>(MethodOffset.ScreenScissorState);
-            Size sizeHint = new Size(scissor.X + scissor.Width, scissor.Y + scissor.Height, 1);
-
-            bool changedScale = false;
-
-            for (int index = 0; index < Constants.TotalRenderTargets; index++)
-            {
-                int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index;
-
-                var colorState = state.Get<RtColorState>(MethodOffset.RtColorState, rtIndex);
-
-                if (index >= count || !IsRtEnabled(colorState))
-                {
-                    changedScale |= state.Channel.TextureManager.SetRenderTargetColor(index, null);
-
-                    continue;
-                }
-
-                Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
-                    memoryManager,
-                    colorState,
-                    samplesInX,
-                    samplesInY,
-                    sizeHint);
-
-                changedScale |= state.Channel.TextureManager.SetRenderTargetColor(index, color);
-            }
-
-            bool dsEnable = state.Get<Boolean32>(MethodOffset.RtDepthStencilEnable);
-
-            Texture depthStencil = null;
-
-            if (dsEnable)
-            {
-                var dsState = state.Get<RtDepthStencilState>(MethodOffset.RtDepthStencilState);
-                var dsSize  = state.Get<Size3D>(MethodOffset.RtDepthStencilSize);
-
-                depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
-                    memoryManager,
-                    dsState,
-                    dsSize,
-                    samplesInX,
-                    samplesInY,
-                    sizeHint);
-            }
-
-            changedScale |= state.Channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);
-
-            if (changedScale)
-            {
-                state.Channel.TextureManager.UpdateRenderTargetScale(singleUse);
-                _context.Renderer.Pipeline.SetRenderTargetScale(state.Channel.TextureManager.RenderTargetScale);
-
-                UpdateViewportTransform(state);
-                UpdateScissorState(state);
-            }
-        }
-
-        /// <summary>
-        /// Checks if a render target color buffer is used.
-        /// </summary>
-        /// <param name="colorState">Color buffer information</param>
-        /// <returns>True if the specified buffer is enabled/used, false otherwise</returns>
-        private static bool IsRtEnabled(RtColorState colorState)
-        {
-            // Colors are disabled by writing 0 to the format.
-            return colorState.Format != 0 && colorState.WidthOrStride != 0;
-        }
-
-        /// <summary>
-        /// Updates host scissor test state based on current GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateScissorState(GpuState state)
-        {
-            for (int index = 0; index < Constants.TotalViewports; index++)
-            {
-                ScissorState scissor = state.Get<ScissorState>(MethodOffset.ScissorState, index);
-
-                bool enable = scissor.Enable && (scissor.X1 != 0 || scissor.Y1 != 0 || scissor.X2 != 0xffff || scissor.Y2 != 0xffff);
-
-                if (enable)
-                {
-                    int x = scissor.X1;
-                    int y = scissor.Y1;
-                    int width = scissor.X2 - x;
-                    int height = scissor.Y2 - y;
-
-                    float scale = state.Channel.TextureManager.RenderTargetScale;
-                    if (scale != 1f)
-                    {
-                        x = (int)(x * scale);
-                        y = (int)(y * scale);
-                        width = (int)Math.Ceiling(width * scale);
-                        height = (int)Math.Ceiling(height * scale);
-                    }
-
-                    _context.Renderer.Pipeline.SetScissor(index, true, x, y, width, height);
-                }
-                else
-                {
-                    _context.Renderer.Pipeline.SetScissor(index, false, 0, 0, 0, 0);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Updates host depth clamp state based on current GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateDepthClampState(GpuState state)
-        {
-            ViewVolumeClipControl clip = state.Get<ViewVolumeClipControl>(MethodOffset.ViewVolumeClipControl);
-            _context.Renderer.Pipeline.SetDepthClamp((clip & ViewVolumeClipControl.DepthClampDisabled) == 0);
-        }
-
-        /// <summary>
-        /// Updates host alpha test state based on current GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateAlphaTestState(GpuState state)
-        {
-            _context.Renderer.Pipeline.SetAlphaTest(
-                state.Get<Boolean32>(MethodOffset.AlphaTestEnable),
-                state.Get<float>(MethodOffset.AlphaTestRef),
-                state.Get<CompareOp>(MethodOffset.AlphaTestFunc));
-        }
-
-        /// <summary>
-        /// Updates host depth test state based on current GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateDepthTestState(GpuState state)
-        {
-            _context.Renderer.Pipeline.SetDepthTest(new DepthTestDescriptor(
-                state.Get<Boolean32>(MethodOffset.DepthTestEnable),
-                state.Get<Boolean32>(MethodOffset.DepthWriteEnable),
-                state.Get<CompareOp>(MethodOffset.DepthTestFunc)));
-        }
-
-        /// <summary>
-        /// Updates host viewport transform and clipping state based on current GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateViewportTransform(GpuState state)
-        {
-            var yControl = state.Get<YControl> (MethodOffset.YControl);
-            var face     = state.Get<FaceState>(MethodOffset.FaceState);
-
-            UpdateFrontFace(yControl, face.FrontFace);
-
-            bool flipY = yControl.HasFlag(YControl.NegateY);
-
-            Span<Viewport> viewports = stackalloc Viewport[Constants.TotalViewports];
-
-            for (int index = 0; index < Constants.TotalViewports; index++)
-            {
-                var transform = state.Get<ViewportTransform>(MethodOffset.ViewportTransform, index);
-                var extents   = state.Get<ViewportExtents>  (MethodOffset.ViewportExtents,   index);
-
-                float scaleX = MathF.Abs(transform.ScaleX);
-                float scaleY = transform.ScaleY;
-
-                if (flipY)
-                {
-                    scaleY = -scaleY;
-                }
-
-                if (!_context.Capabilities.SupportsViewportSwizzle && transform.UnpackSwizzleY() == ViewportSwizzle.NegativeY)
-                {
-                    scaleY = -scaleY;
-                }
-
-                if (index == 0)
-                {
-                    // Try to guess the depth mode being used on the high level API
-                    // based on current transform.
-                    // It is setup like so by said APIs:
-                    // If depth mode is ZeroToOne:
-                    //  TranslateZ = Near
-                    //  ScaleZ = Far - Near
-                    // If depth mode is MinusOneToOne:
-                    //  TranslateZ = (Near + Far) / 2
-                    //  ScaleZ = (Far - Near) / 2
-                    // DepthNear/Far are sorted such as that Near is always less than Far.
-                    DepthMode depthMode = extents.DepthNear != transform.TranslateZ &&
-                                          extents.DepthFar  != transform.TranslateZ ? DepthMode.MinusOneToOne : DepthMode.ZeroToOne;
-
-                    _context.Renderer.Pipeline.SetDepthMode(depthMode);
-                }
-
-                float x = transform.TranslateX - scaleX;
-                float y = transform.TranslateY - scaleY;
-
-                float width  = scaleX * 2;
-                float height = scaleY * 2;
-
-                float scale = state.Channel.TextureManager.RenderTargetScale;
-                if (scale != 1f)
-                {
-                    x *= scale;
-                    y *= scale;
-                    width *= scale;
-                    height *= scale;
-                }
-
-                RectangleF region = new RectangleF(x, y, width, height);
-
-                ViewportSwizzle swizzleX = transform.UnpackSwizzleX();
-                ViewportSwizzle swizzleY = transform.UnpackSwizzleY();
-                ViewportSwizzle swizzleZ = transform.UnpackSwizzleZ();
-                ViewportSwizzle swizzleW = transform.UnpackSwizzleW();
-
-                float depthNear = extents.DepthNear;
-                float depthFar  = extents.DepthFar;
-
-                if (transform.ScaleZ < 0)
-                {
-                    float temp = depthNear;
-                    depthNear  = depthFar;
-                    depthFar   = temp;
-                }
-
-                viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar);
-            }
-
-            _context.Renderer.Pipeline.SetViewports(0, viewports);
-        }
-
-        /// <summary>
-        /// Updates host depth bias (also called polygon offset) state based on current GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateDepthBiasState(GpuState state)
-        {
-            var depthBias = state.Get<DepthBiasState>(MethodOffset.DepthBiasState);
-
-            float factor = state.Get<float>(MethodOffset.DepthBiasFactor);
-            float units  = state.Get<float>(MethodOffset.DepthBiasUnits);
-            float clamp  = state.Get<float>(MethodOffset.DepthBiasClamp);
-
-            PolygonModeMask enables;
-
-            enables  = (depthBias.PointEnable ? PolygonModeMask.Point : 0);
-            enables |= (depthBias.LineEnable  ? PolygonModeMask.Line  : 0);
-            enables |= (depthBias.FillEnable  ? PolygonModeMask.Fill  : 0);
-
-            _context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp);
-        }
-
-        /// <summary>
-        /// Updates host stencil test state based on current GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateStencilTestState(GpuState state)
-        {
-            var backMasks = state.Get<StencilBackMasks>(MethodOffset.StencilBackMasks);
-            var test      = state.Get<StencilTestState>(MethodOffset.StencilTestState);
-            var backTest  = state.Get<StencilBackTestState>(MethodOffset.StencilBackTestState);
-
-            CompareOp backFunc;
-            StencilOp backSFail;
-            StencilOp backDpPass;
-            StencilOp backDpFail;
-            int       backFuncRef;
-            int       backFuncMask;
-            int       backMask;
-
-            if (backTest.TwoSided)
-            {
-                backFunc     = backTest.BackFunc;
-                backSFail    = backTest.BackSFail;
-                backDpPass   = backTest.BackDpPass;
-                backDpFail   = backTest.BackDpFail;
-                backFuncRef  = backMasks.FuncRef;
-                backFuncMask = backMasks.FuncMask;
-                backMask     = backMasks.Mask;
-            }
-            else
-            {
-                backFunc     = test.FrontFunc;
-                backSFail    = test.FrontSFail;
-                backDpPass   = test.FrontDpPass;
-                backDpFail   = test.FrontDpFail;
-                backFuncRef  = test.FrontFuncRef;
-                backFuncMask = test.FrontFuncMask;
-                backMask     = test.FrontMask;
-            }
-
-            _context.Renderer.Pipeline.SetStencilTest(new StencilTestDescriptor(
-                test.Enable,
-                test.FrontFunc,
-                test.FrontSFail,
-                test.FrontDpPass,
-                test.FrontDpFail,
-                test.FrontFuncRef,
-                test.FrontFuncMask,
-                test.FrontMask,
-                backFunc,
-                backSFail,
-                backDpPass,
-                backDpFail,
-                backFuncRef,
-                backFuncMask,
-                backMask));
-        }
-
-        /// <summary>
-        /// Updates current sampler pool address and size based on guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateSamplerPoolState(GpuState state)
-        {
-            var texturePool = state.Get<PoolState>(MethodOffset.TexturePoolState);
-            var samplerPool = state.Get<PoolState>(MethodOffset.SamplerPoolState);
-
-            var samplerIndex = state.Get<SamplerIndex>(MethodOffset.SamplerIndex);
-
-            int maximumId = samplerIndex == SamplerIndex.ViaHeaderIndex
-                ? texturePool.MaximumId
-                : samplerPool.MaximumId;
-
-            state.Channel.TextureManager.SetGraphicsSamplerPool(samplerPool.Address.Pack(), maximumId, samplerIndex);
-        }
-
-        /// <summary>
-        /// Updates current texture pool address and size based on guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateTexturePoolState(GpuState state)
-        {
-            var texturePool = state.Get<PoolState>(MethodOffset.TexturePoolState);
-
-            state.Channel.TextureManager.SetGraphicsTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);
-            state.Channel.TextureManager.SetGraphicsTextureBufferIndex(state.Get<int>(MethodOffset.TextureBufferIndex));
-        }
-
-        /// <summary>
-        /// Updates host vertex attributes based on guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateVertexAttribState(GpuState state)
-        {
-            Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs];
-
-            for (int index = 0; index < Constants.TotalVertexAttribs; index++)
-            {
-                var vertexAttrib = state.Get<VertexAttribState>(MethodOffset.VertexAttribState, index);
-
-                if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
-                {
-                    Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
-
-                    format = Format.R32G32B32A32Float;
-                }
-
-                vertexAttribs[index] = new VertexAttribDescriptor(
-                    vertexAttrib.UnpackBufferIndex(),
-                    vertexAttrib.UnpackOffset(),
-                    vertexAttrib.UnpackIsConstant(),
-                    format);
-            }
-
-            _context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs);
-        }
-
-        /// <summary>
-        /// Updates host line width based on guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateLineState(GpuState state)
-        {
-            float width = state.Get<float>(MethodOffset.LineWidthSmooth);
-            bool smooth = state.Get<Boolean32>(MethodOffset.LineSmoothEnable);
-
-            _context.Renderer.Pipeline.SetLineParameters(width, smooth);
-        }
-
-        /// <summary>
-        /// Updates host point size based on guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdatePointState(GpuState state)
-        {
-            float size = state.Get<float>(MethodOffset.PointSize);
-            bool isProgramPointSize = state.Get<Boolean32>(MethodOffset.VertexProgramPointSize);
-            bool enablePointSprite = state.Get<Boolean32>(MethodOffset.PointSpriteEnable);
-
-            // TODO: Need to figure out a way to map PointCoordReplace enable bit.
-            Origin origin = (state.Get<int>(MethodOffset.PointCoordReplace) & 4) == 0 ? Origin.LowerLeft : Origin.UpperLeft;
-
-            _context.Renderer.Pipeline.SetPointParameters(size, isProgramPointSize, enablePointSprite, origin);
-        }
-
-        /// <summary>
-        /// Updates host primitive restart based on guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdatePrimitiveRestartState(GpuState state)
-        {
-            PrimitiveRestartState primitiveRestart = state.Get<PrimitiveRestartState>(MethodOffset.PrimitiveRestartState);
-
-            _context.Renderer.Pipeline.SetPrimitiveRestart(
-                primitiveRestart.Enable,
-                primitiveRestart.Index);
-        }
-
-        /// <summary>
-        /// Updates host index buffer binding based on guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
-        /// <param name="indexCount">Number of index buffer elements used on the draw</param>
-        private void UpdateIndexBufferState(GpuState state, int firstIndex, int indexCount)
-        {
-            var indexBuffer = state.Get<IndexBufferState>(MethodOffset.IndexBufferState);
-
-            if (indexCount == 0)
-            {
-                return;
-            }
-
-            ulong gpuVa = indexBuffer.Address.Pack();
-
-            // Do not use the end address to calculate the size, because
-            // the result may be much larger than the real size of the index buffer.
-            ulong size = (ulong)(firstIndex + indexCount);
-
-            switch (indexBuffer.Type)
-            {
-                case IndexType.UShort: size *= 2; break;
-                case IndexType.UInt:   size *= 4; break;
-            }
-
-            state.Channel.BufferManager.SetIndexBuffer(gpuVa, size, indexBuffer.Type);
-
-            // The index buffer affects the vertex buffer size calculation, we
-            // need to ensure that they are updated.
-            UpdateVertexBufferState(state);
-        }
-
-        /// <summary>
-        /// Updates host vertex buffer bindings based on guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateVertexBufferState(GpuState state)
-        {
-            _isAnyVbInstanced = false;
-
-            for (int index = 0; index < Constants.TotalVertexBuffers; index++)
-            {
-                var vertexBuffer = state.Get<VertexBufferState>(MethodOffset.VertexBufferState, index);
-
-                if (!vertexBuffer.UnpackEnable())
-                {
-                    state.Channel.BufferManager.SetVertexBuffer(index, 0, 0, 0, 0);
-
-                    continue;
-                }
-
-                GpuVa endAddress = state.Get<GpuVa>(MethodOffset.VertexBufferEndAddress, index);
-
-                ulong address = vertexBuffer.Address.Pack();
-
-                int stride = vertexBuffer.UnpackStride();
-
-                bool instanced = state.Get<Boolean32>(MethodOffset.VertexBufferInstanced + index);
-
-                int divisor = instanced ? vertexBuffer.Divisor : 0;
-
-                _isAnyVbInstanced |= divisor != 0;
-
-                ulong size;
-
-                if (_ibStreamer.HasInlineIndexData || _drawIndexed || stride == 0 || instanced)
-                {
-                    // This size may be (much) larger than the real vertex buffer size.
-                    // Avoid calculating it this way, unless we don't have any other option.
-                    size = endAddress.Pack() - address + 1;
-                }
-                else
-                {
-                    // For non-indexed draws, we can guess the size from the vertex count
-                    // and stride.
-                    int firstInstance = state.Get<int>(MethodOffset.FirstInstance);
-
-                    var drawState = state.Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);
-
-                    size = (ulong)((firstInstance + drawState.First + drawState.Count) * stride);
-                }
-
-                state.Channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor);
-            }
-        }
-
-        /// <summary>
-        /// Updates host face culling and orientation based on guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateFaceState(GpuState state)
-        {
-            var yControl = state.Get<YControl> (MethodOffset.YControl);
-            var face     = state.Get<FaceState>(MethodOffset.FaceState);
-
-            _context.Renderer.Pipeline.SetFaceCulling(face.CullEnable, face.CullFace);
-
-            UpdateFrontFace(yControl, face.FrontFace);
-        }
-
-        /// <summary>
-        /// Updates the front face based on the current front face and the origin.
-        /// </summary>
-        /// <param name="yControl">Y control register value, where the origin is located</param>
-        /// <param name="frontFace">Front face</param>
-        private void UpdateFrontFace(YControl yControl, FrontFace frontFace)
-        {
-            bool isUpperLeftOrigin = !yControl.HasFlag(YControl.TriangleRastFlip);
-
-            if (isUpperLeftOrigin)
-            {
-                frontFace = frontFace == FrontFace.CounterClockwise ? FrontFace.Clockwise : FrontFace.CounterClockwise;
-            }
-
-            _context.Renderer.Pipeline.SetFrontFace(frontFace);
-        }
-
-        /// <summary>
-        /// Updates host render target color masks, based on guest GPU state.
-        /// This defines which color channels are written to each color buffer.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateRtColorMask(GpuState state)
-        {
-            bool rtColorMaskShared = state.Get<Boolean32>(MethodOffset.RtColorMaskShared);
-
-            Span<uint> componentMasks = stackalloc uint[Constants.TotalRenderTargets];
-
-            for (int index = 0; index < Constants.TotalRenderTargets; index++)
-            {
-                var colorMask = state.Get<RtColorMask>(MethodOffset.RtColorMask, rtColorMaskShared ? 0 : index);
-
-                uint componentMask;
-
-                componentMask  = (colorMask.UnpackRed()   ? 1u : 0u);
-                componentMask |= (colorMask.UnpackGreen() ? 2u : 0u);
-                componentMask |= (colorMask.UnpackBlue()  ? 4u : 0u);
-                componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u);
-
-                componentMasks[index] = componentMask;
-            }
-
-            _context.Renderer.Pipeline.SetRenderTargetColorMasks(componentMasks);
-        }
-
-        /// <summary>
-        /// Updates host render target color buffer blending state, based on guest state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateBlendState(GpuState state)
-        {
-            bool blendIndependent = state.Get<Boolean32>(MethodOffset.BlendIndependent);
-            ColorF blendConstant = state.Get<ColorF>(MethodOffset.BlendConstant);
-
-            for (int index = 0; index < Constants.TotalRenderTargets; index++)
-            {
-                BlendDescriptor descriptor;
-
-                if (blendIndependent)
-                {
-                    bool enable = state.Get<Boolean32> (MethodOffset.BlendEnable, index);
-                    var  blend  = state.Get<BlendState>(MethodOffset.BlendState,  index);
-
-                    descriptor = new BlendDescriptor(
-                        enable,
-                        blendConstant,
-                        blend.ColorOp,
-                        blend.ColorSrcFactor,
-                        blend.ColorDstFactor,
-                        blend.AlphaOp,
-                        blend.AlphaSrcFactor,
-                        blend.AlphaDstFactor);
-                }
-                else
-                {
-                    bool enable = state.Get<Boolean32>       (MethodOffset.BlendEnable, 0);
-                    var  blend  = state.Get<BlendStateCommon>(MethodOffset.BlendStateCommon);
-
-                    descriptor = new BlendDescriptor(
-                        enable,
-                        blendConstant,
-                        blend.ColorOp,
-                        blend.ColorSrcFactor,
-                        blend.ColorDstFactor,
-                        blend.AlphaOp,
-                        blend.AlphaSrcFactor,
-                        blend.AlphaDstFactor);
-                }
-
-                _context.Renderer.Pipeline.SetBlendState(index, descriptor);
-            }
-        }
-
-        /// <summary>
-        /// Updates host logical operation state, based on guest state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        public void UpdateLogicOpState(GpuState state)
-        {
-            LogicalOpState logicOpState = state.Get<LogicalOpState>(MethodOffset.LogicOpState);
-
-            _context.Renderer.Pipeline.SetLogicOpState(logicOpState.Enable, logicOpState.LogicalOp);
-        }
-
-        /// <summary>
-        /// Updates host shaders based on the guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateShaderState(GpuState state)
-        {
-            ShaderAddresses addresses = new ShaderAddresses();
-
-            Span<ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1);
-
-            Span<ulong> addressesArray = MemoryMarshal.Cast<ShaderAddresses, ulong>(addressesSpan);
-
-            ulong baseAddress = state.Get<GpuVa>(MethodOffset.ShaderBaseAddress).Pack();
-
-            for (int index = 0; index < 6; index++)
-            {
-                var shader = state.Get<ShaderState>(MethodOffset.ShaderState, index);
-
-                if (!shader.UnpackEnable() && index != 1)
-                {
-                    continue;
-                }
-
-                addressesArray[index] = baseAddress + shader.Offset;
-            }
-
-            ShaderBundle gs = state.Channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(state, addresses);
-
-            byte oldVsClipDistancesWritten = _vsClipDistancesWritten;
-
-            _vsUsesInstanceId       = gs.Shaders[0]?.Info.UsesInstanceId ?? false;
-            _vsClipDistancesWritten = gs.Shaders[0]?.Info.ClipDistancesWritten ?? 0;
-
-            if (oldVsClipDistancesWritten != _vsClipDistancesWritten)
-            {
-                UpdateUserClipState(state);
-            }
-
-            int storageBufferBindingsCount = 0;
-            int uniformBufferBindingsCount = 0;
-
-            for (int stage = 0; stage < Constants.ShaderStages; stage++)
-            {
-                ShaderProgramInfo info = gs.Shaders[stage]?.Info;
-
-                _currentProgramInfo[stage] = info;
-
-                if (info == null)
-                {
-                    state.Channel.TextureManager.SetGraphicsTextures(stage, Array.Empty<TextureBindingInfo>());
-                    state.Channel.TextureManager.SetGraphicsImages(stage, Array.Empty<TextureBindingInfo>());
-                    state.Channel.BufferManager.SetGraphicsStorageBufferBindings(stage, null);
-                    state.Channel.BufferManager.SetGraphicsUniformBufferBindings(stage, null);
-                    continue;
-                }
-
-                var textureBindings = new TextureBindingInfo[info.Textures.Count];
-
-                for (int index = 0; index < info.Textures.Count; index++)
-                {
-                    var descriptor = info.Textures[index];
-
-                    Target target = ShaderTexture.GetTarget(descriptor.Type);
-
-                    textureBindings[index] = new TextureBindingInfo(
-                        target,
-                        descriptor.Binding,
-                        descriptor.CbufSlot,
-                        descriptor.HandleIndex,
-                        descriptor.Flags);
-                }
-
-                state.Channel.TextureManager.SetGraphicsTextures(stage, textureBindings);
-
-                var imageBindings = new TextureBindingInfo[info.Images.Count];
-
-                for (int index = 0; index < info.Images.Count; index++)
-                {
-                    var descriptor = info.Images[index];
-
-                    Target target = ShaderTexture.GetTarget(descriptor.Type);
-                    Format format = ShaderTexture.GetFormat(descriptor.Format);
-
-                    imageBindings[index] = new TextureBindingInfo(
-                        target,
-                        format,
-                        descriptor.Binding,
-                        descriptor.CbufSlot,
-                        descriptor.HandleIndex,
-                        descriptor.Flags);
-                }
-
-                state.Channel.TextureManager.SetGraphicsImages(stage, imageBindings);
-
-                state.Channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
-                state.Channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);
-
-                if (info.SBuffers.Count != 0)
-                {
-                    storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1);
-                }
-
-                if (info.CBuffers.Count != 0)
-                {
-                    uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1);
-                }
-            }
-
-            state.Channel.BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount);
-            state.Channel.BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount);
-
-            _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
-        }
-
-        /// <summary>
-        /// Forces the shaders to be rebound on the next draw.
-        /// </summary>
-        public void ForceShaderUpdate()
-        {
-            _forceShaderUpdate = true;
-        }
-
-        /// <summary>
-        /// Updates transform feedback buffer state based on the guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateTfBufferState(GpuState state)
-        {
-            for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
-            {
-                TfBufferState tfb = state.Get<TfBufferState>(MethodOffset.TfBufferState, index);
-
-                if (!tfb.Enable)
-                {
-                    state.Channel.BufferManager.SetTransformFeedbackBuffer(index, 0, 0);
-
-                    continue;
-                }
-
-                state.Channel.BufferManager.SetTransformFeedbackBuffer(index, tfb.Address.Pack(), (uint)tfb.Size);
-            }
-        }
-
-        /// <summary>
-        /// Updates user-defined clipping based on the guest GPU state.
-        /// </summary>
-        /// <param name="state">Current GPU state</param>
-        private void UpdateUserClipState(GpuState state)
-        {
-            int clipMask = state.Get<int>(MethodOffset.ClipDistanceEnable) & _vsClipDistancesWritten;
-
-            for (int i = 0; i < Constants.TotalClipDistances; ++i)
-            {
-                _context.Renderer.Pipeline.SetUserClipDistance(i, (clipMask & (1 << i)) != 0);
-            }
-        }
-
-        /// <summary>
-        /// Issues a texture barrier.
-        /// This waits until previous texture writes from the GPU to finish, before
-        /// performing new operations with said textures.
-        /// </summary>
-        /// <param name="state">Current GPU state (unused)</param>
-        /// <param name="argument">Method call argument (unused)</param>
-        private void TextureBarrier(GpuState state, int argument)
-        {
-            _context.Renderer.Pipeline.TextureBarrier();
-        }
-
-        /// <summary>
-        /// Issues a texture barrier.
-        /// This waits until previous texture writes from the GPU to finish, before
-        /// performing new operations with said textures.
-        /// This performs a per-tile wait, it is only valid if both the previous write
-        /// and current access has the same access patterns.
-        /// This may be faster than the regular barrier on tile-based rasterizers.
-        /// </summary>
-        /// <param name="state">Current GPU state (unused)</param>
-        /// <param name="argument">Method call argument (unused)</param>
-        private void TextureBarrierTiled(GpuState state, int argument)
-        {
-            _context.Renderer.Pipeline.TextureBarrierTiled();
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs b/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs
index df9021e068..a32956168b 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs
@@ -3,6 +3,9 @@ using System.Runtime.InteropServices;
 
 namespace Ryujinx.Graphics.Gpu.Engine
 {
+    /// <summary>
+    /// Represents temporary storage used by macros.
+    /// </summary>
     [StructLayout(LayoutKind.Sequential, Size = 1024)]
     struct MmeShadowScratch
     {
diff --git a/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs b/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs
new file mode 100644
index 0000000000..060d35cad0
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Graphics.Gpu.Engine
+{
+    /// <summary>
+    /// MME shadow RAM control mode.
+    /// </summary>
+    enum SetMmeShadowRamControlMode
+    {
+        MethodTrack = 0,
+        MethodTrackWithFilter = 1,
+        MethodPassthrough = 2,
+        MethodReplay = 3,
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs
similarity index 56%
rename from Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs
rename to Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs
index 039ed78ea8..85f669850f 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs
@@ -1,37 +1,41 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Types;
 using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.State;
 
-namespace Ryujinx.Graphics.Gpu.Engine
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
 {
-    partial class Methods
+    /// <summary>
+    /// Helper methods used for conditional rendering.
+    /// </summary>
+    static class ConditionalRendering
     {
         /// <summary>
         /// Checks if draws and clears should be performed, according
         /// to currently set conditional rendering conditions.
         /// </summary>
-        /// <param name="state">GPU state</param>
+        /// <param name="context">GPU context</param>
+        /// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
+        /// <param name="address">Conditional rendering buffer address</param>
+        /// <param name="condition">Conditional rendering condition</param>
         /// <returns>True if rendering is enabled, false otherwise</returns>
-        private ConditionalRenderEnabled GetRenderEnable(GpuState state)
+        public static ConditionalRenderEnabled GetRenderEnable(GpuContext context, MemoryManager memoryManager, GpuVa address, Condition condition)
         {
-            ConditionState condState = state.Get<ConditionState>(MethodOffset.ConditionState);
-
-            switch (condState.Condition)
+            switch (condition)
             {
                 case Condition.Always:
                     return ConditionalRenderEnabled.True;
                 case Condition.Never:
                     return ConditionalRenderEnabled.False;
                 case Condition.ResultNonZero:
-                    return CounterNonZero(state, condState.Address.Pack());
+                    return CounterNonZero(context, memoryManager, address.Pack());
                 case Condition.Equal:
-                    return CounterCompare(state, condState.Address.Pack(), true);
+                    return CounterCompare(context, memoryManager, address.Pack(), true);
                 case Condition.NotEqual:
-                    return CounterCompare(state, condState.Address.Pack(), false);
+                    return CounterCompare(context, memoryManager, address.Pack(), false);
             }
 
-            Logger.Warning?.Print(LogClass.Gpu, $"Invalid conditional render condition \"{condState.Condition}\".");
+            Logger.Warning?.Print(LogClass.Gpu, $"Invalid conditional render condition \"{condition}\".");
 
             return ConditionalRenderEnabled.True;
         }
@@ -39,54 +43,56 @@ namespace Ryujinx.Graphics.Gpu.Engine
         /// <summary>
         /// Checks if the counter value at a given GPU memory address is non-zero.
         /// </summary>
-        /// <param name="state">GPU state</param>
+        /// <param name="context">GPU context</param>
+        /// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
         /// <param name="gpuVa">GPU virtual address of the counter value</param>
         /// <returns>True if the value is not zero, false otherwise. Returns host if handling with host conditional rendering</returns>
-        private ConditionalRenderEnabled CounterNonZero(GpuState state, ulong gpuVa)
+        private static ConditionalRenderEnabled CounterNonZero(GpuContext context, MemoryManager memoryManager, ulong gpuVa)
         {
-            ICounterEvent evt = state.Channel.MemoryManager.CounterCache.FindEvent(gpuVa);
+            ICounterEvent evt = memoryManager.CounterCache.FindEvent(gpuVa);
 
             if (evt == null)
             {
                 return ConditionalRenderEnabled.False;
             }
 
-            if (_context.Renderer.Pipeline.TryHostConditionalRendering(evt, 0L, false))
+            if (context.Renderer.Pipeline.TryHostConditionalRendering(evt, 0L, false))
             {
                 return ConditionalRenderEnabled.Host;
             }
             else
             {
                 evt.Flush();
-                return (state.Channel.MemoryManager.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
+                return (memoryManager.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
             }
         }
 
         /// <summary>
         /// Checks if the counter at a given GPU memory address passes a specified equality comparison.
         /// </summary>
-        /// <param name="state">GPU state</param>
+        /// <param name="context">GPU context</param>
+        /// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
         /// <param name="gpuVa">GPU virtual address</param>
         /// <param name="isEqual">True to check if the values are equal, false to check if they are not equal</param>
         /// <returns>True if the condition is met, false otherwise. Returns host if handling with host conditional rendering</returns>
-        private ConditionalRenderEnabled CounterCompare(GpuState state, ulong gpuVa, bool isEqual)
+        private static ConditionalRenderEnabled CounterCompare(GpuContext context, MemoryManager memoryManager, ulong gpuVa, bool isEqual)
         {
-            ICounterEvent evt = FindEvent(state.Channel.MemoryManager.CounterCache, gpuVa);
-            ICounterEvent evt2 = FindEvent(state.Channel.MemoryManager.CounterCache, gpuVa + 16);
+            ICounterEvent evt = FindEvent(memoryManager.CounterCache, gpuVa);
+            ICounterEvent evt2 = FindEvent(memoryManager.CounterCache, gpuVa + 16);
 
             bool useHost;
 
             if (evt != null && evt2 == null)
             {
-                useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, state.Channel.MemoryManager.Read<ulong>(gpuVa + 16), isEqual);
+                useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt, memoryManager.Read<ulong>(gpuVa + 16), isEqual);
             }
             else if (evt == null && evt2 != null)
             {
-                useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, state.Channel.MemoryManager.Read<ulong>(gpuVa), isEqual);
+                useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt2, memoryManager.Read<ulong>(gpuVa), isEqual);
             }
             else if (evt != null && evt2 != null)
             {
-                useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, evt2, isEqual);
+                useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt, evt2, isEqual);
             }
             else
             {
@@ -102,8 +108,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
                 evt?.Flush();
                 evt2?.Flush();
 
-                ulong x = state.Channel.MemoryManager.Read<ulong>(gpuVa);
-                ulong y = state.Channel.MemoryManager.Read<ulong>(gpuVa + 16);
+                ulong x = memoryManager.Read<ulong>(gpuVa);
+                ulong y = memoryManager.Read<ulong>(gpuVa + 16);
 
                 return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
             }
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs
new file mode 100644
index 0000000000..f4006ba990
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+    /// <summary>
+    /// Constant buffer updater.
+    /// </summary>
+    class ConstantBufferUpdater
+    {
+        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;
+
+        /// <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;
+                memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
+
+                _ubFollowUpAddress = 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)
+            {
+                FlushUboDirty();
+
+                _ubByteCount = 0;
+                _ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
+            }
+
+            var byteData = MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1));
+            _channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
+
+            _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)
+            {
+                FlushUboDirty();
+
+                _ubByteCount = 0;
+                _ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
+            }
+
+            var byteData = MemoryMarshal.Cast<int, byte>(data);
+            _channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
+
+            _ubFollowUpAddress = address + size;
+            _ubByteCount += size;
+
+            _state.State.UniformBufferState.Offset += data.Length * 4;
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
new file mode 100644
index 0000000000..d58f175db2
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
@@ -0,0 +1,410 @@
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Types;
+using System.Text;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+    /// <summary>
+    /// Draw manager.
+    /// </summary>
+    class DrawManager
+    {
+        private readonly GpuContext _context;
+        private readonly GpuChannel _channel;
+        private readonly DeviceStateWithShadow<ThreedClassState> _state;
+        private readonly DrawState _drawState;
+
+        private bool _instancedDrawPending;
+        private bool _instancedIndexed;
+
+        private int _instancedFirstIndex;
+        private int _instancedFirstVertex;
+        private int _instancedFirstInstance;
+        private int _instancedIndexCount;
+        private int _instancedDrawStateFirst;
+        private int _instancedDrawStateCount;
+
+        private int _instanceIndex;
+
+        /// <summary>
+        /// Creates a new instance of the draw manager.
+        /// </summary>
+        /// <param name="context">GPU context</param>
+        /// <param name="channel">GPU channel</param>
+        /// <param name="state">Channel state</param>
+        /// <param name="drawState">Draw state</param>
+        public DrawManager(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState)
+        {
+            _context = context;
+            _channel = channel;
+            _state = state;
+            _drawState = drawState;
+        }
+
+        /// <summary>
+        /// Pushes four 8-bit index buffer elements.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        public void VbElementU8(int argument)
+        {
+            _drawState.IbStreamer.VbElementU8(_context.Renderer, argument);
+        }
+
+        /// <summary>
+        /// Pushes two 16-bit index buffer elements.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        public void VbElementU16(int argument)
+        {
+            _drawState.IbStreamer.VbElementU16(_context.Renderer, argument);
+        }
+
+        /// <summary>
+        /// Pushes one 32-bit index buffer element.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        public void VbElementU32(int argument)
+        {
+            _drawState.IbStreamer.VbElementU32(_context.Renderer, argument);
+        }
+
+        /// <summary>
+        /// Finishes the draw call.
+        /// This draws geometry on the bound buffers based on the current GPU state.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        public void DrawEnd(ThreedClass engine, int argument)
+        {
+            DrawEnd(engine, _state.State.IndexBufferState.First, (int)_state.State.IndexBufferCount);
+        }
+
+        /// <summary>
+        /// Finishes the draw call.
+        /// This draws geometry on the bound buffers based on the current GPU state.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
+        /// <param name="indexCount">Number of index buffer elements used on the draw</param>
+        private void DrawEnd(ThreedClass engine, int firstIndex, int indexCount)
+        {
+            ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
+                _context,
+                _channel.MemoryManager,
+                _state.State.RenderEnableAddress,
+                _state.State.RenderEnableCondition);
+
+            if (renderEnable == ConditionalRenderEnabled.False || _instancedDrawPending)
+            {
+                if (renderEnable == ConditionalRenderEnabled.False)
+                {
+                    PerformDeferredDraws();
+                }
+
+                _drawState.DrawIndexed = false;
+
+                if (renderEnable == ConditionalRenderEnabled.Host)
+                {
+                    _context.Renderer.Pipeline.EndHostConditionalRendering();
+                }
+
+                return;
+            }
+
+            _drawState.FirstIndex = firstIndex;
+            _drawState.IndexCount = indexCount;
+
+            engine.UpdateState();
+
+            bool instanced = _drawState.VsUsesInstanceId || _drawState.IsAnyVbInstanced;
+
+            if (instanced)
+            {
+                _instancedDrawPending = true;
+
+                _instancedIndexed = _drawState.DrawIndexed;
+
+                _instancedFirstIndex = firstIndex;
+                _instancedFirstVertex = (int)_state.State.FirstVertex;
+                _instancedFirstInstance = (int)_state.State.FirstInstance;
+
+                _instancedIndexCount = indexCount;
+
+                var drawState = _state.State.VertexBufferDrawState;
+
+                _instancedDrawStateFirst = drawState.First;
+                _instancedDrawStateCount = drawState.Count;
+
+                _drawState.DrawIndexed = false;
+
+                if (renderEnable == ConditionalRenderEnabled.Host)
+                {
+                    _context.Renderer.Pipeline.EndHostConditionalRendering();
+                }
+
+                return;
+            }
+
+            int firstInstance = (int)_state.State.FirstInstance;
+
+            int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount();
+
+            if (inlineIndexCount != 0)
+            {
+                int firstVertex = (int)_state.State.FirstVertex;
+
+                BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
+
+                _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
+
+                _context.Renderer.Pipeline.DrawIndexed(inlineIndexCount, 1, firstIndex, firstVertex, firstInstance);
+            }
+            else if (_drawState.DrawIndexed)
+            {
+                int firstVertex = (int)_state.State.FirstVertex;
+
+                _context.Renderer.Pipeline.DrawIndexed(indexCount, 1, firstIndex, firstVertex, firstInstance);
+            }
+            else
+            {
+                var drawState = _state.State.VertexBufferDrawState;
+
+                _context.Renderer.Pipeline.Draw(drawState.Count, 1, drawState.First, firstInstance);
+            }
+
+            _drawState.DrawIndexed = false;
+
+            if (renderEnable == ConditionalRenderEnabled.Host)
+            {
+                _context.Renderer.Pipeline.EndHostConditionalRendering();
+            }
+        }
+
+        /// <summary>
+        /// Starts draw.
+        /// This sets primitive type and instanced draw parameters.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        public void DrawBegin(int argument)
+        {
+            bool incrementInstance = (argument & (1 << 26)) != 0;
+            bool resetInstance = (argument & (1 << 27)) == 0;
+
+            if (_state.State.PrimitiveTypeOverrideEnable)
+            {
+                PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
+                DrawBegin(incrementInstance, resetInstance, typeOverride.Convert());
+            }
+            else
+            {
+                PrimitiveType type = (PrimitiveType)(argument & 0xffff);
+                DrawBegin(incrementInstance, resetInstance, type.Convert());
+            }
+        }
+
+        /// <summary>
+        /// Starts draw.
+        /// This sets primitive type and instanced draw parameters.
+        /// </summary>
+        /// <param name="incrementInstance">Indicates if the current instance should be incremented</param>
+        /// <param name="resetInstance">Indicates if the current instance should be set to zero</param>
+        /// <param name="topology">Primitive topology</param>
+        private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveTopology topology)
+        {
+            if (incrementInstance)
+            {
+                _instanceIndex++;
+            }
+            else if (resetInstance)
+            {
+                PerformDeferredDraws();
+
+                _instanceIndex = 0;
+            }
+
+            _context.Renderer.Pipeline.SetPrimitiveTopology(topology);
+
+            _drawState.Topology = topology;
+        }
+
+        /// <summary>
+        /// Sets the index buffer count.
+        /// This also sets internal state that indicates that the next draw is an indexed draw.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        public void SetIndexBufferCount(int argument)
+        {
+            _drawState.DrawIndexed = true;
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with a low number of index buffer elements.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        public void DrawIndexedSmall(ThreedClass engine, int argument)
+        {
+            DrawIndexedSmall(engine, argument, false);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with a low number of index buffer elements.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        public void DrawIndexedSmall2(ThreedClass engine, int argument)
+        {
+            DrawIndexedSmall(engine, argument);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with a low number of index buffer elements,
+        /// while also pre-incrementing the current instance value.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        public void DrawIndexedSmallIncInstance(ThreedClass engine, int argument)
+        {
+            DrawIndexedSmall(engine, argument, true);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with a low number of index buffer elements,
+        /// while also pre-incrementing the current instance value.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        public void DrawIndexedSmallIncInstance2(ThreedClass engine, int argument)
+        {
+            DrawIndexedSmallIncInstance(engine, argument);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with a low number of index buffer elements,
+        /// while optionally also pre-incrementing the current instance value.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        /// <param name="instanced">True to increment the current instance value, false otherwise</param>
+        private void DrawIndexedSmall(ThreedClass engine, int argument, bool instanced)
+        {
+            PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
+
+            DrawBegin(instanced, !instanced, typeOverride.Convert());
+
+            int firstIndex = argument & 0xffff;
+            int indexCount = (argument >> 16) & 0xfff;
+
+            bool oldDrawIndexed = _drawState.DrawIndexed;
+
+            _drawState.DrawIndexed = true;
+
+            DrawEnd(engine, firstIndex, indexCount);
+
+            _drawState.DrawIndexed = oldDrawIndexed;
+        }
+
+        /// <summary>
+        /// Perform any deferred draws.
+        /// This is used for instanced draws.
+        /// Since each instance is a separate draw, we defer the draw and accumulate the instance count.
+        /// Once we detect the last instanced draw, then we perform the host instanced draw,
+        /// with the accumulated instance count.
+        /// </summary>
+        public void PerformDeferredDraws()
+        {
+            // Perform any pending instanced draw.
+            if (_instancedDrawPending)
+            {
+                _instancedDrawPending = false;
+
+                if (_instancedIndexed)
+                {
+                    _context.Renderer.Pipeline.DrawIndexed(
+                        _instancedIndexCount,
+                        _instanceIndex + 1,
+                        _instancedFirstIndex,
+                        _instancedFirstVertex,
+                        _instancedFirstInstance);
+                }
+                else
+                {
+                    _context.Renderer.Pipeline.Draw(
+                        _instancedDrawStateCount,
+                        _instanceIndex + 1,
+                        _instancedDrawStateFirst,
+                        _instancedFirstInstance);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Clears the current color and depth-stencil buffers.
+        /// Which buffers should be cleared is also specified on the argument.
+        /// </summary>
+        /// <param name="engine">3D engine where this method is being called</param>
+        /// <param name="argument">Method call argument</param>
+        public void Clear(ThreedClass engine, int argument)
+        {
+            ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
+                _context,
+                _channel.MemoryManager,
+                _state.State.RenderEnableAddress,
+                _state.State.RenderEnableCondition);
+
+            if (renderEnable == ConditionalRenderEnabled.False)
+            {
+                return;
+            }
+
+            // Scissor and rasterizer discard also affect clears.
+            engine.UpdateState((1UL << StateUpdater.RasterizerStateIndex) | (1UL << StateUpdater.ScissorStateIndex));
+
+            int index = (argument >> 6) & 0xf;
+
+            engine.UpdateRenderTargetState(useControl: false, singleUse: index);
+
+            _channel.TextureManager.UpdateRenderTargets();
+
+            bool clearDepth = (argument & 1) != 0;
+            bool clearStencil = (argument & 2) != 0;
+
+            uint componentMask = (uint)((argument >> 2) & 0xf);
+
+            if (componentMask != 0)
+            {
+                var clearColor = _state.State.ClearColors;
+
+                ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha);
+
+                _context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color);
+            }
+
+            if (clearDepth || clearStencil)
+            {
+                float depthValue = _state.State.ClearDepthValue;
+                int stencilValue = (int)_state.State.ClearStencilValue;
+
+                int stencilMask = 0;
+
+                if (clearStencil)
+                {
+                    stencilMask = _state.State.StencilTestState.FrontMask;
+                }
+
+                _context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
+                    depthValue,
+                    clearDepth,
+                    stencilValue,
+                    stencilMask);
+            }
+
+            engine.UpdateRenderTargetState(useControl: true);
+
+            if (renderEnable == ConditionalRenderEnabled.Host)
+            {
+                _context.Renderer.Pipeline.EndHostConditionalRendering();
+            }
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs
new file mode 100644
index 0000000000..ff186acc98
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs
@@ -0,0 +1,45 @@
+using Ryujinx.Graphics.GAL;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+    /// <summary>
+    /// Draw state.
+    /// </summary>
+    class DrawState
+    {
+        /// <summary>
+        /// First index to be used for the draw on the index buffer.
+        /// </summary>
+        public int FirstIndex;
+
+        /// <summary>
+        /// Number of indices to be used for the draw on the index buffer.
+        /// </summary>
+        public int IndexCount;
+
+        /// <summary>
+        /// Indicates if the next draw will be a indexed draw.
+        /// </summary>
+        public bool DrawIndexed;
+
+        /// <summary>
+        /// Indicates if any of the currently used vertex shaders reads the instance ID.
+        /// </summary>
+        public bool VsUsesInstanceId;
+
+        /// <summary>
+        /// Indicates if any of the currently used vertex buffers is instanced.
+        /// </summary>
+        public bool IsAnyVbInstanced;
+
+        /// <summary>
+        /// Primitive topology for the next draw.
+        /// </summary>
+        public PrimitiveTopology Topology;
+
+        /// <summary>
+        /// Index buffer data streamer for inline index buffer updates, such as those used in legacy OpenGL.
+        /// </summary>
+        public IbStreamer IbStreamer = new IbStreamer();
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/IbStreamer.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs
similarity index 96%
rename from Ryujinx.Graphics.Gpu/Engine/IbStreamer.cs
rename to Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs
index b407c941e0..96b2ed9c70 100644
--- a/Ryujinx.Graphics.Gpu/Engine/IbStreamer.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs
@@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL;
 using System;
 using System.Runtime.InteropServices;
 
-namespace Ryujinx.Graphics.Gpu.Engine
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
 {
     /// <summary>
     /// Holds inline index buffer state.
@@ -15,6 +15,9 @@ namespace Ryujinx.Graphics.Gpu.Engine
         private int _inlineIndexBufferSize;
         private int _inlineIndexCount;
 
+        /// <summary>
+        /// Indicates if any index buffer data has been pushed.
+        /// </summary>
         public bool HasInlineIndexData => _inlineIndexCount != 0;
 
         /// <summary>
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs
new file mode 100644
index 0000000000..37fa51c4e8
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs
@@ -0,0 +1,222 @@
+using Ryujinx.Common;
+using Ryujinx.Graphics.GAL;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+    /// <summary>
+    /// Semaphore updater.
+    /// </summary>
+    class SemaphoreUpdater
+    {
+        private const int NsToTicksFractionNumerator = 384;
+        private const int NsToTicksFractionDenominator = 625;
+
+        /// <summary>
+        /// GPU semaphore operation.
+        /// </summary>
+        private enum SemaphoreOperation
+        {
+            Release = 0,
+            Acquire = 1,
+            Counter = 2
+        }
+
+        /// <summary>
+        /// Counter type for GPU counter reset.
+        /// </summary>
+        private enum ResetCounterType
+        {
+            SamplesPassed = 1,
+            ZcullStats = 2,
+            TransformFeedbackPrimitivesWritten = 0x10,
+            InputVertices = 0x12,
+            InputPrimitives = 0x13,
+            VertexShaderInvocations = 0x15,
+            TessControlShaderInvocations = 0x16,
+            TessEvaluationShaderInvocations = 0x17,
+            TessEvaluationShaderPrimitives = 0x18,
+            GeometryShaderInvocations = 0x1a,
+            GeometryShaderPrimitives = 0x1b,
+            ClipperInputPrimitives = 0x1c,
+            ClipperOutputPrimitives = 0x1d,
+            FragmentShaderInvocations = 0x1e,
+            PrimitivesGenerated = 0x1f
+        }
+
+        /// <summary>
+        /// Counter type for GPU counter reporting.
+        /// </summary>
+        private enum ReportCounterType
+        {
+            Zero = 0,
+            InputVertices = 1,
+            InputPrimitives = 3,
+            VertexShaderInvocations = 5,
+            GeometryShaderInvocations = 7,
+            GeometryShaderPrimitives = 9,
+            ZcullStats0 = 0xa,
+            TransformFeedbackPrimitivesWritten = 0xb,
+            ZcullStats1 = 0xc,
+            ZcullStats2 = 0xe,
+            ClipperInputPrimitives = 0xf,
+            ZcullStats3 = 0x10,
+            ClipperOutputPrimitives = 0x11,
+            PrimitivesGenerated = 0x12,
+            FragmentShaderInvocations = 0x13,
+            SamplesPassed = 0x15,
+            TransformFeedbackOffset = 0x1a,
+            TessControlShaderInvocations = 0x1b,
+            TessEvaluationShaderInvocations = 0x1d,
+            TessEvaluationShaderPrimitives = 0x1f
+        }
+
+        private readonly GpuContext _context;
+        private readonly GpuChannel _channel;
+        private readonly DeviceStateWithShadow<ThreedClassState> _state;
+
+        /// <summary>
+        /// Creates a new instance of the semaphore updater.
+        /// </summary>
+        /// <param name="context">GPU context</param>
+        /// <param name="channel">GPU channel</param>
+        /// <param name="state">Channel state</param>
+        public SemaphoreUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state)
+        {
+            _context = context;
+            _channel = channel;
+            _state = state;
+        }
+
+        /// <summary>
+        /// Resets the value of an internal GPU counter back to zero.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        public void ResetCounter(int argument)
+        {
+            ResetCounterType type = (ResetCounterType)argument;
+
+            switch (type)
+            {
+                case ResetCounterType.SamplesPassed:
+                    _context.Renderer.ResetCounter(CounterType.SamplesPassed);
+                    break;
+                case ResetCounterType.PrimitivesGenerated:
+                    _context.Renderer.ResetCounter(CounterType.PrimitivesGenerated);
+                    break;
+                case ResetCounterType.TransformFeedbackPrimitivesWritten:
+                    _context.Renderer.ResetCounter(CounterType.TransformFeedbackPrimitivesWritten);
+                    break;
+            }
+        }
+
+        /// <summary>
+        /// Writes a GPU counter to guest memory.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        public void Report(int argument)
+        {
+            SemaphoreOperation op = (SemaphoreOperation)(argument & 3);
+            ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f);
+
+            switch (op)
+            {
+                case SemaphoreOperation.Release: ReleaseSemaphore(); break;
+                case SemaphoreOperation.Counter: ReportCounter(type); break;
+            }
+        }
+
+        /// <summary>
+        /// Writes (or Releases) a GPU semaphore value to guest memory.
+        /// </summary>
+        private void ReleaseSemaphore()
+        {
+            _channel.MemoryManager.Write(_state.State.SemaphoreAddress.Pack(), _state.State.SemaphorePayload);
+
+            _context.AdvanceSequence();
+        }
+
+        /// <summary>
+        /// Packed GPU counter data (including GPU timestamp) in memory.
+        /// </summary>
+        private struct CounterData
+        {
+            public ulong Counter;
+            public ulong Timestamp;
+        }
+
+        /// <summary>
+        /// Writes a GPU counter to guest memory.
+        /// This also writes the current timestamp value.
+        /// </summary>
+        /// <param name="type">Counter to be written to memory</param>
+        private void ReportCounter(ReportCounterType type)
+        {
+            ulong gpuVa = _state.State.SemaphoreAddress.Pack();
+
+            ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
+
+            if (GraphicsConfig.FastGpuTime)
+            {
+                // Divide by some amount to report time as if operations were performed faster than they really are.
+                // This can prevent some games from switching to a lower resolution because rendering is too slow.
+                ticks /= 256;
+            }
+
+            ICounterEvent counter = null;
+
+            void resultHandler(object evt, ulong result)
+            {
+                CounterData counterData = new CounterData
+                {
+                    Counter = result,
+                    Timestamp = ticks
+                };
+
+                if (counter?.Invalid != true)
+                {
+                    _channel.MemoryManager.Write(gpuVa, counterData);
+                }
+            }
+
+            switch (type)
+            {
+                case ReportCounterType.Zero:
+                    resultHandler(null, 0);
+                    break;
+                case ReportCounterType.SamplesPassed:
+                    counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler);
+                    break;
+                case ReportCounterType.PrimitivesGenerated:
+                    counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler);
+                    break;
+                case ReportCounterType.TransformFeedbackPrimitivesWritten:
+                    counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler);
+                    break;
+            }
+
+            _channel.MemoryManager.CounterCache.AddOrUpdate(gpuVa, counter);
+        }
+
+        /// <summary>
+        /// Converts a nanoseconds timestamp value to Maxwell time ticks.
+        /// </summary>
+        /// <remarks>
+        /// The frequency is 614400000 Hz.
+        /// </remarks>
+        /// <param name="nanoseconds">Timestamp in nanoseconds</param>
+        /// <returns>Maxwell ticks</returns>
+        private static ulong ConvertNanosecondsToTicks(ulong nanoseconds)
+        {
+            // We need to divide first to avoid overflows.
+            // We fix up the result later by calculating the difference and adding
+            // that to the result.
+            ulong divided = nanoseconds / NsToTicksFractionDenominator;
+
+            ulong rounded = divided * NsToTicksFractionDenominator;
+
+            ulong errorBias = (nanoseconds - rounded) * NsToTicksFractionNumerator / NsToTicksFractionDenominator;
+
+            return divided * NsToTicksFractionNumerator + errorBias;
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
new file mode 100644
index 0000000000..2af7a4023a
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
@@ -0,0 +1,166 @@
+using Ryujinx.Graphics.Device;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+    /// <summary>
+    /// State update callback entry, with the callback function and associated field names.
+    /// </summary>
+    struct StateUpdateCallbackEntry
+    {
+        /// <summary>
+        /// Callback function, to be called if the register was written as the state needs to be updated.
+        /// </summary>
+        public Action Callback { get; }
+
+        /// <summary>
+        /// Name of the state fields (registers) associated with the callback function.
+        /// </summary>
+        public string[] FieldNames { get; }
+
+        /// <summary>
+        /// Creates a new state update callback entry.
+        /// </summary>
+        /// <param name="callback">Callback function, to be called if the register was written as the state needs to be updated</param>
+        /// <param name="fieldNames">Name of the state fields (registers) associated with the callback function</param>
+        public StateUpdateCallbackEntry(Action callback, params string[] fieldNames)
+        {
+            Callback = callback;
+            FieldNames = fieldNames;
+        }
+    }
+
+    /// <summary>
+    /// GPU state update tracker.
+    /// </summary>
+    /// <typeparam name="TState">State type</typeparam>
+    class StateUpdateTracker<TState>
+    {
+        private const int BlockSize = 0xe00;
+        private const int RegisterSize = sizeof(uint);
+
+        private readonly byte[] _registerToGroupMapping;
+        private readonly Action[] _callbacks;
+        private ulong _dirtyMask;
+
+        /// <summary>
+        /// Creates a new instance of the state update tracker.
+        /// </summary>
+        /// <param name="entries">Update tracker callback entries</param>
+        public StateUpdateTracker(StateUpdateCallbackEntry[] entries)
+        {
+            _registerToGroupMapping = new byte[BlockSize];
+            _callbacks = new Action[entries.Length];
+
+            var fieldToDelegate = new Dictionary<string, int>();
+
+            for (int entryIndex = 0; entryIndex < entries.Length; entryIndex++)
+            {
+                var entry = entries[entryIndex];
+
+                foreach (var fieldName in entry.FieldNames)
+                {
+                    fieldToDelegate.Add(fieldName, entryIndex);
+                }
+
+                _callbacks[entryIndex] = entry.Callback;
+            }
+
+            var fields = typeof(TState).GetFields();
+            int offset = 0;
+
+            for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
+            {
+                var field = fields[fieldIndex];
+
+                int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
+
+                if (fieldToDelegate.TryGetValue(field.Name, out int entryIndex))
+                {
+                    for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
+                    {
+                        _registerToGroupMapping[(offset + i) / RegisterSize] = (byte)(entryIndex + 1);
+                    }
+                }
+
+                offset += sizeOfField;
+            }
+
+            Debug.Assert(offset == Unsafe.SizeOf<TState>());
+        }
+
+        /// <summary>
+        /// Sets a register as modified.
+        /// </summary>
+        /// <param name="offset">Register offset in bytes</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void SetDirty(int offset)
+        {
+            uint index = (uint)offset / RegisterSize;
+
+            if (index < BlockSize)
+            {
+                int groupIndex = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_registerToGroupMapping), (IntPtr)index);
+                if (groupIndex != 0)
+                {
+                    groupIndex--;
+                    _dirtyMask |= 1UL << groupIndex;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Forces a register group as dirty, by index.
+        /// </summary>
+        /// <param name="groupIndex">Index of the group to be dirtied</param>
+        public void ForceDirty(int groupIndex)
+        {
+            if ((uint)groupIndex >= _callbacks.Length)
+            {
+                throw new ArgumentOutOfRangeException(nameof(groupIndex));
+            }
+
+            _dirtyMask |= 1UL << groupIndex;
+        }
+
+        /// <summary>
+        /// Forces all register groups as dirty, triggering a full update on the next call to <see cref="Update"/>.
+        /// </summary>
+        public void SetAllDirty()
+        {
+            Debug.Assert(_callbacks.Length <= sizeof(ulong) * 8);
+            _dirtyMask = ulong.MaxValue >> ((sizeof(ulong) * 8) - _callbacks.Length);
+        }
+
+        /// <summary>
+        /// Check all the groups specified by <paramref name="checkMask"/> for modification, and update if modified.
+        /// </summary>
+        /// <param name="checkMask">Mask, where each bit set corresponds to a group index that should be checked</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Update(ulong checkMask)
+        {
+            ulong mask = _dirtyMask & checkMask;
+            if (mask == 0)
+            {
+                return;
+            }
+
+            do
+            {
+                int groupIndex = BitOperations.TrailingZeroCount(mask);
+
+                _callbacks[groupIndex]();
+
+                mask &= ~(1UL << groupIndex);
+            }
+            while (mask != 0);
+
+            _dirtyMask &= ~checkMask;
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
new file mode 100644
index 0000000000..4ff084e94b
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -0,0 +1,1044 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Types;
+using Ryujinx.Graphics.Gpu.Image;
+using Ryujinx.Graphics.Gpu.Shader;
+using Ryujinx.Graphics.Shader;
+using Ryujinx.Graphics.Texture;
+using System;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+    /// <summary>
+    /// GPU state updater.
+    /// </summary>
+    class StateUpdater
+    {
+        public const int ShaderStateIndex = 0;
+        public const int RasterizerStateIndex = 1;
+        public const int ScissorStateIndex = 2;
+        public const int VertexBufferStateIndex = 3;
+
+        private readonly GpuContext _context;
+        private readonly GpuChannel _channel;
+        private readonly DeviceStateWithShadow<ThreedClassState> _state;
+        private readonly DrawState _drawState;
+
+        private readonly StateUpdateTracker<ThreedClassState> _updateTracker;
+
+        private readonly ShaderProgramInfo[] _currentProgramInfo;
+
+        private byte _vsClipDistancesWritten;
+
+        private bool _prevDrawIndexed;
+        private bool _prevTfEnable;
+
+        /// <summary>
+        /// Creates a new instance of the state updater.
+        /// </summary>
+        /// <param name="context">GPU context</param>
+        /// <param name="channel">GPU channel</param>
+        /// <param name="state">3D engine state</param>
+        /// <param name="drawState">Draw state</param>
+        public StateUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState)
+        {
+            _context = context;
+            _channel = channel;
+            _state = state;
+            _drawState = drawState;
+            _currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
+
+            // ShaderState must be the first, as other state updates depends on information from the currently bound shader.
+            // Rasterizer and scissor states are checked by render target clear, their indexes
+            // must be updated on the constants "RasterizerStateIndex" and "ScissorStateIndex" if modified.
+            // The vertex buffer state may be forced dirty when a indexed draw starts, the "VertexBufferStateIndex"
+            // constant must be updated if modified.
+            // The order of the other state updates doesn't matter.
+            _updateTracker = new StateUpdateTracker<ThreedClassState>(new[]
+            {
+                new StateUpdateCallbackEntry(UpdateShaderState,
+                    nameof(ThreedClassState.ShaderBaseAddress),
+                    nameof(ThreedClassState.ShaderState)),
+
+                new StateUpdateCallbackEntry(UpdateRasterizerState, nameof(ThreedClassState.RasterizeEnable)),
+                new StateUpdateCallbackEntry(UpdateScissorState, nameof(ThreedClassState.ScissorState)),
+
+                new StateUpdateCallbackEntry(UpdateVertexBufferState,
+                    nameof(ThreedClassState.VertexBufferDrawState),
+                    nameof(ThreedClassState.VertexBufferInstanced),
+                    nameof(ThreedClassState.VertexBufferState),
+                    nameof(ThreedClassState.VertexBufferEndAddress)),
+
+                new StateUpdateCallbackEntry(UpdateTfBufferState, nameof(ThreedClassState.TfBufferState)),
+                new StateUpdateCallbackEntry(UpdateUserClipState, nameof(ThreedClassState.ClipDistanceEnable)),
+
+                new StateUpdateCallbackEntry(UpdateRenderTargetState,
+                    nameof(ThreedClassState.RtColorState),
+                    nameof(ThreedClassState.RtDepthStencilState),
+                    nameof(ThreedClassState.RtControl),
+                    nameof(ThreedClassState.RtDepthStencilSize),
+                    nameof(ThreedClassState.RtDepthStencilEnable)),
+
+                new StateUpdateCallbackEntry(UpdateDepthClampState, nameof(ThreedClassState.ViewVolumeClipControl)),
+
+                new StateUpdateCallbackEntry(UpdateAlphaTestState,
+                    nameof(ThreedClassState.AlphaTestEnable),
+                    nameof(ThreedClassState.AlphaTestRef),
+                    nameof(ThreedClassState.AlphaTestFunc)),
+
+                new StateUpdateCallbackEntry(UpdateDepthTestState,
+                    nameof(ThreedClassState.DepthTestEnable),
+                    nameof(ThreedClassState.DepthWriteEnable),
+                    nameof(ThreedClassState.DepthTestFunc)),
+
+                new StateUpdateCallbackEntry(UpdateViewportTransform,
+                    nameof(ThreedClassState.DepthMode),
+                    nameof(ThreedClassState.ViewportTransform),
+                    nameof(ThreedClassState.ViewportExtents),
+                    nameof(ThreedClassState.YControl)),
+
+                new StateUpdateCallbackEntry(UpdateDepthBiasState,
+                    nameof(ThreedClassState.DepthBiasState),
+                    nameof(ThreedClassState.DepthBiasFactor),
+                    nameof(ThreedClassState.DepthBiasUnits),
+                    nameof(ThreedClassState.DepthBiasClamp)),
+
+                new StateUpdateCallbackEntry(UpdateStencilTestState,
+                    nameof(ThreedClassState.StencilBackMasks),
+                    nameof(ThreedClassState.StencilTestState),
+                    nameof(ThreedClassState.StencilBackTestState)),
+
+                new StateUpdateCallbackEntry(UpdateSamplerPoolState,
+                    nameof(ThreedClassState.SamplerPoolState),
+                    nameof(ThreedClassState.SamplerIndex)),
+
+                new StateUpdateCallbackEntry(UpdateTexturePoolState, nameof(ThreedClassState.TexturePoolState)),
+                new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)),
+
+                new StateUpdateCallbackEntry(UpdateLineState,
+                    nameof(ThreedClassState.LineWidthSmooth),
+                    nameof(ThreedClassState.LineSmoothEnable)),
+
+                new StateUpdateCallbackEntry(UpdatePointState,
+                    nameof(ThreedClassState.PointSize),
+                    nameof(ThreedClassState.VertexProgramPointSize),
+                    nameof(ThreedClassState.PointSpriteEnable),
+                    nameof(ThreedClassState.PointCoordReplace)),
+
+                new StateUpdateCallbackEntry(UpdatePrimitiveRestartState, nameof(ThreedClassState.PrimitiveRestartState)),
+
+                new StateUpdateCallbackEntry(UpdateIndexBufferState,
+                    nameof(ThreedClassState.IndexBufferState),
+                    nameof(ThreedClassState.IndexBufferCount)),
+
+                new StateUpdateCallbackEntry(UpdateFaceState, nameof(ThreedClassState.FaceState)),
+
+                new StateUpdateCallbackEntry(UpdateRtColorMask,
+                    nameof(ThreedClassState.RtColorMaskShared),
+                    nameof(ThreedClassState.RtColorMask)),
+
+                new StateUpdateCallbackEntry(UpdateBlendState,
+                    nameof(ThreedClassState.BlendIndependent),
+                    nameof(ThreedClassState.BlendConstant),
+                    nameof(ThreedClassState.BlendStateCommon),
+                    nameof(ThreedClassState.BlendEnableCommon),
+                    nameof(ThreedClassState.BlendEnable),
+                    nameof(ThreedClassState.BlendState)),
+
+                new StateUpdateCallbackEntry(UpdateLogicOpState, nameof(ThreedClassState.LogicOpState))
+            });
+        }
+
+        /// <summary>
+        /// Sets a register at a specific offset as dirty.
+        /// This must be called if the register value was modified.
+        /// </summary>
+        /// <param name="offset">Register offset</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void SetDirty(int offset)
+        {
+            _updateTracker.SetDirty(offset);
+        }
+
+        /// <summary>
+        /// Force all the guest state to be marked as dirty.
+        /// The next call to <see cref="Update"/> will update all the host state.
+        /// </summary>
+        public void SetAllDirty()
+        {
+            _updateTracker.SetAllDirty();
+        }
+
+        /// <summary>
+        /// Updates host state for any modified guest state, since the last time this function was called.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Update()
+        {
+            // The vertex buffer size is calculated using a different
+            // method when doing indexed draws, so we need to make sure
+            // to update the vertex buffers if we are doing a regular
+            // draw after a indexed one and vice-versa.
+            if (_drawState.DrawIndexed != _prevDrawIndexed)
+            {
+                _updateTracker.ForceDirty(VertexBufferStateIndex);
+                _prevDrawIndexed = _drawState.DrawIndexed;
+            }
+
+            bool tfEnable = _state.State.TfEnable;
+
+            if (!tfEnable && _prevTfEnable)
+            {
+                _context.Renderer.Pipeline.EndTransformFeedback();
+                _prevTfEnable = false;
+            }
+
+            _updateTracker.Update(ulong.MaxValue);
+
+            CommitBindings();
+
+            if (tfEnable && !_prevTfEnable)
+            {
+                _context.Renderer.Pipeline.BeginTransformFeedback(_drawState.Topology);
+                _prevTfEnable = true;
+            }
+        }
+
+        /// <summary>
+        /// Updates the host state for any modified guest state group with the respective bit set on <paramref name="mask"/>.
+        /// </summary>
+        /// <param name="mask">Mask, where each bit set corresponds to a group index that should be checked and updated</param>
+        public void Update(ulong mask)
+        {
+            _updateTracker.Update(mask);
+        }
+
+        /// <summary>
+        /// Ensures that the bindings are visible to the host GPU.
+        /// Note: this actually performs the binding using the host graphics API.
+        /// </summary>
+        private void CommitBindings()
+        {
+            UpdateStorageBuffers();
+
+            _channel.TextureManager.CommitGraphicsBindings();
+            _channel.BufferManager.CommitGraphicsBindings();
+        }
+
+        /// <summary>
+        /// Updates storage buffer bindings.
+        /// </summary>
+        private void UpdateStorageBuffers()
+        {
+            for (int stage = 0; stage < Constants.ShaderStages; stage++)
+            {
+                ShaderProgramInfo info = _currentProgramInfo[stage];
+
+                if (info == null)
+                {
+                    continue;
+                }
+
+                for (int index = 0; index < info.SBuffers.Count; index++)
+                {
+                    BufferDescriptor sb = info.SBuffers[index];
+
+                    ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, 0);
+
+                    int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10;
+
+                    sbDescAddress += (ulong)sbDescOffset;
+
+                    SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
+
+                    _channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Updates transform feedback buffer state based on the guest GPU state.
+        /// </summary>
+        private void UpdateTfBufferState()
+        {
+            for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
+            {
+                TfBufferState tfb = _state.State.TfBufferState[index];
+
+                if (!tfb.Enable)
+                {
+                    _channel.BufferManager.SetTransformFeedbackBuffer(index, 0, 0);
+
+                    continue;
+                }
+
+                _channel.BufferManager.SetTransformFeedbackBuffer(index, tfb.Address.Pack(), (uint)tfb.Size);
+            }
+        }
+
+        /// <summary>
+        /// Updates Rasterizer primitive discard state based on guest gpu state.
+        /// </summary>
+        private void UpdateRasterizerState()
+        {
+            bool enable = _state.State.RasterizeEnable;
+            _context.Renderer.Pipeline.SetRasterizerDiscard(!enable);
+        }
+
+        /// <summary>
+        /// Updates render targets (color and depth-stencil buffers) based on current render target state.
+        /// </summary>
+        private void UpdateRenderTargetState()
+        {
+            UpdateRenderTargetState(true);
+        }
+
+        /// <summary>
+        /// Updates render targets (color and depth-stencil buffers) based on current render target state.
+        /// </summary>
+        /// <param name="useControl">Use draw buffers information from render target control register</param>
+        /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
+        public void UpdateRenderTargetState(bool useControl, int singleUse = -1)
+        {
+            var memoryManager = _channel.MemoryManager;
+            var rtControl = _state.State.RtControl;
+
+            int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
+
+            var msaaMode = _state.State.RtMsaaMode;
+
+            int samplesInX = msaaMode.SamplesInX();
+            int samplesInY = msaaMode.SamplesInY();
+
+            var scissor = _state.State.ScreenScissorState;
+            Size sizeHint = new Size(scissor.X + scissor.Width, scissor.Y + scissor.Height, 1);
+
+            bool changedScale = false;
+
+            for (int index = 0; index < Constants.TotalRenderTargets; index++)
+            {
+                int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index;
+
+                var colorState = _state.State.RtColorState[rtIndex];
+
+                if (index >= count || !IsRtEnabled(colorState))
+                {
+                    changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null);
+
+                    continue;
+                }
+
+                Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
+                    memoryManager,
+                    colorState,
+                    samplesInX,
+                    samplesInY,
+                    sizeHint);
+
+                changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color);
+            }
+
+            bool dsEnable = _state.State.RtDepthStencilEnable;
+
+            Image.Texture depthStencil = null;
+
+            if (dsEnable)
+            {
+                var dsState = _state.State.RtDepthStencilState;
+                var dsSize = _state.State.RtDepthStencilSize;
+
+                depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
+                    memoryManager,
+                    dsState,
+                    dsSize,
+                    samplesInX,
+                    samplesInY,
+                    sizeHint);
+            }
+
+            changedScale |= _channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);
+
+            if (changedScale)
+            {
+                _channel.TextureManager.UpdateRenderTargetScale(singleUse);
+                _context.Renderer.Pipeline.SetRenderTargetScale(_channel.TextureManager.RenderTargetScale);
+
+                UpdateViewportTransform();
+                UpdateScissorState();
+            }
+        }
+
+        /// <summary>
+        /// Checks if a render target color buffer is used.
+        /// </summary>
+        /// <param name="colorState">Color buffer information</param>
+        /// <returns>True if the specified buffer is enabled/used, false otherwise</returns>
+        private static bool IsRtEnabled(RtColorState colorState)
+        {
+            // Colors are disabled by writing 0 to the format.
+            return colorState.Format != 0 && colorState.WidthOrStride != 0;
+        }
+
+        /// <summary>
+        /// Updates host scissor test state based on current GPU state.
+        /// </summary>
+        private void UpdateScissorState()
+        {
+            for (int index = 0; index < Constants.TotalViewports; index++)
+            {
+                ScissorState scissor = _state.State.ScissorState[index];
+
+                bool enable = scissor.Enable && (scissor.X1 != 0 || scissor.Y1 != 0 || scissor.X2 != 0xffff || scissor.Y2 != 0xffff);
+
+                if (enable)
+                {
+                    int x = scissor.X1;
+                    int y = scissor.Y1;
+                    int width = scissor.X2 - x;
+                    int height = scissor.Y2 - y;
+
+                    float scale = _channel.TextureManager.RenderTargetScale;
+                    if (scale != 1f)
+                    {
+                        x = (int)(x * scale);
+                        y = (int)(y * scale);
+                        width = (int)Math.Ceiling(width * scale);
+                        height = (int)Math.Ceiling(height * scale);
+                    }
+
+                    _context.Renderer.Pipeline.SetScissor(index, true, x, y, width, height);
+                }
+                else
+                {
+                    _context.Renderer.Pipeline.SetScissor(index, false, 0, 0, 0, 0);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Updates host depth clamp state based on current GPU state.
+        /// </summary>
+        /// <param name="state">Current GPU state</param>
+        private void UpdateDepthClampState()
+        {
+            ViewVolumeClipControl clip = _state.State.ViewVolumeClipControl;
+            _context.Renderer.Pipeline.SetDepthClamp((clip & ViewVolumeClipControl.DepthClampDisabled) == 0);
+        }
+
+        /// <summary>
+        /// Updates host alpha test state based on current GPU state.
+        /// </summary>
+        private void UpdateAlphaTestState()
+        {
+            _context.Renderer.Pipeline.SetAlphaTest(
+                _state.State.AlphaTestEnable,
+                _state.State.AlphaTestRef,
+                _state.State.AlphaTestFunc);
+        }
+
+        /// <summary>
+        /// Updates host depth test state based on current GPU state.
+        /// </summary>
+        private void UpdateDepthTestState()
+        {
+            _context.Renderer.Pipeline.SetDepthTest(new DepthTestDescriptor(
+                _state.State.DepthTestEnable,
+                _state.State.DepthWriteEnable,
+                _state.State.DepthTestFunc));
+        }
+
+        /// <summary>
+        /// Updates host viewport transform and clipping state based on current GPU state.
+        /// </summary>
+        private void UpdateViewportTransform()
+        {
+            var yControl = _state.State.YControl;
+            var face = _state.State.FaceState;
+
+            UpdateFrontFace(yControl, face.FrontFace);
+
+            bool flipY = yControl.HasFlag(YControl.NegateY);
+
+            Span<Viewport> viewports = stackalloc Viewport[Constants.TotalViewports];
+
+            for (int index = 0; index < Constants.TotalViewports; index++)
+            {
+                var transform = _state.State.ViewportTransform[index];
+                var extents = _state.State.ViewportExtents[index];
+
+                float scaleX = MathF.Abs(transform.ScaleX);
+                float scaleY = transform.ScaleY;
+
+                if (flipY)
+                {
+                    scaleY = -scaleY;
+                }
+
+                if (!_context.Capabilities.SupportsViewportSwizzle && transform.UnpackSwizzleY() == ViewportSwizzle.NegativeY)
+                {
+                    scaleY = -scaleY;
+                }
+
+                if (index == 0)
+                {
+                    // Try to guess the depth mode being used on the high level API
+                    // based on current transform.
+                    // It is setup like so by said APIs:
+                    // If depth mode is ZeroToOne:
+                    //  TranslateZ = Near
+                    //  ScaleZ = Far - Near
+                    // If depth mode is MinusOneToOne:
+                    //  TranslateZ = (Near + Far) / 2
+                    //  ScaleZ = (Far - Near) / 2
+                    // DepthNear/Far are sorted such as that Near is always less than Far.
+                    DepthMode depthMode = extents.DepthNear != transform.TranslateZ &&
+                                          extents.DepthFar != transform.TranslateZ ? DepthMode.MinusOneToOne : DepthMode.ZeroToOne;
+
+                    _context.Renderer.Pipeline.SetDepthMode(depthMode);
+                }
+
+                float x = transform.TranslateX - scaleX;
+                float y = transform.TranslateY - scaleY;
+
+                float width = scaleX * 2;
+                float height = scaleY * 2;
+
+                float scale = _channel.TextureManager.RenderTargetScale;
+                if (scale != 1f)
+                {
+                    x *= scale;
+                    y *= scale;
+                    width *= scale;
+                    height *= scale;
+                }
+
+                RectangleF region = new RectangleF(x, y, width, height);
+
+                ViewportSwizzle swizzleX = transform.UnpackSwizzleX();
+                ViewportSwizzle swizzleY = transform.UnpackSwizzleY();
+                ViewportSwizzle swizzleZ = transform.UnpackSwizzleZ();
+                ViewportSwizzle swizzleW = transform.UnpackSwizzleW();
+
+                float depthNear = extents.DepthNear;
+                float depthFar = extents.DepthFar;
+
+                if (transform.ScaleZ < 0)
+                {
+                    float temp = depthNear;
+                    depthNear = depthFar;
+                    depthFar = temp;
+                }
+
+                viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar);
+            }
+
+            _context.Renderer.Pipeline.SetViewports(0, viewports);
+        }
+
+        /// <summary>
+        /// Updates host depth bias (also called polygon offset) state based on current GPU state.
+        /// </summary>
+        private void UpdateDepthBiasState()
+        {
+            var depthBias = _state.State.DepthBiasState;
+
+            float factor = _state.State.DepthBiasFactor;
+            float units = _state.State.DepthBiasUnits;
+            float clamp = _state.State.DepthBiasClamp;
+
+            PolygonModeMask enables;
+
+            enables = (depthBias.PointEnable ? PolygonModeMask.Point : 0);
+            enables |= (depthBias.LineEnable ? PolygonModeMask.Line : 0);
+            enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0);
+
+            _context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp);
+        }
+
+        /// <summary>
+        /// Updates host stencil test state based on current GPU state.
+        /// </summary>
+        private void UpdateStencilTestState()
+        {
+            var backMasks = _state.State.StencilBackMasks;
+            var test = _state.State.StencilTestState;
+            var backTest = _state.State.StencilBackTestState;
+
+            CompareOp backFunc;
+            StencilOp backSFail;
+            StencilOp backDpPass;
+            StencilOp backDpFail;
+            int backFuncRef;
+            int backFuncMask;
+            int backMask;
+
+            if (backTest.TwoSided)
+            {
+                backFunc = backTest.BackFunc;
+                backSFail = backTest.BackSFail;
+                backDpPass = backTest.BackDpPass;
+                backDpFail = backTest.BackDpFail;
+                backFuncRef = backMasks.FuncRef;
+                backFuncMask = backMasks.FuncMask;
+                backMask = backMasks.Mask;
+            }
+            else
+            {
+                backFunc = test.FrontFunc;
+                backSFail = test.FrontSFail;
+                backDpPass = test.FrontDpPass;
+                backDpFail = test.FrontDpFail;
+                backFuncRef = test.FrontFuncRef;
+                backFuncMask = test.FrontFuncMask;
+                backMask = test.FrontMask;
+            }
+
+            _context.Renderer.Pipeline.SetStencilTest(new StencilTestDescriptor(
+                test.Enable,
+                test.FrontFunc,
+                test.FrontSFail,
+                test.FrontDpPass,
+                test.FrontDpFail,
+                test.FrontFuncRef,
+                test.FrontFuncMask,
+                test.FrontMask,
+                backFunc,
+                backSFail,
+                backDpPass,
+                backDpFail,
+                backFuncRef,
+                backFuncMask,
+                backMask));
+        }
+
+        /// <summary>
+        /// Updates user-defined clipping based on the guest GPU state.
+        /// </summary>
+        private void UpdateUserClipState()
+        {
+            uint clipMask = _state.State.ClipDistanceEnable & _vsClipDistancesWritten;
+
+            for (int i = 0; i < Constants.TotalClipDistances; ++i)
+            {
+                _context.Renderer.Pipeline.SetUserClipDistance(i, (clipMask & (1 << i)) != 0);
+            }
+        }
+
+        /// <summary>
+        /// Updates current sampler pool address and size based on guest GPU state.
+        /// </summary>
+        private void UpdateSamplerPoolState()
+        {
+            var texturePool = _state.State.TexturePoolState;
+            var samplerPool = _state.State.SamplerPoolState;
+
+            var samplerIndex = _state.State.SamplerIndex;
+
+            int maximumId = samplerIndex == SamplerIndex.ViaHeaderIndex
+                ? texturePool.MaximumId
+                : samplerPool.MaximumId;
+
+            _channel.TextureManager.SetGraphicsSamplerPool(samplerPool.Address.Pack(), maximumId, samplerIndex);
+        }
+
+        /// <summary>
+        /// Updates current texture pool address and size based on guest GPU state.
+        /// </summary>
+        private void UpdateTexturePoolState()
+        {
+            var texturePool = _state.State.TexturePoolState;
+
+            _channel.TextureManager.SetGraphicsTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);
+            _channel.TextureManager.SetGraphicsTextureBufferIndex((int)_state.State.TextureBufferIndex);
+        }
+
+        /// <summary>
+        /// Updates host vertex attributes based on guest GPU state.
+        /// </summary>
+        private void UpdateVertexAttribState()
+        {
+            Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs];
+
+            for (int index = 0; index < Constants.TotalVertexAttribs; index++)
+            {
+                var vertexAttrib = _state.State.VertexAttribState[index];
+
+                if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
+                {
+                    Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
+
+                    format = Format.R32G32B32A32Float;
+                }
+
+                vertexAttribs[index] = new VertexAttribDescriptor(
+                    vertexAttrib.UnpackBufferIndex(),
+                    vertexAttrib.UnpackOffset(),
+                    vertexAttrib.UnpackIsConstant(),
+                    format);
+            }
+
+            _context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs);
+        }
+
+        /// <summary>
+        /// Updates host line width based on guest GPU state.
+        /// </summary>
+        private void UpdateLineState()
+        {
+            float width = _state.State.LineWidthSmooth;
+            bool smooth = _state.State.LineSmoothEnable;
+
+            _context.Renderer.Pipeline.SetLineParameters(width, smooth);
+        }
+
+        /// <summary>
+        /// Updates host point size based on guest GPU state.
+        /// </summary>
+        private void UpdatePointState()
+        {
+            float size = _state.State.PointSize;
+            bool isProgramPointSize = _state.State.VertexProgramPointSize;
+            bool enablePointSprite = _state.State.PointSpriteEnable;
+
+            // TODO: Need to figure out a way to map PointCoordReplace enable bit.
+            Origin origin = (_state.State.PointCoordReplace & 4) == 0 ? Origin.LowerLeft : Origin.UpperLeft;
+
+            _context.Renderer.Pipeline.SetPointParameters(size, isProgramPointSize, enablePointSprite, origin);
+        }
+
+        /// <summary>
+        /// Updates host primitive restart based on guest GPU state.
+        /// </summary>
+        private void UpdatePrimitiveRestartState()
+        {
+            PrimitiveRestartState primitiveRestart = _state.State.PrimitiveRestartState;
+
+            _context.Renderer.Pipeline.SetPrimitiveRestart(primitiveRestart.Enable, primitiveRestart.Index);
+        }
+
+        /// <summary>
+        /// Updates host index buffer binding based on guest GPU state.
+        /// </summary>
+        private void UpdateIndexBufferState()
+        {
+            var indexBuffer = _state.State.IndexBufferState;
+
+            if (_drawState.IndexCount == 0)
+            {
+                return;
+            }
+
+            ulong gpuVa = indexBuffer.Address.Pack();
+
+            // Do not use the end address to calculate the size, because
+            // the result may be much larger than the real size of the index buffer.
+            ulong size = (ulong)(_drawState.FirstIndex + _drawState.IndexCount);
+
+            switch (indexBuffer.Type)
+            {
+                case IndexType.UShort: size *= 2; break;
+                case IndexType.UInt: size *= 4; break;
+            }
+
+            _channel.BufferManager.SetIndexBuffer(gpuVa, size, indexBuffer.Type);
+        }
+
+        /// <summary>
+        /// Updates host vertex buffer bindings based on guest GPU state.
+        /// </summary>
+        private void UpdateVertexBufferState()
+        {
+            _drawState.IsAnyVbInstanced = false;
+
+            for (int index = 0; index < Constants.TotalVertexBuffers; index++)
+            {
+                var vertexBuffer = _state.State.VertexBufferState[index];
+
+                if (!vertexBuffer.UnpackEnable())
+                {
+                    _channel.BufferManager.SetVertexBuffer(index, 0, 0, 0, 0);
+
+                    continue;
+                }
+
+                GpuVa endAddress = _state.State.VertexBufferEndAddress[index];
+
+                ulong address = vertexBuffer.Address.Pack();
+
+                int stride = vertexBuffer.UnpackStride();
+
+                bool instanced = _state.State.VertexBufferInstanced[index];
+
+                int divisor = instanced ? vertexBuffer.Divisor : 0;
+
+                _drawState.IsAnyVbInstanced |= divisor != 0;
+
+                ulong size;
+
+                if (_drawState.IbStreamer.HasInlineIndexData || _drawState.DrawIndexed || stride == 0 || instanced)
+                {
+                    // This size may be (much) larger than the real vertex buffer size.
+                    // Avoid calculating it this way, unless we don't have any other option.
+                    size = endAddress.Pack() - address + 1;
+                }
+                else
+                {
+                    // For non-indexed draws, we can guess the size from the vertex count
+                    // and stride.
+                    int firstInstance = (int)_state.State.FirstInstance;
+
+                    var drawState = _state.State.VertexBufferDrawState;
+
+                    size = (ulong)((firstInstance + drawState.First + drawState.Count) * stride);
+                }
+
+                _channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor);
+            }
+        }
+
+        /// <summary>
+        /// Updates host face culling and orientation based on guest GPU state.
+        /// </summary>
+        private void UpdateFaceState()
+        {
+            var yControl = _state.State.YControl;
+            var face = _state.State.FaceState;
+
+            _context.Renderer.Pipeline.SetFaceCulling(face.CullEnable, face.CullFace);
+
+            UpdateFrontFace(yControl, face.FrontFace);
+        }
+
+        /// <summary>
+        /// Updates the front face based on the current front face and the origin.
+        /// </summary>
+        /// <param name="yControl">Y control register value, where the origin is located</param>
+        /// <param name="frontFace">Front face</param>
+        private void UpdateFrontFace(YControl yControl, FrontFace frontFace)
+        {
+            bool isUpperLeftOrigin = !yControl.HasFlag(YControl.TriangleRastFlip);
+
+            if (isUpperLeftOrigin)
+            {
+                frontFace = frontFace == FrontFace.CounterClockwise ? FrontFace.Clockwise : FrontFace.CounterClockwise;
+            }
+
+            _context.Renderer.Pipeline.SetFrontFace(frontFace);
+        }
+
+        /// <summary>
+        /// Updates host render target color masks, based on guest GPU state.
+        /// This defines which color channels are written to each color buffer.
+        /// </summary>
+        private void UpdateRtColorMask()
+        {
+            bool rtColorMaskShared = _state.State.RtColorMaskShared;
+
+            Span<uint> componentMasks = stackalloc uint[Constants.TotalRenderTargets];
+
+            for (int index = 0; index < Constants.TotalRenderTargets; index++)
+            {
+                var colorMask = _state.State.RtColorMask[rtColorMaskShared ? 0 : index];
+
+                uint componentMask;
+
+                componentMask = (colorMask.UnpackRed() ? 1u : 0u);
+                componentMask |= (colorMask.UnpackGreen() ? 2u : 0u);
+                componentMask |= (colorMask.UnpackBlue() ? 4u : 0u);
+                componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u);
+
+                componentMasks[index] = componentMask;
+            }
+
+            _context.Renderer.Pipeline.SetRenderTargetColorMasks(componentMasks);
+        }
+
+        /// <summary>
+        /// Updates host render target color buffer blending state, based on guest state.
+        /// </summary>
+        private void UpdateBlendState()
+        {
+            bool blendIndependent = _state.State.BlendIndependent;
+            ColorF blendConstant = _state.State.BlendConstant;
+
+            for (int index = 0; index < Constants.TotalRenderTargets; index++)
+            {
+                BlendDescriptor descriptor;
+
+                if (blendIndependent)
+                {
+                    bool enable = _state.State.BlendEnable[index];
+                    var blend = _state.State.BlendState[index];
+
+                    descriptor = new BlendDescriptor(
+                        enable,
+                        blendConstant,
+                        blend.ColorOp,
+                        blend.ColorSrcFactor,
+                        blend.ColorDstFactor,
+                        blend.AlphaOp,
+                        blend.AlphaSrcFactor,
+                        blend.AlphaDstFactor);
+                }
+                else
+                {
+                    bool enable = _state.State.BlendEnable[0];
+                    var blend = _state.State.BlendStateCommon;
+
+                    descriptor = new BlendDescriptor(
+                        enable,
+                        blendConstant,
+                        blend.ColorOp,
+                        blend.ColorSrcFactor,
+                        blend.ColorDstFactor,
+                        blend.AlphaOp,
+                        blend.AlphaSrcFactor,
+                        blend.AlphaDstFactor);
+                }
+
+                _context.Renderer.Pipeline.SetBlendState(index, descriptor);
+            }
+        }
+
+        /// <summary>
+        /// Updates host logical operation state, based on guest state.
+        /// </summary>
+        private void UpdateLogicOpState()
+        {
+            LogicalOpState logicOpState = _state.State.LogicOpState;
+
+            _context.Renderer.Pipeline.SetLogicOpState(logicOpState.Enable, logicOpState.LogicalOp);
+        }
+
+        /// <summary>
+        /// Updates host shaders based on the guest GPU state.
+        /// </summary>
+        private void UpdateShaderState()
+        {
+            ShaderAddresses addresses = new ShaderAddresses();
+
+            Span<ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1);
+
+            Span<ulong> addressesArray = MemoryMarshal.Cast<ShaderAddresses, ulong>(addressesSpan);
+
+            ulong baseAddress = _state.State.ShaderBaseAddress.Pack();
+
+            for (int index = 0; index < 6; index++)
+            {
+                var shader = _state.State.ShaderState[index];
+
+                if (!shader.UnpackEnable() && index != 1)
+                {
+                    continue;
+                }
+
+                addressesArray[index] = baseAddress + shader.Offset;
+            }
+
+            GpuAccessorState gas = new GpuAccessorState(
+                _state.State.TexturePoolState.Address.Pack(),
+                _state.State.TexturePoolState.MaximumId,
+                (int)_state.State.TextureBufferIndex,
+                _state.State.EarlyZForce,
+                _drawState.Topology);
+
+            ShaderBundle gs = _channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(ref _state.State, _channel, gas, addresses);
+
+            byte oldVsClipDistancesWritten = _vsClipDistancesWritten;
+
+            _drawState.VsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false;
+            _vsClipDistancesWritten = gs.Shaders[0]?.Info.ClipDistancesWritten ?? 0;
+
+            if (oldVsClipDistancesWritten != _vsClipDistancesWritten)
+            {
+                UpdateUserClipState();
+            }
+
+            int storageBufferBindingsCount = 0;
+            int uniformBufferBindingsCount = 0;
+
+            for (int stage = 0; stage < Constants.ShaderStages; stage++)
+            {
+                ShaderProgramInfo info = gs.Shaders[stage]?.Info;
+
+                _currentProgramInfo[stage] = info;
+
+                if (info == null)
+                {
+                    _channel.TextureManager.SetGraphicsTextures(stage, Array.Empty<TextureBindingInfo>());
+                    _channel.TextureManager.SetGraphicsImages(stage, Array.Empty<TextureBindingInfo>());
+                    _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, null);
+                    _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, null);
+                    continue;
+                }
+
+                var textureBindings = new TextureBindingInfo[info.Textures.Count];
+
+                for (int index = 0; index < info.Textures.Count; index++)
+                {
+                    var descriptor = info.Textures[index];
+
+                    Target target = ShaderTexture.GetTarget(descriptor.Type);
+
+                    textureBindings[index] = new TextureBindingInfo(
+                        target,
+                        descriptor.Binding,
+                        descriptor.CbufSlot,
+                        descriptor.HandleIndex,
+                        descriptor.Flags);
+                }
+
+                _channel.TextureManager.SetGraphicsTextures(stage, textureBindings);
+
+                var imageBindings = new TextureBindingInfo[info.Images.Count];
+
+                for (int index = 0; index < info.Images.Count; index++)
+                {
+                    var descriptor = info.Images[index];
+
+                    Target target = ShaderTexture.GetTarget(descriptor.Type);
+                    Format format = ShaderTexture.GetFormat(descriptor.Format);
+
+                    imageBindings[index] = new TextureBindingInfo(
+                        target,
+                        format,
+                        descriptor.Binding,
+                        descriptor.CbufSlot,
+                        descriptor.HandleIndex,
+                        descriptor.Flags);
+                }
+
+                _channel.TextureManager.SetGraphicsImages(stage, imageBindings);
+
+                _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
+                _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);
+
+                if (info.SBuffers.Count != 0)
+                {
+                    storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1);
+                }
+
+                if (info.CBuffers.Count != 0)
+                {
+                    uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1);
+                }
+            }
+
+            _channel.BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount);
+            _channel.BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount);
+
+            _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
+        }
+
+        /// <summary>
+        /// Forces the shaders to be rebound on the next draw.
+        /// </summary>
+        public void ForceShaderUpdate()
+        {
+            _updateTracker.ForceDirty(ShaderStateIndex);
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
new file mode 100644
index 0000000000..ad6a1d0e09
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
@@ -0,0 +1,428 @@
+using Ryujinx.Graphics.Device;
+using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+    /// <summary>
+    /// Represents a 3D engine class.
+    /// </summary>
+    class ThreedClass : IDeviceState
+    {
+        private readonly GpuContext _context;
+        private readonly DeviceStateWithShadow<ThreedClassState> _state;
+
+        private readonly InlineToMemoryClass _i2mClass;
+        private readonly DrawManager _drawManager;
+        private readonly SemaphoreUpdater _semaphoreUpdater;
+        private readonly ConstantBufferUpdater _cbUpdater;
+        private readonly StateUpdater _stateUpdater;
+
+        /// <summary>
+        /// Creates a new instance of the 3D engine class.
+        /// </summary>
+        /// <param name="context">GPU context</param>
+        /// <param name="channel">GPU channel</param>
+        public ThreedClass(GpuContext context, GpuChannel channel)
+        {
+            _context = context;
+            _state = new DeviceStateWithShadow<ThreedClassState>(new Dictionary<string, RwCallback>
+            {
+                { nameof(ThreedClassState.LaunchDma), new RwCallback(LaunchDma, null) },
+                { nameof(ThreedClassState.LoadInlineData), new RwCallback(LoadInlineData, null) },
+                { nameof(ThreedClassState.SyncpointAction), new RwCallback(IncrementSyncpoint, null) },
+                { nameof(ThreedClassState.TextureBarrier), new RwCallback(TextureBarrier, null) },
+                { nameof(ThreedClassState.TextureBarrierTiled), new RwCallback(TextureBarrierTiled, null) },
+                { nameof(ThreedClassState.VbElementU8), new RwCallback(VbElementU8, null) },
+                { nameof(ThreedClassState.VbElementU16), new RwCallback(VbElementU16, null) },
+                { nameof(ThreedClassState.VbElementU32), new RwCallback(VbElementU32, null) },
+                { nameof(ThreedClassState.ResetCounter), new RwCallback(ResetCounter, null) },
+                { nameof(ThreedClassState.RenderEnableCondition), new RwCallback(null, Zero) },
+                { nameof(ThreedClassState.DrawEnd), new RwCallback(DrawEnd, null) },
+                { nameof(ThreedClassState.DrawBegin), new RwCallback(DrawBegin, null) },
+                { nameof(ThreedClassState.DrawIndexedSmall), new RwCallback(DrawIndexedSmall, null) },
+                { nameof(ThreedClassState.DrawIndexedSmall2), new RwCallback(DrawIndexedSmall2, null) },
+                { nameof(ThreedClassState.DrawIndexedSmallIncInstance), new RwCallback(DrawIndexedSmallIncInstance, null) },
+                { nameof(ThreedClassState.DrawIndexedSmallIncInstance2), new RwCallback(DrawIndexedSmallIncInstance2, null) },
+                { nameof(ThreedClassState.IndexBufferCount), new RwCallback(SetIndexBufferCount, null) },
+                { nameof(ThreedClassState.Clear), new RwCallback(Clear, null) },
+                { nameof(ThreedClassState.SemaphoreControl), new RwCallback(Report, null) },
+                { nameof(ThreedClassState.SetFalcon04), new RwCallback(SetFalcon04, null) },
+                { nameof(ThreedClassState.UniformBufferUpdateData), new RwCallback(ConstantBufferUpdate, null) },
+                { nameof(ThreedClassState.UniformBufferBindVertex), new RwCallback(ConstantBufferBindVertex, null) },
+                { nameof(ThreedClassState.UniformBufferBindTessControl), new RwCallback(ConstantBufferBindTessControl, null) },
+                { nameof(ThreedClassState.UniformBufferBindTessEvaluation), new RwCallback(ConstantBufferBindTessEvaluation, null) },
+                { nameof(ThreedClassState.UniformBufferBindGeometry), new RwCallback(ConstantBufferBindGeometry, null) },
+                { nameof(ThreedClassState.UniformBufferBindFragment), new RwCallback(ConstantBufferBindFragment, null) }
+            });
+
+            _i2mClass = new InlineToMemoryClass(context, channel, initializeState: false);
+
+            var drawState = new DrawState();
+
+            _drawManager = new DrawManager(context, channel, _state, drawState);
+            _semaphoreUpdater = new SemaphoreUpdater(context, channel, _state);
+            _cbUpdater = new ConstantBufferUpdater(channel, _state);
+            _stateUpdater = new StateUpdater(context, channel, _state, drawState);
+
+            // This defaults to "always", even without any register write.
+            // Reads just return 0, regardless of what was set there.
+            _state.State.RenderEnableCondition = Condition.Always;
+        }
+
+        /// <summary>
+        /// Reads data from the class registers.
+        /// </summary>
+        /// <param name="offset">Register byte offset</param>
+        /// <returns>Data at the specified offset</returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public int Read(int offset) => _state.Read(offset);
+
+        /// <summary>
+        /// Writes data to the class registers.
+        /// </summary>
+        /// <param name="offset">Register byte offset</param>
+        /// <param name="data">Data to be written</param>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Write(int offset, int data)
+        {
+            _state.WriteWithRedundancyCheck(offset, data, out bool valueChanged);
+
+            if (valueChanged)
+            {
+                _stateUpdater.SetDirty(offset);
+            }
+        }
+
+        /// <summary>
+        /// Sets the shadow ram control value of all sub-channels.
+        /// </summary>
+        /// <param name="control">New shadow ram control value</param>
+        public void SetShadowRamControl(int control)
+        {
+            _state.State.SetMmeShadowRamControl = (uint)control;
+        }
+
+        /// <summary>
+        /// Updates current host state for all registers modified since the last call to this method.
+        /// </summary>
+        public void UpdateState()
+        {
+            _cbUpdater.FlushUboDirty();
+            _stateUpdater.Update();
+        }
+
+        /// <summary>
+        /// Updates current host state for all registers modified since the last call to this method.
+        /// </summary>
+        /// <param name="mask">Mask where each bit set indicates that the respective state group index should be checked</param>
+        public void UpdateState(ulong mask)
+        {
+            _stateUpdater.Update(mask);
+        }
+
+        /// <summary>
+        /// Updates render targets (color and depth-stencil buffers) based on current render target state.
+        /// </summary>
+        /// <param name="useControl">Use draw buffers information from render target control register</param>
+        /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
+        public void UpdateRenderTargetState(bool useControl, int singleUse = -1)
+        {
+            _stateUpdater.UpdateRenderTargetState(useControl, singleUse);
+        }
+
+        /// <summary>
+        /// Marks the entire state as dirty, forcing a full host state update before the next draw.
+        /// </summary>
+        public void ForceStateDirty()
+        {
+            _stateUpdater.SetAllDirty();
+        }
+
+        /// <summary>
+        /// Forces the shaders to be rebound on the next draw.
+        /// </summary>
+        public void ForceShaderUpdate()
+        {
+            _stateUpdater.ForceShaderUpdate();
+        }
+
+        /// <summary>
+        /// Flushes any queued UBO updates.
+        /// </summary>
+        public void FlushUboDirty()
+        {
+            _cbUpdater.FlushUboDirty();
+        }
+
+        /// <summary>
+        /// Perform any deferred draws.
+        /// </summary>
+        public void PerformDeferredDraws()
+        {
+            _drawManager.PerformDeferredDraws();
+        }
+
+        /// <summary>
+        /// Updates the currently bound constant buffer.
+        /// </summary>
+        /// <param name="data">Data to be written to the buffer</param>
+        public void ConstantBufferUpdate(ReadOnlySpan<int> data)
+        {
+            _cbUpdater.Update(data);
+        }
+
+        /// <summary>
+        /// Launches the Inline-to-Memory DMA copy operation.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void LaunchDma(int argument)
+        {
+            _i2mClass.LaunchDma(ref Unsafe.As<ThreedClassState, InlineToMemoryClassState>(ref _state.State), argument);
+        }
+
+        /// <summary>
+        /// Pushes a word of data to the Inline-to-Memory engine.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void LoadInlineData(int argument)
+        {
+            _i2mClass.LoadInlineData(argument);
+        }
+
+        /// <summary>
+        /// Performs an incrementation on a syncpoint.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        public void IncrementSyncpoint(int argument)
+        {
+            uint syncpointId = (uint)argument & 0xFFFF;
+
+            _context.CreateHostSyncIfNeeded();
+            _context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result.
+            _context.Synchronization.IncrementSyncpoint(syncpointId);
+        }
+
+        /// <summary>
+        /// Issues a texture barrier.
+        /// This waits until previous texture writes from the GPU to finish, before
+        /// performing new operations with said textures.
+        /// </summary>
+        /// <param name="argument">Method call argument (unused)</param>
+        private void TextureBarrier(int argument)
+        {
+            _context.Renderer.Pipeline.TextureBarrier();
+        }
+
+        /// <summary>
+        /// Issues a texture barrier.
+        /// This waits until previous texture writes from the GPU to finish, before
+        /// performing new operations with said textures.
+        /// This performs a per-tile wait, it is only valid if both the previous write
+        /// and current access has the same access patterns.
+        /// This may be faster than the regular barrier on tile-based rasterizers.
+        /// </summary>
+        /// <param name="argument">Method call argument (unused)</param>
+        private void TextureBarrierTiled(int argument)
+        {
+            _context.Renderer.Pipeline.TextureBarrierTiled();
+        }
+
+        /// <summary>
+        /// Pushes four 8-bit index buffer elements.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void VbElementU8(int argument)
+        {
+            _drawManager.VbElementU8(argument);
+        }
+
+        /// <summary>
+        /// Pushes two 16-bit index buffer elements.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void VbElementU16(int argument)
+        {
+            _drawManager.VbElementU16(argument);
+        }
+
+        /// <summary>
+        /// Pushes one 32-bit index buffer element.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void VbElementU32(int argument)
+        {
+            _drawManager.VbElementU32(argument);
+        }
+
+        /// <summary>
+        /// Resets the value of an internal GPU counter back to zero.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void ResetCounter(int argument)
+        {
+            _semaphoreUpdater.ResetCounter(argument);
+        }
+
+        /// <summary>
+        /// Finishes the draw call.
+        /// This draws geometry on the bound buffers based on the current GPU state.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void DrawEnd(int argument)
+        {
+            _drawManager.DrawEnd(this, argument);
+        }
+
+        /// <summary>
+        /// Starts draw.
+        /// This sets primitive type and instanced draw parameters.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void DrawBegin(int argument)
+        {
+            _drawManager.DrawBegin(argument);
+        }
+
+        /// <summary>
+        /// Sets the index buffer count.
+        /// This also sets internal state that indicates that the next draw is an indexed draw.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void SetIndexBufferCount(int argument)
+        {
+            _drawManager.SetIndexBufferCount(argument);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with a low number of index buffer elements.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void DrawIndexedSmall(int argument)
+        {
+            _drawManager.DrawIndexedSmall(this, argument);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with a low number of index buffer elements.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void DrawIndexedSmall2(int argument)
+        {
+            _drawManager.DrawIndexedSmall2(this, argument);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with a low number of index buffer elements,
+        /// while also pre-incrementing the current instance value.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void DrawIndexedSmallIncInstance(int argument)
+        {
+            _drawManager.DrawIndexedSmallIncInstance(this, argument);
+        }
+
+        /// <summary>
+        /// Performs a indexed draw with a low number of index buffer elements,
+        /// while also pre-incrementing the current instance value.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void DrawIndexedSmallIncInstance2(int argument)
+        {
+            _drawManager.DrawIndexedSmallIncInstance2(this, argument);
+        }
+
+        /// <summary>
+        /// Clears the current color and depth-stencil buffers.
+        /// Which buffers should be cleared is also specified on the argument.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void Clear(int argument)
+        {
+            _drawManager.Clear(this, argument);
+        }
+
+        /// <summary>
+        /// Writes a GPU counter to guest memory.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void Report(int argument)
+        {
+            _semaphoreUpdater.Report(argument);
+        }
+
+        /// <summary>
+        /// Performs high-level emulation of Falcon microcode function number "4".
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void SetFalcon04(int argument)
+        {
+            _state.State.SetMmeShadowScratch[0] = 1;
+        }
+
+        /// <summary>
+        /// Updates the uniform buffer data with inline data.
+        /// </summary>
+        /// <param name="argument">New uniform buffer data word</param>
+        private void ConstantBufferUpdate(int argument)
+        {
+            _cbUpdater.Update(argument);
+        }
+
+        /// <summary>
+        /// Binds a uniform buffer for the vertex shader stage.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void ConstantBufferBindVertex(int argument)
+        {
+            _cbUpdater.BindVertex(argument);
+        }
+
+        /// <summary>
+        /// Binds a uniform buffer for the tessellation control shader stage.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void ConstantBufferBindTessControl(int argument)
+        {
+            _cbUpdater.BindTessControl(argument);
+        }
+
+        /// <summary>
+        /// Binds a uniform buffer for the tessellation evaluation shader stage.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void ConstantBufferBindTessEvaluation(int argument)
+        {
+            _cbUpdater.BindTessEvaluation(argument);
+        }
+
+        /// <summary>
+        /// Binds a uniform buffer for the geometry shader stage.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void ConstantBufferBindGeometry(int argument)
+        {
+            _cbUpdater.BindGeometry(argument);
+        }
+
+        /// <summary>
+        /// Binds a uniform buffer for the fragment shader stage.
+        /// </summary>
+        /// <param name="argument">Method call argument</param>
+        private void ConstantBufferBindFragment(int argument)
+        {
+            _cbUpdater.BindFragment(argument);
+        }
+
+        /// <summary>
+        /// Generic register read function that just returns 0.
+        /// </summary>
+        /// <returns>Zero</returns>
+        private static int Zero()
+        {
+            return 0;
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
new file mode 100644
index 0000000000..a6392e3d70
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
@@ -0,0 +1,861 @@
+using Ryujinx.Common.Memory;
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
+using Ryujinx.Graphics.Gpu.Engine.Types;
+using Ryujinx.Graphics.Gpu.Image;
+using System;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+    /// <summary>
+    /// Shader stage name.
+    /// </summary>
+    enum ShaderType
+    {
+        Vertex,
+        TessellationControl,
+        TessellationEvaluation,
+        Geometry,
+        Fragment
+    }
+
+    /// <summary>
+    /// Transform feedback buffer state.
+    /// </summary>
+    struct TfBufferState
+    {
+#pragma warning disable CS0649
+        public Boolean32 Enable;
+        public GpuVa Address;
+        public int Size;
+        public int Offset;
+        public uint Padding0;
+        public uint Padding1;
+        public uint Padding2;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Transform feedback state.
+    /// </summary>
+    struct TfState
+    {
+#pragma warning disable CS0649
+        public int BufferIndex;
+        public int VaryingsCount;
+        public int Stride;
+        public uint Padding;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Render target color buffer state.
+    /// </summary>
+    struct RtColorState
+    {
+#pragma warning disable CS0649
+        public GpuVa Address;
+        public int WidthOrStride;
+        public int Height;
+        public ColorFormat Format;
+        public MemoryLayout MemoryLayout;
+        public int Depth;
+        public int LayerSize;
+        public int BaseLayer;
+        public int Unknown0x24;
+        public int Padding0;
+        public int Padding1;
+        public int Padding2;
+        public int Padding3;
+        public int Padding4;
+        public int Padding5;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Viewport transform parameters, for viewport transformation.
+    /// </summary>
+    struct ViewportTransform
+    {
+#pragma warning disable CS0649
+        public float ScaleX;
+        public float ScaleY;
+        public float ScaleZ;
+        public float TranslateX;
+        public float TranslateY;
+        public float TranslateZ;
+        public uint Swizzle;
+        public uint SubpixelPrecisionBias;
+#pragma warning restore CS0649
+
+        /// <summary>
+        /// Unpacks viewport swizzle of the position X component.
+        /// </summary>
+        /// <returns>Swizzle enum value</returns>
+        public ViewportSwizzle UnpackSwizzleX()
+        {
+            return (ViewportSwizzle)(Swizzle & 7);
+        }
+
+        /// <summary>
+        /// Unpacks viewport swizzle of the position Y component.
+        /// </summary>
+        /// <returns>Swizzle enum value</returns>
+        public ViewportSwizzle UnpackSwizzleY()
+        {
+            return (ViewportSwizzle)((Swizzle >> 4) & 7);
+        }
+
+        /// <summary>
+        /// Unpacks viewport swizzle of the position Z component.
+        /// </summary>
+        /// <returns>Swizzle enum value</returns>
+        public ViewportSwizzle UnpackSwizzleZ()
+        {
+            return (ViewportSwizzle)((Swizzle >> 8) & 7);
+        }
+
+        /// <summary>
+        /// Unpacks viewport swizzle of the position W component.
+        /// </summary>
+        /// <returns>Swizzle enum value</returns>
+        public ViewportSwizzle UnpackSwizzleW()
+        {
+            return (ViewportSwizzle)((Swizzle >> 12) & 7);
+        }
+    }
+
+    /// <summary>
+    /// Viewport extents for viewport clipping, also includes depth range.
+    /// </summary>
+    struct ViewportExtents
+    {
+#pragma warning disable CS0649
+        public ushort X;
+        public ushort Width;
+        public ushort Y;
+        public ushort Height;
+        public float DepthNear;
+        public float DepthFar;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Draw state for non-indexed draws.
+    /// </summary>
+    struct VertexBufferDrawState
+    {
+#pragma warning disable CS0649
+        public int First;
+        public int Count;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Color buffer clear color.
+    /// </summary>
+    struct ClearColors
+    {
+#pragma warning disable CS0649
+        public float Red;
+        public float Green;
+        public float Blue;
+        public float Alpha;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Depth bias (also called polygon offset) parameters.
+    /// </summary>
+    struct DepthBiasState
+    {
+#pragma warning disable CS0649
+        public Boolean32 PointEnable;
+        public Boolean32 LineEnable;
+        public Boolean32 FillEnable;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Scissor state.
+    /// </summary>
+    struct ScissorState
+    {
+#pragma warning disable CS0649
+        public Boolean32 Enable;
+        public ushort X1;
+        public ushort X2;
+        public ushort Y1;
+        public ushort Y2;
+        public uint Padding;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Stencil test masks for back tests.
+    /// </summary>
+    struct StencilBackMasks
+    {
+#pragma warning disable CS0649
+        public int FuncRef;
+        public int Mask;
+        public int FuncMask;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Render target depth-stencil buffer state.
+    /// </summary>
+    struct RtDepthStencilState
+    {
+#pragma warning disable CS0649
+        public GpuVa Address;
+        public ZetaFormat Format;
+        public MemoryLayout MemoryLayout;
+        public int LayerSize;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Screen scissor state.
+    /// </summary>
+    struct ScreenScissorState
+    {
+#pragma warning disable CS0649
+        public ushort X;
+        public ushort Width;
+        public ushort Y;
+        public ushort Height;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Vertex buffer attribute state.
+    /// </summary>
+    struct VertexAttribState
+    {
+#pragma warning disable CS0649
+        public uint Attribute;
+#pragma warning restore CS0649
+
+        /// <summary>
+        /// Unpacks the index of the vertex buffer this attribute belongs to.
+        /// </summary>
+        /// <returns>Vertex buffer index</returns>
+        public int UnpackBufferIndex()
+        {
+            return (int)(Attribute & 0x1f);
+        }
+
+        /// <summary>
+        /// Unpacks the attribute constant flag.
+        /// </summary>
+        /// <returns>True if the attribute is constant, false otherwise</returns>
+        public bool UnpackIsConstant()
+        {
+            return (Attribute & 0x40) != 0;
+        }
+
+        /// <summary>
+        /// Unpacks the offset, in bytes, of the attribute on the vertex buffer.
+        /// </summary>
+        /// <returns>Attribute offset in bytes</returns>
+        public int UnpackOffset()
+        {
+            return (int)((Attribute >> 7) & 0x3fff);
+        }
+
+        /// <summary>
+        /// Unpacks the Maxwell attribute format integer.
+        /// </summary>
+        /// <returns>Attribute format integer</returns>
+        public uint UnpackFormat()
+        {
+            return Attribute & 0x3fe00000;
+        }
+    }
+
+    /// <summary>
+    /// Render target draw buffers control.
+    /// </summary>
+    struct RtControl
+    {
+#pragma warning disable CS0649
+        public uint Packed;
+#pragma warning restore CS0649
+
+        /// <summary>
+        /// Unpacks the number of active draw buffers.
+        /// </summary>
+        /// <returns>Number of active draw buffers</returns>
+        public int UnpackCount()
+        {
+            return (int)(Packed & 0xf);
+        }
+
+        /// <summary>
+        /// Unpacks the color attachment index for a given draw buffer.
+        /// </summary>
+        /// <param name="index">Index of the draw buffer</param>
+        /// <returns>Attachment index</returns>
+        public int UnpackPermutationIndex(int index)
+        {
+            return (int)((Packed >> (4 + index * 3)) & 7);
+        }
+    }
+
+    /// <summary>
+    /// 3D, 2D or 1D texture size.
+    /// </summary>
+    struct Size3D
+    {
+#pragma warning disable CS0649
+        public int Width;
+        public int Height;
+        public int Depth;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Stencil front test state and masks.
+    /// </summary>
+    struct StencilTestState
+    {
+#pragma warning disable CS0649
+        public Boolean32 Enable;
+        public StencilOp FrontSFail;
+        public StencilOp FrontDpFail;
+        public StencilOp FrontDpPass;
+        public CompareOp FrontFunc;
+        public int FrontFuncRef;
+        public int FrontFuncMask;
+        public int FrontMask;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Screen Y control register.
+    /// </summary>
+    [Flags]
+    enum YControl
+    {
+        NegateY = 1 << 0,
+        TriangleRastFlip = 1 << 4
+    }
+
+    /// <summary>
+    /// Condition for conditional rendering.
+    /// </summary>
+    enum Condition
+    {
+        Never,
+        Always,
+        ResultNonZero,
+        Equal,
+        NotEqual
+    }
+
+    /// <summary>
+    /// Texture or sampler pool state.
+    /// </summary>
+    struct PoolState
+    {
+#pragma warning disable CS0649
+        public GpuVa Address;
+        public int MaximumId;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Stencil back test state.
+    /// </summary>
+    struct StencilBackTestState
+    {
+#pragma warning disable CS0649
+        public Boolean32 TwoSided;
+        public StencilOp BackSFail;
+        public StencilOp BackDpFail;
+        public StencilOp BackDpPass;
+        public CompareOp BackFunc;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Primitive restart state.
+    /// </summary>
+    struct PrimitiveRestartState
+    {
+#pragma warning disable CS0649
+        public Boolean32 Enable;
+        public int Index;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// GPU index buffer state.
+    /// This is used on indexed draws.
+    /// </summary>
+    struct IndexBufferState
+    {
+#pragma warning disable CS0649
+        public GpuVa Address;
+        public GpuVa EndAddress;
+        public IndexType Type;
+        public int First;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Face culling and orientation parameters.
+    /// </summary>
+    struct FaceState
+    {
+#pragma warning disable CS0649
+        public Boolean32 CullEnable;
+        public FrontFace FrontFace;
+        public Face CullFace;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// View volume clip control.
+    /// </summary>
+    [Flags]
+    enum ViewVolumeClipControl
+    {
+        ForceDepthRangeZeroToOne = 1 << 0,
+        DepthClampDisabled = 1 << 11
+    }
+
+    /// <summary>
+    /// Logical operation state.
+    /// </summary>
+    struct LogicalOpState
+    {
+#pragma warning disable CS0649
+        public Boolean32 Enable;
+        public LogicalOp LogicalOp;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Render target color buffer mask.
+    /// This defines which color channels are written to the color buffer.
+    /// </summary>
+    struct RtColorMask
+    {
+#pragma warning disable CS0649
+        public uint Packed;
+#pragma warning restore CS0649
+
+        /// <summary>
+        /// Unpacks red channel enable.
+        /// </summary>
+        /// <returns>True to write the new red channel color, false to keep the old value</returns>
+        public bool UnpackRed()
+        {
+            return (Packed & 0x1) != 0;
+        }
+
+        /// <summary>
+        /// Unpacks green channel enable.
+        /// </summary>
+        /// <returns>True to write the new green channel color, false to keep the old value</returns>
+        public bool UnpackGreen()
+        {
+            return (Packed & 0x10) != 0;
+        }
+
+        /// <summary>
+        /// Unpacks blue channel enable.
+        /// </summary>
+        /// <returns>True to write the new blue channel color, false to keep the old value</returns>
+        public bool UnpackBlue()
+        {
+            return (Packed & 0x100) != 0;
+        }
+
+        /// <summary>
+        /// Unpacks alpha channel enable.
+        /// </summary>
+        /// <returns>True to write the new alpha channel color, false to keep the old value</returns>
+        public bool UnpackAlpha()
+        {
+            return (Packed & 0x1000) != 0;
+        }
+    }
+
+    /// <summary>
+    /// Vertex buffer state.
+    /// </summary>
+    struct VertexBufferState
+    {
+#pragma warning disable CS0649
+        public uint Control;
+        public GpuVa Address;
+        public int Divisor;
+#pragma warning restore CS0649
+
+        /// <summary>
+        /// Vertex buffer stride, defined as the number of bytes occupied by each vertex in memory.
+        /// </summary>
+        /// <returns>Vertex buffer stride</returns>
+        public int UnpackStride()
+        {
+            return (int)(Control & 0xfff);
+        }
+
+        /// <summary>
+        /// Vertex buffer enable.
+        /// </summary>
+        /// <returns>True if the vertex buffer is enabled, false otherwise</returns>
+        public bool UnpackEnable()
+        {
+            return (Control & (1 << 12)) != 0;
+        }
+    }
+
+    /// <summary>
+    /// Color buffer blending parameters, shared by all color buffers.
+    /// </summary>
+    struct BlendStateCommon
+    {
+#pragma warning disable CS0649
+        public Boolean32 SeparateAlpha;
+        public BlendOp ColorOp;
+        public BlendFactor ColorSrcFactor;
+        public BlendFactor ColorDstFactor;
+        public BlendOp AlphaOp;
+        public BlendFactor AlphaSrcFactor;
+        public uint Unknown0x1354;
+        public BlendFactor AlphaDstFactor;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Color buffer blending parameters.
+    /// </summary>
+    struct BlendState
+    {
+#pragma warning disable CS0649
+        public Boolean32 SeparateAlpha;
+        public BlendOp ColorOp;
+        public BlendFactor ColorSrcFactor;
+        public BlendFactor ColorDstFactor;
+        public BlendOp AlphaOp;
+        public BlendFactor AlphaSrcFactor;
+        public BlendFactor AlphaDstFactor;
+        public uint Padding;
+#pragma warning restore CS0649
+    }
+
+    /// <summary>
+    /// Graphics shader stage state.
+    /// </summary>
+    struct ShaderState
+    {
+#pragma warning disable CS0649
+        public uint Control;
+        public uint Offset;
+        public uint Unknown0x8;
+        public int MaxRegisters;
+        public ShaderType Type;
+        public uint Unknown0x14;
+        public uint Unknown0x18;
+        public uint Unknown0x1c;
+        public uint Unknown0x20;
+        public uint Unknown0x24;
+        public uint Unknown0x28;
+        public uint Unknown0x2c;
+        public uint Unknown0x30;
+        public uint Unknown0x34;
+        public uint Unknown0x38;
+        public uint Unknown0x3c;
+#pragma warning restore CS0649
+
+        /// <summary>
+        /// Unpacks shader enable information.
+        /// Must be ignored for vertex shaders, those are always enabled.
+        /// </summary>
+        /// <returns>True if the stage is enabled, false otherwise</returns>
+        public bool UnpackEnable()
+        {
+            return (Control & 1) != 0;
+        }
+    }
+
+    /// <summary>
+    /// Uniform buffer state for the uniform buffer currently being modified.
+    /// </summary>
+    struct UniformBufferState
+    {
+#pragma warning disable CS0649
+        public int Size;
+        public GpuVa Address;
+        public int Offset;
+#pragma warning restore CS0649
+    }
+
+    unsafe struct ThreedClassState : IShadowState
+    {
+#pragma warning disable CS0649
+        public uint SetObject;
+        public int SetObjectClassId => (int)((SetObject >> 0) & 0xFFFF);
+        public int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F);
+        public fixed uint Reserved04[63];
+        public uint NoOperation;
+        public uint SetNotifyA;
+        public int SetNotifyAAddressUpper => (int)((SetNotifyA >> 0) & 0xFF);
+        public uint SetNotifyB;
+        public uint Notify;
+        public NotifyType NotifyType => (NotifyType)(Notify);
+        public uint WaitForIdle;
+        public uint LoadMmeInstructionRamPointer;
+        public uint LoadMmeInstructionRam;
+        public uint LoadMmeStartAddressRamPointer;
+        public uint LoadMmeStartAddressRam;
+        public uint SetMmeShadowRamControl;
+        public SetMmeShadowRamControlMode SetMmeShadowRamControlMode => (SetMmeShadowRamControlMode)((SetMmeShadowRamControl >> 0) & 0x3);
+        public fixed uint Reserved128[2];
+        public uint SetGlobalRenderEnableA;
+        public int SetGlobalRenderEnableAOffsetUpper => (int)((SetGlobalRenderEnableA >> 0) & 0xFF);
+        public uint SetGlobalRenderEnableB;
+        public uint SetGlobalRenderEnableC;
+        public int SetGlobalRenderEnableCMode => (int)((SetGlobalRenderEnableC >> 0) & 0x7);
+        public uint SendGoIdle;
+        public uint PmTrigger;
+        public uint PmTriggerWfi;
+        public fixed uint Reserved148[2];
+        public uint SetInstrumentationMethodHeader;
+        public uint SetInstrumentationMethodData;
+        public fixed uint Reserved158[10];
+        public uint LineLengthIn;
+        public uint LineCount;
+        public uint OffsetOutUpper;
+        public int OffsetOutUpperValue => (int)((OffsetOutUpper >> 0) & 0xFF);
+        public uint OffsetOut;
+        public uint PitchOut;
+        public uint SetDstBlockSize;
+        public SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)((SetDstBlockSize >> 0) & 0xF);
+        public SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF);
+        public SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF);
+        public uint SetDstWidth;
+        public uint SetDstHeight;
+        public uint SetDstDepth;
+        public uint SetDstLayer;
+        public uint SetDstOriginBytesX;
+        public int SetDstOriginBytesXV => (int)((SetDstOriginBytesX >> 0) & 0xFFFFF);
+        public uint SetDstOriginSamplesY;
+        public int SetDstOriginSamplesYV => (int)((SetDstOriginSamplesY >> 0) & 0xFFFF);
+        public uint LaunchDma;
+        public LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)((LaunchDma >> 0) & 0x1);
+        public LaunchDmaCompletionType LaunchDmaCompletionType => (LaunchDmaCompletionType)((LaunchDma >> 4) & 0x3);
+        public LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 8) & 0x3);
+        public LaunchDmaSemaphoreStructSize LaunchDmaSemaphoreStructSize => (LaunchDmaSemaphoreStructSize)((LaunchDma >> 12) & 0x1);
+        public bool LaunchDmaReductionEnable => (LaunchDma & 0x2) != 0;
+        public LaunchDmaReductionOp LaunchDmaReductionOp => (LaunchDmaReductionOp)((LaunchDma >> 13) & 0x7);
+        public LaunchDmaReductionFormat LaunchDmaReductionFormat => (LaunchDmaReductionFormat)((LaunchDma >> 2) & 0x3);
+        public bool LaunchDmaSysmembarDisable => (LaunchDma & 0x40) != 0;
+        public uint LoadInlineData;
+        public fixed uint Reserved1B8[22];
+        public Boolean32 EarlyZForce;
+        public fixed uint Reserved214[45];
+        public uint SyncpointAction;
+        public fixed uint Reserved2CC[44];
+        public Boolean32 RasterizeEnable;
+        public Array4<TfBufferState> TfBufferState;
+        public fixed uint Reserved400[192];
+        public Array4<TfState> TfState;
+        public fixed uint Reserved740[1];
+        public Boolean32 TfEnable;
+        public fixed uint Reserved748[46];
+        public Array8<RtColorState> RtColorState;
+        public Array16<ViewportTransform> ViewportTransform;
+        public Array16<ViewportExtents> ViewportExtents;
+        public fixed uint ReservedD00[29];
+        public VertexBufferDrawState VertexBufferDrawState;
+        public uint DepthMode;
+        public ClearColors ClearColors;
+        public float ClearDepthValue;
+        public fixed uint ReservedD94[3];
+        public uint ClearStencilValue;
+        public fixed uint ReservedDA4[7];
+        public DepthBiasState DepthBiasState;
+        public fixed uint ReservedDCC[5];
+        public uint TextureBarrier;
+        public fixed uint ReservedDE4[7];
+        public Array16<ScissorState> ScissorState;
+        public fixed uint ReservedF00[21];
+        public StencilBackMasks StencilBackMasks;
+        public fixed uint ReservedF60[5];
+        public uint InvalidateTextures;
+        public fixed uint ReservedF78[1];
+        public uint TextureBarrierTiled;
+        public fixed uint ReservedF80[4];
+        public Boolean32 RtColorMaskShared;
+        public fixed uint ReservedF94[19];
+        public RtDepthStencilState RtDepthStencilState;
+        public ScreenScissorState ScreenScissorState;
+        public fixed uint ReservedFFC[89];
+        public Array16<VertexAttribState> VertexAttribState;
+        public fixed uint Reserved11A0[31];
+        public RtControl RtControl;
+        public fixed uint Reserved1220[2];
+        public Size3D RtDepthStencilSize;
+        public SamplerIndex SamplerIndex;
+        public fixed uint Reserved1238[37];
+        public Boolean32 DepthTestEnable;
+        public fixed uint Reserved12D0[5];
+        public Boolean32 BlendIndependent;
+        public Boolean32 DepthWriteEnable;
+        public Boolean32 AlphaTestEnable;
+        public fixed uint Reserved12F0[5];
+        public uint VbElementU8;
+        public uint Reserved1308;
+        public CompareOp DepthTestFunc;
+        public float AlphaTestRef;
+        public CompareOp AlphaTestFunc;
+        public uint Reserved1318;
+        public ColorF BlendConstant;
+        public fixed uint Reserved132C[4];
+        public BlendStateCommon BlendStateCommon;
+        public Boolean32 BlendEnableCommon;
+        public Array8<Boolean32> BlendEnable;
+        public StencilTestState StencilTestState;
+        public fixed uint Reserved13A0[3];
+        public YControl YControl;
+        public float LineWidthSmooth;
+        public float LineWidthAliased;
+        public fixed uint Reserved13B8[31];
+        public uint FirstVertex;
+        public uint FirstInstance;
+        public fixed uint Reserved143C[53];
+        public uint ClipDistanceEnable;
+        public uint Reserved1514;
+        public float PointSize;
+        public uint Reserved151C;
+        public Boolean32 PointSpriteEnable;
+        public fixed uint Reserved1524[3];
+        public uint ResetCounter;
+        public uint Reserved1534;
+        public Boolean32 RtDepthStencilEnable;
+        public fixed uint Reserved153C[5];
+        public GpuVa RenderEnableAddress;
+        public Condition RenderEnableCondition;
+        public PoolState SamplerPoolState;
+        public uint Reserved1568;
+        public float DepthBiasFactor;
+        public Boolean32 LineSmoothEnable;
+        public PoolState TexturePoolState;
+        public fixed uint Reserved1580[5];
+        public StencilBackTestState StencilBackTestState;
+        public fixed uint Reserved15A8[5];
+        public float DepthBiasUnits;
+        public fixed uint Reserved15C0[4];
+        public TextureMsaaMode RtMsaaMode;
+        public fixed uint Reserved15D4[5];
+        public uint VbElementU32;
+        public uint Reserved15EC;
+        public uint VbElementU16;
+        public fixed uint Reserved15F4[4];
+        public uint PointCoordReplace;
+        public GpuVa ShaderBaseAddress;
+        public uint Reserved1610;
+        public uint DrawEnd;
+        public uint DrawBegin;
+        public fixed uint Reserved161C[10];
+        public PrimitiveRestartState PrimitiveRestartState;
+        public fixed uint Reserved164C[95];
+        public IndexBufferState IndexBufferState;
+        public uint IndexBufferCount;
+        public uint DrawIndexedSmall;
+        public uint DrawIndexedSmall2;
+        public uint Reserved17EC;
+        public uint DrawIndexedSmallIncInstance;
+        public uint DrawIndexedSmallIncInstance2;
+        public fixed uint Reserved17F8[33];
+        public float DepthBiasClamp;
+        public Array16<Boolean32> VertexBufferInstanced;
+        public fixed uint Reserved18C0[20];
+        public Boolean32 VertexProgramPointSize;
+        public uint Reserved1914;
+        public FaceState FaceState;
+        public fixed uint Reserved1924[2];
+        public uint ViewportTransformEnable;
+        public fixed uint Reserved1930[3];
+        public ViewVolumeClipControl ViewVolumeClipControl;
+        public fixed uint Reserved1940[2];
+        public Boolean32 PrimitiveTypeOverrideEnable;
+        public fixed uint Reserved194C[9];
+        public PrimitiveTypeOverride PrimitiveTypeOverride;
+        public fixed uint Reserved1974[20];
+        public LogicalOpState LogicOpState;
+        public uint Reserved19CC;
+        public uint Clear;
+        public fixed uint Reserved19D4[11];
+        public Array8<RtColorMask> RtColorMask;
+        public fixed uint Reserved1A20[56];
+        public GpuVa SemaphoreAddress;
+        public int SemaphorePayload;
+        public uint SemaphoreControl;
+        public fixed uint Reserved1B10[60];
+        public Array16<VertexBufferState> VertexBufferState;
+        public fixed uint Reserved1D00[64];
+        public Array8<BlendState> BlendState;
+        public Array16<GpuVa> VertexBufferEndAddress;
+        public fixed uint Reserved1F80[32];
+        public Array6<ShaderState> ShaderState;
+        public fixed uint Reserved2180[96];
+        public uint SetFalcon00;
+        public uint SetFalcon01;
+        public uint SetFalcon02;
+        public uint SetFalcon03;
+        public uint SetFalcon04;
+        public uint SetFalcon05;
+        public uint SetFalcon06;
+        public uint SetFalcon07;
+        public uint SetFalcon08;
+        public uint SetFalcon09;
+        public uint SetFalcon10;
+        public uint SetFalcon11;
+        public uint SetFalcon12;
+        public uint SetFalcon13;
+        public uint SetFalcon14;
+        public uint SetFalcon15;
+        public uint SetFalcon16;
+        public uint SetFalcon17;
+        public uint SetFalcon18;
+        public uint SetFalcon19;
+        public uint SetFalcon20;
+        public uint SetFalcon21;
+        public uint SetFalcon22;
+        public uint SetFalcon23;
+        public uint SetFalcon24;
+        public uint SetFalcon25;
+        public uint SetFalcon26;
+        public uint SetFalcon27;
+        public uint SetFalcon28;
+        public uint SetFalcon29;
+        public uint SetFalcon30;
+        public uint SetFalcon31;
+        public UniformBufferState UniformBufferState;
+        public Array16<uint> UniformBufferUpdateData;
+        public fixed uint Reserved23D0[16];
+        public uint UniformBufferBindVertex;
+        public fixed uint Reserved2414[7];
+        public uint UniformBufferBindTessControl;
+        public fixed uint Reserved2434[7];
+        public uint UniformBufferBindTessEvaluation;
+        public fixed uint Reserved2454[7];
+        public uint UniformBufferBindGeometry;
+        public fixed uint Reserved2474[7];
+        public uint UniformBufferBindFragment;
+        public fixed uint Reserved2494[93];
+        public uint TextureBufferIndex;
+        public fixed uint Reserved260C[125];
+        public Array4<Array32<uint>> TfVaryingLocations;
+        public fixed uint Reserved2A00[640];
+        public MmeShadowScratch SetMmeShadowScratch;
+#pragma warning restore CS0649
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs
index d4f6d879ea..3c74c1647a 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs
@@ -1,7 +1,7 @@
 using Ryujinx.Graphics.Device;
 using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Types;
 using Ryujinx.Graphics.Gpu.Image;
-using Ryujinx.Graphics.Gpu.State;
 using Ryujinx.Graphics.Texture;
 using System;
 using System.Collections.Generic;
@@ -52,8 +52,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
         {
             var memoryManager = _channel.MemoryManager;
 
-            var dstCopyTexture = Unsafe.As<uint, CopyTexture>(ref _state.State.SetDstFormat);
-            var srcCopyTexture = Unsafe.As<uint, CopyTexture>(ref _state.State.SetSrcFormat);
+            var dstCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetDstFormat);
+            var srcCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetSrcFormat);
 
             long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac;
             long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac;
diff --git a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs
index fdc4204db7..46fddb04c0 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs
@@ -13,17 +13,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
         WriteThenAwaken = 1,
     }
 
-    /// <summary>
-    /// MME shadow RAM control mode.
-    /// </summary>
-    enum SetMmeShadowRamControlMode
-    {
-        MethodTrack = 0,
-        MethodTrackWithFilter = 1,
-        MethodPassthrough = 2,
-        MethodReplay = 3,
-    }
-
     /// <summary>
     /// Format of the destination texture.
     /// </summary>
@@ -506,7 +495,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
     /// <summary>
     /// 2D class state.
     /// </summary>
-    unsafe struct TwodClassState
+    unsafe struct TwodClassState : IShadowState
     {
 #pragma warning disable CS0649
         public uint SetObject;
diff --git a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs
new file mode 100644
index 0000000000..c28da0948b
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs
@@ -0,0 +1,22 @@
+using Ryujinx.Graphics.Gpu.Engine.Types;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Twod
+{
+    /// <summary>
+    /// Texture to texture (with optional resizing) copy parameters.
+    /// </summary>
+    struct TwodTexture
+    {
+#pragma warning disable CS0649
+        public ColorFormat Format;
+        public Boolean32 LinearLayout;
+        public MemoryLayout MemoryLayout;
+        public int Depth;
+        public int Layer;
+        public int Stride;
+        public int Width;
+        public int Height;
+        public GpuVa Address;
+#pragma warning restore CS0649
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/State/Boolean32.cs b/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs
similarity index 88%
rename from Ryujinx.Graphics.Gpu/State/Boolean32.cs
rename to Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs
index 78e13b733c..c982347a1c 100644
--- a/Ryujinx.Graphics.Gpu/State/Boolean32.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Graphics.Gpu.State
+namespace Ryujinx.Graphics.Gpu.Engine.Types
 {
     /// <summary>
     /// Boolean value, stored as a 32-bits integer in memory.
diff --git a/Ryujinx.Graphics.Gpu/State/ColorFormat.cs b/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs
similarity index 99%
rename from Ryujinx.Graphics.Gpu/State/ColorFormat.cs
rename to Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs
index 111263968d..e780ec2350 100644
--- a/Ryujinx.Graphics.Gpu/State/ColorFormat.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs
@@ -1,7 +1,7 @@
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Gpu.Image;
 
-namespace Ryujinx.Graphics.Gpu.State
+namespace Ryujinx.Graphics.Gpu.Engine.Types
 {
     /// <summary>
     /// Color texture format.
diff --git a/Ryujinx.Graphics.Gpu/State/GpuVa.cs b/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs
similarity index 91%
rename from Ryujinx.Graphics.Gpu/State/GpuVa.cs
rename to Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs
index d6b7def30f..839faac9b4 100644
--- a/Ryujinx.Graphics.Gpu/State/GpuVa.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Graphics.Gpu.State
+namespace Ryujinx.Graphics.Gpu.Engine.Types
 {
     /// <summary>
     /// Split GPU virtual address.
diff --git a/Ryujinx.Graphics.Gpu/State/MemoryLayout.cs b/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs
similarity index 94%
rename from Ryujinx.Graphics.Gpu/State/MemoryLayout.cs
rename to Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs
index c98831f9df..6da96bd445 100644
--- a/Ryujinx.Graphics.Gpu/State/MemoryLayout.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Graphics.Gpu.State
+namespace Ryujinx.Graphics.Gpu.Engine.Types
 {
     /// <summary>
     /// Memory layout parameters, for block linear textures.
diff --git a/Ryujinx.Graphics.Gpu/State/PrimitiveType.cs b/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs
similarity index 98%
rename from Ryujinx.Graphics.Gpu/State/PrimitiveType.cs
rename to Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs
index b49088aacf..dae631248d 100644
--- a/Ryujinx.Graphics.Gpu/State/PrimitiveType.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs
@@ -1,6 +1,6 @@
 using Ryujinx.Graphics.GAL;
 
-namespace Ryujinx.Graphics.Gpu.State
+namespace Ryujinx.Graphics.Gpu.Engine.Types
 {
     /// <summary>
     /// Draw primitive type.
@@ -29,7 +29,6 @@ namespace Ryujinx.Graphics.Gpu.State
     /// </summary>
     enum PrimitiveTypeOverride
     {
-        Invalid = 0,
         Points = 1,
         Lines = 2,
         LineStrip = 3,
diff --git a/Ryujinx.Graphics.Gpu/State/SamplerIndex.cs b/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs
similarity index 78%
rename from Ryujinx.Graphics.Gpu/State/SamplerIndex.cs
rename to Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs
index c2aaff43f1..839a4d0af3 100644
--- a/Ryujinx.Graphics.Gpu/State/SamplerIndex.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Graphics.Gpu.State
+namespace Ryujinx.Graphics.Gpu.Engine.Types
 {
     /// <summary>
     /// Sampler pool indexing mode.
diff --git a/Ryujinx.Graphics.Gpu/State/SbDescriptor.cs b/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs
similarity index 90%
rename from Ryujinx.Graphics.Gpu/State/SbDescriptor.cs
rename to Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs
index 9723b71979..c457dbf917 100644
--- a/Ryujinx.Graphics.Gpu/State/SbDescriptor.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Graphics.Gpu.State
+namespace Ryujinx.Graphics.Gpu.Engine.Types
 {
     /// <summary>
     /// Storage buffer address and size information.
diff --git a/Ryujinx.Graphics.Gpu/State/ZetaFormat.cs b/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs
similarity index 97%
rename from Ryujinx.Graphics.Gpu/State/ZetaFormat.cs
rename to Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs
index 01b608b3cf..2de38fd20d 100644
--- a/Ryujinx.Graphics.Gpu/State/ZetaFormat.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs
@@ -1,7 +1,7 @@
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Gpu.Image;
 
-namespace Ryujinx.Graphics.Gpu.State
+namespace Ryujinx.Graphics.Gpu.Engine.Types
 {
     /// <summary>
     /// Depth-stencil texture format.
diff --git a/Ryujinx.Graphics.Gpu/GpuChannel.cs b/Ryujinx.Graphics.Gpu/GpuChannel.cs
index e9f08eb850..b9d91f93e3 100644
--- a/Ryujinx.Graphics.Gpu/GpuChannel.cs
+++ b/Ryujinx.Graphics.Gpu/GpuChannel.cs
@@ -64,6 +64,17 @@ namespace Ryujinx.Graphics.Gpu
             memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
         }
 
+        /// <summary>
+        /// Writes data directly to the state of the specified class.
+        /// </summary>
+        /// <param name="classId">ID of the class to write the data into</param>
+        /// <param name="offset">State offset in bytes</param>
+        /// <param name="value">Value to be written</param>
+        public void Write(ClassId classId, int offset, uint value)
+        {
+            _processor.Write(classId, offset, (int)value);
+        }
+
         /// <summary>
         /// Push a GPFIFO entry in the form of a prefetched command buffer.
         /// It is intended to be used by nvservices to handle special cases.
diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs
index 7fae249e3a..734fc4928f 100644
--- a/Ryujinx.Graphics.Gpu/GpuContext.cs
+++ b/Ryujinx.Graphics.Gpu/GpuContext.cs
@@ -1,5 +1,4 @@
 using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Engine;
 using Ryujinx.Graphics.Gpu.Engine.GPFifo;
 using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.Graphics.Gpu.Shader;
@@ -26,11 +25,6 @@ namespace Ryujinx.Graphics.Gpu
         /// </summary>
         public IRenderer Renderer { get; }
 
-        /// <summary>
-        /// GPU engine methods processing.
-        /// </summary>
-        internal Methods Methods { get; }
-
         /// <summary>
         /// GPU General Purpose FIFO queue.
         /// </summary>
@@ -94,8 +88,6 @@ namespace Ryujinx.Graphics.Gpu
         {
             Renderer = renderer;
 
-            Methods = new Methods(this);
-
             GPFifo = new GPFifoDevice(this);
 
             Synchronization = new SynchronizationManager();
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
index b247f99ff4..96eb6f04c6 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
@@ -1,5 +1,5 @@
 using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Gpu.Engine.Types;
 using Ryujinx.Graphics.Shader;
 using System;
 
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index 106dc8e894..1b54033cff 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -1,8 +1,11 @@
 using Ryujinx.Common;
 using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Dma;
+using Ryujinx.Graphics.Gpu.Engine.Threed;
+using Ryujinx.Graphics.Gpu.Engine.Twod;
+using Ryujinx.Graphics.Gpu.Engine.Types;
 using Ryujinx.Graphics.Gpu.Image;
 using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.State;
 using Ryujinx.Graphics.Texture;
 using Ryujinx.Memory.Range;
 using System;
@@ -151,7 +154,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// <returns>The texture</returns>
         public Texture FindOrCreateTexture(
             MemoryManager memoryManager,
-            CopyTexture copyTexture,
+            TwodTexture copyTexture,
             ulong offset,
             FormatInfo formatInfo,
             bool preferScaling = true,
@@ -762,7 +765,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// <returns>A matching texture, or null if there is no match</returns>
         public Texture FindTexture(
             MemoryManager memoryManager,
-            CopyBufferTexture tex,
+            DmaTexture tex,
             ulong gpuVa,
             int bpp,
             int stride,
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
index fcc67f7236..157b7c17ef 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs
@@ -1,5 +1,5 @@
 using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Gpu.Engine.Types;
 using System;
 
 namespace Ryujinx.Graphics.Gpu.Image
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs
index 6c5116ba3d..58dd838e59 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs
@@ -1,5 +1,4 @@
 using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.State;
 using Ryujinx.Memory.Range;
 using System;
 using System.Collections.Generic;
diff --git a/Ryujinx.Graphics.Gpu/MethodParams.cs b/Ryujinx.Graphics.Gpu/MethodParams.cs
deleted file mode 100644
index dd60f77cd0..0000000000
--- a/Ryujinx.Graphics.Gpu/MethodParams.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-namespace Ryujinx.Graphics
-{
-    /// <summary>
-    /// Method call parameters.
-    /// </summary>
-    struct MethodParams
-    {
-        /// <summary>
-        /// Method offset.
-        /// </summary>
-        public int Method { get; }
-
-        /// <summary>
-        /// Method call argument.
-        /// </summary>
-        public int Argument { get; }
-
-        /// <summary>
-        /// Sub-channel where the call should be sent.
-        /// </summary>
-        public int SubChannel { get; }
-
-        /// <summary>
-        /// For multiple calls to the same method, this is the remaining calls count.
-        /// </summary>
-        public int MethodCount { get; }
-
-        /// <summary>
-        /// Indicates if the current call is the last one from a batch of calls to the same method.
-        /// </summary>
-        public bool IsLastCall => MethodCount <= 1;
-
-        /// <summary>
-        /// Constructs the method call parameters structure.
-        /// </summary>
-        /// <param name="method">Method offset</param>
-        /// <param name="argument">Method call argument</param>
-        /// <param name="subChannel">Optional sub-channel where the method should be sent (not required for macro calls)</param>
-        /// <param name="methodCount">Optional remaining calls count (not required for macro calls)</param>
-        public MethodParams(
-            int method,
-            int argument,
-            int subChannel  = 0,
-            int methodCount = 0)
-        {
-            Method      = method;
-            Argument    = argument;
-            SubChannel  = subChannel;
-            MethodCount = methodCount;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
index 7fb979f4a5..b7059b51d5 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
@@ -144,27 +144,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
         /// <returns>Current primitive topology</returns>
         public InputTopology QueryPrimitiveTopology()
         {
-            switch (_context.Methods.Topology)
+            return _state.Topology switch
             {
-                case PrimitiveTopology.Points:
-                    return InputTopology.Points;
-                case PrimitiveTopology.Lines:
-                case PrimitiveTopology.LineLoop:
-                case PrimitiveTopology.LineStrip:
-                    return InputTopology.Lines;
-                case PrimitiveTopology.LinesAdjacency:
-                case PrimitiveTopology.LineStripAdjacency:
-                    return InputTopology.LinesAdjacency;
-                case PrimitiveTopology.Triangles:
-                case PrimitiveTopology.TriangleStrip:
-                case PrimitiveTopology.TriangleFan:
-                    return InputTopology.Triangles;
-                case PrimitiveTopology.TrianglesAdjacency:
-                case PrimitiveTopology.TriangleStripAdjacency:
-                    return InputTopology.TrianglesAdjacency;
-            }
-
-            return InputTopology.Points;
+                PrimitiveTopology.Points => InputTopology.Points,
+                PrimitiveTopology.Lines or
+                PrimitiveTopology.LineLoop or
+                PrimitiveTopology.LineStrip => InputTopology.Lines,
+                PrimitiveTopology.LinesAdjacency or
+                PrimitiveTopology.LineStripAdjacency => InputTopology.LinesAdjacency,
+                PrimitiveTopology.Triangles or
+                PrimitiveTopology.TriangleStrip or
+                PrimitiveTopology.TriangleFan => InputTopology.Triangles,
+                PrimitiveTopology.TrianglesAdjacency or
+                PrimitiveTopology.TriangleStripAdjacency => InputTopology.TrianglesAdjacency,
+                _ => InputTopology.Points,
+            };
         }
 
         /// <summary>
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs
index 17660cf9ff..8d81711321 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Graphics.GAL;
+
 namespace Ryujinx.Graphics.Gpu.Shader
 {
     /// <summary>
@@ -25,6 +27,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
         /// </summary>
         public bool EarlyZForce { get; }
 
+        /// <summary>
+        /// Primitive topology of current draw.
+        /// </summary>
+        public PrimitiveTopology Topology { get; }
+
         /// <summary>
         /// Creates a new instance of the GPU accessor state.
         /// </summary>
@@ -32,12 +39,19 @@ namespace Ryujinx.Graphics.Gpu.Shader
         /// <param name="texturePoolMaximumId">Maximum ID of the texture pool</param>
         /// <param name="textureBufferIndex">Constant buffer slot where the texture handles are located</param>
         /// <param name="earlyZForce">Early Z force enable</param>
-        public GpuAccessorState(ulong texturePoolGpuVa, int texturePoolMaximumId, int textureBufferIndex, bool earlyZForce)
+        /// <param name="topology">Primitive topology</param>
+        public GpuAccessorState(
+            ulong texturePoolGpuVa,
+            int texturePoolMaximumId,
+            int textureBufferIndex,
+            bool earlyZForce,
+            PrimitiveTopology topology)
         {
             TexturePoolGpuVa = texturePoolGpuVa;
             TexturePoolMaximumId = texturePoolMaximumId;
             TextureBufferIndex = textureBufferIndex;
             EarlyZForce = earlyZForce;
+            Topology = topology;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index 031c95a93c..e5c1fb83e9 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -1,15 +1,16 @@
 using Ryujinx.Common;
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Threed;
 using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.Graphics.Gpu.Shader.Cache;
 using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
-using Ryujinx.Graphics.Gpu.State;
 using Ryujinx.Graphics.Shader;
 using Ryujinx.Graphics.Shader.Translation;
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -593,10 +594,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
         /// <remarks>
         /// This automatically translates, compiles and adds the code to the cache if not present.
         /// </remarks>
-        /// <param name="state">Current GPU state</param>
+        /// <param name="state">GPU state</param>
+        /// <param name="channel">GPU channel</param>
+        /// <param name="gas">GPU accessor state</param>
         /// <param name="addresses">Addresses of the shaders for each stage</param>
         /// <returns>Compiled graphics shader code</returns>
-        public ShaderBundle GetGraphicsShader(GpuState state, ShaderAddresses addresses)
+        public ShaderBundle GetGraphicsShader(ref ThreedClassState state, GpuChannel channel, GpuAccessorState gas, ShaderAddresses addresses)
         {
             bool isCached = _gpPrograms.TryGetValue(addresses, out List<ShaderBundle> list);
 
@@ -604,7 +607,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
             {
                 foreach (ShaderBundle cachedGpShaders in list)
                 {
-                    if (IsShaderEqual(state.Channel.MemoryManager, cachedGpShaders, addresses))
+                    if (IsShaderEqual(channel.MemoryManager, cachedGpShaders, addresses))
                     {
                         return cachedGpShaders;
                     }
@@ -613,7 +616,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
 
             TranslatorContext[] shaderContexts = new TranslatorContext[Constants.ShaderStages + 1];
 
-            TransformFeedbackDescriptor[] tfd = GetTransformFeedbackDescriptors(state);
+            TransformFeedbackDescriptor[] tfd = GetTransformFeedbackDescriptors(ref state);
 
             TranslationFlags flags = DefaultFlags;
 
@@ -626,14 +629,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
 
             if (addresses.VertexA != 0)
             {
-                shaderContexts[0] = DecodeGraphicsShader(state, counts, flags | TranslationFlags.VertexA, ShaderStage.Vertex, addresses.VertexA);
+                shaderContexts[0] = DecodeGraphicsShader(channel, gas, counts, flags | TranslationFlags.VertexA, ShaderStage.Vertex, addresses.VertexA);
             }
 
-            shaderContexts[1] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Vertex, addresses.Vertex);
-            shaderContexts[2] = DecodeGraphicsShader(state, counts, flags, ShaderStage.TessellationControl, addresses.TessControl);
-            shaderContexts[3] = DecodeGraphicsShader(state, counts, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation);
-            shaderContexts[4] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Geometry, addresses.Geometry);
-            shaderContexts[5] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Fragment, addresses.Fragment);
+            shaderContexts[1] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Vertex, addresses.Vertex);
+            shaderContexts[2] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.TessellationControl, addresses.TessControl);
+            shaderContexts[3] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation);
+            shaderContexts[4] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Geometry, addresses.Geometry);
+            shaderContexts[5] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Fragment, addresses.Fragment);
 
             bool isShaderCacheEnabled = _cacheManager != null;
             bool isShaderCacheReadOnly = false;
@@ -656,7 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
                 isShaderCacheReadOnly = _cacheManager.IsReadOnly;
 
                 // Compute hash and prepare data for shader disk cache comparison.
-                shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(state.Channel.MemoryManager, shaderContexts);
+                shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(channel.MemoryManager, shaderContexts);
                 programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries, tfd);
             }
 
@@ -673,11 +676,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
                 // The shader isn't currently cached, translate it and compile it.
                 ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages];
 
-                shaders[0] = TranslateShader(state.Channel.MemoryManager, shaderContexts[1], shaderContexts[0]);
-                shaders[1] = TranslateShader(state.Channel.MemoryManager, shaderContexts[2]);
-                shaders[2] = TranslateShader(state.Channel.MemoryManager, shaderContexts[3]);
-                shaders[3] = TranslateShader(state.Channel.MemoryManager, shaderContexts[4]);
-                shaders[4] = TranslateShader(state.Channel.MemoryManager, shaderContexts[5]);
+                shaders[0] = TranslateShader(channel.MemoryManager, shaderContexts[1], shaderContexts[0]);
+                shaders[1] = TranslateShader(channel.MemoryManager, shaderContexts[2]);
+                shaders[2] = TranslateShader(channel.MemoryManager, shaderContexts[3]);
+                shaders[3] = TranslateShader(channel.MemoryManager, shaderContexts[4]);
+                shaders[4] = TranslateShader(channel.MemoryManager, shaderContexts[5]);
 
                 List<IShader> hostShaders = new List<IShader>();
 
@@ -733,9 +736,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
         /// </summary>
         /// <param name="state">Current GPU state</param>
         /// <returns>Four transform feedback descriptors for the enabled TFBs, or null if TFB is disabled</returns>
-        private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(GpuState state)
+        private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(ref ThreedClassState state)
         {
-            bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable);
+            bool tfEnable = state.TfEnable;
 
             if (!tfEnable)
             {
@@ -746,13 +749,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
 
             for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
             {
-                var tf = state.Get<TfState>(MethodOffset.TfState, i);
+                var tf = state.TfState[i];
 
                 int length = (int)Math.Min((uint)tf.VaryingsCount, 0x80);
 
-                var varyingLocations = state.GetSpan(MethodOffset.TfVaryingLocations + i * 0x80, length).ToArray();
+                var varyingLocations = MemoryMarshal.Cast<uint, byte>(state.TfVaryingLocations[i].ToSpan()).Slice(0, length);
 
-                descs[i] = new TransformFeedbackDescriptor(tf.BufferIndex, tf.Stride, varyingLocations);
+                descs[i] = new TransformFeedbackDescriptor(tf.BufferIndex, tf.Stride, varyingLocations.ToArray());
             }
 
             return descs;
@@ -871,14 +874,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
         /// <remarks>
         /// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader.
         /// </remarks>
-        /// <param name="state">Current GPU state</param>
+        /// <param name="channel">GPU channel</param>
+        /// <param name="gas">GPU accessor state</param>
         /// <param name="counts">Cumulative shader resource counts</param>
         /// <param name="flags">Flags that controls shader translation</param>
         /// <param name="stage">Shader stage</param>
         /// <param name="gpuVa">GPU virtual address of the shader code</param>
         /// <returns>The generated translator context</returns>
         private TranslatorContext DecodeGraphicsShader(
-            GpuState state,
+            GpuChannel channel,
+            GpuAccessorState gas,
             TranslationCounts counts,
             TranslationFlags flags,
             ShaderStage stage,
@@ -889,13 +894,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
                 return null;
             }
 
-            GpuAccessorState gas = new GpuAccessorState(
-                state.Get<PoolState>(MethodOffset.TexturePoolState).Address.Pack(),
-                state.Get<PoolState>(MethodOffset.TexturePoolState).MaximumId,
-                state.Get<int>(MethodOffset.TextureBufferIndex),
-                state.Get<Boolean32>(MethodOffset.EarlyZForce));
-
-            GpuAccessor gpuAccessor = new GpuAccessor(_context, state.Channel, gas, (int)stage - 1);
+            GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gas, (int)stage - 1);
 
             var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, flags);
             return Translator.CreateContext(gpuVa, gpuAccessor, options, counts);
diff --git a/Ryujinx.Graphics.Gpu/ShadowRamControl.cs b/Ryujinx.Graphics.Gpu/ShadowRamControl.cs
deleted file mode 100644
index 10dd39bccf..0000000000
--- a/Ryujinx.Graphics.Gpu/ShadowRamControl.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-namespace Ryujinx.Graphics.Gpu
-{
-    /// <summary>
-    /// Shadow RAM Control setting.
-    /// </summary>
-    enum ShadowRamControl
-    {
-        /// <summary>
-        /// Track data writes and store them on shadow RAM.
-        /// </summary>
-        Track = 0,
-
-        /// <summary>
-        /// Track data writes and store them on shadow RAM, with filtering.
-        /// </summary>
-        TrackWithFilter = 1,
-
-        /// <summary>
-        /// Writes data directly without storing on shadow RAM.
-        /// </summary>
-        Passthrough = 2,
-
-        /// <summary>
-        /// Ignore data being written and replace with data on shadow RAM instead.
-        /// </summary>
-        Replay = 3
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/BlendState.cs b/Ryujinx.Graphics.Gpu/State/BlendState.cs
deleted file mode 100644
index ba16b8bd8b..0000000000
--- a/Ryujinx.Graphics.Gpu/State/BlendState.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Ryujinx.Graphics.GAL;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Color buffer blending parameters.
-    /// </summary>
-    struct BlendState
-    {
-#pragma warning disable CS0649
-        public Boolean32   SeparateAlpha;
-        public BlendOp     ColorOp;
-        public BlendFactor ColorSrcFactor;
-        public BlendFactor ColorDstFactor;
-        public BlendOp     AlphaOp;
-        public BlendFactor AlphaSrcFactor;
-        public BlendFactor AlphaDstFactor;
-        public uint        Padding;
-#pragma warning restore CS0649
-
-        public static BlendState Default = new BlendState
-        {
-            ColorOp = BlendOp.Add,
-            ColorSrcFactor = BlendFactor.One,
-            ColorDstFactor = BlendFactor.Zero,
-            AlphaOp = BlendOp.Add,
-            AlphaSrcFactor = BlendFactor.One,
-            AlphaDstFactor = BlendFactor.Zero
-        };
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/BlendStateCommon.cs b/Ryujinx.Graphics.Gpu/State/BlendStateCommon.cs
deleted file mode 100644
index f402a5a70e..0000000000
--- a/Ryujinx.Graphics.Gpu/State/BlendStateCommon.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Ryujinx.Graphics.GAL;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Color buffer blending parameters, shared by all color buffers.
-    /// </summary>
-    struct BlendStateCommon
-    {
-#pragma warning disable CS0649
-        public Boolean32   SeparateAlpha;
-        public BlendOp     ColorOp;
-        public BlendFactor ColorSrcFactor;
-        public BlendFactor ColorDstFactor;
-        public BlendOp     AlphaOp;
-        public BlendFactor AlphaSrcFactor;
-        public uint        Unknown0x1354;
-        public BlendFactor AlphaDstFactor;
-#pragma warning restore CS0649
-
-        public static BlendStateCommon Default = new BlendStateCommon
-        {
-            ColorOp = BlendOp.Add,
-            ColorSrcFactor = BlendFactor.One,
-            ColorDstFactor = BlendFactor.Zero,
-            AlphaOp = BlendOp.Add,
-            AlphaSrcFactor = BlendFactor.One,
-            AlphaDstFactor = BlendFactor.Zero
-        };
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/ClearColors.cs b/Ryujinx.Graphics.Gpu/State/ClearColors.cs
deleted file mode 100644
index ba29c899c8..0000000000
--- a/Ryujinx.Graphics.Gpu/State/ClearColors.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Color buffer clear color.
-    /// </summary>
-    struct ClearColors
-    {
-#pragma warning disable CS0649
-        public float Red;
-        public float Green;
-        public float Blue;
-        public float Alpha;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/Condition.cs b/Ryujinx.Graphics.Gpu/State/Condition.cs
deleted file mode 100644
index 5afdbe3edb..0000000000
--- a/Ryujinx.Graphics.Gpu/State/Condition.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Condition for conditional rendering.
-    /// </summary>
-    enum Condition
-    {
-        Never,
-        Always,
-        ResultNonZero,
-        Equal,
-        NotEqual
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/ConditionState.cs b/Ryujinx.Graphics.Gpu/State/ConditionState.cs
deleted file mode 100644
index 3388e8f029..0000000000
--- a/Ryujinx.Graphics.Gpu/State/ConditionState.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Condition parameters for conditional rendering.
-    /// </summary>
-    struct ConditionState
-    {
-#pragma warning disable CS0649
-        public GpuVa     Address;
-        public Condition Condition;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/CopyBufferTexture.cs b/Ryujinx.Graphics.Gpu/State/CopyBufferTexture.cs
deleted file mode 100644
index dfbab37ad7..0000000000
--- a/Ryujinx.Graphics.Gpu/State/CopyBufferTexture.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Buffer to texture copy parameters.
-    /// </summary>
-    struct CopyBufferTexture
-    {
-#pragma warning disable CS0649
-        public MemoryLayout MemoryLayout;
-        public int          Width;
-        public int          Height;
-        public int          Depth;
-        public int          RegionZ;
-        public ushort       RegionX;
-        public ushort       RegionY;
-#pragma warning restore CS0649
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/CopyRegion.cs b/Ryujinx.Graphics.Gpu/State/CopyRegion.cs
deleted file mode 100644
index 29889835fa..0000000000
--- a/Ryujinx.Graphics.Gpu/State/CopyRegion.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Texture copy region.
-    /// </summary>
-    struct CopyRegion
-    {
-#pragma warning disable CS0649
-        public int  DstX;
-        public int  DstY;
-        public int  DstWidth;
-        public int  DstHeight;
-        public long SrcWidthRF;
-        public long SrcHeightRF;
-        public long SrcXF;
-        public long SrcYF;
-#pragma warning restore CS0649
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/CopyTexture.cs b/Ryujinx.Graphics.Gpu/State/CopyTexture.cs
deleted file mode 100644
index 28ea0bd81e..0000000000
--- a/Ryujinx.Graphics.Gpu/State/CopyTexture.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Texture to texture (with optional resizing) copy parameters.
-    /// </summary>
-    struct CopyTexture
-    {
-#pragma warning disable CS0649
-        public ColorFormat  Format;
-        public Boolean32    LinearLayout;
-        public MemoryLayout MemoryLayout;
-        public int          Depth;
-        public int          Layer;
-        public int          Stride;
-        public int          Width;
-        public int          Height;
-        public GpuVa        Address;
-#pragma warning restore CS0649
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/DepthBiasState.cs b/Ryujinx.Graphics.Gpu/State/DepthBiasState.cs
deleted file mode 100644
index 0a125804e4..0000000000
--- a/Ryujinx.Graphics.Gpu/State/DepthBiasState.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Depth bias (also called polygon offset) parameters.
-    /// </summary>
-    struct DepthBiasState
-    {
-#pragma warning disable CS0649
-        public Boolean32 PointEnable;
-        public Boolean32 LineEnable;
-        public Boolean32 FillEnable;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/FaceState.cs b/Ryujinx.Graphics.Gpu/State/FaceState.cs
deleted file mode 100644
index e817b3ae3e..0000000000
--- a/Ryujinx.Graphics.Gpu/State/FaceState.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using Ryujinx.Graphics.GAL;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Face culling and orientation parameters.
-    /// </summary>
-    struct FaceState
-    {
-#pragma warning disable CS0649
-        public Boolean32 CullEnable;
-        public FrontFace FrontFace;
-        public Face      CullFace;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/GpuState.cs b/Ryujinx.Graphics.Gpu/State/GpuState.cs
deleted file mode 100644
index 0b209da706..0000000000
--- a/Ryujinx.Graphics.Gpu/State/GpuState.cs
+++ /dev/null
@@ -1,477 +0,0 @@
-using Ryujinx.Graphics.Device;
-using Ryujinx.Graphics.Gpu.Image;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// GPU state.
-    /// </summary>
-    class GpuState
-    {
-        private const int RegistersCount = 0xe00;
-
-        public delegate void MethodCallback(GpuState state, int argument);
-
-        private readonly int[] _memory;
-        private readonly int[] _shadow;
-
-        /// <summary>
-        /// GPU register information.
-        /// </summary>
-        private struct Register
-        {
-            public MethodCallback Callback;
-
-            public MethodOffset BaseOffset;
-
-            public int Stride;
-            public int Count;
-
-            public bool Modified;
-        }
-
-        private readonly Register[] _registers;
-
-        private readonly IDeviceState _deviceState;
-
-        /// <summary>
-        /// Gets or sets the shadow ram control used for this sub-channel.
-        /// </summary>
-        public ShadowRamControl ShadowRamControl { get; set; }
-
-        /// <summary>
-        /// GPU channel for the sub-channel state.
-        /// </summary>
-        public GpuChannel Channel { get; }
-
-        /// <summary>
-        /// Creates a new instance of the GPU state.
-        /// </summary>
-        /// <param name="channel">Channel that the sub-channel state belongs to</param>
-        /// <param name="deviceState">Optional device state that will replace the internal backing storage</param>
-        public GpuState(GpuChannel channel, IDeviceState deviceState = null)
-        {
-            Channel = channel;
-            _deviceState = deviceState;
-
-            _memory = new int[RegistersCount];
-            _shadow = 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(_memory);
-            InitializeDefaultState(_shadow);
-        }
-
-        /// <summary>
-        /// Calls a GPU method, using this state.
-        /// </summary>
-        /// <param name="meth">The GPU method to be called</param>
-        public void CallMethod(MethodParams meth)
-        {
-            int value = meth.Argument;
-
-            // Methods < 0x80 shouldn't be affected by shadow RAM at all.
-            if (meth.Method >= 0x80)
-            {
-                ShadowRamControl shadowCtrl = ShadowRamControl;
-
-                // TODO: Figure out what TrackWithFilter does, compared to Track.
-                if (shadowCtrl == ShadowRamControl.Track ||
-                    shadowCtrl == ShadowRamControl.TrackWithFilter)
-                {
-                    _shadow[meth.Method] = value;
-                }
-                else if (shadowCtrl == ShadowRamControl.Replay)
-                {
-                    value = _shadow[meth.Method];
-                }
-            }
-
-            if (_deviceState != null)
-            {
-                _deviceState.Write(meth.Method * 4, meth.Argument);
-            }
-            else
-            {
-                Register register = _registers[meth.Method];
-
-                if (_memory[meth.Method] != value)
-                {
-                    _registers[(int)register.BaseOffset].Modified = true;
-                }
-
-                _memory[meth.Method] = value;
-
-                register.Callback?.Invoke(this, value);
-            }
-        }
-
-        /// <summary>
-        /// Reads data from a GPU register at the given offset.
-        /// </summary>
-        /// <param name="offset">Offset to be read</param>
-        /// <returns>Data at the register</returns>
-        public int Read(int offset)
-        {
-            if (_deviceState != null)
-            {
-                return _deviceState.Read(offset * 4);
-            }
-
-            return _memory[offset];
-        }
-
-        /// <summary>
-        /// Writes data to the GPU register at the given offset.
-        /// </summary>
-        /// <param name="offset">Offset to be written</param>
-        /// <param name="value">Value to be written</param>
-        public void Write(int offset, int value)
-        {
-            _memory[offset] = value;
-        }
-
-        /// <summary>
-        /// Writes an offset value at the uniform buffer offset register.
-        /// </summary>
-        /// <param name="offset">The offset to be written</param>
-        public void SetUniformBufferOffset(int offset)
-        {
-            _memory[(int)MethodOffset.UniformBufferState + 3] = offset;
-        }
-
-        /// <summary>
-        /// Initializes registers with the default state.
-        /// </summary>
-        private void InitializeDefaultState(int[] memory)
-        {
-            // Enable Rasterizer
-            memory[(int)MethodOffset.RasterizeEnable] = 1;
-
-            // Depth ranges.
-            for (int index = 0; index < Constants.TotalViewports; index++)
-            {
-                memory[(int)MethodOffset.ViewportExtents + index * 4 + 2] = 0;
-                memory[(int)MethodOffset.ViewportExtents + index * 4 + 3] = 0x3F800000;
-
-                // Set swizzle to +XYZW
-                memory[(int)MethodOffset.ViewportTransform + index * 8 + 6] = 0x6420;
-            }
-
-            // Viewport transform enable.
-            memory[(int)MethodOffset.ViewportTransformEnable] = 1;
-
-            // Default front stencil mask.
-            memory[0x4e7] = 0xff;
-
-            // Conditional rendering condition.
-            memory[0x556] = (int)Condition.Always;
-
-            // Default color mask.
-            for (int index = 0; index < Constants.TotalRenderTargets; index++)
-            {
-                memory[(int)MethodOffset.RtColorMask + index] = 0x1111;
-            }
-
-            // Default blend states
-            Set(MethodOffset.BlendStateCommon, BlendStateCommon.Default);
-
-            for (int index = 0; index < Constants.TotalRenderTargets; index++)
-            {
-                Set(MethodOffset.BlendState, index, BlendState.Default);
-            }
-
-            // Default Point Parameters
-            memory[(int)MethodOffset.PointSpriteEnable] = 1;
-            memory[(int)MethodOffset.PointSize] = 0x3F800000; // 1.0f
-            memory[(int)MethodOffset.PointCoordReplace] = 0x8; // Enable
-        }
-
-        /// <summary>
-        /// Registers a callback that is called every time a GPU method, or methods are called.
-        /// </summary>
-        /// <param name="offset">Offset of the method</param>
-        /// <param name="count">Word count of the methods region</param>
-        /// <param name="callback">Calllback to be called</param>
-        public void RegisterCallback(MethodOffset offset, int count, MethodCallback callback)
-        {
-            for (int index = 0; index < count; index++)
-            {
-                _registers[(int)offset + index].Callback = callback;
-            }
-        }
-
-        /// <summary>
-        /// Registers a callback that is called every time a GPU method is called.
-        /// </summary>
-        /// <param name="offset">Offset of the method</param>
-        /// <param name="callback">Calllback to be called</param>
-        public void RegisterCallback(MethodOffset offset, MethodCallback callback)
-        {
-            _registers[(int)offset].Callback = callback;
-        }
-
-        /// <summary>
-        /// Clear all registered callbacks.
-        /// </summary>
-        public void ClearCallbacks()
-        {
-            for (int index = 0; index < _registers.Length; index++)
-            {
-                _registers[index].Callback = null;
-            }
-        }
-
-        /// <summary>
-        /// Forces a full host state update by marking all state as modified,
-        /// and also requests all GPU resources in use to be rebound.
-        /// </summary>
-        public void ForceAllDirty()
-        {
-            for (int index = 0; index < _registers.Length; index++)
-            {
-                _registers[index].Modified = true;
-            }
-
-            Channel.BufferManager.Rebind();
-            Channel.TextureManager.Rebind();
-        }
-
-        /// <summary>
-        /// Checks if a given register has been modified since the last call to this method.
-        /// </summary>
-        /// <param name="offset">Register offset</param>
-        /// <returns>True if modified, false otherwise</returns>
-        public bool QueryModified(MethodOffset offset)
-        {
-            bool modified = _registers[(int)offset].Modified;
-
-            _registers[(int)offset].Modified = false;
-
-            return modified;
-        }
-
-        /// <summary>
-        /// Checks if two registers have been modified since the last call to this method.
-        /// </summary>
-        /// <param name="m1">First register offset</param>
-        /// <param name="m2">Second register offset</param>
-        /// <returns>True if any register was modified, false otherwise</returns>
-        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;
-        }
-
-        /// <summary>
-        /// Checks if three registers have been modified since the last call to this method.
-        /// </summary>
-        /// <param name="m1">First register offset</param>
-        /// <param name="m2">Second register offset</param>
-        /// <param name="m3">Third register offset</param>
-        /// <returns>True if any register was modified, false otherwise</returns>
-        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;
-        }
-
-        /// <summary>
-        /// Checks if four registers have been modified since the last call to this method.
-        /// </summary>
-        /// <param name="m1">First register offset</param>
-        /// <param name="m2">Second register offset</param>
-        /// <param name="m3">Third register offset</param>
-        /// <param name="m4">Fourth register offset</param>
-        /// <returns>True if any register was modified, false otherwise</returns>
-        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;
-        }
-
-        /// <summary>
-        /// Checks if five registers have been modified since the last call to this method.
-        /// </summary>
-        /// <param name="m1">First register offset</param>
-        /// <param name="m2">Second register offset</param>
-        /// <param name="m3">Third register offset</param>
-        /// <param name="m4">Fourth register offset</param>
-        /// <param name="m5">Fifth register offset</param>
-        /// <returns>True if any register was modified, false otherwise</returns>
-        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;
-        }
-
-        /// <summary>
-        /// Checks if six registers have been modified since the last call to this method.
-        /// </summary>
-        /// <param name="m1">First register offset</param>
-        /// <param name="m2">Second register offset</param>
-        /// <param name="m3">Third register offset</param>
-        /// <param name="m4">Fourth register offset</param>
-        /// <param name="m5">Fifth register offset</param>
-        /// <param name="m6">Sixth register offset</param>
-        /// <returns>True if any register was modified, false otherwise</returns>
-        public bool QueryModified(
-            MethodOffset m1,
-            MethodOffset m2,
-            MethodOffset m3,
-            MethodOffset m4,
-            MethodOffset m5,
-            MethodOffset m6)
-        {
-            bool modified = _registers[(int)m1].Modified ||
-                            _registers[(int)m2].Modified ||
-                            _registers[(int)m3].Modified ||
-                            _registers[(int)m4].Modified ||
-                            _registers[(int)m5].Modified ||
-                            _registers[(int)m6].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;
-            _registers[(int)m6].Modified = false;
-
-            return modified;
-        }
-
-        /// <summary>
-        /// Gets indexed data from a given register offset.
-        /// </summary>
-        /// <typeparam name="T">Type of the data</typeparam>
-        /// <param name="offset">Register offset</param>
-        /// <param name="index">Index for indexed data</param>
-        /// <returns>The data at the specified location</returns>
-        public T Get<T>(MethodOffset offset, int index) where T : unmanaged
-        {
-            Register register = _registers[(int)offset];
-
-            if ((uint)index >= register.Count)
-            {
-                throw new ArgumentOutOfRangeException(nameof(index));
-            }
-
-            return Get<T>(offset + index * register.Stride);
-        }
-
-        /// <summary>
-        /// Gets data from a given register offset.
-        /// </summary>
-        /// <typeparam name="T">Type of the data</typeparam>
-        /// <param name="offset">Register offset</param>
-        /// <returns>The data at the specified location</returns>
-        public T Get<T>(MethodOffset offset) where T : unmanaged
-        {
-            return MemoryMarshal.Cast<int, T>(_memory.AsSpan().Slice((int)offset))[0];
-        }
-
-        /// <summary>
-        /// Gets a span of the data at a given register offset.
-        /// </summary>
-        /// <param name="offset">Register offset</param>
-        /// <param name="length">Length of the data in bytes</param>
-        /// <returns>The data at the specified location</returns>
-        public Span<byte> GetSpan(MethodOffset offset, int length)
-        {
-            return MemoryMarshal.Cast<int, byte>(_memory.AsSpan().Slice((int)offset)).Slice(0, length);
-        }
-
-        /// <summary>
-        /// Sets indexed data to a given register offset.
-        /// </summary>
-        /// <typeparam name="T">Type of the data</typeparam>
-        /// <param name="offset">Register offset</param>
-        /// <param name="index">Index for indexed data</param>
-        /// <param name="data">The data to set</param>
-        public void Set<T>(MethodOffset offset, int index, T data) where T : unmanaged
-        {
-            Register register = _registers[(int)offset];
-
-            if ((uint)index >= register.Count)
-            {
-                throw new ArgumentOutOfRangeException(nameof(index));
-            }
-
-            Set(offset + index * register.Stride, data);
-        }
-
-        /// <summary>
-        /// Sets data to a given register offset.
-        /// </summary>
-        /// <typeparam name="T">Type of the data</typeparam>
-        /// <param name="offset">Register offset</param>
-        /// <param name="data">The data to set</param>
-        public void Set<T>(MethodOffset offset, T data) where T : unmanaged
-        {
-            ReadOnlySpan<int> intSpan = MemoryMarshal.Cast<T, int>(MemoryMarshal.CreateReadOnlySpan(ref data, 1));
-            intSpan.CopyTo(_memory.AsSpan().Slice((int)offset, intSpan.Length));
-        }
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/GpuStateTable.cs b/Ryujinx.Graphics.Gpu/State/GpuStateTable.cs
deleted file mode 100644
index 899e0a59d1..0000000000
--- a/Ryujinx.Graphics.Gpu/State/GpuStateTable.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// GPU State item sizes table.
-    /// </summary>
-    static class GpuStateTable
-    {
-        /// <summary>
-        /// GPU state table item, with size for structures, and count for indexed state data.
-        /// </summary>
-        public struct TableItem
-        {
-            /// <summary>
-            /// Offset of the data.
-            /// </summary>
-            public MethodOffset Offset { get; }
-
-            /// <summary>
-            /// Size in words.
-            /// </summary>
-            public int Size { get; }
-
-            /// <summary>
-            /// Count for indexed data, or 1 if not indexed.
-            /// </summary>
-            public int Count { get; }
-
-            /// <summary>
-            /// Constructs the table item structure.
-            /// </summary>
-            /// <param name="offset">Data offset</param>
-            /// <param name="type">Data type</param>
-            /// <param name="count">Data count, for indexed data</param>
-            public TableItem(MethodOffset offset, Type type, int count)
-            {
-                int sizeInBytes = Marshal.SizeOf(type);
-
-                Debug.Assert((sizeInBytes & 3) == 0);
-
-                Offset = offset;
-                Size   = sizeInBytes / 4;
-                Count  = count;
-            }
-        }
-
-        /// <summary>
-        /// Table of GPU state structure sizes and counts.
-        /// </summary>
-        public static TableItem[] Table = new TableItem[]
-        {
-            new TableItem(MethodOffset.TfBufferState,          typeof(TfBufferState),          Constants.TotalTransformFeedbackBuffers),
-            new TableItem(MethodOffset.TfState,                typeof(TfState),                Constants.TotalTransformFeedbackBuffers),
-            new TableItem(MethodOffset.RtColorState,           typeof(RtColorState),           Constants.TotalRenderTargets),
-            new TableItem(MethodOffset.ViewportTransform,      typeof(ViewportTransform),      Constants.TotalViewports),
-            new TableItem(MethodOffset.ViewportExtents,        typeof(ViewportExtents),        Constants.TotalViewports),
-            new TableItem(MethodOffset.VertexBufferDrawState,  typeof(VertexBufferDrawState),  1),
-            new TableItem(MethodOffset.DepthBiasState,         typeof(DepthBiasState),         1),
-            new TableItem(MethodOffset.ScissorState,           typeof(ScissorState),           Constants.TotalViewports),
-            new TableItem(MethodOffset.StencilBackMasks,       typeof(StencilBackMasks),       1),
-            new TableItem(MethodOffset.RtDepthStencilState,    typeof(RtDepthStencilState),    1),
-            new TableItem(MethodOffset.VertexAttribState,      typeof(VertexAttribState),      Constants.TotalVertexAttribs),
-            new TableItem(MethodOffset.RtDepthStencilSize,     typeof(Size3D),                 1),
-            new TableItem(MethodOffset.BlendEnable,            typeof(Boolean32),              Constants.TotalRenderTargets),
-            new TableItem(MethodOffset.StencilTestState,       typeof(StencilTestState),       1),
-            new TableItem(MethodOffset.SamplerPoolState,       typeof(PoolState),              1),
-            new TableItem(MethodOffset.TexturePoolState,       typeof(PoolState),              1),
-            new TableItem(MethodOffset.StencilBackTestState,   typeof(StencilBackTestState),   1),
-            new TableItem(MethodOffset.ShaderBaseAddress,      typeof(GpuVa),                  1),
-            new TableItem(MethodOffset.PrimitiveRestartState,  typeof(PrimitiveRestartState),  1),
-            new TableItem(MethodOffset.IndexBufferState,       typeof(IndexBufferState),       1),
-            new TableItem(MethodOffset.VertexBufferInstanced,  typeof(Boolean32),              Constants.TotalVertexBuffers),
-            new TableItem(MethodOffset.FaceState,              typeof(FaceState),              1),
-            new TableItem(MethodOffset.RtColorMask,            typeof(RtColorMask),            Constants.TotalRenderTargets),
-            new TableItem(MethodOffset.VertexBufferState,      typeof(VertexBufferState),      Constants.TotalVertexBuffers),
-            new TableItem(MethodOffset.BlendConstant,          typeof(ColorF),                 1),
-            new TableItem(MethodOffset.BlendStateCommon,       typeof(BlendStateCommon),       1),
-            new TableItem(MethodOffset.BlendState,             typeof(BlendState),             Constants.TotalRenderTargets),
-            new TableItem(MethodOffset.VertexBufferEndAddress, typeof(GpuVa),                  Constants.TotalVertexBuffers),
-            new TableItem(MethodOffset.ShaderState,            typeof(ShaderState),            Constants.ShaderStages + 1),
-        };
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/IndexBufferState.cs b/Ryujinx.Graphics.Gpu/State/IndexBufferState.cs
deleted file mode 100644
index 8ae38bb8e8..0000000000
--- a/Ryujinx.Graphics.Gpu/State/IndexBufferState.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Ryujinx.Graphics.GAL;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// GPU index buffer state.
-    /// This is used on indexed draws.
-    /// </summary>
-    struct IndexBufferState
-    {
-#pragma warning disable CS0649
-        public GpuVa     Address;
-        public GpuVa     EndAddress;
-        public IndexType Type;
-        public int       First;
-        public int       Count;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/Inline2MemoryParams.cs b/Ryujinx.Graphics.Gpu/State/Inline2MemoryParams.cs
deleted file mode 100644
index f4009592ab..0000000000
--- a/Ryujinx.Graphics.Gpu/State/Inline2MemoryParams.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Inline-to-memory copy parameters.
-    /// </summary>
-    struct Inline2MemoryParams
-    {
-#pragma warning disable CS0649
-        public int          LineLengthIn;
-        public int          LineCount;
-        public GpuVa        DstAddress;
-        public int          DstStride;
-        public MemoryLayout DstMemoryLayout;
-        public int          DstWidth;
-        public int          DstHeight;
-        public int          DstDepth;
-        public int          DstZ;
-        public int          DstX;
-        public int          DstY;
-#pragma warning restore CS0649
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/LogicalOpState.cs b/Ryujinx.Graphics.Gpu/State/LogicalOpState.cs
deleted file mode 100644
index d052a45c60..0000000000
--- a/Ryujinx.Graphics.Gpu/State/LogicalOpState.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Ryujinx.Graphics.GAL;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    struct LogicalOpState
-    {
-#pragma warning disable CS0649
-        public Boolean32 Enable;
-        public LogicalOp LogicalOp;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs
deleted file mode 100644
index 1ddef95cb4..0000000000
--- a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// GPU method offset.
-    /// </summary>
-    /// <remarks>
-    /// This is indexed in 32 bits word.
-    /// </remarks>
-    enum MethodOffset
-    {
-        BindChannel                     = 0x0,
-        I2mParams                       = 0x60,
-        LaunchDma                       = 0x6c,
-        LoadInlineData                  = 0x6d,
-        CopyDstTexture                  = 0x80,
-        EarlyZForce                     = 0x84,
-        CopySrcTexture                  = 0x8c,
-        DispatchParamsAddress           = 0xad,
-        Dispatch                        = 0xaf,
-        SyncpointAction                 = 0xb2,
-        CopyBuffer                      = 0xc0,
-        RasterizeEnable                 = 0xdf,
-        TfBufferState                   = 0xe0,
-        CopyBufferParams                = 0x100,
-        TfState                         = 0x1c0,
-        CopyBufferConstA                = 0x1c0,
-        CopyBufferConstB                = 0x1c1,
-        CopyBufferSwizzle               = 0x1c2,
-        CopyBufferDstTexture            = 0x1c3,
-        CopyBufferSrcTexture            = 0x1ca,
-        TfEnable                        = 0x1d1,
-        RtColorState                    = 0x200,
-        CopyTextureControl              = 0x223,
-        CopyRegion                      = 0x22c,
-        CopyTexture                     = 0x237,
-        ViewportTransform               = 0x280,
-        ViewportExtents                 = 0x300,
-        VertexBufferDrawState           = 0x35d,
-        DepthMode                       = 0x35f,
-        ClearColors                     = 0x360,
-        ClearDepthValue                 = 0x364,
-        ClearStencilValue               = 0x368,
-        DepthBiasState                  = 0x370,
-        TextureBarrier                  = 0x378,
-        ScissorState                    = 0x380,
-        StencilBackMasks                = 0x3d5,
-        InvalidateTextures              = 0x3dd,
-        TextureBarrierTiled             = 0x3df,
-        RtColorMaskShared               = 0x3e4,
-        RtDepthStencilState             = 0x3f8,
-        ScreenScissorState              = 0x3fd,
-        VertexAttribState               = 0x458,
-        RtControl                       = 0x487,
-        RtDepthStencilSize              = 0x48a,
-        SamplerIndex                    = 0x48d,
-        DepthTestEnable                 = 0x4b3,
-        BlendIndependent                = 0x4b9,
-        DepthWriteEnable                = 0x4ba,
-        AlphaTestEnable                 = 0x4bb,
-        VbElementU8                     = 0x4c1,
-        DepthTestFunc                   = 0x4c3,
-        AlphaTestRef                    = 0x4c4,
-        AlphaTestFunc                   = 0x4c5,
-        BlendConstant                   = 0x4c7,
-        BlendStateCommon                = 0x4cf,
-        BlendEnableCommon               = 0x4d7,
-        BlendEnable                     = 0x4d8,
-        StencilTestState                = 0x4e0,
-        YControl                        = 0x4eb,
-        LineWidthSmooth                 = 0x4ec,
-        LineWidthAliased                = 0x4ed,
-        FirstVertex                     = 0x50d,
-        FirstInstance                   = 0x50e,
-        ClipDistanceEnable              = 0x544,
-        PointSize                       = 0x546,
-        PointSpriteEnable               = 0x548,
-        ResetCounter                    = 0x54c,
-        RtDepthStencilEnable            = 0x54e,
-        ConditionState                  = 0x554,
-        SamplerPoolState                = 0x557,
-        DepthBiasFactor                 = 0x55b,
-        LineSmoothEnable                = 0x55c,
-        TexturePoolState                = 0x55d,
-        StencilBackTestState            = 0x565,
-        DepthBiasUnits                  = 0x56f,
-        RtMsaaMode                      = 0x574,
-        VbElementU32                    = 0x57a,
-        VbElementU16                    = 0x57c,
-        PointCoordReplace               = 0x581,
-        ShaderBaseAddress               = 0x582,
-        DrawEnd                         = 0x585,
-        DrawBegin                       = 0x586,
-        PrimitiveRestartState           = 0x591,
-        IndexBufferState                = 0x5f2,
-        IndexBufferCount                = 0x5f8,
-        DrawIndexedSmall                = 0x5f9,
-        DrawIndexedSmall2               = 0x5fa,
-        DrawIndexedSmallIncInstance     = 0x5fc,
-        DrawIndexedSmallIncInstance2    = 0x5fd,
-        DepthBiasClamp                  = 0x61f,
-        VertexBufferInstanced           = 0x620,
-        VertexProgramPointSize          = 0x644,
-        FaceState                       = 0x646,
-        ViewportTransformEnable         = 0x64b,
-        ViewVolumeClipControl           = 0x64f,
-        PrimitiveTypeOverride           = 0x65c,
-        LogicOpState                    = 0x671,
-        Clear                           = 0x674,
-        RtColorMask                     = 0x680,
-        ReportState                     = 0x6c0,
-        Report                          = 0x6c3,
-        VertexBufferState               = 0x700,
-        BlendState                      = 0x780,
-        VertexBufferEndAddress          = 0x7c0,
-        ShaderState                     = 0x800,
-        FirmwareCall0                   = 0x8c0,
-        FirmwareCall1                   = 0x8c1,
-        FirmwareCall2                   = 0x8c2,
-        FirmwareCall3                   = 0x8c3,
-        FirmwareCall4                   = 0x8c4,
-        FirmwareCall5                   = 0x8c5,
-        FirmwareCall6                   = 0x8c6,
-        FirmwareCall7                   = 0x8c7,
-        UniformBufferState              = 0x8e0,
-        UniformBufferUpdateData         = 0x8e4,
-        UniformBufferBindVertex         = 0x904,
-        UniformBufferBindTessControl    = 0x90c,
-        UniformBufferBindTessEvaluation = 0x914,
-        UniformBufferBindGeometry       = 0x91c,
-        UniformBufferBindFragment       = 0x924,
-        TextureBufferIndex              = 0x982,
-        TfVaryingLocations              = 0xa00
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/PoolState.cs b/Ryujinx.Graphics.Gpu/State/PoolState.cs
deleted file mode 100644
index ba4dfb78bd..0000000000
--- a/Ryujinx.Graphics.Gpu/State/PoolState.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Texture or sampler pool state.
-    /// </summary>
-    struct PoolState
-    {
-#pragma warning disable CS0649
-        public GpuVa Address;
-        public int   MaximumId;
-#pragma warning restore CS0649
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/PrimitiveRestartState.cs b/Ryujinx.Graphics.Gpu/State/PrimitiveRestartState.cs
deleted file mode 100644
index d046b5222a..0000000000
--- a/Ryujinx.Graphics.Gpu/State/PrimitiveRestartState.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Primitive restart state.
-    /// </summary>
-    struct PrimitiveRestartState
-    {
-#pragma warning disable CS0649
-        public Boolean32 Enable;
-        public int       Index;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/ReportCounterType.cs b/Ryujinx.Graphics.Gpu/State/ReportCounterType.cs
deleted file mode 100644
index 6bde284415..0000000000
--- a/Ryujinx.Graphics.Gpu/State/ReportCounterType.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Counter type for GPU counter reporting.
-    /// </summary>
-    enum ReportCounterType
-    {
-        Zero                               = 0,
-        InputVertices                      = 1,
-        InputPrimitives                    = 3,
-        VertexShaderInvocations            = 5,
-        GeometryShaderInvocations          = 7,
-        GeometryShaderPrimitives           = 9,
-        ZcullStats0                        = 0xa,
-        TransformFeedbackPrimitivesWritten = 0xb,
-        ZcullStats1                        = 0xc,
-        ZcullStats2                        = 0xe,
-        ClipperInputPrimitives             = 0xf,
-        ZcullStats3                        = 0x10,
-        ClipperOutputPrimitives            = 0x11,
-        PrimitivesGenerated                = 0x12,
-        FragmentShaderInvocations          = 0x13,
-        SamplesPassed                      = 0x15,
-        TransformFeedbackOffset            = 0x1a,
-        TessControlShaderInvocations       = 0x1b,
-        TessEvaluationShaderInvocations    = 0x1d,
-        TessEvaluationShaderPrimitives     = 0x1f
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/ResetCounterType.cs b/Ryujinx.Graphics.Gpu/State/ResetCounterType.cs
deleted file mode 100644
index aaf575e1b4..0000000000
--- a/Ryujinx.Graphics.Gpu/State/ResetCounterType.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Counter type for GPU counter reset.
-    /// </summary>
-    enum ResetCounterType
-    {
-        SamplesPassed                      = 1,
-        ZcullStats                         = 2,
-        TransformFeedbackPrimitivesWritten = 0x10,
-        InputVertices                      = 0x12,
-        InputPrimitives                    = 0x13,
-        VertexShaderInvocations            = 0x15,
-        TessControlShaderInvocations       = 0x16,
-        TessEvaluationShaderInvocations    = 0x17,
-        TessEvaluationShaderPrimitives     = 0x18,
-        GeometryShaderInvocations          = 0x1a,
-        GeometryShaderPrimitives           = 0x1b,
-        ClipperInputPrimitives             = 0x1c,
-        ClipperOutputPrimitives            = 0x1d,
-        FragmentShaderInvocations          = 0x1e,
-        PrimitivesGenerated                = 0x1f
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/RtColorMask.cs b/Ryujinx.Graphics.Gpu/State/RtColorMask.cs
deleted file mode 100644
index 3567bf3772..0000000000
--- a/Ryujinx.Graphics.Gpu/State/RtColorMask.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Render target color buffer mask.
-    /// This defines which color channels are written to the color buffer.
-    /// </summary>
-    struct RtColorMask
-    {
-#pragma warning disable CS0649
-        public uint Packed;
-#pragma warning restore CS0649
-
-        /// <summary>
-        /// Unpacks red channel enable.
-        /// </summary>
-        /// <returns>True to write the new red channel color, false to keep the old value</returns>
-        public bool UnpackRed()
-        {
-            return (Packed & 0x1) != 0;
-        }
-
-        /// <summary>
-        /// Unpacks green channel enable.
-        /// </summary>
-        /// <returns>True to write the new green channel color, false to keep the old value</returns>
-        public bool UnpackGreen()
-        {
-            return (Packed & 0x10) != 0;
-        }
-
-        /// <summary>
-        /// Unpacks blue channel enable.
-        /// </summary>
-        /// <returns>True to write the new blue channel color, false to keep the old value</returns>
-        public bool UnpackBlue()
-        {
-            return (Packed & 0x100) != 0;
-        }
-
-        /// <summary>
-        /// Unpacks alpha channel enable.
-        /// </summary>
-        /// <returns>True to write the new alpha channel color, false to keep the old value</returns>
-        public bool UnpackAlpha()
-        {
-            return (Packed & 0x1000) != 0;
-        }
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/RtColorState.cs b/Ryujinx.Graphics.Gpu/State/RtColorState.cs
deleted file mode 100644
index 457725ff34..0000000000
--- a/Ryujinx.Graphics.Gpu/State/RtColorState.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Render target color buffer state.
-    /// </summary>
-    struct RtColorState
-    {
-#pragma warning disable CS0649
-        public GpuVa        Address;
-        public int          WidthOrStride;
-        public int          Height;
-        public ColorFormat  Format;
-        public MemoryLayout MemoryLayout;
-        public int          Depth;
-        public int          LayerSize;
-        public int          BaseLayer;
-        public int          Unknown0x24;
-        public int          Padding0;
-        public int          Padding1;
-        public int          Padding2;
-        public int          Padding3;
-        public int          Padding4;
-        public int          Padding5;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/RtControl.cs b/Ryujinx.Graphics.Gpu/State/RtControl.cs
deleted file mode 100644
index 8b6b186706..0000000000
--- a/Ryujinx.Graphics.Gpu/State/RtControl.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Render target draw buffers control.
-    /// </summary>
-    struct RtControl
-    {
-#pragma warning disable CS0649
-        public uint Packed;
-#pragma warning restore CS0649
-
-        /// <summary>
-        /// Unpacks the number of active draw buffers.
-        /// </summary>
-        /// <returns>Number of active draw buffers</returns>
-        public int UnpackCount()
-        {
-            return (int)(Packed & 0xf);
-        }
-
-        /// <summary>
-        /// Unpacks the color attachment index for a given draw buffer.
-        /// </summary>
-        /// <param name="index">Index of the draw buffer</param>
-        /// <returns>Attachment index</returns>
-        public int UnpackPermutationIndex(int index)
-        {
-            return (int)((Packed >> (4 + index * 3)) & 7);
-        }
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/RtDepthStencilState.cs b/Ryujinx.Graphics.Gpu/State/RtDepthStencilState.cs
deleted file mode 100644
index 3886f58cd0..0000000000
--- a/Ryujinx.Graphics.Gpu/State/RtDepthStencilState.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Render target depth-stencil buffer state.
-    /// </summary>
-    struct RtDepthStencilState
-    {
-#pragma warning disable CS0649
-        public GpuVa        Address;
-        public ZetaFormat   Format;
-        public MemoryLayout MemoryLayout;
-        public int          LayerSize;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/ScissorState.cs b/Ryujinx.Graphics.Gpu/State/ScissorState.cs
deleted file mode 100644
index 4f4b02dcb5..0000000000
--- a/Ryujinx.Graphics.Gpu/State/ScissorState.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    struct ScissorState
-    {
-#pragma warning disable CS0649
-        public Boolean32 Enable;
-        public ushort X1;
-        public ushort X2;
-        public ushort Y1;
-        public ushort Y2;
-        public uint Padding;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/ScreenScissorState.cs b/Ryujinx.Graphics.Gpu/State/ScreenScissorState.cs
deleted file mode 100644
index 2fbf99344d..0000000000
--- a/Ryujinx.Graphics.Gpu/State/ScreenScissorState.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    struct ScreenScissorState
-    {
-#pragma warning disable CS0649
-        public ushort X;
-        public ushort Width;
-        public ushort Y;
-        public ushort Height;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/SemaphoreOperation.cs b/Ryujinx.Graphics.Gpu/State/SemaphoreOperation.cs
deleted file mode 100644
index 67f3c127f6..0000000000
--- a/Ryujinx.Graphics.Gpu/State/SemaphoreOperation.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// GPU semaphore operation.
-    /// </summary>
-    enum SemaphoreOperation
-    {
-        Release = 0,
-        Acquire = 1,
-        Counter = 2
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/SemaphoreState.cs b/Ryujinx.Graphics.Gpu/State/SemaphoreState.cs
deleted file mode 100644
index bfc5720a40..0000000000
--- a/Ryujinx.Graphics.Gpu/State/SemaphoreState.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// GPU semaphore state.
-    /// </summary>
-    struct SemaphoreState
-    {
-#pragma warning disable CS0649
-        public GpuVa Address;
-        public int   Payload;
-        public uint  Control;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/ShaderState.cs b/Ryujinx.Graphics.Gpu/State/ShaderState.cs
deleted file mode 100644
index 4cf67c7469..0000000000
--- a/Ryujinx.Graphics.Gpu/State/ShaderState.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Graphics shader stage state.
-    /// </summary>
-    struct ShaderState
-    {
-#pragma warning disable CS0649
-        public uint       Control;
-        public uint       Offset;
-        public uint       Unknown0x8;
-        public int        MaxRegisters;
-        public ShaderType Type;
-        public uint       Unknown0x14;
-        public uint       Unknown0x18;
-        public uint       Unknown0x1c;
-        public uint       Unknown0x20;
-        public uint       Unknown0x24;
-        public uint       Unknown0x28;
-        public uint       Unknown0x2c;
-        public uint       Unknown0x30;
-        public uint       Unknown0x34;
-        public uint       Unknown0x38;
-        public uint       Unknown0x3c;
-#pragma warning restore CS0649
-
-        /// <summary>
-        /// Unpacks shader enable information.
-        /// Must be ignored for vertex shaders, those are always enabled.
-        /// </summary>
-        /// <returns>True if the stage is enabled, false otherwise</returns>
-        public bool UnpackEnable()
-        {
-            return (Control & 1) != 0;
-        }
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/ShaderType.cs b/Ryujinx.Graphics.Gpu/State/ShaderType.cs
deleted file mode 100644
index 58506821de..0000000000
--- a/Ryujinx.Graphics.Gpu/State/ShaderType.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Shader stage name.
-    /// </summary>
-    enum ShaderType
-    {
-        Vertex,
-        TessellationControl,
-        TessellationEvaluation,
-        Geometry,
-        Fragment
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/Size3D.cs b/Ryujinx.Graphics.Gpu/State/Size3D.cs
deleted file mode 100644
index 1c127da6ac..0000000000
--- a/Ryujinx.Graphics.Gpu/State/Size3D.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// 3D, 2D or 1D texture size.
-    /// </summary>
-    struct Size3D
-    {
-#pragma warning disable CS0649
-        public int Width;
-        public int Height;
-        public int Depth;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/StencilBackMasks.cs b/Ryujinx.Graphics.Gpu/State/StencilBackMasks.cs
deleted file mode 100644
index 49061cc5e9..0000000000
--- a/Ryujinx.Graphics.Gpu/State/StencilBackMasks.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Stencil test masks for back tests.
-    /// </summary>
-    struct StencilBackMasks
-    {
-#pragma warning disable CS0649
-        public int FuncRef;
-        public int Mask;
-        public int FuncMask;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/StencilBackTestState.cs b/Ryujinx.Graphics.Gpu/State/StencilBackTestState.cs
deleted file mode 100644
index 1665532262..0000000000
--- a/Ryujinx.Graphics.Gpu/State/StencilBackTestState.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Ryujinx.Graphics.GAL;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Stencil back test state.
-    /// </summary>
-    struct StencilBackTestState
-    {
-#pragma warning disable CS0649
-        public Boolean32 TwoSided;
-        public StencilOp BackSFail;
-        public StencilOp BackDpFail;
-        public StencilOp BackDpPass;
-        public CompareOp BackFunc;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/StencilTestState.cs b/Ryujinx.Graphics.Gpu/State/StencilTestState.cs
deleted file mode 100644
index 72f85f2bb3..0000000000
--- a/Ryujinx.Graphics.Gpu/State/StencilTestState.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Ryujinx.Graphics.GAL;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Stencil front test state and masks.
-    /// </summary>
-    struct StencilTestState
-    {
-#pragma warning disable CS0649
-        public Boolean32 Enable;
-        public StencilOp FrontSFail;
-        public StencilOp FrontDpFail;
-        public StencilOp FrontDpPass;
-        public CompareOp FrontFunc;
-        public int       FrontFuncRef;
-        public int       FrontFuncMask;
-        public int       FrontMask;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/TfBufferState.cs b/Ryujinx.Graphics.Gpu/State/TfBufferState.cs
deleted file mode 100644
index 24dc0952fe..0000000000
--- a/Ryujinx.Graphics.Gpu/State/TfBufferState.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Transform feedback buffer state.
-    /// </summary>
-    struct TfBufferState
-    {
-#pragma warning disable CS0649
-        public Boolean32 Enable;
-        public GpuVa     Address;
-        public int       Size;
-        public int       Offset;
-        public uint      Padding0;
-        public uint      Padding1;
-        public uint      Padding2;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/TfState.cs b/Ryujinx.Graphics.Gpu/State/TfState.cs
deleted file mode 100644
index fb8b950bcf..0000000000
--- a/Ryujinx.Graphics.Gpu/State/TfState.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Transform feedback state.
-    /// </summary>
-    struct TfState
-    {
-#pragma warning disable CS0649
-        public int BufferIndex;
-        public int VaryingsCount;
-        public int Stride;
-        public uint Padding;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/UniformBufferState.cs b/Ryujinx.Graphics.Gpu/State/UniformBufferState.cs
deleted file mode 100644
index d547ea82c2..0000000000
--- a/Ryujinx.Graphics.Gpu/State/UniformBufferState.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Uniform buffer state for the uniform buffer currently being modified.
-    /// </summary>
-    struct UniformBufferState
-    {
-#pragma warning disable CS0649
-        public int   Size;
-        public GpuVa Address;
-        public int   Offset;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/VertexAttribState.cs b/Ryujinx.Graphics.Gpu/State/VertexAttribState.cs
deleted file mode 100644
index dbb75a5b1e..0000000000
--- a/Ryujinx.Graphics.Gpu/State/VertexAttribState.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Vertex buffer attribute state.
-    /// </summary>
-    struct VertexAttribState
-    {
-#pragma warning disable CS0649
-        public uint Attribute;
-#pragma warning restore CS0649
-
-        /// <summary>
-        /// Unpacks the index of the vertex buffer this attribute belongs to.
-        /// </summary>
-        /// <returns>Vertex buffer index</returns>
-        public int UnpackBufferIndex()
-        {
-            return (int)(Attribute & 0x1f);
-        }
-
-        /// <summary>
-        /// Unpacks the attribute constant flag.
-        /// </summary>
-        /// <returns>True if the attribute is constant, false otherwise</returns>
-        public bool UnpackIsConstant()
-        {
-            return (Attribute & 0x40) != 0;
-        }
-
-        /// <summary>
-        /// Unpacks the offset, in bytes, of the attribute on the vertex buffer.
-        /// </summary>
-        /// <returns>Attribute offset in bytes</returns>
-        public int UnpackOffset()
-        {
-            return (int)((Attribute >> 7) & 0x3fff);
-        }
-
-        /// <summary>
-        /// Unpacks the Maxwell attribute format integer.
-        /// </summary>
-        /// <returns>Attribute format integer</returns>
-        public uint UnpackFormat()
-        {
-            return Attribute & 0x3fe00000;
-        }
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/VertexBufferDrawState.cs b/Ryujinx.Graphics.Gpu/State/VertexBufferDrawState.cs
deleted file mode 100644
index 3e11838a67..0000000000
--- a/Ryujinx.Graphics.Gpu/State/VertexBufferDrawState.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Draw state for non-indexed draws.
-    /// </summary>
-    struct VertexBufferDrawState
-    {
-#pragma warning disable CS0649
-        public int First;
-        public int Count;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/VertexBufferState.cs b/Ryujinx.Graphics.Gpu/State/VertexBufferState.cs
deleted file mode 100644
index 85176d8219..0000000000
--- a/Ryujinx.Graphics.Gpu/State/VertexBufferState.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Vertex buffer state.
-    /// </summary>
-    struct VertexBufferState
-    {
-#pragma warning disable CS0649
-        public uint  Control;
-        public GpuVa Address;
-        public int   Divisor;
-#pragma warning restore CS0649
-
-        /// <summary>
-        /// Vertex buffer stride, defined as the number of bytes occupied by each vertex in memory.
-        /// </summary>
-        /// <returns>Vertex buffer stride</returns>
-        public int UnpackStride()
-        {
-            return (int)(Control & 0xfff);
-        }
-
-        /// <summary>
-        /// Vertex buffer enable.
-        /// </summary>
-        /// <returns>True if the vertex buffer is enabled, false otherwise</returns>
-        public bool UnpackEnable()
-        {
-            return (Control & (1 << 12)) != 0;
-        }
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/ViewVolumeClipControl.cs b/Ryujinx.Graphics.Gpu/State/ViewVolumeClipControl.cs
deleted file mode 100644
index ace8342ce1..0000000000
--- a/Ryujinx.Graphics.Gpu/State/ViewVolumeClipControl.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    [Flags]
-    enum ViewVolumeClipControl
-    {
-        ForceDepthRangeZeroToOne = 1 << 0,
-        DepthClampDisabled       = 1 << 11,
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/ViewportExtents.cs b/Ryujinx.Graphics.Gpu/State/ViewportExtents.cs
deleted file mode 100644
index d7728f41b6..0000000000
--- a/Ryujinx.Graphics.Gpu/State/ViewportExtents.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Viewport extents for viewport clipping, also includes depth range.
-    /// </summary>
-    struct ViewportExtents
-    {
-#pragma warning disable CS0649
-        public ushort X;
-        public ushort Width;
-        public ushort Y;
-        public ushort Height;
-        public float  DepthNear;
-        public float  DepthFar;
-#pragma warning restore CS0649
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/ViewportTransform.cs b/Ryujinx.Graphics.Gpu/State/ViewportTransform.cs
deleted file mode 100644
index b795ea1587..0000000000
--- a/Ryujinx.Graphics.Gpu/State/ViewportTransform.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using Ryujinx.Graphics.GAL;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    /// <summary>
-    /// Viewport transform parameters, for viewport transformation.
-    /// </summary>
-    struct ViewportTransform
-    {
-#pragma warning disable CS0649
-        public float ScaleX;
-        public float ScaleY;
-        public float ScaleZ;
-        public float TranslateX;
-        public float TranslateY;
-        public float TranslateZ;
-        public uint  Swizzle;
-        public uint  SubpixelPrecisionBias;
-#pragma warning restore CS0649
-
-        /// <summary>
-        /// Unpacks viewport swizzle of the position X component.
-        /// </summary>
-        /// <returns>Swizzle enum value</returns>
-        public ViewportSwizzle UnpackSwizzleX()
-        {
-            return (ViewportSwizzle)(Swizzle & 7);
-        }
-
-        /// <summary>
-        /// Unpacks viewport swizzle of the position Y component.
-        /// </summary>
-        /// <returns>Swizzle enum value</returns>
-        public ViewportSwizzle UnpackSwizzleY()
-        {
-            return (ViewportSwizzle)((Swizzle >> 4) & 7);
-        }
-
-        /// <summary>
-        /// Unpacks viewport swizzle of the position Z component.
-        /// </summary>
-        /// <returns>Swizzle enum value</returns>
-        public ViewportSwizzle UnpackSwizzleZ()
-        {
-            return (ViewportSwizzle)((Swizzle >> 8) & 7);
-        }
-
-        /// <summary>
-        /// Unpacks viewport swizzle of the position W component.
-        /// </summary>
-        /// <returns>Swizzle enum value</returns>
-        public ViewportSwizzle UnpackSwizzleW()
-        {
-            return (ViewportSwizzle)((Swizzle >> 12) & 7);
-        }
-    }
-}
diff --git a/Ryujinx.Graphics.Gpu/State/YControl.cs b/Ryujinx.Graphics.Gpu/State/YControl.cs
deleted file mode 100644
index 58e000d93e..0000000000
--- a/Ryujinx.Graphics.Gpu/State/YControl.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-
-namespace Ryujinx.Graphics.Gpu.State
-{
-    [Flags]
-    enum YControl
-    {
-        NegateY          = 1 << 0,
-        TriangleRastFlip = 1 << 4
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs b/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs
index 1fcc12b50d..024b0bb4ac 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs
@@ -1,9 +1,9 @@
-using Ryujinx.Graphics.Gpu;
 using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.Graphics.Host1x;
 using Ryujinx.Graphics.Nvdec;
 using Ryujinx.Graphics.Vic;
 using System;
+using GpuContext = Ryujinx.Graphics.Gpu.GpuContext;
 
 namespace Ryujinx.HLE.HOS.Services.Nv
 {
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs
new file mode 100644
index 0000000000..87a06bd399
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/ChannelInitialization.cs
@@ -0,0 +1,1361 @@
+using Ryujinx.Graphics.Gpu;
+
+namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
+{
+    static class ChannelInitialization
+    {
+        public static void InitializeState(GpuChannel channel)
+        {
+            channel.Write(ClassId.Threed, 0x800, 0x0);
+            channel.Write(ClassId.Threed, 0x840, 0x0);
+            channel.Write(ClassId.Threed, 0x880, 0x0);
+            channel.Write(ClassId.Threed, 0x8C0, 0x0);
+            channel.Write(ClassId.Threed, 0x900, 0x0);
+            channel.Write(ClassId.Threed, 0x940, 0x0);
+            channel.Write(ClassId.Threed, 0x980, 0x0);
+            channel.Write(ClassId.Threed, 0x9C0, 0x0);
+            channel.Write(ClassId.Threed, 0x804, 0x0);
+            channel.Write(ClassId.Threed, 0x844, 0x0);
+            channel.Write(ClassId.Threed, 0x884, 0x0);
+            channel.Write(ClassId.Threed, 0x8C4, 0x0);
+            channel.Write(ClassId.Threed, 0x904, 0x0);
+            channel.Write(ClassId.Threed, 0x944, 0x0);
+            channel.Write(ClassId.Threed, 0x984, 0x0);
+            channel.Write(ClassId.Threed, 0x9C4, 0x0);
+            channel.Write(ClassId.Threed, 0x808, 0x400);
+            channel.Write(ClassId.Threed, 0x848, 0x400);
+            channel.Write(ClassId.Threed, 0x888, 0x400);
+            channel.Write(ClassId.Threed, 0x8C8, 0x400);
+            channel.Write(ClassId.Threed, 0x908, 0x400);
+            channel.Write(ClassId.Threed, 0x948, 0x400);
+            channel.Write(ClassId.Threed, 0x988, 0x400);
+            channel.Write(ClassId.Threed, 0x9C8, 0x400);
+            channel.Write(ClassId.Threed, 0x80C, 0x300);
+            channel.Write(ClassId.Threed, 0x84C, 0x300);
+            channel.Write(ClassId.Threed, 0x88C, 0x300);
+            channel.Write(ClassId.Threed, 0x8CC, 0x300);
+            channel.Write(ClassId.Threed, 0x90C, 0x300);
+            channel.Write(ClassId.Threed, 0x94C, 0x300);
+            channel.Write(ClassId.Threed, 0x98C, 0x300);
+            channel.Write(ClassId.Threed, 0x9CC, 0x300);
+            channel.Write(ClassId.Threed, 0x810, 0xCF);
+            channel.Write(ClassId.Threed, 0x850, 0x0);
+            channel.Write(ClassId.Threed, 0x890, 0x0);
+            channel.Write(ClassId.Threed, 0x8D0, 0x0);
+            channel.Write(ClassId.Threed, 0x910, 0x0);
+            channel.Write(ClassId.Threed, 0x950, 0x0);
+            channel.Write(ClassId.Threed, 0x990, 0x0);
+            channel.Write(ClassId.Threed, 0x9D0, 0x0);
+            channel.Write(ClassId.Threed, 0x814, 0x40);
+            channel.Write(ClassId.Threed, 0x854, 0x40);
+            channel.Write(ClassId.Threed, 0x894, 0x40);
+            channel.Write(ClassId.Threed, 0x8D4, 0x40);
+            channel.Write(ClassId.Threed, 0x914, 0x40);
+            channel.Write(ClassId.Threed, 0x954, 0x40);
+            channel.Write(ClassId.Threed, 0x994, 0x40);
+            channel.Write(ClassId.Threed, 0x9D4, 0x40);
+            channel.Write(ClassId.Threed, 0x818, 0x1);
+            channel.Write(ClassId.Threed, 0x858, 0x1);
+            channel.Write(ClassId.Threed, 0x898, 0x1);
+            channel.Write(ClassId.Threed, 0x8D8, 0x1);
+            channel.Write(ClassId.Threed, 0x918, 0x1);
+            channel.Write(ClassId.Threed, 0x958, 0x1);
+            channel.Write(ClassId.Threed, 0x998, 0x1);
+            channel.Write(ClassId.Threed, 0x9D8, 0x1);
+            channel.Write(ClassId.Threed, 0x81C, 0x0);
+            channel.Write(ClassId.Threed, 0x85C, 0x0);
+            channel.Write(ClassId.Threed, 0x89C, 0x0);
+            channel.Write(ClassId.Threed, 0x8DC, 0x0);
+            channel.Write(ClassId.Threed, 0x91C, 0x0);
+            channel.Write(ClassId.Threed, 0x95C, 0x0);
+            channel.Write(ClassId.Threed, 0x99C, 0x0);
+            channel.Write(ClassId.Threed, 0x9DC, 0x0);
+            channel.Write(ClassId.Threed, 0x820, 0x0);
+            channel.Write(ClassId.Threed, 0x860, 0x0);
+            channel.Write(ClassId.Threed, 0x8A0, 0x0);
+            channel.Write(ClassId.Threed, 0x8E0, 0x0);
+            channel.Write(ClassId.Threed, 0x920, 0x0);
+            channel.Write(ClassId.Threed, 0x960, 0x0);
+            channel.Write(ClassId.Threed, 0x9A0, 0x0);
+            channel.Write(ClassId.Threed, 0x9E0, 0x0);
+            channel.Write(ClassId.Threed, 0x1C00, 0x0);
+            channel.Write(ClassId.Threed, 0x1C10, 0x0);
+            channel.Write(ClassId.Threed, 0x1C20, 0x0);
+            channel.Write(ClassId.Threed, 0x1C30, 0x0);
+            channel.Write(ClassId.Threed, 0x1C40, 0x0);
+            channel.Write(ClassId.Threed, 0x1C50, 0x0);
+            channel.Write(ClassId.Threed, 0x1C60, 0x0);
+            channel.Write(ClassId.Threed, 0x1C70, 0x0);
+            channel.Write(ClassId.Threed, 0x1C80, 0x0);
+            channel.Write(ClassId.Threed, 0x1C90, 0x0);
+            channel.Write(ClassId.Threed, 0x1CA0, 0x0);
+            channel.Write(ClassId.Threed, 0x1CB0, 0x0);
+            channel.Write(ClassId.Threed, 0x1CC0, 0x0);
+            channel.Write(ClassId.Threed, 0x1CD0, 0x0);
+            channel.Write(ClassId.Threed, 0x1CE0, 0x0);
+            channel.Write(ClassId.Threed, 0x1CF0, 0x0);
+            channel.Write(ClassId.Threed, 0x1C04, 0x0);
+            channel.Write(ClassId.Threed, 0x1C14, 0x0);
+            channel.Write(ClassId.Threed, 0x1C24, 0x0);
+            channel.Write(ClassId.Threed, 0x1C34, 0x0);
+            channel.Write(ClassId.Threed, 0x1C44, 0x0);
+            channel.Write(ClassId.Threed, 0x1C54, 0x0);
+            channel.Write(ClassId.Threed, 0x1C64, 0x0);
+            channel.Write(ClassId.Threed, 0x1C74, 0x0);
+            channel.Write(ClassId.Threed, 0x1C84, 0x0);
+            channel.Write(ClassId.Threed, 0x1C94, 0x0);
+            channel.Write(ClassId.Threed, 0x1CA4, 0x0);
+            channel.Write(ClassId.Threed, 0x1CB4, 0x0);
+            channel.Write(ClassId.Threed, 0x1CC4, 0x0);
+            channel.Write(ClassId.Threed, 0x1CD4, 0x0);
+            channel.Write(ClassId.Threed, 0x1CE4, 0x0);
+            channel.Write(ClassId.Threed, 0x1CF4, 0x0);
+            channel.Write(ClassId.Threed, 0x1C08, 0x0);
+            channel.Write(ClassId.Threed, 0x1C18, 0x0);
+            channel.Write(ClassId.Threed, 0x1C28, 0x0);
+            channel.Write(ClassId.Threed, 0x1C38, 0x0);
+            channel.Write(ClassId.Threed, 0x1C48, 0x0);
+            channel.Write(ClassId.Threed, 0x1C58, 0x0);
+            channel.Write(ClassId.Threed, 0x1C68, 0x0);
+            channel.Write(ClassId.Threed, 0x1C78, 0x0);
+            channel.Write(ClassId.Threed, 0x1C88, 0x0);
+            channel.Write(ClassId.Threed, 0x1C98, 0x0);
+            channel.Write(ClassId.Threed, 0x1CA8, 0x0);
+            channel.Write(ClassId.Threed, 0x1CB8, 0x0);
+            channel.Write(ClassId.Threed, 0x1CC8, 0x0);
+            channel.Write(ClassId.Threed, 0x1CD8, 0x0);
+            channel.Write(ClassId.Threed, 0x1CE8, 0x0);
+            channel.Write(ClassId.Threed, 0x1CF8, 0x0);
+            channel.Write(ClassId.Threed, 0x1C0C, 0x0);
+            channel.Write(ClassId.Threed, 0x1C1C, 0x0);
+            channel.Write(ClassId.Threed, 0x1C2C, 0x0);
+            channel.Write(ClassId.Threed, 0x1C3C, 0x0);
+            channel.Write(ClassId.Threed, 0x1C4C, 0x0);
+            channel.Write(ClassId.Threed, 0x1C5C, 0x0);
+            channel.Write(ClassId.Threed, 0x1C6C, 0x0);
+            channel.Write(ClassId.Threed, 0x1C7C, 0x0);
+            channel.Write(ClassId.Threed, 0x1C8C, 0x0);
+            channel.Write(ClassId.Threed, 0x1C9C, 0x0);
+            channel.Write(ClassId.Threed, 0x1CAC, 0x0);
+            channel.Write(ClassId.Threed, 0x1CBC, 0x0);
+            channel.Write(ClassId.Threed, 0x1CCC, 0x0);
+            channel.Write(ClassId.Threed, 0x1CDC, 0x0);
+            channel.Write(ClassId.Threed, 0x1CEC, 0x0);
+            channel.Write(ClassId.Threed, 0x1CFC, 0x0);
+            channel.Write(ClassId.Threed, 0x1D00, 0x0);
+            channel.Write(ClassId.Threed, 0x1D10, 0x0);
+            channel.Write(ClassId.Threed, 0x1D20, 0x0);
+            channel.Write(ClassId.Threed, 0x1D30, 0x0);
+            channel.Write(ClassId.Threed, 0x1D40, 0x0);
+            channel.Write(ClassId.Threed, 0x1D50, 0x0);
+            channel.Write(ClassId.Threed, 0x1D60, 0x0);
+            channel.Write(ClassId.Threed, 0x1D70, 0x0);
+            channel.Write(ClassId.Threed, 0x1D80, 0x0);
+            channel.Write(ClassId.Threed, 0x1D90, 0x0);
+            channel.Write(ClassId.Threed, 0x1DA0, 0x0);
+            channel.Write(ClassId.Threed, 0x1DB0, 0x0);
+            channel.Write(ClassId.Threed, 0x1DC0, 0x0);
+            channel.Write(ClassId.Threed, 0x1DD0, 0x0);
+            channel.Write(ClassId.Threed, 0x1DE0, 0x0);
+            channel.Write(ClassId.Threed, 0x1DF0, 0x0);
+            channel.Write(ClassId.Threed, 0x1D04, 0x0);
+            channel.Write(ClassId.Threed, 0x1D14, 0x0);
+            channel.Write(ClassId.Threed, 0x1D24, 0x0);
+            channel.Write(ClassId.Threed, 0x1D34, 0x0);
+            channel.Write(ClassId.Threed, 0x1D44, 0x0);
+            channel.Write(ClassId.Threed, 0x1D54, 0x0);
+            channel.Write(ClassId.Threed, 0x1D64, 0x0);
+            channel.Write(ClassId.Threed, 0x1D74, 0x0);
+            channel.Write(ClassId.Threed, 0x1D84, 0x0);
+            channel.Write(ClassId.Threed, 0x1D94, 0x0);
+            channel.Write(ClassId.Threed, 0x1DA4, 0x0);
+            channel.Write(ClassId.Threed, 0x1DB4, 0x0);
+            channel.Write(ClassId.Threed, 0x1DC4, 0x0);
+            channel.Write(ClassId.Threed, 0x1DD4, 0x0);
+            channel.Write(ClassId.Threed, 0x1DE4, 0x0);
+            channel.Write(ClassId.Threed, 0x1DF4, 0x0);
+            channel.Write(ClassId.Threed, 0x1D08, 0x0);
+            channel.Write(ClassId.Threed, 0x1D18, 0x0);
+            channel.Write(ClassId.Threed, 0x1D28, 0x0);
+            channel.Write(ClassId.Threed, 0x1D38, 0x0);
+            channel.Write(ClassId.Threed, 0x1D48, 0x0);
+            channel.Write(ClassId.Threed, 0x1D58, 0x0);
+            channel.Write(ClassId.Threed, 0x1D68, 0x0);
+            channel.Write(ClassId.Threed, 0x1D78, 0x0);
+            channel.Write(ClassId.Threed, 0x1D88, 0x0);
+            channel.Write(ClassId.Threed, 0x1D98, 0x0);
+            channel.Write(ClassId.Threed, 0x1DA8, 0x0);
+            channel.Write(ClassId.Threed, 0x1DB8, 0x0);
+            channel.Write(ClassId.Threed, 0x1DC8, 0x0);
+            channel.Write(ClassId.Threed, 0x1DD8, 0x0);
+            channel.Write(ClassId.Threed, 0x1DE8, 0x0);
+            channel.Write(ClassId.Threed, 0x1DF8, 0x0);
+            channel.Write(ClassId.Threed, 0x1D0C, 0x0);
+            channel.Write(ClassId.Threed, 0x1D1C, 0x0);
+            channel.Write(ClassId.Threed, 0x1D2C, 0x0);
+            channel.Write(ClassId.Threed, 0x1D3C, 0x0);
+            channel.Write(ClassId.Threed, 0x1D4C, 0x0);
+            channel.Write(ClassId.Threed, 0x1D5C, 0x0);
+            channel.Write(ClassId.Threed, 0x1D6C, 0x0);
+            channel.Write(ClassId.Threed, 0x1D7C, 0x0);
+            channel.Write(ClassId.Threed, 0x1D8C, 0x0);
+            channel.Write(ClassId.Threed, 0x1D9C, 0x0);
+            channel.Write(ClassId.Threed, 0x1DAC, 0x0);
+            channel.Write(ClassId.Threed, 0x1DBC, 0x0);
+            channel.Write(ClassId.Threed, 0x1DCC, 0x0);
+            channel.Write(ClassId.Threed, 0x1DDC, 0x0);
+            channel.Write(ClassId.Threed, 0x1DEC, 0x0);
+            channel.Write(ClassId.Threed, 0x1DFC, 0x0);
+            channel.Write(ClassId.Threed, 0x1F00, 0x0);
+            channel.Write(ClassId.Threed, 0x1F08, 0x0);
+            channel.Write(ClassId.Threed, 0x1F10, 0x0);
+            channel.Write(ClassId.Threed, 0x1F18, 0x0);
+            channel.Write(ClassId.Threed, 0x1F20, 0x0);
+            channel.Write(ClassId.Threed, 0x1F28, 0x0);
+            channel.Write(ClassId.Threed, 0x1F30, 0x0);
+            channel.Write(ClassId.Threed, 0x1F38, 0x0);
+            channel.Write(ClassId.Threed, 0x1F40, 0x0);
+            channel.Write(ClassId.Threed, 0x1F48, 0x0);
+            channel.Write(ClassId.Threed, 0x1F50, 0x0);
+            channel.Write(ClassId.Threed, 0x1F58, 0x0);
+            channel.Write(ClassId.Threed, 0x1F60, 0x0);
+            channel.Write(ClassId.Threed, 0x1F68, 0x0);
+            channel.Write(ClassId.Threed, 0x1F70, 0x0);
+            channel.Write(ClassId.Threed, 0x1F78, 0x0);
+            channel.Write(ClassId.Threed, 0x1F04, 0x0);
+            channel.Write(ClassId.Threed, 0x1F0C, 0x0);
+            channel.Write(ClassId.Threed, 0x1F14, 0x0);
+            channel.Write(ClassId.Threed, 0x1F1C, 0x0);
+            channel.Write(ClassId.Threed, 0x1F24, 0x0);
+            channel.Write(ClassId.Threed, 0x1F2C, 0x0);
+            channel.Write(ClassId.Threed, 0x1F34, 0x0);
+            channel.Write(ClassId.Threed, 0x1F3C, 0x0);
+            channel.Write(ClassId.Threed, 0x1F44, 0x0);
+            channel.Write(ClassId.Threed, 0x1F4C, 0x0);
+            channel.Write(ClassId.Threed, 0x1F54, 0x0);
+            channel.Write(ClassId.Threed, 0x1F5C, 0x0);
+            channel.Write(ClassId.Threed, 0x1F64, 0x0);
+            channel.Write(ClassId.Threed, 0x1F6C, 0x0);
+            channel.Write(ClassId.Threed, 0x1F74, 0x0);
+            channel.Write(ClassId.Threed, 0x1F7C, 0x0);
+            channel.Write(ClassId.Threed, 0x1F80, 0x0);
+            channel.Write(ClassId.Threed, 0x1F88, 0x0);
+            channel.Write(ClassId.Threed, 0x1F90, 0x0);
+            channel.Write(ClassId.Threed, 0x1F98, 0x0);
+            channel.Write(ClassId.Threed, 0x1FA0, 0x0);
+            channel.Write(ClassId.Threed, 0x1FA8, 0x0);
+            channel.Write(ClassId.Threed, 0x1FB0, 0x0);
+            channel.Write(ClassId.Threed, 0x1FB8, 0x0);
+            channel.Write(ClassId.Threed, 0x1FC0, 0x0);
+            channel.Write(ClassId.Threed, 0x1FC8, 0x0);
+            channel.Write(ClassId.Threed, 0x1FD0, 0x0);
+            channel.Write(ClassId.Threed, 0x1FD8, 0x0);
+            channel.Write(ClassId.Threed, 0x1FE0, 0x0);
+            channel.Write(ClassId.Threed, 0x1FE8, 0x0);
+            channel.Write(ClassId.Threed, 0x1FF0, 0x0);
+            channel.Write(ClassId.Threed, 0x1FF8, 0x0);
+            channel.Write(ClassId.Threed, 0x1F84, 0x0);
+            channel.Write(ClassId.Threed, 0x1F8C, 0x0);
+            channel.Write(ClassId.Threed, 0x1F94, 0x0);
+            channel.Write(ClassId.Threed, 0x1F9C, 0x0);
+            channel.Write(ClassId.Threed, 0x1FA4, 0x0);
+            channel.Write(ClassId.Threed, 0x1FAC, 0x0);
+            channel.Write(ClassId.Threed, 0x1FB4, 0x0);
+            channel.Write(ClassId.Threed, 0x1FBC, 0x0);
+            channel.Write(ClassId.Threed, 0x1FC4, 0x0);
+            channel.Write(ClassId.Threed, 0x1FCC, 0x0);
+            channel.Write(ClassId.Threed, 0x1FD4, 0x0);
+            channel.Write(ClassId.Threed, 0x1FDC, 0x0);
+            channel.Write(ClassId.Threed, 0x1FE4, 0x0);
+            channel.Write(ClassId.Threed, 0x1FEC, 0x0);
+            channel.Write(ClassId.Threed, 0x1FF4, 0x0);
+            channel.Write(ClassId.Threed, 0x1FFC, 0x0);
+            channel.Write(ClassId.Threed, 0x2000, 0x0);
+            channel.Write(ClassId.Threed, 0x2040, 0x11);
+            channel.Write(ClassId.Threed, 0x2080, 0x20);
+            channel.Write(ClassId.Threed, 0x20C0, 0x30);
+            channel.Write(ClassId.Threed, 0x2100, 0x40);
+            channel.Write(ClassId.Threed, 0x2140, 0x51);
+            channel.Write(ClassId.Threed, 0x200C, 0x1);
+            channel.Write(ClassId.Threed, 0x204C, 0x1);
+            channel.Write(ClassId.Threed, 0x208C, 0x1);
+            channel.Write(ClassId.Threed, 0x20CC, 0x1);
+            channel.Write(ClassId.Threed, 0x210C, 0x1);
+            channel.Write(ClassId.Threed, 0x214C, 0x1);
+            channel.Write(ClassId.Threed, 0x2010, 0x0);
+            channel.Write(ClassId.Threed, 0x2050, 0x0);
+            channel.Write(ClassId.Threed, 0x2090, 0x1);
+            channel.Write(ClassId.Threed, 0x20D0, 0x2);
+            channel.Write(ClassId.Threed, 0x2110, 0x3);
+            channel.Write(ClassId.Threed, 0x2150, 0x4);
+            channel.Write(ClassId.Threed, 0x380, 0x0);
+            channel.Write(ClassId.Threed, 0x3A0, 0x0);
+            channel.Write(ClassId.Threed, 0x3C0, 0x0);
+            channel.Write(ClassId.Threed, 0x3E0, 0x0);
+            channel.Write(ClassId.Threed, 0x384, 0x0);
+            channel.Write(ClassId.Threed, 0x3A4, 0x0);
+            channel.Write(ClassId.Threed, 0x3C4, 0x0);
+            channel.Write(ClassId.Threed, 0x3E4, 0x0);
+            channel.Write(ClassId.Threed, 0x388, 0x0);
+            channel.Write(ClassId.Threed, 0x3A8, 0x0);
+            channel.Write(ClassId.Threed, 0x3C8, 0x0);
+            channel.Write(ClassId.Threed, 0x3E8, 0x0);
+            channel.Write(ClassId.Threed, 0x38C, 0x0);
+            channel.Write(ClassId.Threed, 0x3AC, 0x0);
+            channel.Write(ClassId.Threed, 0x3CC, 0x0);
+            channel.Write(ClassId.Threed, 0x3EC, 0x0);
+            channel.Write(ClassId.Threed, 0x700, 0x0);
+            channel.Write(ClassId.Threed, 0x710, 0x0);
+            channel.Write(ClassId.Threed, 0x720, 0x0);
+            channel.Write(ClassId.Threed, 0x730, 0x0);
+            channel.Write(ClassId.Threed, 0x704, 0x0);
+            channel.Write(ClassId.Threed, 0x714, 0x0);
+            channel.Write(ClassId.Threed, 0x724, 0x0);
+            channel.Write(ClassId.Threed, 0x734, 0x0);
+            channel.Write(ClassId.Threed, 0x708, 0x0);
+            channel.Write(ClassId.Threed, 0x718, 0x0);
+            channel.Write(ClassId.Threed, 0x728, 0x0);
+            channel.Write(ClassId.Threed, 0x738, 0x0);
+            channel.Write(ClassId.Threed, 0x2800, 0x0);
+            channel.Write(ClassId.Threed, 0x2804, 0x0);
+            channel.Write(ClassId.Threed, 0x2808, 0x0);
+            channel.Write(ClassId.Threed, 0x280C, 0x0);
+            channel.Write(ClassId.Threed, 0x2810, 0x0);
+            channel.Write(ClassId.Threed, 0x2814, 0x0);
+            channel.Write(ClassId.Threed, 0x2818, 0x0);
+            channel.Write(ClassId.Threed, 0x281C, 0x0);
+            channel.Write(ClassId.Threed, 0x2820, 0x0);
+            channel.Write(ClassId.Threed, 0x2824, 0x0);
+            channel.Write(ClassId.Threed, 0x2828, 0x0);
+            channel.Write(ClassId.Threed, 0x282C, 0x0);
+            channel.Write(ClassId.Threed, 0x2830, 0x0);
+            channel.Write(ClassId.Threed, 0x2834, 0x0);
+            channel.Write(ClassId.Threed, 0x2838, 0x0);
+            channel.Write(ClassId.Threed, 0x283C, 0x0);
+            channel.Write(ClassId.Threed, 0x2840, 0x0);
+            channel.Write(ClassId.Threed, 0x2844, 0x0);
+            channel.Write(ClassId.Threed, 0x2848, 0x0);
+            channel.Write(ClassId.Threed, 0x284C, 0x0);
+            channel.Write(ClassId.Threed, 0x2850, 0x0);
+            channel.Write(ClassId.Threed, 0x2854, 0x0);
+            channel.Write(ClassId.Threed, 0x2858, 0x0);
+            channel.Write(ClassId.Threed, 0x285C, 0x0);
+            channel.Write(ClassId.Threed, 0x2860, 0x0);
+            channel.Write(ClassId.Threed, 0x2864, 0x0);
+            channel.Write(ClassId.Threed, 0x2868, 0x0);
+            channel.Write(ClassId.Threed, 0x286C, 0x0);
+            channel.Write(ClassId.Threed, 0x2870, 0x0);
+            channel.Write(ClassId.Threed, 0x2874, 0x0);
+            channel.Write(ClassId.Threed, 0x2878, 0x0);
+            channel.Write(ClassId.Threed, 0x287C, 0x0);
+            channel.Write(ClassId.Threed, 0x2880, 0x0);
+            channel.Write(ClassId.Threed, 0x2884, 0x0);
+            channel.Write(ClassId.Threed, 0x2888, 0x0);
+            channel.Write(ClassId.Threed, 0x288C, 0x0);
+            channel.Write(ClassId.Threed, 0x2890, 0x0);
+            channel.Write(ClassId.Threed, 0x2894, 0x0);
+            channel.Write(ClassId.Threed, 0x2898, 0x0);
+            channel.Write(ClassId.Threed, 0x289C, 0x0);
+            channel.Write(ClassId.Threed, 0x28A0, 0x0);
+            channel.Write(ClassId.Threed, 0x28A4, 0x0);
+            channel.Write(ClassId.Threed, 0x28A8, 0x0);
+            channel.Write(ClassId.Threed, 0x28AC, 0x0);
+            channel.Write(ClassId.Threed, 0x28B0, 0x0);
+            channel.Write(ClassId.Threed, 0x28B4, 0x0);
+            channel.Write(ClassId.Threed, 0x28B8, 0x0);
+            channel.Write(ClassId.Threed, 0x28BC, 0x0);
+            channel.Write(ClassId.Threed, 0x28C0, 0x0);
+            channel.Write(ClassId.Threed, 0x28C4, 0x0);
+            channel.Write(ClassId.Threed, 0x28C8, 0x0);
+            channel.Write(ClassId.Threed, 0x28CC, 0x0);
+            channel.Write(ClassId.Threed, 0x28D0, 0x0);
+            channel.Write(ClassId.Threed, 0x28D4, 0x0);
+            channel.Write(ClassId.Threed, 0x28D8, 0x0);
+            channel.Write(ClassId.Threed, 0x28DC, 0x0);
+            channel.Write(ClassId.Threed, 0x28E0, 0x0);
+            channel.Write(ClassId.Threed, 0x28E4, 0x0);
+            channel.Write(ClassId.Threed, 0x28E8, 0x0);
+            channel.Write(ClassId.Threed, 0x28EC, 0x0);
+            channel.Write(ClassId.Threed, 0x28F0, 0x0);
+            channel.Write(ClassId.Threed, 0x28F4, 0x0);
+            channel.Write(ClassId.Threed, 0x28F8, 0x0);
+            channel.Write(ClassId.Threed, 0x28FC, 0x0);
+            channel.Write(ClassId.Threed, 0x2900, 0x0);
+            channel.Write(ClassId.Threed, 0x2904, 0x0);
+            channel.Write(ClassId.Threed, 0x2908, 0x0);
+            channel.Write(ClassId.Threed, 0x290C, 0x0);
+            channel.Write(ClassId.Threed, 0x2910, 0x0);
+            channel.Write(ClassId.Threed, 0x2914, 0x0);
+            channel.Write(ClassId.Threed, 0x2918, 0x0);
+            channel.Write(ClassId.Threed, 0x291C, 0x0);
+            channel.Write(ClassId.Threed, 0x2920, 0x0);
+            channel.Write(ClassId.Threed, 0x2924, 0x0);
+            channel.Write(ClassId.Threed, 0x2928, 0x0);
+            channel.Write(ClassId.Threed, 0x292C, 0x0);
+            channel.Write(ClassId.Threed, 0x2930, 0x0);
+            channel.Write(ClassId.Threed, 0x2934, 0x0);
+            channel.Write(ClassId.Threed, 0x2938, 0x0);
+            channel.Write(ClassId.Threed, 0x293C, 0x0);
+            channel.Write(ClassId.Threed, 0x2940, 0x0);
+            channel.Write(ClassId.Threed, 0x2944, 0x0);
+            channel.Write(ClassId.Threed, 0x2948, 0x0);
+            channel.Write(ClassId.Threed, 0x294C, 0x0);
+            channel.Write(ClassId.Threed, 0x2950, 0x0);
+            channel.Write(ClassId.Threed, 0x2954, 0x0);
+            channel.Write(ClassId.Threed, 0x2958, 0x0);
+            channel.Write(ClassId.Threed, 0x295C, 0x0);
+            channel.Write(ClassId.Threed, 0x2960, 0x0);
+            channel.Write(ClassId.Threed, 0x2964, 0x0);
+            channel.Write(ClassId.Threed, 0x2968, 0x0);
+            channel.Write(ClassId.Threed, 0x296C, 0x0);
+            channel.Write(ClassId.Threed, 0x2970, 0x0);
+            channel.Write(ClassId.Threed, 0x2974, 0x0);
+            channel.Write(ClassId.Threed, 0x2978, 0x0);
+            channel.Write(ClassId.Threed, 0x297C, 0x0);
+            channel.Write(ClassId.Threed, 0x2980, 0x0);
+            channel.Write(ClassId.Threed, 0x2984, 0x0);
+            channel.Write(ClassId.Threed, 0x2988, 0x0);
+            channel.Write(ClassId.Threed, 0x298C, 0x0);
+            channel.Write(ClassId.Threed, 0x2990, 0x0);
+            channel.Write(ClassId.Threed, 0x2994, 0x0);
+            channel.Write(ClassId.Threed, 0x2998, 0x0);
+            channel.Write(ClassId.Threed, 0x299C, 0x0);
+            channel.Write(ClassId.Threed, 0x29A0, 0x0);
+            channel.Write(ClassId.Threed, 0x29A4, 0x0);
+            channel.Write(ClassId.Threed, 0x29A8, 0x0);
+            channel.Write(ClassId.Threed, 0x29AC, 0x0);
+            channel.Write(ClassId.Threed, 0x29B0, 0x0);
+            channel.Write(ClassId.Threed, 0x29B4, 0x0);
+            channel.Write(ClassId.Threed, 0x29B8, 0x0);
+            channel.Write(ClassId.Threed, 0x29BC, 0x0);
+            channel.Write(ClassId.Threed, 0x29C0, 0x0);
+            channel.Write(ClassId.Threed, 0x29C4, 0x0);
+            channel.Write(ClassId.Threed, 0x29C8, 0x0);
+            channel.Write(ClassId.Threed, 0x29CC, 0x0);
+            channel.Write(ClassId.Threed, 0x29D0, 0x0);
+            channel.Write(ClassId.Threed, 0x29D4, 0x0);
+            channel.Write(ClassId.Threed, 0x29D8, 0x0);
+            channel.Write(ClassId.Threed, 0x29DC, 0x0);
+            channel.Write(ClassId.Threed, 0x29E0, 0x0);
+            channel.Write(ClassId.Threed, 0x29E4, 0x0);
+            channel.Write(ClassId.Threed, 0x29E8, 0x0);
+            channel.Write(ClassId.Threed, 0x29EC, 0x0);
+            channel.Write(ClassId.Threed, 0x29F0, 0x0);
+            channel.Write(ClassId.Threed, 0x29F4, 0x0);
+            channel.Write(ClassId.Threed, 0x29F8, 0x0);
+            channel.Write(ClassId.Threed, 0x29FC, 0x0);
+            channel.Write(ClassId.Threed, 0xA00, 0x0);
+            channel.Write(ClassId.Threed, 0xA20, 0x0);
+            channel.Write(ClassId.Threed, 0xA40, 0x0);
+            channel.Write(ClassId.Threed, 0xA60, 0x0);
+            channel.Write(ClassId.Threed, 0xA80, 0x0);
+            channel.Write(ClassId.Threed, 0xAA0, 0x0);
+            channel.Write(ClassId.Threed, 0xAC0, 0x0);
+            channel.Write(ClassId.Threed, 0xAE0, 0x0);
+            channel.Write(ClassId.Threed, 0xB00, 0x0);
+            channel.Write(ClassId.Threed, 0xB20, 0x0);
+            channel.Write(ClassId.Threed, 0xB40, 0x0);
+            channel.Write(ClassId.Threed, 0xB60, 0x0);
+            channel.Write(ClassId.Threed, 0xB80, 0x0);
+            channel.Write(ClassId.Threed, 0xBA0, 0x0);
+            channel.Write(ClassId.Threed, 0xBC0, 0x0);
+            channel.Write(ClassId.Threed, 0xBE0, 0x0);
+            channel.Write(ClassId.Threed, 0xA04, 0x0);
+            channel.Write(ClassId.Threed, 0xA24, 0x0);
+            channel.Write(ClassId.Threed, 0xA44, 0x0);
+            channel.Write(ClassId.Threed, 0xA64, 0x0);
+            channel.Write(ClassId.Threed, 0xA84, 0x0);
+            channel.Write(ClassId.Threed, 0xAA4, 0x0);
+            channel.Write(ClassId.Threed, 0xAC4, 0x0);
+            channel.Write(ClassId.Threed, 0xAE4, 0x0);
+            channel.Write(ClassId.Threed, 0xB04, 0x0);
+            channel.Write(ClassId.Threed, 0xB24, 0x0);
+            channel.Write(ClassId.Threed, 0xB44, 0x0);
+            channel.Write(ClassId.Threed, 0xB64, 0x0);
+            channel.Write(ClassId.Threed, 0xB84, 0x0);
+            channel.Write(ClassId.Threed, 0xBA4, 0x0);
+            channel.Write(ClassId.Threed, 0xBC4, 0x0);
+            channel.Write(ClassId.Threed, 0xBE4, 0x0);
+            channel.Write(ClassId.Threed, 0xA08, 0x0);
+            channel.Write(ClassId.Threed, 0xA28, 0x0);
+            channel.Write(ClassId.Threed, 0xA48, 0x0);
+            channel.Write(ClassId.Threed, 0xA68, 0x0);
+            channel.Write(ClassId.Threed, 0xA88, 0x0);
+            channel.Write(ClassId.Threed, 0xAA8, 0x0);
+            channel.Write(ClassId.Threed, 0xAC8, 0x0);
+            channel.Write(ClassId.Threed, 0xAE8, 0x0);
+            channel.Write(ClassId.Threed, 0xB08, 0x0);
+            channel.Write(ClassId.Threed, 0xB28, 0x0);
+            channel.Write(ClassId.Threed, 0xB48, 0x0);
+            channel.Write(ClassId.Threed, 0xB68, 0x0);
+            channel.Write(ClassId.Threed, 0xB88, 0x0);
+            channel.Write(ClassId.Threed, 0xBA8, 0x0);
+            channel.Write(ClassId.Threed, 0xBC8, 0x0);
+            channel.Write(ClassId.Threed, 0xBE8, 0x0);
+            channel.Write(ClassId.Threed, 0xA0C, 0x0);
+            channel.Write(ClassId.Threed, 0xA2C, 0x0);
+            channel.Write(ClassId.Threed, 0xA4C, 0x0);
+            channel.Write(ClassId.Threed, 0xA6C, 0x0);
+            channel.Write(ClassId.Threed, 0xA8C, 0x0);
+            channel.Write(ClassId.Threed, 0xAAC, 0x0);
+            channel.Write(ClassId.Threed, 0xACC, 0x0);
+            channel.Write(ClassId.Threed, 0xAEC, 0x0);
+            channel.Write(ClassId.Threed, 0xB0C, 0x0);
+            channel.Write(ClassId.Threed, 0xB2C, 0x0);
+            channel.Write(ClassId.Threed, 0xB4C, 0x0);
+            channel.Write(ClassId.Threed, 0xB6C, 0x0);
+            channel.Write(ClassId.Threed, 0xB8C, 0x0);
+            channel.Write(ClassId.Threed, 0xBAC, 0x0);
+            channel.Write(ClassId.Threed, 0xBCC, 0x0);
+            channel.Write(ClassId.Threed, 0xBEC, 0x0);
+            channel.Write(ClassId.Threed, 0xA10, 0x0);
+            channel.Write(ClassId.Threed, 0xA30, 0x0);
+            channel.Write(ClassId.Threed, 0xA50, 0x0);
+            channel.Write(ClassId.Threed, 0xA70, 0x0);
+            channel.Write(ClassId.Threed, 0xA90, 0x0);
+            channel.Write(ClassId.Threed, 0xAB0, 0x0);
+            channel.Write(ClassId.Threed, 0xAD0, 0x0);
+            channel.Write(ClassId.Threed, 0xAF0, 0x0);
+            channel.Write(ClassId.Threed, 0xB10, 0x0);
+            channel.Write(ClassId.Threed, 0xB30, 0x0);
+            channel.Write(ClassId.Threed, 0xB50, 0x0);
+            channel.Write(ClassId.Threed, 0xB70, 0x0);
+            channel.Write(ClassId.Threed, 0xB90, 0x0);
+            channel.Write(ClassId.Threed, 0xBB0, 0x0);
+            channel.Write(ClassId.Threed, 0xBD0, 0x0);
+            channel.Write(ClassId.Threed, 0xBF0, 0x0);
+            channel.Write(ClassId.Threed, 0xA14, 0x0);
+            channel.Write(ClassId.Threed, 0xA34, 0x0);
+            channel.Write(ClassId.Threed, 0xA54, 0x0);
+            channel.Write(ClassId.Threed, 0xA74, 0x0);
+            channel.Write(ClassId.Threed, 0xA94, 0x0);
+            channel.Write(ClassId.Threed, 0xAB4, 0x0);
+            channel.Write(ClassId.Threed, 0xAD4, 0x0);
+            channel.Write(ClassId.Threed, 0xAF4, 0x0);
+            channel.Write(ClassId.Threed, 0xB14, 0x0);
+            channel.Write(ClassId.Threed, 0xB34, 0x0);
+            channel.Write(ClassId.Threed, 0xB54, 0x0);
+            channel.Write(ClassId.Threed, 0xB74, 0x0);
+            channel.Write(ClassId.Threed, 0xB94, 0x0);
+            channel.Write(ClassId.Threed, 0xBB4, 0x0);
+            channel.Write(ClassId.Threed, 0xBD4, 0x0);
+            channel.Write(ClassId.Threed, 0xBF4, 0x0);
+            channel.Write(ClassId.Threed, 0xA18, 0x6420);
+            channel.Write(ClassId.Threed, 0xA38, 0x6420);
+            channel.Write(ClassId.Threed, 0xA58, 0x6420);
+            channel.Write(ClassId.Threed, 0xA78, 0x6420);
+            channel.Write(ClassId.Threed, 0xA98, 0x6420);
+            channel.Write(ClassId.Threed, 0xAB8, 0x6420);
+            channel.Write(ClassId.Threed, 0xAD8, 0x6420);
+            channel.Write(ClassId.Threed, 0xAF8, 0x6420);
+            channel.Write(ClassId.Threed, 0xB18, 0x6420);
+            channel.Write(ClassId.Threed, 0xB38, 0x6420);
+            channel.Write(ClassId.Threed, 0xB58, 0x6420);
+            channel.Write(ClassId.Threed, 0xB78, 0x6420);
+            channel.Write(ClassId.Threed, 0xB98, 0x6420);
+            channel.Write(ClassId.Threed, 0xBB8, 0x6420);
+            channel.Write(ClassId.Threed, 0xBD8, 0x6420);
+            channel.Write(ClassId.Threed, 0xBF8, 0x6420);
+            channel.Write(ClassId.Threed, 0xA1C, 0x0);
+            channel.Write(ClassId.Threed, 0xA3C, 0x0);
+            channel.Write(ClassId.Threed, 0xA5C, 0x0);
+            channel.Write(ClassId.Threed, 0xA7C, 0x0);
+            channel.Write(ClassId.Threed, 0xA9C, 0x0);
+            channel.Write(ClassId.Threed, 0xABC, 0x0);
+            channel.Write(ClassId.Threed, 0xADC, 0x0);
+            channel.Write(ClassId.Threed, 0xAFC, 0x0);
+            channel.Write(ClassId.Threed, 0xB1C, 0x0);
+            channel.Write(ClassId.Threed, 0xB3C, 0x0);
+            channel.Write(ClassId.Threed, 0xB5C, 0x0);
+            channel.Write(ClassId.Threed, 0xB7C, 0x0);
+            channel.Write(ClassId.Threed, 0xB9C, 0x0);
+            channel.Write(ClassId.Threed, 0xBBC, 0x0);
+            channel.Write(ClassId.Threed, 0xBDC, 0x0);
+            channel.Write(ClassId.Threed, 0xBFC, 0x0);
+            channel.Write(ClassId.Threed, 0xC00, 0x0);
+            channel.Write(ClassId.Threed, 0xC10, 0x0);
+            channel.Write(ClassId.Threed, 0xC20, 0x0);
+            channel.Write(ClassId.Threed, 0xC30, 0x0);
+            channel.Write(ClassId.Threed, 0xC40, 0x0);
+            channel.Write(ClassId.Threed, 0xC50, 0x0);
+            channel.Write(ClassId.Threed, 0xC60, 0x0);
+            channel.Write(ClassId.Threed, 0xC70, 0x0);
+            channel.Write(ClassId.Threed, 0xC80, 0x0);
+            channel.Write(ClassId.Threed, 0xC90, 0x0);
+            channel.Write(ClassId.Threed, 0xCA0, 0x0);
+            channel.Write(ClassId.Threed, 0xCB0, 0x0);
+            channel.Write(ClassId.Threed, 0xCC0, 0x0);
+            channel.Write(ClassId.Threed, 0xCD0, 0x0);
+            channel.Write(ClassId.Threed, 0xCE0, 0x0);
+            channel.Write(ClassId.Threed, 0xCF0, 0x0);
+            channel.Write(ClassId.Threed, 0xC04, 0x0);
+            channel.Write(ClassId.Threed, 0xC14, 0x0);
+            channel.Write(ClassId.Threed, 0xC24, 0x0);
+            channel.Write(ClassId.Threed, 0xC34, 0x0);
+            channel.Write(ClassId.Threed, 0xC44, 0x0);
+            channel.Write(ClassId.Threed, 0xC54, 0x0);
+            channel.Write(ClassId.Threed, 0xC64, 0x0);
+            channel.Write(ClassId.Threed, 0xC74, 0x0);
+            channel.Write(ClassId.Threed, 0xC84, 0x0);
+            channel.Write(ClassId.Threed, 0xC94, 0x0);
+            channel.Write(ClassId.Threed, 0xCA4, 0x0);
+            channel.Write(ClassId.Threed, 0xCB4, 0x0);
+            channel.Write(ClassId.Threed, 0xCC4, 0x0);
+            channel.Write(ClassId.Threed, 0xCD4, 0x0);
+            channel.Write(ClassId.Threed, 0xCE4, 0x0);
+            channel.Write(ClassId.Threed, 0xCF4, 0x0);
+            channel.Write(ClassId.Threed, 0xC08, 0x0);
+            channel.Write(ClassId.Threed, 0xC18, 0x0);
+            channel.Write(ClassId.Threed, 0xC28, 0x0);
+            channel.Write(ClassId.Threed, 0xC38, 0x0);
+            channel.Write(ClassId.Threed, 0xC48, 0x0);
+            channel.Write(ClassId.Threed, 0xC58, 0x0);
+            channel.Write(ClassId.Threed, 0xC68, 0x0);
+            channel.Write(ClassId.Threed, 0xC78, 0x0);
+            channel.Write(ClassId.Threed, 0xC88, 0x0);
+            channel.Write(ClassId.Threed, 0xC98, 0x0);
+            channel.Write(ClassId.Threed, 0xCA8, 0x0);
+            channel.Write(ClassId.Threed, 0xCB8, 0x0);
+            channel.Write(ClassId.Threed, 0xCC8, 0x0);
+            channel.Write(ClassId.Threed, 0xCD8, 0x0);
+            channel.Write(ClassId.Threed, 0xCE8, 0x0);
+            channel.Write(ClassId.Threed, 0xCF8, 0x0);
+            channel.Write(ClassId.Threed, 0xC0C, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xC1C, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xC2C, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xC3C, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xC4C, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xC5C, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xC6C, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xC7C, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xC8C, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xC9C, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xCAC, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xCBC, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xCCC, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xCDC, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xCEC, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xCFC, 0x3F800000);
+            channel.Write(ClassId.Threed, 0xD00, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD08, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD10, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD18, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD20, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD28, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD30, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD38, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD04, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD0C, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD14, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD1C, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD24, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD2C, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD34, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD3C, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE00, 0x0);
+            channel.Write(ClassId.Threed, 0xE10, 0x0);
+            channel.Write(ClassId.Threed, 0xE20, 0x0);
+            channel.Write(ClassId.Threed, 0xE30, 0x0);
+            channel.Write(ClassId.Threed, 0xE40, 0x0);
+            channel.Write(ClassId.Threed, 0xE50, 0x0);
+            channel.Write(ClassId.Threed, 0xE60, 0x0);
+            channel.Write(ClassId.Threed, 0xE70, 0x0);
+            channel.Write(ClassId.Threed, 0xE80, 0x0);
+            channel.Write(ClassId.Threed, 0xE90, 0x0);
+            channel.Write(ClassId.Threed, 0xEA0, 0x0);
+            channel.Write(ClassId.Threed, 0xEB0, 0x0);
+            channel.Write(ClassId.Threed, 0xEC0, 0x0);
+            channel.Write(ClassId.Threed, 0xED0, 0x0);
+            channel.Write(ClassId.Threed, 0xEE0, 0x0);
+            channel.Write(ClassId.Threed, 0xEF0, 0x0);
+            channel.Write(ClassId.Threed, 0xE04, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE14, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE24, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE34, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE44, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE54, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE64, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE74, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE84, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE94, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xEA4, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xEB4, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xEC4, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xED4, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xEE4, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xEF4, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE08, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE18, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE28, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE38, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE48, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE58, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE68, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE78, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE88, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xE98, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xEA8, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xEB8, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xEC8, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xED8, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xEE8, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xEF8, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD40, 0x0);
+            channel.Write(ClassId.Threed, 0xD48, 0x0);
+            channel.Write(ClassId.Threed, 0xD50, 0x0);
+            channel.Write(ClassId.Threed, 0xD58, 0x0);
+            channel.Write(ClassId.Threed, 0xD44, 0x0);
+            channel.Write(ClassId.Threed, 0xD4C, 0x0);
+            channel.Write(ClassId.Threed, 0xD54, 0x0);
+            channel.Write(ClassId.Threed, 0xD5C, 0x0);
+            channel.Write(ClassId.Threed, 0x1E00, 0x1);
+            channel.Write(ClassId.Threed, 0x1E20, 0x1);
+            channel.Write(ClassId.Threed, 0x1E40, 0x1);
+            channel.Write(ClassId.Threed, 0x1E60, 0x1);
+            channel.Write(ClassId.Threed, 0x1E80, 0x1);
+            channel.Write(ClassId.Threed, 0x1EA0, 0x1);
+            channel.Write(ClassId.Threed, 0x1EC0, 0x1);
+            channel.Write(ClassId.Threed, 0x1EE0, 0x1);
+            channel.Write(ClassId.Threed, 0x1E04, 0x1);
+            channel.Write(ClassId.Threed, 0x1E24, 0x1);
+            channel.Write(ClassId.Threed, 0x1E44, 0x1);
+            channel.Write(ClassId.Threed, 0x1E64, 0x1);
+            channel.Write(ClassId.Threed, 0x1E84, 0x1);
+            channel.Write(ClassId.Threed, 0x1EA4, 0x1);
+            channel.Write(ClassId.Threed, 0x1EC4, 0x1);
+            channel.Write(ClassId.Threed, 0x1EE4, 0x1);
+            channel.Write(ClassId.Threed, 0x1E08, 0x2);
+            channel.Write(ClassId.Threed, 0x1E28, 0x2);
+            channel.Write(ClassId.Threed, 0x1E48, 0x2);
+            channel.Write(ClassId.Threed, 0x1E68, 0x2);
+            channel.Write(ClassId.Threed, 0x1E88, 0x2);
+            channel.Write(ClassId.Threed, 0x1EA8, 0x2);
+            channel.Write(ClassId.Threed, 0x1EC8, 0x2);
+            channel.Write(ClassId.Threed, 0x1EE8, 0x2);
+            channel.Write(ClassId.Threed, 0x1E0C, 0x1);
+            channel.Write(ClassId.Threed, 0x1E2C, 0x1);
+            channel.Write(ClassId.Threed, 0x1E4C, 0x1);
+            channel.Write(ClassId.Threed, 0x1E6C, 0x1);
+            channel.Write(ClassId.Threed, 0x1E8C, 0x1);
+            channel.Write(ClassId.Threed, 0x1EAC, 0x1);
+            channel.Write(ClassId.Threed, 0x1ECC, 0x1);
+            channel.Write(ClassId.Threed, 0x1EEC, 0x1);
+            channel.Write(ClassId.Threed, 0x1E10, 0x1);
+            channel.Write(ClassId.Threed, 0x1E30, 0x1);
+            channel.Write(ClassId.Threed, 0x1E50, 0x1);
+            channel.Write(ClassId.Threed, 0x1E70, 0x1);
+            channel.Write(ClassId.Threed, 0x1E90, 0x1);
+            channel.Write(ClassId.Threed, 0x1EB0, 0x1);
+            channel.Write(ClassId.Threed, 0x1ED0, 0x1);
+            channel.Write(ClassId.Threed, 0x1EF0, 0x1);
+            channel.Write(ClassId.Threed, 0x1E14, 0x2);
+            channel.Write(ClassId.Threed, 0x1E34, 0x2);
+            channel.Write(ClassId.Threed, 0x1E54, 0x2);
+            channel.Write(ClassId.Threed, 0x1E74, 0x2);
+            channel.Write(ClassId.Threed, 0x1E94, 0x2);
+            channel.Write(ClassId.Threed, 0x1EB4, 0x2);
+            channel.Write(ClassId.Threed, 0x1ED4, 0x2);
+            channel.Write(ClassId.Threed, 0x1EF4, 0x2);
+            channel.Write(ClassId.Threed, 0x1E18, 0x1);
+            channel.Write(ClassId.Threed, 0x1E38, 0x1);
+            channel.Write(ClassId.Threed, 0x1E58, 0x1);
+            channel.Write(ClassId.Threed, 0x1E78, 0x1);
+            channel.Write(ClassId.Threed, 0x1E98, 0x1);
+            channel.Write(ClassId.Threed, 0x1EB8, 0x1);
+            channel.Write(ClassId.Threed, 0x1ED8, 0x1);
+            channel.Write(ClassId.Threed, 0x1EF8, 0x1);
+            channel.Write(ClassId.Threed, 0x1480, 0x0);
+            channel.Write(ClassId.Threed, 0x1490, 0x0);
+            channel.Write(ClassId.Threed, 0x14A0, 0x0);
+            channel.Write(ClassId.Threed, 0x14B0, 0x0);
+            channel.Write(ClassId.Threed, 0x14C0, 0x0);
+            channel.Write(ClassId.Threed, 0x14D0, 0x0);
+            channel.Write(ClassId.Threed, 0x14E0, 0x0);
+            channel.Write(ClassId.Threed, 0x14F0, 0x0);
+            channel.Write(ClassId.Threed, 0x1484, 0x0);
+            channel.Write(ClassId.Threed, 0x1494, 0x0);
+            channel.Write(ClassId.Threed, 0x14A4, 0x0);
+            channel.Write(ClassId.Threed, 0x14B4, 0x0);
+            channel.Write(ClassId.Threed, 0x14C4, 0x0);
+            channel.Write(ClassId.Threed, 0x14D4, 0x0);
+            channel.Write(ClassId.Threed, 0x14E4, 0x0);
+            channel.Write(ClassId.Threed, 0x14F4, 0x0);
+            channel.Write(ClassId.Threed, 0x1488, 0x0);
+            channel.Write(ClassId.Threed, 0x1498, 0x0);
+            channel.Write(ClassId.Threed, 0x14A8, 0x0);
+            channel.Write(ClassId.Threed, 0x14B8, 0x0);
+            channel.Write(ClassId.Threed, 0x14C8, 0x0);
+            channel.Write(ClassId.Threed, 0x14D8, 0x0);
+            channel.Write(ClassId.Threed, 0x14E8, 0x0);
+            channel.Write(ClassId.Threed, 0x14F8, 0x0);
+            channel.Write(ClassId.Threed, 0x3400, 0x0);
+            channel.Write(ClassId.Threed, 0x3404, 0x0);
+            channel.Write(ClassId.Threed, 0x3408, 0x0);
+            channel.Write(ClassId.Threed, 0x340C, 0x0);
+            channel.Write(ClassId.Threed, 0x3410, 0x0);
+            channel.Write(ClassId.Threed, 0x3414, 0x0);
+            channel.Write(ClassId.Threed, 0x3418, 0x0);
+            channel.Write(ClassId.Threed, 0x341C, 0x0);
+            channel.Write(ClassId.Threed, 0x3420, 0x0);
+            channel.Write(ClassId.Threed, 0x3424, 0x0);
+            channel.Write(ClassId.Threed, 0x3428, 0x0);
+            channel.Write(ClassId.Threed, 0x342C, 0x0);
+            channel.Write(ClassId.Threed, 0x3430, 0x0);
+            channel.Write(ClassId.Threed, 0x3434, 0x0);
+            channel.Write(ClassId.Threed, 0x3438, 0x0);
+            channel.Write(ClassId.Threed, 0x343C, 0x0);
+            channel.Write(ClassId.Threed, 0x3440, 0x0);
+            channel.Write(ClassId.Threed, 0x3444, 0x0);
+            channel.Write(ClassId.Threed, 0x3448, 0x0);
+            channel.Write(ClassId.Threed, 0x344C, 0x0);
+            channel.Write(ClassId.Threed, 0x3450, 0x0);
+            channel.Write(ClassId.Threed, 0x3454, 0x0);
+            channel.Write(ClassId.Threed, 0x3458, 0x0);
+            channel.Write(ClassId.Threed, 0x345C, 0x0);
+            channel.Write(ClassId.Threed, 0x3460, 0x0);
+            channel.Write(ClassId.Threed, 0x3464, 0x0);
+            channel.Write(ClassId.Threed, 0x3468, 0x0);
+            channel.Write(ClassId.Threed, 0x346C, 0x0);
+            channel.Write(ClassId.Threed, 0x3470, 0x0);
+            channel.Write(ClassId.Threed, 0x3474, 0x0);
+            channel.Write(ClassId.Threed, 0x3478, 0x0);
+            channel.Write(ClassId.Threed, 0x347C, 0x0);
+            channel.Write(ClassId.Threed, 0x3480, 0x0);
+            channel.Write(ClassId.Threed, 0x3484, 0x0);
+            channel.Write(ClassId.Threed, 0x3488, 0x0);
+            channel.Write(ClassId.Threed, 0x348C, 0x0);
+            channel.Write(ClassId.Threed, 0x3490, 0x0);
+            channel.Write(ClassId.Threed, 0x3494, 0x0);
+            channel.Write(ClassId.Threed, 0x3498, 0x0);
+            channel.Write(ClassId.Threed, 0x349C, 0x0);
+            channel.Write(ClassId.Threed, 0x34A0, 0x0);
+            channel.Write(ClassId.Threed, 0x34A4, 0x0);
+            channel.Write(ClassId.Threed, 0x34A8, 0x0);
+            channel.Write(ClassId.Threed, 0x34AC, 0x0);
+            channel.Write(ClassId.Threed, 0x34B0, 0x0);
+            channel.Write(ClassId.Threed, 0x34B4, 0x0);
+            channel.Write(ClassId.Threed, 0x34B8, 0x0);
+            channel.Write(ClassId.Threed, 0x34BC, 0x0);
+            channel.Write(ClassId.Threed, 0x34C0, 0x0);
+            channel.Write(ClassId.Threed, 0x34C4, 0x0);
+            channel.Write(ClassId.Threed, 0x34C8, 0x0);
+            channel.Write(ClassId.Threed, 0x34CC, 0x0);
+            channel.Write(ClassId.Threed, 0x34D0, 0x0);
+            channel.Write(ClassId.Threed, 0x34D4, 0x0);
+            channel.Write(ClassId.Threed, 0x34D8, 0x0);
+            channel.Write(ClassId.Threed, 0x34DC, 0x0);
+            channel.Write(ClassId.Threed, 0x34E0, 0x0);
+            channel.Write(ClassId.Threed, 0x34E4, 0x0);
+            channel.Write(ClassId.Threed, 0x34E8, 0x0);
+            channel.Write(ClassId.Threed, 0x34EC, 0x0);
+            channel.Write(ClassId.Threed, 0x34F0, 0x0);
+            channel.Write(ClassId.Threed, 0x34F4, 0x0);
+            channel.Write(ClassId.Threed, 0x34F8, 0x0);
+            channel.Write(ClassId.Threed, 0x34FC, 0x0);
+            channel.Write(ClassId.Threed, 0x3500, 0x0);
+            channel.Write(ClassId.Threed, 0x3504, 0x0);
+            channel.Write(ClassId.Threed, 0x3508, 0x0);
+            channel.Write(ClassId.Threed, 0x350C, 0x0);
+            channel.Write(ClassId.Threed, 0x3510, 0x0);
+            channel.Write(ClassId.Threed, 0x3514, 0x0);
+            channel.Write(ClassId.Threed, 0x3518, 0x0);
+            channel.Write(ClassId.Threed, 0x351C, 0x0);
+            channel.Write(ClassId.Threed, 0x3520, 0x0);
+            channel.Write(ClassId.Threed, 0x3524, 0x0);
+            channel.Write(ClassId.Threed, 0x3528, 0x0);
+            channel.Write(ClassId.Threed, 0x352C, 0x0);
+            channel.Write(ClassId.Threed, 0x3530, 0x0);
+            channel.Write(ClassId.Threed, 0x3534, 0x0);
+            channel.Write(ClassId.Threed, 0x3538, 0x0);
+            channel.Write(ClassId.Threed, 0x353C, 0x0);
+            channel.Write(ClassId.Threed, 0x3540, 0x0);
+            channel.Write(ClassId.Threed, 0x3544, 0x0);
+            channel.Write(ClassId.Threed, 0x3548, 0x0);
+            channel.Write(ClassId.Threed, 0x354C, 0x0);
+            channel.Write(ClassId.Threed, 0x3550, 0x0);
+            channel.Write(ClassId.Threed, 0x3554, 0x0);
+            channel.Write(ClassId.Threed, 0x3558, 0x0);
+            channel.Write(ClassId.Threed, 0x355C, 0x0);
+            channel.Write(ClassId.Threed, 0x3560, 0x0);
+            channel.Write(ClassId.Threed, 0x3564, 0x0);
+            channel.Write(ClassId.Threed, 0x3568, 0x0);
+            channel.Write(ClassId.Threed, 0x356C, 0x0);
+            channel.Write(ClassId.Threed, 0x3570, 0x0);
+            channel.Write(ClassId.Threed, 0x3574, 0x0);
+            channel.Write(ClassId.Threed, 0x3578, 0x0);
+            channel.Write(ClassId.Threed, 0x357C, 0x0);
+            channel.Write(ClassId.Threed, 0x3580, 0x0);
+            channel.Write(ClassId.Threed, 0x3584, 0x0);
+            channel.Write(ClassId.Threed, 0x3588, 0x0);
+            channel.Write(ClassId.Threed, 0x358C, 0x0);
+            channel.Write(ClassId.Threed, 0x3590, 0x0);
+            channel.Write(ClassId.Threed, 0x3594, 0x0);
+            channel.Write(ClassId.Threed, 0x3598, 0x0);
+            channel.Write(ClassId.Threed, 0x359C, 0x0);
+            channel.Write(ClassId.Threed, 0x35A0, 0x0);
+            channel.Write(ClassId.Threed, 0x35A4, 0x0);
+            channel.Write(ClassId.Threed, 0x35A8, 0x0);
+            channel.Write(ClassId.Threed, 0x35AC, 0x0);
+            channel.Write(ClassId.Threed, 0x35B0, 0x0);
+            channel.Write(ClassId.Threed, 0x35B4, 0x0);
+            channel.Write(ClassId.Threed, 0x35B8, 0x0);
+            channel.Write(ClassId.Threed, 0x35BC, 0x0);
+            channel.Write(ClassId.Threed, 0x35C0, 0x0);
+            channel.Write(ClassId.Threed, 0x35C4, 0x0);
+            channel.Write(ClassId.Threed, 0x35C8, 0x0);
+            channel.Write(ClassId.Threed, 0x35CC, 0x0);
+            channel.Write(ClassId.Threed, 0x35D0, 0x0);
+            channel.Write(ClassId.Threed, 0x35D4, 0x0);
+            channel.Write(ClassId.Threed, 0x35D8, 0x0);
+            channel.Write(ClassId.Threed, 0x35DC, 0x0);
+            channel.Write(ClassId.Threed, 0x35E0, 0x0);
+            channel.Write(ClassId.Threed, 0x35E4, 0x0);
+            channel.Write(ClassId.Threed, 0x35E8, 0x0);
+            channel.Write(ClassId.Threed, 0x35EC, 0x0);
+            channel.Write(ClassId.Threed, 0x35F0, 0x0);
+            channel.Write(ClassId.Threed, 0x35F4, 0x0);
+            channel.Write(ClassId.Threed, 0x35F8, 0x0);
+            channel.Write(ClassId.Threed, 0x35FC, 0x0);
+            channel.Write(ClassId.Threed, 0x30C, 0x1);
+            channel.Write(ClassId.Threed, 0x1944, 0x0);
+            channel.Write(ClassId.Threed, 0x1514, 0x0);
+            channel.Write(ClassId.Threed, 0xD68, 0xFFFF);
+            channel.Write(ClassId.Threed, 0x121C, 0xFAC6881);
+            channel.Write(ClassId.Threed, 0xFAC, 0x1);
+            channel.Write(ClassId.Threed, 0x1538, 0x1);
+            channel.Write(ClassId.Threed, 0xFE0, 0x0);
+            channel.Write(ClassId.Threed, 0xFE4, 0x0);
+            channel.Write(ClassId.Threed, 0xFE8, 0x14);
+            channel.Write(ClassId.Threed, 0xFEC, 0x40);
+            channel.Write(ClassId.Threed, 0xFF0, 0x0);
+            channel.Write(ClassId.Threed, 0x179C, 0x0);
+            channel.Write(ClassId.Threed, 0x1228, 0x400);
+            channel.Write(ClassId.Threed, 0x122C, 0x300);
+            channel.Write(ClassId.Threed, 0x1230, 0x10001);
+            channel.Write(ClassId.Threed, 0x7F8, 0x0);
+            channel.Write(ClassId.Threed, 0x1208, 0x0);
+            channel.Write(ClassId.Threed, 0x15B4, 0x1);
+            channel.Write(ClassId.Threed, 0x15CC, 0x0);
+            channel.Write(ClassId.Threed, 0x1534, 0x0);
+            channel.Write(ClassId.Threed, 0x754, 0x1);
+            channel.Write(ClassId.Threed, 0xFB0, 0x0);
+            channel.Write(ClassId.Threed, 0x15D0, 0x0);
+            channel.Write(ClassId.Threed, 0x11E0, 0x88888888);
+            channel.Write(ClassId.Threed, 0x11E4, 0x88888888);
+            channel.Write(ClassId.Threed, 0x11E8, 0x88888888);
+            channel.Write(ClassId.Threed, 0x11EC, 0x88888888);
+            channel.Write(ClassId.Threed, 0x153C, 0x0);
+            channel.Write(ClassId.Threed, 0x16B4, 0x3);
+            channel.Write(ClassId.Threed, 0xFA4, 0x1);
+            channel.Write(ClassId.Threed, 0xFBC, 0xFFFF);
+            channel.Write(ClassId.Threed, 0xFC0, 0xFFFF);
+            channel.Write(ClassId.Threed, 0xFC4, 0xFFFF);
+            channel.Write(ClassId.Threed, 0xFC8, 0xFFFF);
+            channel.Write(ClassId.Threed, 0xFA8, 0xFFFF);
+            channel.Write(ClassId.Threed, 0xDF8, 0x0);
+            channel.Write(ClassId.Threed, 0xDFC, 0x0);
+            channel.Write(ClassId.Threed, 0x1948, 0x0);
+            channel.Write(ClassId.Threed, 0x1970, 0x1);
+            channel.Write(ClassId.Threed, 0x161C, 0x9F0);
+            channel.Write(ClassId.Threed, 0xDCC, 0x10);
+            channel.Write(ClassId.Threed, 0x15E4, 0x0);
+            channel.Write(ClassId.Threed, 0x1160, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1164, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1168, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x116C, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1170, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1174, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1178, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x117C, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1180, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1184, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1188, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x118C, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1190, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1194, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1198, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x119C, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11A0, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11A4, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11A8, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11AC, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11B0, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11B4, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11B8, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11BC, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11C0, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11C4, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11C8, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11CC, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11D0, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11D4, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11D8, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x11DC, 0x25E00040);
+            channel.Write(ClassId.Threed, 0x1880, 0x0);
+            channel.Write(ClassId.Threed, 0x1884, 0x0);
+            channel.Write(ClassId.Threed, 0x1888, 0x0);
+            channel.Write(ClassId.Threed, 0x188C, 0x0);
+            channel.Write(ClassId.Threed, 0x1890, 0x0);
+            channel.Write(ClassId.Threed, 0x1894, 0x0);
+            channel.Write(ClassId.Threed, 0x1898, 0x0);
+            channel.Write(ClassId.Threed, 0x189C, 0x0);
+            channel.Write(ClassId.Threed, 0x18A0, 0x0);
+            channel.Write(ClassId.Threed, 0x18A4, 0x0);
+            channel.Write(ClassId.Threed, 0x18A8, 0x0);
+            channel.Write(ClassId.Threed, 0x18AC, 0x0);
+            channel.Write(ClassId.Threed, 0x18B0, 0x0);
+            channel.Write(ClassId.Threed, 0x18B4, 0x0);
+            channel.Write(ClassId.Threed, 0x18B8, 0x0);
+            channel.Write(ClassId.Threed, 0x18BC, 0x0);
+            channel.Write(ClassId.Threed, 0x18C0, 0x0);
+            channel.Write(ClassId.Threed, 0x18C4, 0x0);
+            channel.Write(ClassId.Threed, 0x18C8, 0x0);
+            channel.Write(ClassId.Threed, 0x18CC, 0x0);
+            channel.Write(ClassId.Threed, 0x18D0, 0x0);
+            channel.Write(ClassId.Threed, 0x18D4, 0x0);
+            channel.Write(ClassId.Threed, 0x18D8, 0x0);
+            channel.Write(ClassId.Threed, 0x18DC, 0x0);
+            channel.Write(ClassId.Threed, 0x18E0, 0x0);
+            channel.Write(ClassId.Threed, 0x18E4, 0x0);
+            channel.Write(ClassId.Threed, 0x18E8, 0x0);
+            channel.Write(ClassId.Threed, 0x18EC, 0x0);
+            channel.Write(ClassId.Threed, 0x18F0, 0x0);
+            channel.Write(ClassId.Threed, 0x18F4, 0x0);
+            channel.Write(ClassId.Threed, 0x18F8, 0x0);
+            channel.Write(ClassId.Threed, 0x18FC, 0x0);
+            channel.Write(ClassId.Threed, 0xF84, 0x0);
+            channel.Write(ClassId.Threed, 0xF88, 0x0);
+            channel.Write(ClassId.Threed, 0x17C8, 0x0);
+            channel.Write(ClassId.Threed, 0x17CC, 0x0);
+            channel.Write(ClassId.Threed, 0x17D0, 0xFF);
+            channel.Write(ClassId.Threed, 0x17D4, 0xFFFFFFFF);
+            channel.Write(ClassId.Threed, 0x17D8, 0x2);
+            channel.Write(ClassId.Threed, 0x17DC, 0x0);
+            channel.Write(ClassId.Threed, 0x15F4, 0x0);
+            channel.Write(ClassId.Threed, 0x15F8, 0x0);
+            channel.Write(ClassId.Threed, 0x1434, 0x0);
+            channel.Write(ClassId.Threed, 0x1438, 0x0);
+            channel.Write(ClassId.Threed, 0xD74, 0x0);
+            channel.Write(ClassId.Threed, 0x13A4, 0x0);
+            channel.Write(ClassId.Threed, 0x1318, 0x1);
+            channel.Write(ClassId.Threed, 0x1080, 0x0);
+            channel.Write(ClassId.Threed, 0x1084, 0x0);
+            channel.Write(ClassId.Threed, 0x1088, 0x1);
+            channel.Write(ClassId.Threed, 0x108C, 0x1);
+            channel.Write(ClassId.Threed, 0x1090, 0x0);
+            channel.Write(ClassId.Threed, 0x1094, 0x1);
+            channel.Write(ClassId.Threed, 0x1098, 0x0);
+            channel.Write(ClassId.Threed, 0x109C, 0x1);
+            channel.Write(ClassId.Threed, 0x10A0, 0x0);
+            channel.Write(ClassId.Threed, 0x10A4, 0x0);
+            channel.Write(ClassId.Threed, 0x1644, 0x0);
+            channel.Write(ClassId.Threed, 0x748, 0x0);
+            channel.Write(ClassId.Threed, 0xDE8, 0x0);
+            channel.Write(ClassId.Threed, 0x1648, 0x0);
+            channel.Write(ClassId.Threed, 0x12A4, 0x0);
+            channel.Write(ClassId.Threed, 0x1120, 0x0);
+            channel.Write(ClassId.Threed, 0x1124, 0x0);
+            channel.Write(ClassId.Threed, 0x1128, 0x0);
+            channel.Write(ClassId.Threed, 0x112C, 0x0);
+            channel.Write(ClassId.Threed, 0x1118, 0x0);
+            channel.Write(ClassId.Threed, 0x164C, 0x0);
+            channel.Write(ClassId.Threed, 0x1658, 0x0);
+            channel.Write(ClassId.Threed, 0x1910, 0x290);
+            channel.Write(ClassId.Threed, 0x1518, 0x0);
+            channel.Write(ClassId.Threed, 0x165C, 0x1);
+            channel.Write(ClassId.Threed, 0x1520, 0x0);
+            channel.Write(ClassId.Threed, 0x1604, 0x0);
+            channel.Write(ClassId.Threed, 0x1570, 0x0);
+            channel.Write(ClassId.Threed, 0x13B0, 0x3F800000);
+            channel.Write(ClassId.Threed, 0x13B4, 0x3F800000);
+            channel.Write(ClassId.Threed, 0x20C, 0x0);
+            channel.Write(ClassId.Threed, 0x1670, 0x30201000);
+            channel.Write(ClassId.Threed, 0x1674, 0x70605040);
+            channel.Write(ClassId.Threed, 0x1678, 0xB8A89888);
+            channel.Write(ClassId.Threed, 0x167C, 0xF8E8D8C8);
+            channel.Write(ClassId.Threed, 0x166C, 0x0);
+            channel.Write(ClassId.Threed, 0x1680, 0xFFFF00);
+            channel.Write(ClassId.Threed, 0x12D0, 0x3);
+            channel.Write(ClassId.Threed, 0x113C, 0x0);
+            channel.Write(ClassId.Threed, 0x12D4, 0x2);
+            channel.Write(ClassId.Threed, 0x1684, 0x0);
+            channel.Write(ClassId.Threed, 0x1688, 0x0);
+            channel.Write(ClassId.Threed, 0xDAC, 0x1B02);
+            channel.Write(ClassId.Threed, 0xDB0, 0x1B02);
+            channel.Write(ClassId.Threed, 0xDB4, 0x0);
+            channel.Write(ClassId.Threed, 0x168C, 0x0);
+            channel.Write(ClassId.Threed, 0x15BC, 0x0);
+            channel.Write(ClassId.Threed, 0x156C, 0x0);
+            channel.Write(ClassId.Threed, 0x187C, 0x0);
+            channel.Write(ClassId.Threed, 0x1110, 0x1);
+            channel.Write(ClassId.Threed, 0xDC0, 0x0);
+            channel.Write(ClassId.Threed, 0xDC4, 0x0);
+            channel.Write(ClassId.Threed, 0xDC8, 0x0);
+            channel.Write(ClassId.Threed, 0xF40, 0x0);
+            channel.Write(ClassId.Threed, 0xF44, 0x0);
+            channel.Write(ClassId.Threed, 0xF48, 0x0);
+            channel.Write(ClassId.Threed, 0xF4C, 0x0);
+            channel.Write(ClassId.Threed, 0xF50, 0x0);
+            channel.Write(ClassId.Threed, 0x1234, 0x0);
+            channel.Write(ClassId.Threed, 0x1690, 0x0);
+            channel.Write(ClassId.Threed, 0x790, 0x0);
+            channel.Write(ClassId.Threed, 0x794, 0x0);
+            channel.Write(ClassId.Threed, 0x798, 0x0);
+            channel.Write(ClassId.Threed, 0x79C, 0x0);
+            channel.Write(ClassId.Threed, 0x7A0, 0x0);
+            channel.Write(ClassId.Threed, 0x77C, 0x0);
+            channel.Write(ClassId.Threed, 0x1000, 0x10);
+            channel.Write(ClassId.Threed, 0x10FC, 0x0);
+            channel.Write(ClassId.Threed, 0x1290, 0x0);
+            channel.Write(ClassId.Threed, 0x218, 0x10);
+            channel.Write(ClassId.Threed, 0x12D8, 0x0);
+            channel.Write(ClassId.Threed, 0x12DC, 0x10);
+            channel.Write(ClassId.Threed, 0xD94, 0x1);
+            channel.Write(ClassId.Threed, 0x155C, 0x0);
+            channel.Write(ClassId.Threed, 0x1560, 0x0);
+            channel.Write(ClassId.Threed, 0x1564, 0xFFF);
+            channel.Write(ClassId.Threed, 0x1574, 0x0);
+            channel.Write(ClassId.Threed, 0x1578, 0x0);
+            channel.Write(ClassId.Threed, 0x157C, 0xFFFFF);
+            channel.Write(ClassId.Threed, 0x1354, 0x0);
+            channel.Write(ClassId.Threed, 0x1610, 0x12);
+            channel.Write(ClassId.Threed, 0x1608, 0x0);
+            channel.Write(ClassId.Threed, 0x160C, 0x0);
+            channel.Write(ClassId.Threed, 0x260C, 0x0);
+            channel.Write(ClassId.Threed, 0x7AC, 0x0);
+            channel.Write(ClassId.Threed, 0x162C, 0x3);
+            channel.Write(ClassId.Threed, 0x210, 0x0);
+            channel.Write(ClassId.Threed, 0x320, 0x0);
+            channel.Write(ClassId.Threed, 0x324, 0x3F800000);
+            channel.Write(ClassId.Threed, 0x328, 0x3F800000);
+            channel.Write(ClassId.Threed, 0x32C, 0x3F800000);
+            channel.Write(ClassId.Threed, 0x330, 0x3F800000);
+            channel.Write(ClassId.Threed, 0x334, 0x3F800000);
+            channel.Write(ClassId.Threed, 0x338, 0x3F800000);
+            channel.Write(ClassId.Threed, 0x750, 0x0);
+            channel.Write(ClassId.Threed, 0x760, 0x39291909);
+            channel.Write(ClassId.Threed, 0x764, 0x79695949);
+            channel.Write(ClassId.Threed, 0x768, 0xB9A99989);
+            channel.Write(ClassId.Threed, 0x76C, 0xF9E9D9C9);
+            channel.Write(ClassId.Threed, 0x770, 0x30201000);
+            channel.Write(ClassId.Threed, 0x774, 0x70605040);
+            channel.Write(ClassId.Threed, 0x778, 0x9080);
+            channel.Write(ClassId.Threed, 0x780, 0x39291909);
+            channel.Write(ClassId.Threed, 0x784, 0x79695949);
+            channel.Write(ClassId.Threed, 0x788, 0xB9A99989);
+            channel.Write(ClassId.Threed, 0x78C, 0xF9E9D9C9);
+            channel.Write(ClassId.Threed, 0x7D0, 0x30201000);
+            channel.Write(ClassId.Threed, 0x7D4, 0x70605040);
+            channel.Write(ClassId.Threed, 0x7D8, 0x9080);
+            channel.Write(ClassId.Threed, 0x1004, 0x0);
+            channel.Write(ClassId.Threed, 0x1240, 0x0);
+            channel.Write(ClassId.Threed, 0x1244, 0x0);
+            channel.Write(ClassId.Threed, 0x1248, 0x0);
+            channel.Write(ClassId.Threed, 0x124C, 0x0);
+            channel.Write(ClassId.Threed, 0x1250, 0x0);
+            channel.Write(ClassId.Threed, 0x1254, 0x0);
+            channel.Write(ClassId.Threed, 0x1258, 0x0);
+            channel.Write(ClassId.Threed, 0x125C, 0x0);
+            channel.Write(ClassId.Threed, 0x37C, 0x1);
+            channel.Write(ClassId.Threed, 0x740, 0x0);
+            channel.Write(ClassId.Threed, 0x1148, 0x0);
+            channel.Write(ClassId.Threed, 0xFB4, 0x0);
+            channel.Write(ClassId.Threed, 0xFB8, 0x2);
+            channel.Write(ClassId.Threed, 0x1130, 0x2);
+            channel.Write(ClassId.Threed, 0xFD4, 0x0);
+            channel.Write(ClassId.Threed, 0xFD8, 0x0);
+            channel.Write(ClassId.Threed, 0x1030, 0x20181008);
+            channel.Write(ClassId.Threed, 0x1034, 0x40383028);
+            channel.Write(ClassId.Threed, 0x1038, 0x60585048);
+            channel.Write(ClassId.Threed, 0x103C, 0x80787068);
+            channel.Write(ClassId.Threed, 0x744, 0x0);
+            channel.Write(ClassId.Threed, 0x2600, 0x0);
+            channel.Write(ClassId.Threed, 0x1918, 0x0);
+            channel.Write(ClassId.Threed, 0x191C, 0x900);
+            channel.Write(ClassId.Threed, 0x1920, 0x405);
+            channel.Write(ClassId.Threed, 0x1308, 0x1);
+            channel.Write(ClassId.Threed, 0x1924, 0x0);
+            channel.Write(ClassId.Threed, 0x13AC, 0x0);
+            channel.Write(ClassId.Threed, 0x192C, 0x1);
+            channel.Write(ClassId.Threed, 0x193C, 0x2C1C);
+            channel.Write(ClassId.Threed, 0xD7C, 0x0);
+            channel.Write(ClassId.Threed, 0xF8C, 0x0);
+            channel.Write(ClassId.Threed, 0x2C0, 0x1);
+            channel.Write(ClassId.Threed, 0x1510, 0x0);
+            channel.Write(ClassId.Threed, 0x1940, 0x0);
+            channel.Write(ClassId.Threed, 0xFF4, 0x0);
+            channel.Write(ClassId.Threed, 0xFF8, 0x0);
+            channel.Write(ClassId.Threed, 0x194C, 0x0);
+            channel.Write(ClassId.Threed, 0x1950, 0x0);
+            channel.Write(ClassId.Threed, 0x1968, 0x0);
+            channel.Write(ClassId.Threed, 0x1590, 0x3F);
+            channel.Write(ClassId.Threed, 0x7E8, 0x0);
+            channel.Write(ClassId.Threed, 0x7EC, 0x0);
+            channel.Write(ClassId.Threed, 0x7F0, 0x0);
+            channel.Write(ClassId.Threed, 0x7F4, 0x0);
+            channel.Write(ClassId.Threed, 0x196C, 0x11);
+            channel.Write(ClassId.Threed, 0x2E4, 0xB001);
+            channel.Write(ClassId.Threed, 0x36C, 0x0);
+            channel.Write(ClassId.Threed, 0x370, 0x0);
+            channel.Write(ClassId.Threed, 0x197C, 0x0);
+            channel.Write(ClassId.Threed, 0xFCC, 0x0);
+            channel.Write(ClassId.Threed, 0xFD0, 0x0);
+            channel.Write(ClassId.Threed, 0x2D8, 0x40);
+            channel.Write(ClassId.Threed, 0x1980, 0x80);
+            channel.Write(ClassId.Threed, 0x1504, 0x80);
+            channel.Write(ClassId.Threed, 0x1984, 0x0);
+            channel.Write(ClassId.Threed, 0xF60, 0x0);
+            channel.Write(ClassId.Threed, 0xF64, 0x400040);
+            channel.Write(ClassId.Threed, 0xF68, 0x2212);
+            channel.Write(ClassId.Threed, 0xF6C, 0x8080203);
+            channel.Write(ClassId.Threed, 0x1108, 0x8);
+            channel.Write(ClassId.Threed, 0xF70, 0x80001);
+            channel.Write(ClassId.Threed, 0xFFC, 0x0);
+            channel.Write(ClassId.Threed, 0x1134, 0x0);
+            channel.Write(ClassId.Threed, 0xF1C, 0x0);
+            channel.Write(ClassId.Threed, 0x11F8, 0x0);
+            channel.Write(ClassId.Threed, 0x1138, 0x1);
+            channel.Write(ClassId.Threed, 0x300, 0x1);
+            channel.Write(ClassId.Threed, 0x13A8, 0x0);
+            channel.Write(ClassId.Threed, 0x1224, 0x0);
+            channel.Write(ClassId.Threed, 0x12EC, 0x0);
+            channel.Write(ClassId.Threed, 0x1310, 0x0);
+            channel.Write(ClassId.Threed, 0x1314, 0x1);
+            channel.Write(ClassId.Threed, 0x1380, 0x0);
+            channel.Write(ClassId.Threed, 0x1384, 0x1);
+            channel.Write(ClassId.Threed, 0x1388, 0x1);
+            channel.Write(ClassId.Threed, 0x138C, 0x1);
+            channel.Write(ClassId.Threed, 0x1390, 0x1);
+            channel.Write(ClassId.Threed, 0x1394, 0x0);
+            channel.Write(ClassId.Threed, 0x139C, 0x0);
+            channel.Write(ClassId.Threed, 0x1398, 0x0);
+            channel.Write(ClassId.Threed, 0x1594, 0x0);
+            channel.Write(ClassId.Threed, 0x1598, 0x1);
+            channel.Write(ClassId.Threed, 0x159C, 0x1);
+            channel.Write(ClassId.Threed, 0x15A0, 0x1);
+            channel.Write(ClassId.Threed, 0x15A4, 0x1);
+            channel.Write(ClassId.Threed, 0xF54, 0x0);
+            channel.Write(ClassId.Threed, 0xF58, 0x0);
+            channel.Write(ClassId.Threed, 0xF5C, 0x0);
+            channel.Write(ClassId.Threed, 0x19BC, 0x0);
+            channel.Write(ClassId.Threed, 0xF9C, 0x0);
+            channel.Write(ClassId.Threed, 0xFA0, 0x0);
+            channel.Write(ClassId.Threed, 0x12CC, 0x0);
+            channel.Write(ClassId.Threed, 0x12E8, 0x0);
+            channel.Write(ClassId.Threed, 0x130C, 0x1);
+            channel.Write(ClassId.Threed, 0x1360, 0x0);
+            channel.Write(ClassId.Threed, 0x1364, 0x0);
+            channel.Write(ClassId.Threed, 0x1368, 0x0);
+            channel.Write(ClassId.Threed, 0x136C, 0x0);
+            channel.Write(ClassId.Threed, 0x1370, 0x0);
+            channel.Write(ClassId.Threed, 0x1374, 0x0);
+            channel.Write(ClassId.Threed, 0x1378, 0x0);
+            channel.Write(ClassId.Threed, 0x137C, 0x0);
+            channel.Write(ClassId.Threed, 0x133C, 0x1);
+            channel.Write(ClassId.Threed, 0x1340, 0x1);
+            channel.Write(ClassId.Threed, 0x1344, 0x2);
+            channel.Write(ClassId.Threed, 0x1348, 0x1);
+            channel.Write(ClassId.Threed, 0x134C, 0x1);
+            channel.Write(ClassId.Threed, 0x1350, 0x2);
+            channel.Write(ClassId.Threed, 0x1358, 0x1);
+            channel.Write(ClassId.Threed, 0x12E4, 0x0);
+            channel.Write(ClassId.Threed, 0x131C, 0x0);
+            channel.Write(ClassId.Threed, 0x1320, 0x0);
+            channel.Write(ClassId.Threed, 0x1324, 0x0);
+            channel.Write(ClassId.Threed, 0x1328, 0x0);
+            channel.Write(ClassId.Threed, 0x19C0, 0x0);
+            channel.Write(ClassId.Threed, 0x1140, 0x0);
+            channel.Write(ClassId.Threed, 0xDD0, 0x0);
+            channel.Write(ClassId.Threed, 0xDD4, 0x1);
+            channel.Write(ClassId.Threed, 0x2F4, 0x0);
+            channel.Write(ClassId.Threed, 0x19C4, 0x0);
+            channel.Write(ClassId.Threed, 0x19C8, 0x1500);
+            channel.Write(ClassId.Threed, 0x135C, 0x0);
+            channel.Write(ClassId.Threed, 0xF90, 0x0);
+            channel.Write(ClassId.Threed, 0x19E0, 0x1);
+            channel.Write(ClassId.Threed, 0x19E4, 0x1);
+            channel.Write(ClassId.Threed, 0x19E8, 0x1);
+            channel.Write(ClassId.Threed, 0x19EC, 0x1);
+            channel.Write(ClassId.Threed, 0x19F0, 0x1);
+            channel.Write(ClassId.Threed, 0x19F4, 0x1);
+            channel.Write(ClassId.Threed, 0x19F8, 0x1);
+            channel.Write(ClassId.Threed, 0x19FC, 0x1);
+            channel.Write(ClassId.Threed, 0x19CC, 0x1);
+            channel.Write(ClassId.Threed, 0x111C, 0x1);
+            channel.Write(ClassId.Threed, 0x15B8, 0x0);
+            channel.Write(ClassId.Threed, 0x1A00, 0x1111);
+            channel.Write(ClassId.Threed, 0x1A04, 0x0);
+            channel.Write(ClassId.Threed, 0x1A08, 0x0);
+            channel.Write(ClassId.Threed, 0x1A0C, 0x0);
+            channel.Write(ClassId.Threed, 0x1A10, 0x0);
+            channel.Write(ClassId.Threed, 0x1A14, 0x0);
+            channel.Write(ClassId.Threed, 0x1A18, 0x0);
+            channel.Write(ClassId.Threed, 0x1A1C, 0x0);
+            channel.Write(ClassId.Threed, 0xD6C, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0xD70, 0xFFFF0000);
+            channel.Write(ClassId.Threed, 0x10F8, 0x1010);
+            channel.Write(ClassId.Threed, 0xD80, 0x0);
+            channel.Write(ClassId.Threed, 0xD84, 0x0);
+            channel.Write(ClassId.Threed, 0xD88, 0x0);
+            channel.Write(ClassId.Threed, 0xD8C, 0x0);
+            channel.Write(ClassId.Threed, 0xD90, 0x0);
+            channel.Write(ClassId.Threed, 0xDA0, 0x0);
+            channel.Write(ClassId.Threed, 0x7A4, 0x0);
+            channel.Write(ClassId.Threed, 0x7A8, 0x0);
+            channel.Write(ClassId.Threed, 0x1508, 0x80000000);
+            channel.Write(ClassId.Threed, 0x150C, 0x40000000);
+            channel.Write(ClassId.Threed, 0x1668, 0x0);
+            channel.Write(ClassId.Threed, 0x318, 0x8);
+            channel.Write(ClassId.Threed, 0x31C, 0x8);
+            channel.Write(ClassId.Threed, 0xD9C, 0x1);
+            channel.Write(ClassId.Threed, 0xF14, 0x0);
+            channel.Write(ClassId.Threed, 0x374, 0x0);
+            channel.Write(ClassId.Threed, 0x378, 0xC);
+            channel.Write(ClassId.Threed, 0x7DC, 0x0);
+            channel.Write(ClassId.Threed, 0x74C, 0x55);
+            channel.Write(ClassId.Threed, 0x1420, 0x3);
+            channel.Write(ClassId.Threed, 0x1008, 0x8);
+            channel.Write(ClassId.Threed, 0x100C, 0x40);
+            channel.Write(ClassId.Threed, 0x1010, 0x12C);
+            channel.Write(ClassId.Threed, 0xD60, 0x40);
+            channel.Write(ClassId.Threed, 0x1018, 0x20);
+            channel.Write(ClassId.Threed, 0x101C, 0x1);
+            channel.Write(ClassId.Threed, 0x1020, 0x20);
+            channel.Write(ClassId.Threed, 0x1024, 0x1);
+            channel.Write(ClassId.Threed, 0x1444, 0x0);
+            channel.Write(ClassId.Threed, 0x1448, 0x0);
+            channel.Write(ClassId.Threed, 0x144C, 0x0);
+            channel.Write(ClassId.Threed, 0x360, 0x20164010);
+            channel.Write(ClassId.Threed, 0x364, 0x20);
+            channel.Write(ClassId.Threed, 0x368, 0x0);
+            channel.Write(ClassId.Threed, 0xDA8, 0x30);
+            channel.Write(ClassId.Threed, 0xDE4, 0x0);
+            channel.Write(ClassId.Threed, 0x204, 0x6);
+            channel.Write(ClassId.Threed, 0x2D0, 0x3FFFFF);
+            channel.Write(ClassId.Threed, 0x1220, 0x5);
+            channel.Write(ClassId.Threed, 0xFDC, 0x0);
+            channel.Write(ClassId.Threed, 0xF98, 0x400008);
+            channel.Write(ClassId.Threed, 0x1284, 0x8000080);
+            channel.Write(ClassId.Threed, 0x1450, 0x400008);
+            channel.Write(ClassId.Threed, 0x1454, 0x8000080);
+            channel.Write(ClassId.Threed, 0x214, 0x0);
+            channel.Write(ClassId.Twod, 0x200, 0xCF);
+            channel.Write(ClassId.Twod, 0x204, 0x1);
+            channel.Write(ClassId.Twod, 0x208, 0x20);
+            channel.Write(ClassId.Twod, 0x20C, 0x1);
+            channel.Write(ClassId.Twod, 0x210, 0x0);
+            channel.Write(ClassId.Twod, 0x214, 0x80);
+            channel.Write(ClassId.Twod, 0x218, 0x100);
+            channel.Write(ClassId.Twod, 0x21C, 0x100);
+            channel.Write(ClassId.Twod, 0x220, 0x0);
+            channel.Write(ClassId.Twod, 0x224, 0x0);
+            channel.Write(ClassId.Twod, 0x230, 0xCF);
+            channel.Write(ClassId.Twod, 0x234, 0x1);
+            channel.Write(ClassId.Twod, 0x238, 0x20);
+            channel.Write(ClassId.Twod, 0x23C, 0x1);
+            channel.Write(ClassId.Twod, 0x244, 0x80);
+            channel.Write(ClassId.Twod, 0x248, 0x100);
+            channel.Write(ClassId.Twod, 0x24C, 0x100);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs
index ebaab0c913..a85fd44cdc 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs
@@ -54,6 +54,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
             _host1xContext = GetHost1XContext(context.Device.Gpu, owner);
             Channel        = _device.Gpu.CreateChannel();
 
+            ChannelInitialization.InitializeState(Channel);
+
             ChannelSyncpoints = new uint[MaxModuleSyncpoint];
 
             _channelSyncpoint.Id = _device.System.HostSyncpoint.AllocateSyncpoint(false);