From 4da44e09cb2a32f69b4a6b47221117b78e4618dc Mon Sep 17 00:00:00 2001
From: Andrey Sukharev <SukharevAndrey@users.noreply.github.com>
Date: Mon, 5 Dec 2022 16:47:39 +0300
Subject: [PATCH] Make structs readonly when applicable (#4002)

* Make all structs readonly when applicable. It should reduce amount of needless defensive copies

* Make structs with trivial boilerplate equality code record structs

* Remove unnecessary readonly modifiers from TextureCreateInfo

* Make BitMap structs readonly too
---
 .editorconfig                                 |  1 +
 .../RegisterAllocators/AllocationResult.cs    |  2 +-
 .../RegisterAllocators/CopyResolver.cs        |  2 +-
 .../RegisterAllocators/HybridAllocator.cs     |  2 +-
 .../RegisterAllocators/RegisterMasks.cs       |  2 +-
 ARMeilleure/CodeGen/X86/IntrinsicInfo.cs      |  2 +-
 ARMeilleure/Decoders/InstDescriptor.cs        |  2 +-
 ARMeilleure/Decoders/OpCodeTable.cs           |  2 +-
 ARMeilleure/Diagnostics/Symbols.cs            |  2 +-
 .../PhiOperation.cs                           |  2 +-
 .../IntermediateRepresentation/Register.cs    |  2 +-
 ARMeilleure/Translation/Cache/CacheEntry.cs   |  2 +-
 .../Translation/Cache/CacheMemoryAllocator.cs |  2 +-
 ARMeilleure/Translation/CompilerContext.cs    |  2 +-
 ARMeilleure/Translation/RegisterUsage.cs      |  2 +-
 ARMeilleure/Translation/Translator.cs         |  2 +-
 .../Native/libsoundio/SoundIOChannelLayout.cs |  2 +-
 .../libsoundio/SoundIOSampleRateRange.cs      |  2 +-
 Ryujinx.Ava/Ui/Windows/IconColorPicker.cs     |  2 +-
 Ryujinx.Common/Logging/Logger.cs              |  2 +-
 Ryujinx.Common/Memory/SpanOrArray.cs          |  2 +-
 Ryujinx.Cpu/ExceptionCallbacks.cs             |  2 +-
 Ryujinx.Graphics.Device/RwCallback.cs         |  2 +-
 Ryujinx.Graphics.GAL/BlendDescriptor.cs       |  2 +-
 Ryujinx.Graphics.GAL/BufferAssignment.cs      |  2 +-
 Ryujinx.Graphics.GAL/BufferHandle.cs          | 12 +-----
 Ryujinx.Graphics.GAL/BufferRange.cs           |  2 +-
 Ryujinx.Graphics.GAL/Capabilities.cs          |  2 +-
 Ryujinx.Graphics.GAL/ColorF.cs                | 30 +--------------
 Ryujinx.Graphics.GAL/DepthTestDescriptor.cs   |  2 +-
 Ryujinx.Graphics.GAL/DeviceInfo.cs            |  2 +-
 Ryujinx.Graphics.GAL/Extents2D.cs             |  2 +-
 Ryujinx.Graphics.GAL/Extents2DF.cs            |  2 +-
 Ryujinx.Graphics.GAL/HardwareInfo.cs          |  2 +-
 Ryujinx.Graphics.GAL/ImageCrop.cs             |  2 +-
 Ryujinx.Graphics.GAL/MultisampleDescriptor.cs |  2 +-
 Ryujinx.Graphics.GAL/ProgramPipelineState.cs  |  2 +-
 Ryujinx.Graphics.GAL/Rectangle.cs             |  2 +-
 Ryujinx.Graphics.GAL/SamplerCreateInfo.cs     |  2 +-
 .../ScreenCaptureImageInfo.cs                 |  2 +-
 Ryujinx.Graphics.GAL/ShaderBindings.cs        |  2 +-
 Ryujinx.Graphics.GAL/ShaderSource.cs          |  2 +-
 Ryujinx.Graphics.GAL/StencilTestDescriptor.cs |  2 +-
 Ryujinx.Graphics.GAL/TextureCreateInfo.cs     | 20 +++++-----
 .../VertexAttribDescriptor.cs                 | 38 +------------------
 .../VertexBufferDescriptor.cs                 |  2 +-
 Ryujinx.Graphics.GAL/Viewport.cs              |  2 +-
 Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs   |  2 +-
 .../Engine/MME/MacroHLETable.cs               |  2 +-
 .../Engine/Threed/StateUpdateTracker.cs       |  2 +-
 Ryujinx.Graphics.Gpu/Image/FormatInfo.cs      |  2 +-
 .../Image/TextureBindingInfo.cs               |  2 +-
 Ryujinx.Graphics.Gpu/Image/TextureCache.cs    |  2 +-
 Ryujinx.Graphics.Gpu/Image/TextureGroup.cs    |  2 +-
 Ryujinx.Graphics.Gpu/Image/TextureInfo.cs     |  2 +-
 Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs   |  2 +-
 .../Memory/BufferTextureBinding.cs            |  2 +-
 Ryujinx.Graphics.Gpu/Memory/CounterCache.cs   |  2 +-
 .../DiskCache/BackgroundDiskCacheWriter.cs    |  4 +-
 .../Shader/DiskCache/GuestCodeAndCbData.cs    |  2 +-
 .../DiskCache/ParallelDiskCacheLoader.cs      |  6 +--
 .../Shader/GpuChannelComputeState.cs          |  2 +-
 .../Shader/GpuChannelPoolState.cs             |  2 +-
 .../Shader/HashTable/PartitionedHashTable.cs  |  2 +-
 Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs    |  6 +--
 .../Shader/ShaderCodeAccessor.cs              |  2 +-
 .../Shader/ShaderDumpPaths.cs                 |  2 +-
 .../Shader/ShaderSpecializationState.cs       | 17 +--------
 Ryujinx.Graphics.Gpu/Window.cs                |  2 +-
 Ryujinx.Graphics.Host1x/Host1xDevice.cs       |  2 +-
 Ryujinx.Graphics.Host1x/SyncptIncrManager.cs  |  2 +-
 .../FrameDecodedEventArgs.cs                  |  2 +-
 Ryujinx.Graphics.Nvdec/ResourceManager.cs     |  2 +-
 Ryujinx.Graphics.OpenGL/FormatInfo.cs         |  2 +-
 .../CodeGen/Glsl/Instructions/InstInfo.cs     |  2 +-
 .../CodeGen/Glsl/OperandManager.cs            |  2 +-
 .../CodeGen/Spirv/OperationResult.cs          |  2 +-
 .../CodeGen/Spirv/SpirvDelegates.cs           |  2 +-
 .../CodeGen/Spirv/TextureMeta.cs              | 33 +---------------
 Ryujinx.Graphics.Shader/Decoders/Block.cs     |  2 +-
 .../Decoders/DecodedProgram.cs                |  2 +-
 Ryujinx.Graphics.Shader/Decoders/Decoder.cs   |  2 +-
 Ryujinx.Graphics.Shader/Decoders/InstTable.cs |  2 +-
 Ryujinx.Graphics.Shader/Decoders/Register.cs  |  2 +-
 .../StructuredIr/InstructionInfo.cs           |  2 +-
 .../StructuredIr/StructuredProgramInfo.cs     |  2 +-
 .../Translation/AttributeInfo.cs              |  2 +-
 .../Translation/EmitterContext.cs             |  2 +-
 .../Translation/FunctionMatch.cs              |  4 +-
 .../Translation/RegisterUsage.cs              |  2 +-
 .../Translation/ShaderConfig.cs               | 31 +--------------
 .../Translation/ShaderHeader.cs               |  2 +-
 Ryujinx.Graphics.Shader/Translation/Ssa.cs    |  2 +-
 .../Translation/TranslationOptions.cs         |  2 +-
 .../Translation/Translator.cs                 |  2 +-
 Ryujinx.Graphics.Texture/Region.cs            |  2 +-
 Ryujinx.Graphics.Texture/Size.cs              |  2 +-
 Ryujinx.Graphics.Texture/SizeInfo.cs          |  2 +-
 Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs |  2 +-
 Ryujinx.Graphics.Vic/Image/Surface.cs         |  2 +-
 Ryujinx.Graphics.Vic/Rectangle.cs             |  2 +-
 Ryujinx.Graphics.Vic/ResourceManager.cs       |  2 +-
 Ryujinx.Graphics.Video/Plane.cs               | 38 +------------------
 Ryujinx.Graphics.Vulkan/BitMap.cs             |  2 +-
 Ryujinx.Graphics.Vulkan/BufferState.cs        |  2 +-
 Ryujinx.Graphics.Vulkan/CacheByRange.cs       |  4 +-
 .../CommandBufferScoped.cs                    |  2 +-
 Ryujinx.Graphics.Vulkan/DisposableBuffer.cs   |  2 +-
 .../DisposableBufferView.cs                   |  2 +-
 .../DisposableFramebuffer.cs                  |  2 +-
 Ryujinx.Graphics.Vulkan/DisposableImage.cs    |  2 +-
 .../DisposableImageView.cs                    |  2 +-
 Ryujinx.Graphics.Vulkan/DisposableMemory.cs   |  2 +-
 Ryujinx.Graphics.Vulkan/DisposablePipeline.cs |  2 +-
 .../DisposableRenderPass.cs                   |  2 +-
 Ryujinx.Graphics.Vulkan/DisposableSampler.cs  |  2 +-
 .../HardwareCapabilities.cs                   |  2 +-
 Ryujinx.Graphics.Vulkan/MemoryAllocation.cs   |  2 +-
 .../MemoryAllocatorBlockList.cs               |  2 +-
 Ryujinx.Graphics.Vulkan/StagingBuffer.cs      |  2 +-
 Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs   |  2 +-
 Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs  |  2 +-
 Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs  |  4 +-
 .../HOS/Kernel/Process/ProcessCreationInfo.cs |  2 +-
 Ryujinx.HLE/HOS/ModLoader.cs                  |  2 +-
 .../HOS/Services/Account/Acc/Types/UserId.cs  | 31 ++-------------
 .../NvHostAsGpu/NvHostAsGpuDeviceFile.cs      |  2 +-
 Ryujinx.HLE/HOS/Tamper/OperationBlock.cs      |  2 +-
 Ryujinx.HLE/Ui/ThemeColor.cs                  |  2 +-
 .../Kernel/SyscallGenerator.cs                |  4 +-
 Ryujinx.Memory/Range/MemoryRange.cs           | 21 +---------
 Ryujinx.Memory/Range/MultiRange.cs            |  2 +-
 Ryujinx.Memory/Tracking/BitMap.cs             |  2 +-
 133 files changed, 156 insertions(+), 378 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index 214e5469..9e00e3ba 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -89,6 +89,7 @@ csharp_style_conditional_delegate_call = true:suggestion
 # Modifier preferences
 csharp_prefer_static_local_function = true:suggestion
 csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
+csharp_style_prefer_readonly_struct = true
 
 # Code-block preferences
 csharp_prefer_braces = true:silent
diff --git a/ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs b/ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs
index 94ac6991..43e5c7e2 100644
--- a/ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs
+++ b/ARMeilleure/CodeGen/RegisterAllocators/AllocationResult.cs
@@ -1,6 +1,6 @@
 namespace ARMeilleure.CodeGen.RegisterAllocators
 {
-    struct AllocationResult
+    readonly struct AllocationResult
     {
         public int IntUsedRegisters { get; }
         public int VecUsedRegisters { get; }
diff --git a/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs b/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs
index df4b6db1..587b1a02 100644
--- a/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs
+++ b/ARMeilleure/CodeGen/RegisterAllocators/CopyResolver.cs
@@ -11,7 +11,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
     {
         private class ParallelCopy
         {
-            private struct Copy
+            private readonly struct Copy
             {
                 public Register Dest   { get; }
                 public Register Source { get; }
diff --git a/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs b/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs
index de084026..25952c77 100644
--- a/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs
+++ b/ARMeilleure/CodeGen/RegisterAllocators/HybridAllocator.cs
@@ -11,7 +11,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
 {
     class HybridAllocator : IRegisterAllocator
     {
-        private struct BlockInfo
+        private readonly struct BlockInfo
         {
             public bool HasCall { get; }
 
diff --git a/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs b/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs
index 9652224e..5b11aac2 100644
--- a/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs
+++ b/ARMeilleure/CodeGen/RegisterAllocators/RegisterMasks.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace ARMeilleure.CodeGen.RegisterAllocators
 {
-    struct RegisterMasks
+    readonly struct RegisterMasks
     {
         public int IntAvailableRegisters   { get; }
         public int VecAvailableRegisters   { get; }
diff --git a/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs b/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs
index b1af352b..302bf4d3 100644
--- a/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs
+++ b/ARMeilleure/CodeGen/X86/IntrinsicInfo.cs
@@ -1,6 +1,6 @@
 namespace ARMeilleure.CodeGen.X86
 {
-    struct IntrinsicInfo
+    readonly struct IntrinsicInfo
     {
         public X86Instruction Inst { get; }
         public IntrinsicType  Type { get; }
diff --git a/ARMeilleure/Decoders/InstDescriptor.cs b/ARMeilleure/Decoders/InstDescriptor.cs
index 29966d6d..577ff394 100644
--- a/ARMeilleure/Decoders/InstDescriptor.cs
+++ b/ARMeilleure/Decoders/InstDescriptor.cs
@@ -2,7 +2,7 @@ using ARMeilleure.Instructions;
 
 namespace ARMeilleure.Decoders
 {
-    struct InstDescriptor
+    readonly struct InstDescriptor
     {
         public static InstDescriptor Undefined => new InstDescriptor(InstName.Und, InstEmit.Und);
 
diff --git a/ARMeilleure/Decoders/OpCodeTable.cs b/ARMeilleure/Decoders/OpCodeTable.cs
index f44c1540..caa93099 100644
--- a/ARMeilleure/Decoders/OpCodeTable.cs
+++ b/ARMeilleure/Decoders/OpCodeTable.cs
@@ -11,7 +11,7 @@ namespace ARMeilleure.Decoders
 
         private const int FastLookupSize = 0x1000;
 
-        private struct InstInfo
+        private readonly struct InstInfo
         {
             public int Mask  { get; }
             public int Value { get; }
diff --git a/ARMeilleure/Diagnostics/Symbols.cs b/ARMeilleure/Diagnostics/Symbols.cs
index 17764f7e..6bde62f5 100644
--- a/ARMeilleure/Diagnostics/Symbols.cs
+++ b/ARMeilleure/Diagnostics/Symbols.cs
@@ -6,7 +6,7 @@ namespace ARMeilleure.Diagnostics
 {
     static class Symbols
     {
-        private struct RangedSymbol
+        private readonly struct RangedSymbol
         {
             public readonly ulong Start;
             public readonly ulong End;
diff --git a/ARMeilleure/IntermediateRepresentation/PhiOperation.cs b/ARMeilleure/IntermediateRepresentation/PhiOperation.cs
index f2430882..d2a3cf21 100644
--- a/ARMeilleure/IntermediateRepresentation/PhiOperation.cs
+++ b/ARMeilleure/IntermediateRepresentation/PhiOperation.cs
@@ -3,7 +3,7 @@ using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
 
 namespace ARMeilleure.IntermediateRepresentation
 {
-    struct PhiOperation
+    readonly struct PhiOperation
     {
         private readonly Operation _operation;
 
diff --git a/ARMeilleure/IntermediateRepresentation/Register.cs b/ARMeilleure/IntermediateRepresentation/Register.cs
index 745b3153..241e4d13 100644
--- a/ARMeilleure/IntermediateRepresentation/Register.cs
+++ b/ARMeilleure/IntermediateRepresentation/Register.cs
@@ -2,7 +2,7 @@ using System;
 
 namespace ARMeilleure.IntermediateRepresentation
 {
-    struct Register : IEquatable<Register>
+    readonly struct Register : IEquatable<Register>
     {
         public int Index { get; }
 
diff --git a/ARMeilleure/Translation/Cache/CacheEntry.cs b/ARMeilleure/Translation/Cache/CacheEntry.cs
index fce984c3..dc5503b1 100644
--- a/ARMeilleure/Translation/Cache/CacheEntry.cs
+++ b/ARMeilleure/Translation/Cache/CacheEntry.cs
@@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
 
 namespace ARMeilleure.Translation.Cache
 {
-    struct CacheEntry : IComparable<CacheEntry>
+    readonly struct CacheEntry : IComparable<CacheEntry>
     {
         public int Offset { get; }
         public int Size   { get; }
diff --git a/ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs b/ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs
index 3111e886..4c22de40 100644
--- a/ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs
+++ b/ARMeilleure/Translation/Cache/CacheMemoryAllocator.cs
@@ -6,7 +6,7 @@ namespace ARMeilleure.Translation.Cache
 {
     class CacheMemoryAllocator
     {
-        private struct MemoryBlock : IComparable<MemoryBlock>
+        private readonly struct MemoryBlock : IComparable<MemoryBlock>
         {
             public int Offset { get; }
             public int Size { get; }
diff --git a/ARMeilleure/Translation/CompilerContext.cs b/ARMeilleure/Translation/CompilerContext.cs
index cfe5ad1e..510dec58 100644
--- a/ARMeilleure/Translation/CompilerContext.cs
+++ b/ARMeilleure/Translation/CompilerContext.cs
@@ -2,7 +2,7 @@ using ARMeilleure.IntermediateRepresentation;
 
 namespace ARMeilleure.Translation
 {
-    struct CompilerContext
+    readonly struct CompilerContext
     {
         public ControlFlowGraph Cfg { get; }
 
diff --git a/ARMeilleure/Translation/RegisterUsage.cs b/ARMeilleure/Translation/RegisterUsage.cs
index 775fa3ab..3ec0a7b4 100644
--- a/ARMeilleure/Translation/RegisterUsage.cs
+++ b/ARMeilleure/Translation/RegisterUsage.cs
@@ -14,7 +14,7 @@ namespace ARMeilleure.Translation
         private const int RegsCount = 32;
         private const int RegsMask  = RegsCount - 1;
 
-        private struct RegisterMask : IEquatable<RegisterMask>
+        private readonly struct RegisterMask : IEquatable<RegisterMask>
         {
             public long IntMask => Mask.GetElement(0);
             public long VecMask => Mask.GetElement(1);
diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs
index c50b1c3d..2edbe401 100644
--- a/ARMeilleure/Translation/Translator.cs
+++ b/ARMeilleure/Translation/Translator.cs
@@ -293,7 +293,7 @@ namespace ARMeilleure.Translation
             }
         }
 
-        private struct Range
+        private readonly struct Range
         {
             public ulong Start { get; }
             public ulong End { get; }
diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelLayout.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelLayout.cs
index cff6114f..ea617d4b 100644
--- a/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelLayout.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOChannelLayout.cs
@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
 
 namespace SoundIOSharp
 {
-    public struct SoundIOChannelLayout
+    public readonly struct SoundIOChannelLayout
     {
         public static int BuiltInCount
         {
diff --git a/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOSampleRateRange.cs b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOSampleRateRange.cs
index 3cdf7513..daf7921b 100644
--- a/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOSampleRateRange.cs
+++ b/Ryujinx.Audio.Backends.SoundIo/Native/libsoundio/SoundIOSampleRateRange.cs
@@ -1,6 +1,6 @@
 namespace SoundIOSharp
 {
-    public struct SoundIOSampleRateRange
+    public readonly struct SoundIOSampleRateRange
     {
         internal SoundIOSampleRateRange(int min, int max)
         {
diff --git a/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs b/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs
index 7cf4c2ed..c0a2602f 100644
--- a/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs
+++ b/Ryujinx.Ava/Ui/Windows/IconColorPicker.cs
@@ -20,7 +20,7 @@ namespace Ryujinx.Ava.Ui.Windows
 
         private const int CutOffLuminosity = 64;
 
-        private struct PaletteColor
+        private readonly struct PaletteColor
         {
             public int Qck { get; }
             public byte R { get; }
diff --git a/Ryujinx.Common/Logging/Logger.cs b/Ryujinx.Common/Logging/Logger.cs
index 475e3628..c1abdba9 100644
--- a/Ryujinx.Common/Logging/Logger.cs
+++ b/Ryujinx.Common/Logging/Logger.cs
@@ -16,7 +16,7 @@ namespace Ryujinx.Common.Logging
 
         public static event EventHandler<LogEventArgs> Updated;
 
-        public struct Log
+        public readonly struct Log
         {
             internal readonly LogLevel Level;
 
diff --git a/Ryujinx.Common/Memory/SpanOrArray.cs b/Ryujinx.Common/Memory/SpanOrArray.cs
index c1f06655..a9798d27 100644
--- a/Ryujinx.Common/Memory/SpanOrArray.cs
+++ b/Ryujinx.Common/Memory/SpanOrArray.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.Common.Memory
     /// This is useful to keep the Array representation when possible to avoid copies.
     /// </summary>
     /// <typeparam name="T">Element Type</typeparam>
-    public ref struct SpanOrArray<T> where T : unmanaged
+    public readonly ref struct SpanOrArray<T> where T : unmanaged
     {
         public readonly T[] Array;
         public readonly ReadOnlySpan<T> Span;
diff --git a/Ryujinx.Cpu/ExceptionCallbacks.cs b/Ryujinx.Cpu/ExceptionCallbacks.cs
index 1485ca7d..d9293302 100644
--- a/Ryujinx.Cpu/ExceptionCallbacks.cs
+++ b/Ryujinx.Cpu/ExceptionCallbacks.cs
@@ -17,7 +17,7 @@ namespace Ryujinx.Cpu
     /// <summary>
     /// Stores handlers for the various CPU exceptions.
     /// </summary>
-    public struct ExceptionCallbacks
+    public readonly struct ExceptionCallbacks
     {
         /// <summary>
         /// Handler for CPU interrupts triggered using <see cref="IExecutionContext.RequestInterrupt"/>.
diff --git a/Ryujinx.Graphics.Device/RwCallback.cs b/Ryujinx.Graphics.Device/RwCallback.cs
index 6f1c8898..dc8499e9 100644
--- a/Ryujinx.Graphics.Device/RwCallback.cs
+++ b/Ryujinx.Graphics.Device/RwCallback.cs
@@ -2,7 +2,7 @@
 
 namespace Ryujinx.Graphics.Device
 {
-    public struct RwCallback
+    public readonly struct RwCallback
     {
         public Action<int> Write { get; }
         public Func<int> Read { get; }
diff --git a/Ryujinx.Graphics.GAL/BlendDescriptor.cs b/Ryujinx.Graphics.GAL/BlendDescriptor.cs
index cc1e17c7..83b8afe2 100644
--- a/Ryujinx.Graphics.GAL/BlendDescriptor.cs
+++ b/Ryujinx.Graphics.GAL/BlendDescriptor.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct BlendDescriptor
+    public readonly struct BlendDescriptor
     {
         public bool Enable { get; }
 
diff --git a/Ryujinx.Graphics.GAL/BufferAssignment.cs b/Ryujinx.Graphics.GAL/BufferAssignment.cs
index 9f0f56c5..d803d90b 100644
--- a/Ryujinx.Graphics.GAL/BufferAssignment.cs
+++ b/Ryujinx.Graphics.GAL/BufferAssignment.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct BufferAssignment
+    public readonly struct BufferAssignment
     {
         public readonly int Binding;
         public readonly BufferRange Range;
diff --git a/Ryujinx.Graphics.GAL/BufferHandle.cs b/Ryujinx.Graphics.GAL/BufferHandle.cs
index 49f83442..5ba50d19 100644
--- a/Ryujinx.Graphics.GAL/BufferHandle.cs
+++ b/Ryujinx.Graphics.GAL/BufferHandle.cs
@@ -1,22 +1,14 @@
-using System;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.InteropServices;
+using System.Runtime.InteropServices;
 
 namespace Ryujinx.Graphics.GAL
 {
     [StructLayout(LayoutKind.Sequential, Size = 8)]
-    public struct BufferHandle : IEquatable<BufferHandle>
+    public readonly record struct BufferHandle
     {
         private readonly ulong _value;
 
         public static BufferHandle Null => new BufferHandle(0);
 
         private BufferHandle(ulong value) => _value = value;
-
-        public override bool Equals(object obj) => obj is BufferHandle handle && Equals(handle);
-        public bool Equals([AllowNull] BufferHandle other) => other._value == _value;
-        public override int GetHashCode() => _value.GetHashCode();
-        public static bool operator ==(BufferHandle left, BufferHandle right) => left.Equals(right);
-        public static bool operator !=(BufferHandle left, BufferHandle right) => !(left == right);
     }
 }
diff --git a/Ryujinx.Graphics.GAL/BufferRange.cs b/Ryujinx.Graphics.GAL/BufferRange.cs
index 9a030c80..ad9ebee4 100644
--- a/Ryujinx.Graphics.GAL/BufferRange.cs
+++ b/Ryujinx.Graphics.GAL/BufferRange.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct BufferRange
+    public readonly struct BufferRange
     {
         private static readonly BufferRange _empty = new BufferRange(BufferHandle.Null, 0, 0);
 
diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs
index c4428f17..2d38eccc 100644
--- a/Ryujinx.Graphics.GAL/Capabilities.cs
+++ b/Ryujinx.Graphics.GAL/Capabilities.cs
@@ -2,7 +2,7 @@ using Ryujinx.Graphics.Shader.Translation;
 
 namespace Ryujinx.Graphics.GAL
 {
-    public struct Capabilities
+    public readonly struct Capabilities
     {
         public readonly TargetApi Api;
         public readonly string VendorName;
diff --git a/Ryujinx.Graphics.GAL/ColorF.cs b/Ryujinx.Graphics.GAL/ColorF.cs
index b3002f8c..235f4229 100644
--- a/Ryujinx.Graphics.GAL/ColorF.cs
+++ b/Ryujinx.Graphics.GAL/ColorF.cs
@@ -1,32 +1,4 @@
-using System;
-
 namespace Ryujinx.Graphics.GAL
 {
-    public struct ColorF : IEquatable<ColorF>
-    {
-        public float Red   { get; }
-        public float Green { get; }
-        public float Blue  { get; }
-        public float Alpha { get; }
-
-        public ColorF(float red, float green, float blue, float alpha)
-        {
-            Red   = red;
-            Green = green;
-            Blue  = blue;
-            Alpha = alpha;
-        }
-
-        public bool Equals(ColorF color) => Red   == color.Red &&
-                                            Green == color.Green &&
-                                            Blue  == color.Blue &&
-                                            Alpha == color.Alpha;
-
-        public override bool Equals(object obj) => (obj is ColorF color) && Equals(color);
-
-        public override int GetHashCode() => HashCode.Combine(Red, Green, Blue, Alpha);
-
-        public static bool operator ==(ColorF l, ColorF r) => l.Equals(r);
-        public static bool operator !=(ColorF l, ColorF r) => !l.Equals(r);
-    }
+    public readonly record struct ColorF(float Red, float Green, float Blue, float Alpha);
 }
diff --git a/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs b/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs
index c835e941..4c593392 100644
--- a/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs
+++ b/Ryujinx.Graphics.GAL/DepthTestDescriptor.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct DepthTestDescriptor
+    public readonly struct DepthTestDescriptor
     {
         public bool TestEnable  { get; }
         public bool WriteEnable { get; }
diff --git a/Ryujinx.Graphics.GAL/DeviceInfo.cs b/Ryujinx.Graphics.GAL/DeviceInfo.cs
index c525eb60..eb4a016f 100644
--- a/Ryujinx.Graphics.GAL/DeviceInfo.cs
+++ b/Ryujinx.Graphics.GAL/DeviceInfo.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct DeviceInfo
+    public readonly struct DeviceInfo
     {
         public readonly string Id;
         public readonly string Vendor;
diff --git a/Ryujinx.Graphics.GAL/Extents2D.cs b/Ryujinx.Graphics.GAL/Extents2D.cs
index 05b0ce63..bac44f83 100644
--- a/Ryujinx.Graphics.GAL/Extents2D.cs
+++ b/Ryujinx.Graphics.GAL/Extents2D.cs
@@ -2,7 +2,7 @@ using Ryujinx.Common;
 
 namespace Ryujinx.Graphics.GAL
 {
-    public struct Extents2D
+    public readonly struct Extents2D
     {
         public int X1 { get; }
         public int Y1 { get; }
diff --git a/Ryujinx.Graphics.GAL/Extents2DF.cs b/Ryujinx.Graphics.GAL/Extents2DF.cs
index 8fb263c4..43f0e25e 100644
--- a/Ryujinx.Graphics.GAL/Extents2DF.cs
+++ b/Ryujinx.Graphics.GAL/Extents2DF.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct Extents2DF
+    public readonly struct Extents2DF
     {
         public float X1 { get; }
         public float Y1 { get; }
diff --git a/Ryujinx.Graphics.GAL/HardwareInfo.cs b/Ryujinx.Graphics.GAL/HardwareInfo.cs
index 9baf1924..4dd6849b 100644
--- a/Ryujinx.Graphics.GAL/HardwareInfo.cs
+++ b/Ryujinx.Graphics.GAL/HardwareInfo.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct HardwareInfo
+    public readonly struct HardwareInfo
     {
         public string GpuVendor { get; }
         public string GpuModel { get; }
diff --git a/Ryujinx.Graphics.GAL/ImageCrop.cs b/Ryujinx.Graphics.GAL/ImageCrop.cs
index a7d571de..e8220974 100644
--- a/Ryujinx.Graphics.GAL/ImageCrop.cs
+++ b/Ryujinx.Graphics.GAL/ImageCrop.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct ImageCrop
+    public readonly struct ImageCrop
     {
         public int   Left         { get; }
         public int   Right        { get; }
diff --git a/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs b/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs
index 76e56987..a6fb65aa 100644
--- a/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs
+++ b/Ryujinx.Graphics.GAL/MultisampleDescriptor.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct MultisampleDescriptor
+    public readonly struct MultisampleDescriptor
     {
         public bool AlphaToCoverageEnable { get; }
         public bool AlphaToCoverageDitherEnable { get; }
diff --git a/Ryujinx.Graphics.GAL/ProgramPipelineState.cs b/Ryujinx.Graphics.GAL/ProgramPipelineState.cs
index 88728207..41afb34b 100644
--- a/Ryujinx.Graphics.GAL/ProgramPipelineState.cs
+++ b/Ryujinx.Graphics.GAL/ProgramPipelineState.cs
@@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.GAL
     /// <summary>
     /// Descriptor for a pipeline buffer binding.
     /// </summary>
-    public struct BufferPipelineDescriptor
+    public readonly struct BufferPipelineDescriptor
     {
         public bool Enable { get; }
         public int Stride { get; }
diff --git a/Ryujinx.Graphics.GAL/Rectangle.cs b/Ryujinx.Graphics.GAL/Rectangle.cs
index 1e207926..c8fa93d9 100644
--- a/Ryujinx.Graphics.GAL/Rectangle.cs
+++ b/Ryujinx.Graphics.GAL/Rectangle.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct Rectangle<T> where T : unmanaged
+    public readonly struct Rectangle<T> where T : unmanaged
     {
         public T X { get; }
         public T Y { get; }
diff --git a/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs b/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs
index fe711c3a..4c514671 100644
--- a/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs
+++ b/Ryujinx.Graphics.GAL/SamplerCreateInfo.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct SamplerCreateInfo
+    public readonly struct SamplerCreateInfo
     {
         public MinFilter MinFilter { get; }
         public MagFilter MagFilter { get; }
diff --git a/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs b/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs
index 227d64b6..129913ec 100644
--- a/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs
+++ b/Ryujinx.Graphics.GAL/ScreenCaptureImageInfo.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct ScreenCaptureImageInfo
+    public readonly struct ScreenCaptureImageInfo
     {
         public ScreenCaptureImageInfo(int width, int height, bool isBgra, byte[] data, bool flipX, bool flipY)
         {
diff --git a/Ryujinx.Graphics.GAL/ShaderBindings.cs b/Ryujinx.Graphics.GAL/ShaderBindings.cs
index ea8e1749..6ab29382 100644
--- a/Ryujinx.Graphics.GAL/ShaderBindings.cs
+++ b/Ryujinx.Graphics.GAL/ShaderBindings.cs
@@ -2,7 +2,7 @@
 
 namespace Ryujinx.Graphics.GAL
 {
-    public struct ShaderBindings
+    public readonly struct ShaderBindings
     {
         public IReadOnlyCollection<int> UniformBufferBindings { get; }
         public IReadOnlyCollection<int> StorageBufferBindings { get; }
diff --git a/Ryujinx.Graphics.GAL/ShaderSource.cs b/Ryujinx.Graphics.GAL/ShaderSource.cs
index c68ba80d..91d3a632 100644
--- a/Ryujinx.Graphics.GAL/ShaderSource.cs
+++ b/Ryujinx.Graphics.GAL/ShaderSource.cs
@@ -3,7 +3,7 @@ using Ryujinx.Graphics.Shader.Translation;
 
 namespace Ryujinx.Graphics.GAL
 {
-    public struct ShaderSource
+    public readonly struct ShaderSource
     {
         public string Code { get; }
         public byte[] BinaryCode { get; }
diff --git a/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs b/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs
index 8c9d1644..db46c957 100644
--- a/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs
+++ b/Ryujinx.Graphics.GAL/StencilTestDescriptor.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct StencilTestDescriptor
+    public readonly struct StencilTestDescriptor
     {
         public bool TestEnable { get; }
 
diff --git a/Ryujinx.Graphics.GAL/TextureCreateInfo.cs b/Ryujinx.Graphics.GAL/TextureCreateInfo.cs
index 3ccfb700..52b3b11f 100644
--- a/Ryujinx.Graphics.GAL/TextureCreateInfo.cs
+++ b/Ryujinx.Graphics.GAL/TextureCreateInfo.cs
@@ -4,7 +4,7 @@ using System.Numerics;
 
 namespace Ryujinx.Graphics.GAL
 {
-    public struct TextureCreateInfo : IEquatable<TextureCreateInfo>
+    public readonly struct TextureCreateInfo : IEquatable<TextureCreateInfo>
     {
         public int Width         { get; }
         public int Height        { get; }
@@ -62,42 +62,42 @@ namespace Ryujinx.Graphics.GAL
             SwizzleA         = swizzleA;
         }
 
-        public readonly int GetMipSize(int level)
+        public int GetMipSize(int level)
         {
             return GetMipStride(level) * GetLevelHeight(level) * GetLevelDepth(level);
         }
 
-        public readonly int GetMipSize2D(int level)
+        public int GetMipSize2D(int level)
         {
             return GetMipStride(level) * GetLevelHeight(level);
         }
 
-        public readonly int GetMipStride(int level)
+        public int GetMipStride(int level)
         {
             return BitUtils.AlignUp(GetLevelWidth(level) * BytesPerPixel, 4);
         }
 
-        private readonly int GetLevelWidth(int level)
+        private int GetLevelWidth(int level)
         {
             return BitUtils.DivRoundUp(GetLevelSize(Width, level), BlockWidth);
         }
 
-        private readonly int GetLevelHeight(int level)
+        private int GetLevelHeight(int level)
         {
             return BitUtils.DivRoundUp(GetLevelSize(Height, level), BlockHeight);
         }
 
-        private readonly int GetLevelDepth(int level)
+        private int GetLevelDepth(int level)
         {
             return Target == Target.Texture3D ? GetLevelSize(Depth, level) : GetLayers();
         }
 
-        public readonly int GetDepthOrLayers()
+        public int GetDepthOrLayers()
         {
             return Target == Target.Texture3D ? Depth : GetLayers();
         }
 
-        public readonly int GetLayers()
+        public int GetLayers()
         {
             if (Target == Target.Texture2DArray ||
                 Target == Target.Texture2DMultisampleArray ||
@@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.GAL
             return 1;
         }
 
-        public readonly int GetLevelsClamped()
+        public int GetLevelsClamped()
         {
             int maxSize = Width;
 
diff --git a/Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs b/Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs
index b3248b62..4f5ea6a6 100644
--- a/Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs
+++ b/Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs
@@ -1,40 +1,4 @@
-using System;
-
 namespace Ryujinx.Graphics.GAL
 {
-    public struct VertexAttribDescriptor : IEquatable<VertexAttribDescriptor>
-    {
-        public int BufferIndex { get; }
-        public int Offset      { get; }
-
-        public bool IsZero { get; }
-
-        public Format Format { get; }
-
-        public VertexAttribDescriptor(int bufferIndex, int offset, bool isZero, Format format)
-        {
-            BufferIndex = bufferIndex;
-            Offset      = offset;
-            IsZero      = isZero;
-            Format      = format;
-        }
-
-        public override bool Equals(object obj)
-        {
-            return obj is VertexAttribDescriptor other && Equals(other);
-        }
-
-        public bool Equals(VertexAttribDescriptor other)
-        {
-            return BufferIndex == other.BufferIndex &&
-                   Offset      == other.Offset &&
-                   IsZero      == other.IsZero &&
-                   Format      == other.Format;
-        }
-
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(BufferIndex, Offset, IsZero, Format);
-        }
-    }
+    public readonly record struct VertexAttribDescriptor(int BufferIndex, int Offset, bool IsZero, Format Format);
 }
diff --git a/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs b/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs
index bcd3b28f..15f0dff8 100644
--- a/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs
+++ b/Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct VertexBufferDescriptor
+    public readonly struct VertexBufferDescriptor
     {
         public BufferRange Buffer { get; }
 
diff --git a/Ryujinx.Graphics.GAL/Viewport.cs b/Ryujinx.Graphics.GAL/Viewport.cs
index 58135db2..94012c00 100644
--- a/Ryujinx.Graphics.GAL/Viewport.cs
+++ b/Ryujinx.Graphics.GAL/Viewport.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.GAL
 {
-    public struct Viewport
+    public readonly struct Viewport
     {
         public Rectangle<float> Region { get; }
 
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs b/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs
index 640687f0..117961db 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
     /// <summary>
     /// FIFO word.
     /// </summary>
-    struct FifoWord
+    readonly struct FifoWord
     {
         /// <summary>
         /// GPU virtual address where the word is located in memory.
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
index ab6f54ef..719e170f 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
         /// <summary>
         /// Macroo High-level implementation table entry.
         /// </summary>
-        struct TableEntry
+        readonly struct TableEntry
         {
             /// <summary>
             /// Name of the Macro function.
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
index 2af7a402..628eb46a 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
@@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
     /// <summary>
     /// State update callback entry, with the callback function and associated field names.
     /// </summary>
-    struct StateUpdateCallbackEntry
+    readonly struct StateUpdateCallbackEntry
     {
         /// <summary>
         /// Callback function, to be called if the register was written as the state needs to be updated.
diff --git a/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs b/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
index 62f41cbb..9ee649d2 100644
--- a/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
+++ b/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
@@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Gpu.Image
     /// <summary>
     /// Represents texture format information.
     /// </summary>
-    struct FormatInfo
+    readonly struct FormatInfo
     {
         /// <summary>
         /// A default, generic RGBA8 texture format.
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
index be94f310..febe508b 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Image
     /// Texture binding information.
     /// This is used for textures that needs to be accessed from shaders.
     /// </summary>
-    struct TextureBindingInfo
+    readonly struct TextureBindingInfo
     {
         /// <summary>
         /// Shader sampler target type.
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index a6bb5741..16bfc693 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Image
     /// </summary>
     class TextureCache : IDisposable
     {
-        private struct OverlapInfo
+        private readonly struct OverlapInfo
         {
             public TextureViewCompatibility Compatibility { get; }
             public int FirstLayer { get; }
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
index 9efd1802..ca54dc2f 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image
     /// <summary>
     /// An overlapping texture group with a given view compatibility.
     /// </summary>
-    struct TextureIncompatibleOverlap
+    readonly struct TextureIncompatibleOverlap
     {
         public readonly TextureGroup Group;
         public readonly TextureViewCompatibility Compatibility;
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
index 571f440e..65cc698b 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
@@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Gpu.Image
     /// <summary>
     /// Texture information.
     /// </summary>
-    struct TextureInfo
+    readonly struct TextureInfo
     {
         /// <summary>
         /// Address of the texture in GPU mapped memory.
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs b/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
index 5569b947..d513b7ad 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
@@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
     /// <summary>
     /// Memory range used for buffers.
     /// </summary>
-    struct BufferBounds
+    readonly struct BufferBounds
     {
         /// <summary>
         /// Region virtual address.
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs b/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs
index 2a140870..b7a0e726 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
     /// <summary>
     /// A buffer binding to apply to a buffer texture.
     /// </summary>
-    struct BufferTextureBinding
+    readonly struct BufferTextureBinding
     {
         /// <summary>
         /// Shader stage accessing the texture.
diff --git a/Ryujinx.Graphics.Gpu/Memory/CounterCache.cs b/Ryujinx.Graphics.Gpu/Memory/CounterCache.cs
index 90b9187b..e763a899 100644
--- a/Ryujinx.Graphics.Gpu/Memory/CounterCache.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/CounterCache.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
     /// </summary>
     class CounterCache
     {
-        private struct CounterEntry
+        private readonly struct CounterEntry
         {
             public ulong Address { get; }
             public ICounterEvent Event { get; }
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs
index 98655ed6..568fe968 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs
@@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
         /// <summary>
         /// Represents an operation to perform on the <see cref="_fileWriterWorkerQueue"/>.
         /// </summary>
-        private struct CacheFileOperationTask
+        private readonly struct CacheFileOperationTask
         {
             /// <summary>
             /// The type of operation to perform.
@@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
         /// <summary>
         /// Background shader cache write information.
         /// </summary>
-        private struct AddShaderData
+        private readonly struct AddShaderData
         {
             /// <summary>
             /// Cached shader program.
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs
index 0096533d..959d6e18 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/GuestCodeAndCbData.cs
@@ -3,7 +3,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
     /// <summary>
     /// Guest shader code and constant buffer data accessed by the shader.
     /// </summary>
-    struct GuestCodeAndCbData
+    readonly struct GuestCodeAndCbData
     {
         /// <summary>
         /// Maxwell binary shader code.
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs
index 9261cb0d..722e66b3 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ParallelDiskCacheLoader.cs
@@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
         /// <summary>
         /// Program validation entry.
         /// </summary>
-        private struct ProgramEntry
+        private readonly struct ProgramEntry
         {
             /// <summary>
             /// Cached shader program.
@@ -90,7 +90,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
         /// <summary>
         /// Translated shader compilation entry.
         /// </summary>
-        private struct ProgramCompilation
+        private readonly struct ProgramCompilation
         {
             /// <summary>
             /// Translated shader stages.
@@ -143,7 +143,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
         /// <summary>
         /// Program translation entry.
         /// </summary>
-        private struct AsyncProgramTranslation
+        private readonly struct AsyncProgramTranslation
         {
             /// <summary>
             /// Guest code for each active stage.
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs
index 356d3f3e..b65dd75e 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelComputeState.cs
@@ -3,7 +3,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
     /// <summary>
     /// State used by the <see cref="GpuAccessor"/>.
     /// </summary>
-    struct GpuChannelComputeState
+    readonly struct GpuChannelComputeState
     {
         // New fields should be added to the end of the struct to keep disk shader cache compatibility.
 
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs
index b894c57e..1e34c5de 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuChannelPoolState.cs
@@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
     /// <summary>
     /// State used by the <see cref="GpuAccessor"/>.
     /// </summary>
-    struct GpuChannelPoolState : IEquatable<GpuChannelPoolState>
+    readonly struct GpuChannelPoolState : IEquatable<GpuChannelPoolState>
     {
         /// <summary>
         /// GPU virtual address of the texture pool.
diff --git a/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs b/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs
index f26fbdbb..e9a4f654 100644
--- a/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/HashTable/PartitionedHashTable.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.HashTable
         /// <summary>
         /// Entry for a given data size.
         /// </summary>
-        private struct SizeEntry
+        private readonly struct SizeEntry
         {
             /// <summary>
             /// Size for the data that will be stored on the hash table on this entry.
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index 23b213b4..5c045d9b 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
         /// </summary>
         public const TranslationFlags DefaultFlags = TranslationFlags.DebugMode;
 
-        private struct TranslatedShader
+        private readonly struct TranslatedShader
         {
             public readonly CachedShaderStage Shader;
             public readonly ShaderProgram Program;
@@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
             }
         }
 
-        private struct TranslatedShaderVertexPair
+        private readonly struct TranslatedShaderVertexPair
         {
             public readonly CachedShaderStage VertexA;
             public readonly CachedShaderStage VertexB;
@@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
         private readonly Dictionary<ulong, CachedShaderProgram> _cpPrograms;
         private readonly Dictionary<ShaderAddresses, CachedShaderProgram> _gpPrograms;
 
-        private struct ProgramToSave
+        private readonly struct ProgramToSave
         {
             public readonly CachedShaderProgram CachedProgram;
             public readonly IProgram HostProgram;
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs
index dbb33d22..e896493c 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCodeAccessor.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
     /// <summary>
     /// Shader code accessor.
     /// </summary>
-    struct ShaderCodeAccessor : IDataAccessor
+    readonly struct ShaderCodeAccessor : IDataAccessor
     {
         private readonly MemoryManager _memoryManager;
         private readonly ulong _baseAddress;
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs
index f96a8ce1..6ca7daef 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderDumpPaths.cs
@@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
     /// <summary>
     /// Paths where shader code was dumped on disk.
     /// </summary>
-    struct ShaderDumpPaths
+    readonly struct ShaderDumpPaths
     {
         /// <summary>
         /// Path where the full shader code with header was dumped, or null if not dumped.
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
index 14f64bbf..872aaf67 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs
@@ -121,7 +121,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
         /// <summary>
         /// Texture binding information, used to identify each texture accessed by the shader.
         /// </summary>
-        private struct TextureKey : IEquatable<TextureKey>
+        private readonly record struct TextureKey
         {
             // New fields should be added to the end of the struct to keep disk shader cache compatibility.
 
@@ -152,21 +152,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
                 Handle = handle;
                 CbufSlot = cbufSlot;
             }
-
-            public override bool Equals(object obj)
-            {
-                return obj is TextureKey textureKey && Equals(textureKey);
-            }
-
-            public bool Equals(TextureKey other)
-            {
-                return StageIndex == other.StageIndex && Handle == other.Handle && CbufSlot == other.CbufSlot;
-            }
-
-            public override int GetHashCode()
-            {
-                return HashCode.Combine(StageIndex, Handle, CbufSlot);
-            }
         }
 
         private readonly Dictionary<TextureKey, Box<TextureSpecializationState>> _textureSpecialization;
diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/Ryujinx.Graphics.Gpu/Window.cs
index 18320c74..c116d946 100644
--- a/Ryujinx.Graphics.Gpu/Window.cs
+++ b/Ryujinx.Graphics.Gpu/Window.cs
@@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Gpu
         /// <summary>
         /// Texture presented on the window.
         /// </summary>
-        private struct PresentationTexture
+        private readonly struct PresentationTexture
         {
             /// <summary>
             /// Texture cache where the texture might be located.
diff --git a/Ryujinx.Graphics.Host1x/Host1xDevice.cs b/Ryujinx.Graphics.Host1x/Host1xDevice.cs
index 61408fc4..90dd4fa0 100644
--- a/Ryujinx.Graphics.Host1x/Host1xDevice.cs
+++ b/Ryujinx.Graphics.Host1x/Host1xDevice.cs
@@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Host1x
 {
     public sealed class Host1xDevice : IDisposable
     {
-        private struct Command
+        private readonly struct Command
         {
             public int[] Buffer { get; }
             public long ContextId { get; }
diff --git a/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs b/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs
index 82ac5e7d..62c49917 100644
--- a/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs
+++ b/Ryujinx.Graphics.Host1x/SyncptIncrManager.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Host1x
     {
         private readonly SynchronizationManager _syncMgr;
 
-        private struct SyncptIncr
+        private readonly struct SyncptIncr
         {
             public uint Id { get; }
             public ClassId ClassId { get; }
diff --git a/Ryujinx.Graphics.Nvdec/FrameDecodedEventArgs.cs b/Ryujinx.Graphics.Nvdec/FrameDecodedEventArgs.cs
index f5074f48..4ee29d9d 100644
--- a/Ryujinx.Graphics.Nvdec/FrameDecodedEventArgs.cs
+++ b/Ryujinx.Graphics.Nvdec/FrameDecodedEventArgs.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.Nvdec
 {
-    public struct FrameDecodedEventArgs
+    public readonly struct FrameDecodedEventArgs
     {
         public CodecId CodecId { get; }
         public uint LumaOffset { get; }
diff --git a/Ryujinx.Graphics.Nvdec/ResourceManager.cs b/Ryujinx.Graphics.Nvdec/ResourceManager.cs
index 6e0d9ab2..08d24258 100644
--- a/Ryujinx.Graphics.Nvdec/ResourceManager.cs
+++ b/Ryujinx.Graphics.Nvdec/ResourceManager.cs
@@ -3,7 +3,7 @@ using Ryujinx.Graphics.Nvdec.Image;
 
 namespace Ryujinx.Graphics.Nvdec
 {
-    struct ResourceManager
+    readonly struct ResourceManager
     {
         public MemoryManager Gmm { get; }
         public SurfaceCache Cache { get; }
diff --git a/Ryujinx.Graphics.OpenGL/FormatInfo.cs b/Ryujinx.Graphics.OpenGL/FormatInfo.cs
index 6aa70691..aef4dbad 100644
--- a/Ryujinx.Graphics.OpenGL/FormatInfo.cs
+++ b/Ryujinx.Graphics.OpenGL/FormatInfo.cs
@@ -2,7 +2,7 @@ using OpenTK.Graphics.OpenGL;
 
 namespace Ryujinx.Graphics.OpenGL
 {
-    struct FormatInfo
+    readonly struct FormatInfo
     {
         public int  Components { get; }
         public bool Normalized { get; }
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs
index fc9aef7e..7b2a6b46 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
 {
-    struct InstInfo
+    readonly struct InstInfo
     {
         public InstType Type { get; }
 
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
index b7891426..ccc87a7f 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
@@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
     {
         private static readonly string[] StagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" };
 
-        private struct BuiltInAttribute
+        private readonly struct BuiltInAttribute
         {
             public string Name { get; }
 
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs
index f432f1c4..f80c8110 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/OperationResult.cs
@@ -3,7 +3,7 @@ using Spv.Generator;
 
 namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 {
-    struct OperationResult
+    readonly struct OperationResult
     {
         public static OperationResult Invalid => new OperationResult(AggregateType.Invalid, null);
 
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs
index fa0341ee..04c3be1b 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvDelegates.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
     /// <summary>
     /// Delegate cache for SPIR-V instruction generators. Avoids delegate allocation when passing generators as arguments.
     /// </summary>
-    internal struct SpirvDelegates
+    internal readonly struct SpirvDelegates
     {
         // Unary
         public readonly FuncUnaryInstruction GlslFAbs;
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs
index 686259ad..4de05603 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/TextureMeta.cs
@@ -1,33 +1,4 @@
-using System;
-
-namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
+namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 {
-    struct TextureMeta : IEquatable<TextureMeta>
-    {
-        public int CbufSlot { get; }
-        public int Handle { get; }
-        public TextureFormat Format { get; }
-
-        public TextureMeta(int cbufSlot, int handle, TextureFormat format)
-        {
-            CbufSlot = cbufSlot;
-            Handle = handle;
-            Format = format;
-        }
-
-        public override bool Equals(object obj)
-        {
-            return obj is TextureMeta other && Equals(other);
-        }
-
-        public bool Equals(TextureMeta other)
-        {
-            return CbufSlot == other.CbufSlot && Handle == other.Handle && Format == other.Format;
-        }
-
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(CbufSlot, Handle, Format);
-        }
-    }
+    readonly record struct TextureMeta(int CbufSlot, int Handle, TextureFormat Format);
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Decoders/Block.cs b/Ryujinx.Graphics.Shader/Decoders/Block.cs
index ddd81cc5..7d94e3f9 100644
--- a/Ryujinx.Graphics.Shader/Decoders/Block.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/Block.cs
@@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
         }
     }
 
-    struct SyncTarget
+    readonly struct SyncTarget
     {
         public PushOpInfo PushOpInfo { get; }
         public int PushOpId { get; }
diff --git a/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs b/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs
index 80de41d7..2dd60155 100644
--- a/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/DecodedProgram.cs
@@ -4,7 +4,7 @@ using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Shader.Decoders
 {
-    struct DecodedProgram : IEnumerable<DecodedFunction>
+    readonly struct DecodedProgram : IEnumerable<DecodedFunction>
     {
         public DecodedFunction MainFunction { get; }
         private readonly IReadOnlyDictionary<ulong, DecodedFunction> _functions;
diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
index 9dafb089..380c425e 100644
--- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
@@ -473,7 +473,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
             op = Unsafe.As<ulong, T>(ref rawOp);
         }
 
-        private struct BlockLocation
+        private readonly struct BlockLocation
         {
             public Block Block { get; }
             public int Index { get; }
diff --git a/Ryujinx.Graphics.Shader/Decoders/InstTable.cs b/Ryujinx.Graphics.Shader/Decoders/InstTable.cs
index 0a64632e..911f1581 100644
--- a/Ryujinx.Graphics.Shader/Decoders/InstTable.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/InstTable.cs
@@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
     {
         private const int EncodingBits = 14;
 
-        private struct TableEntry
+        private readonly struct TableEntry
         {
             public InstName Name { get; }
             public InstEmitter Emitter { get; }
diff --git a/Ryujinx.Graphics.Shader/Decoders/Register.cs b/Ryujinx.Graphics.Shader/Decoders/Register.cs
index 30840d8c..e375096d 100644
--- a/Ryujinx.Graphics.Shader/Decoders/Register.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/Register.cs
@@ -2,7 +2,7 @@ using System;
 
 namespace Ryujinx.Graphics.Shader.Decoders
 {
-    struct Register : IEquatable<Register>
+    readonly struct Register : IEquatable<Register>
     {
         public int Index { get; }
 
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
index 799aec24..aea36423 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
@@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
 {
     static class InstructionInfo
     {
-        private struct InstInfo
+        private readonly struct InstInfo
         {
             public VariableType DestType { get; }
 
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
index 57253148..489a5910 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
@@ -2,7 +2,7 @@ using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Shader.StructuredIr
 {
-    struct TransformFeedbackOutput
+    readonly struct TransformFeedbackOutput
     {
         public readonly bool Valid;
         public readonly int Buffer;
diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
index 839edbe9..1647f656 100644
--- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
+++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
@@ -2,7 +2,7 @@
 
 namespace Ryujinx.Graphics.Shader.Translation
 {
-    struct AttributeInfo
+    readonly struct AttributeInfo
     {
         private static readonly Dictionary<int, AttributeInfo> _builtInAttributes = new Dictionary<int, AttributeInfo>()
         {
diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
index ef5d7b96..7961ada8 100644
--- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
+++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
@@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Shader.Translation
 
         public int OperationsCount => _operations.Count;
 
-        private struct BrxTarget
+        private readonly struct BrxTarget
         {
             public readonly Operand Selector;
             public readonly int ExpectedValue;
diff --git a/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs b/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs
index 1c5d8c54..073e120a 100644
--- a/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs
+++ b/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs
@@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Shader.Translation
             }
         }
 
-        private struct TreeNodeUse
+        private readonly struct TreeNodeUse
         {
             public TreeNode Node { get; }
             public int Index { get; }
@@ -345,7 +345,7 @@ namespace Ryujinx.Graphics.Shader.Translation
             bool Matches(in InstOp opInfo);
         }
 
-        private struct PatternTreeNodeUse
+        private readonly struct PatternTreeNodeUse
         {
             public IPatternTreeNode Node { get; }
             public int Index { get; }
diff --git a/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs b/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs
index 158ba5ef..9e31831d 100644
--- a/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs
+++ b/Ryujinx.Graphics.Shader/Translation/RegisterUsage.cs
@@ -114,7 +114,7 @@ namespace Ryujinx.Graphics.Shader.Translation
             }
         }
 
-        public struct FunctionRegisterUsage
+        public readonly struct FunctionRegisterUsage
         {
             public Register[] InArguments { get; }
             public Register[] OutArguments { get; }
diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
index ae4107e8..12cd4cd1 100644
--- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
+++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
@@ -69,36 +69,7 @@ namespace Ryujinx.Graphics.Shader.Translation
         private int _usedStorageBuffers;
         private int _usedStorageBuffersWrite;
 
-        private struct TextureInfo : IEquatable<TextureInfo>
-        {
-            public int CbufSlot { get; }
-            public int Handle { get; }
-            public bool Indexed { get; }
-            public TextureFormat Format { get; }
-
-            public TextureInfo(int cbufSlot, int handle, bool indexed, TextureFormat format)
-            {
-                CbufSlot = cbufSlot;
-                Handle = handle;
-                Indexed = indexed;
-                Format = format;
-            }
-
-            public override bool Equals(object obj)
-            {
-                return obj is TextureInfo other && Equals(other);
-            }
-
-            public bool Equals(TextureInfo other)
-            {
-                return CbufSlot == other.CbufSlot && Handle == other.Handle && Indexed == other.Indexed && Format == other.Format;
-            }
-
-            public override int GetHashCode()
-            {
-                return HashCode.Combine(CbufSlot, Handle, Indexed, Format);
-            }
-        }
+        private readonly record struct TextureInfo(int CbufSlot, int Handle, bool Indexed, TextureFormat Format);
 
         private struct TextureMeta
         {
diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs b/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs
index e53c77af..b643262f 100644
--- a/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs
+++ b/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs
@@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Shader.Translation
         ScreenLinear = 3
     }
 
-    struct ImapPixelType
+    readonly struct ImapPixelType
     {
         public PixelImap X { get; }
         public PixelImap Y { get; }
diff --git a/Ryujinx.Graphics.Shader/Translation/Ssa.cs b/Ryujinx.Graphics.Shader/Translation/Ssa.cs
index 8c63d72d..16b8b924 100644
--- a/Ryujinx.Graphics.Shader/Translation/Ssa.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Ssa.cs
@@ -108,7 +108,7 @@ namespace Ryujinx.Graphics.Shader.Translation
             }
         }
 
-        private struct Definition
+        private readonly struct Definition
         {
             public BasicBlock Block { get; }
             public Operand    Local { get; }
diff --git a/Ryujinx.Graphics.Shader/Translation/TranslationOptions.cs b/Ryujinx.Graphics.Shader/Translation/TranslationOptions.cs
index 532e9abb..d9829ac4 100644
--- a/Ryujinx.Graphics.Shader/Translation/TranslationOptions.cs
+++ b/Ryujinx.Graphics.Shader/Translation/TranslationOptions.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.Shader.Translation
 {
-    public struct TranslationOptions
+    public readonly struct TranslationOptions
     {
         public TargetLanguage TargetLanguage { get; }
         public TargetApi TargetApi { get; }
diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs
index f8795c0f..3fb586cb 100644
--- a/Ryujinx.Graphics.Shader/Translation/Translator.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation
     {
         private const int HeaderSize = 0x50;
 
-        internal struct FunctionCode
+        internal readonly struct FunctionCode
         {
             public Operation[] Code { get; }
 
diff --git a/Ryujinx.Graphics.Texture/Region.cs b/Ryujinx.Graphics.Texture/Region.cs
index a60951e3..e59888a0 100644
--- a/Ryujinx.Graphics.Texture/Region.cs
+++ b/Ryujinx.Graphics.Texture/Region.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.Texture
 {
-    public struct Region
+    public readonly struct Region
     {
         public int Offset { get; }
         public int Size { get; }
diff --git a/Ryujinx.Graphics.Texture/Size.cs b/Ryujinx.Graphics.Texture/Size.cs
index 35611b98..21c45b38 100644
--- a/Ryujinx.Graphics.Texture/Size.cs
+++ b/Ryujinx.Graphics.Texture/Size.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.Texture
 {
-    public struct Size
+    public readonly struct Size
     {
         public int Width  { get; }
         public int Height { get; }
diff --git a/Ryujinx.Graphics.Texture/SizeInfo.cs b/Ryujinx.Graphics.Texture/SizeInfo.cs
index ed5379b3..eb573728 100644
--- a/Ryujinx.Graphics.Texture/SizeInfo.cs
+++ b/Ryujinx.Graphics.Texture/SizeInfo.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Texture
 {
-    public struct SizeInfo
+    public readonly struct SizeInfo
     {
         private readonly int[] _mipOffsets;
 
diff --git a/Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs b/Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs
index 749324bf..687df22c 100644
--- a/Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs
+++ b/Ryujinx.Graphics.Texture/Utils/BC7ModeInfo.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.Texture.Utils
 {
-    struct BC7ModeInfo
+    readonly struct BC7ModeInfo
     {
         public readonly int SubsetCount;
         public readonly int PartitionBitCount;
diff --git a/Ryujinx.Graphics.Vic/Image/Surface.cs b/Ryujinx.Graphics.Vic/Image/Surface.cs
index 03767f8a..f393eb15 100644
--- a/Ryujinx.Graphics.Vic/Image/Surface.cs
+++ b/Ryujinx.Graphics.Vic/Image/Surface.cs
@@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
 
 namespace Ryujinx.Graphics.Vic.Image
 {
-    struct Surface : IDisposable
+    readonly struct Surface : IDisposable
     {
         private readonly int _bufferIndex;
 
diff --git a/Ryujinx.Graphics.Vic/Rectangle.cs b/Ryujinx.Graphics.Vic/Rectangle.cs
index 2a13b95c..8a8dd63a 100644
--- a/Ryujinx.Graphics.Vic/Rectangle.cs
+++ b/Ryujinx.Graphics.Vic/Rectangle.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.Vic
 {
-    struct Rectangle
+    readonly struct Rectangle
     {
         public readonly int X;
         public readonly int Y;
diff --git a/Ryujinx.Graphics.Vic/ResourceManager.cs b/Ryujinx.Graphics.Vic/ResourceManager.cs
index 036b30b6..7c3f507e 100644
--- a/Ryujinx.Graphics.Vic/ResourceManager.cs
+++ b/Ryujinx.Graphics.Vic/ResourceManager.cs
@@ -3,7 +3,7 @@ using Ryujinx.Graphics.Vic.Image;
 
 namespace Ryujinx.Graphics.Vic
 {
-    struct ResourceManager
+    readonly struct ResourceManager
     {
         public MemoryManager Gmm { get; }
         public BufferPool<Pixel> SurfacePool { get; }
diff --git a/Ryujinx.Graphics.Video/Plane.cs b/Ryujinx.Graphics.Video/Plane.cs
index c0aca59c..1a2ad251 100644
--- a/Ryujinx.Graphics.Video/Plane.cs
+++ b/Ryujinx.Graphics.Video/Plane.cs
@@ -1,42 +1,6 @@
 using System;
-using System.Diagnostics.CodeAnalysis;
 
 namespace Ryujinx.Graphics.Video
 {
-    public struct Plane : IEquatable<Plane>
-    {
-        public IntPtr Pointer { get; }
-        public int Length { get; }
-
-        public Plane(IntPtr pointer, int length)
-        {
-            Pointer = pointer;
-            Length = length;
-        }
-
-        public override bool Equals(object obj)
-        {
-            return obj is Plane other && Equals(other);
-        }
-
-        public bool Equals([AllowNull] Plane other)
-        {
-            return Pointer == other.Pointer && Length == other.Length;
-        }
-
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(Pointer, Length);
-        }
-
-        public static bool operator ==(Plane left, Plane right)
-        {
-            return left.Equals(right);
-        }
-
-        public static bool operator !=(Plane left, Plane right)
-        {
-            return !(left == right);
-        }
-    }
+    public readonly record struct Plane(IntPtr Pointer, int Length);
 }
diff --git a/Ryujinx.Graphics.Vulkan/BitMap.cs b/Ryujinx.Graphics.Vulkan/BitMap.cs
index ee3c3c93..efa71fc7 100644
--- a/Ryujinx.Graphics.Vulkan/BitMap.cs
+++ b/Ryujinx.Graphics.Vulkan/BitMap.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct BitMap
+    readonly struct BitMap
     {
         public const int IntSize = 64;
 
diff --git a/Ryujinx.Graphics.Vulkan/BufferState.cs b/Ryujinx.Graphics.Vulkan/BufferState.cs
index 88858e86..f3a58469 100644
--- a/Ryujinx.Graphics.Vulkan/BufferState.cs
+++ b/Ryujinx.Graphics.Vulkan/BufferState.cs
@@ -2,7 +2,7 @@
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct BufferState : IDisposable
+    readonly struct BufferState : IDisposable
     {
         public static BufferState Null => new BufferState(null, 0, 0);
 
diff --git a/Ryujinx.Graphics.Vulkan/CacheByRange.cs b/Ryujinx.Graphics.Vulkan/CacheByRange.cs
index afd7140e..a9d1b0ef 100644
--- a/Ryujinx.Graphics.Vulkan/CacheByRange.cs
+++ b/Ryujinx.Graphics.Vulkan/CacheByRange.cs
@@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Vulkan
         }
     }
 
-    struct TopologyConversionIndirectCacheKey : ICacheKey
+    readonly struct TopologyConversionIndirectCacheKey : ICacheKey
     {
         private readonly TopologyConversionCacheKey _baseKey;
         private readonly BufferHolder _indirectDataBuffer;
@@ -178,7 +178,7 @@ namespace Ryujinx.Graphics.Vulkan
         }
     }
 
-    struct Dependency
+    readonly struct Dependency
     {
         private readonly BufferHolder _buffer;
         private readonly int _offset;
diff --git a/Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs b/Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs
index 372950a8..1d9e14bb 100644
--- a/Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs
+++ b/Ryujinx.Graphics.Vulkan/CommandBufferScoped.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct CommandBufferScoped : IDisposable
+    readonly struct CommandBufferScoped : IDisposable
     {
         private readonly CommandBufferPool _pool;
         public CommandBuffer CommandBuffer { get; }
diff --git a/Ryujinx.Graphics.Vulkan/DisposableBuffer.cs b/Ryujinx.Graphics.Vulkan/DisposableBuffer.cs
index 6d227ca2..0f474f97 100644
--- a/Ryujinx.Graphics.Vulkan/DisposableBuffer.cs
+++ b/Ryujinx.Graphics.Vulkan/DisposableBuffer.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct DisposableBuffer : IDisposable
+    readonly struct DisposableBuffer : IDisposable
     {
         private readonly Vk _api;
         private readonly Device _device;
diff --git a/Ryujinx.Graphics.Vulkan/DisposableBufferView.cs b/Ryujinx.Graphics.Vulkan/DisposableBufferView.cs
index 7d3fe6ee..28ddd7dd 100644
--- a/Ryujinx.Graphics.Vulkan/DisposableBufferView.cs
+++ b/Ryujinx.Graphics.Vulkan/DisposableBufferView.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct DisposableBufferView : System.IDisposable
+    readonly struct DisposableBufferView : System.IDisposable
     {
         private readonly Vk _api;
         private readonly Device _device;
diff --git a/Ryujinx.Graphics.Vulkan/DisposableFramebuffer.cs b/Ryujinx.Graphics.Vulkan/DisposableFramebuffer.cs
index 5f219a4a..5b195354 100644
--- a/Ryujinx.Graphics.Vulkan/DisposableFramebuffer.cs
+++ b/Ryujinx.Graphics.Vulkan/DisposableFramebuffer.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct DisposableFramebuffer : IDisposable
+    readonly struct DisposableFramebuffer : IDisposable
     {
         private readonly Vk _api;
         private readonly Device _device;
diff --git a/Ryujinx.Graphics.Vulkan/DisposableImage.cs b/Ryujinx.Graphics.Vulkan/DisposableImage.cs
index 4e9b3bd4..c76091b7 100644
--- a/Ryujinx.Graphics.Vulkan/DisposableImage.cs
+++ b/Ryujinx.Graphics.Vulkan/DisposableImage.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct DisposableImage : IDisposable
+    readonly struct DisposableImage : IDisposable
     {
         private readonly Vk _api;
         private readonly Device _device;
diff --git a/Ryujinx.Graphics.Vulkan/DisposableImageView.cs b/Ryujinx.Graphics.Vulkan/DisposableImageView.cs
index 3509858e..3b3bf6ad 100644
--- a/Ryujinx.Graphics.Vulkan/DisposableImageView.cs
+++ b/Ryujinx.Graphics.Vulkan/DisposableImageView.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct DisposableImageView : IDisposable
+    readonly struct DisposableImageView : IDisposable
     {
         private readonly Vk _api;
         private readonly Device _device;
diff --git a/Ryujinx.Graphics.Vulkan/DisposableMemory.cs b/Ryujinx.Graphics.Vulkan/DisposableMemory.cs
index e0b5f099..638989ac 100644
--- a/Ryujinx.Graphics.Vulkan/DisposableMemory.cs
+++ b/Ryujinx.Graphics.Vulkan/DisposableMemory.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct DisposableMemory : IDisposable
+    readonly struct DisposableMemory : IDisposable
     {
         private readonly Vk _api;
         private readonly Device _device;
diff --git a/Ryujinx.Graphics.Vulkan/DisposablePipeline.cs b/Ryujinx.Graphics.Vulkan/DisposablePipeline.cs
index ff069f7e..6e5cf4db 100644
--- a/Ryujinx.Graphics.Vulkan/DisposablePipeline.cs
+++ b/Ryujinx.Graphics.Vulkan/DisposablePipeline.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct DisposablePipeline : IDisposable
+    readonly struct DisposablePipeline : IDisposable
     {
         private readonly Vk _api;
         private readonly Device _device;
diff --git a/Ryujinx.Graphics.Vulkan/DisposableRenderPass.cs b/Ryujinx.Graphics.Vulkan/DisposableRenderPass.cs
index f561912a..65652f41 100644
--- a/Ryujinx.Graphics.Vulkan/DisposableRenderPass.cs
+++ b/Ryujinx.Graphics.Vulkan/DisposableRenderPass.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct DisposableRenderPass : IDisposable
+    readonly struct DisposableRenderPass : IDisposable
     {
         private readonly Vk _api;
         private readonly Device _device;
diff --git a/Ryujinx.Graphics.Vulkan/DisposableSampler.cs b/Ryujinx.Graphics.Vulkan/DisposableSampler.cs
index 0b93528f..4788b192 100644
--- a/Ryujinx.Graphics.Vulkan/DisposableSampler.cs
+++ b/Ryujinx.Graphics.Vulkan/DisposableSampler.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct DisposableSampler : IDisposable
+    readonly struct DisposableSampler : IDisposable
     {
         private readonly Vk _api;
         private readonly Device _device;
diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
index 7b1beb5f..31acfc9b 100644
--- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
+++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
@@ -2,7 +2,7 @@
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct HardwareCapabilities
+    readonly struct HardwareCapabilities
     {
         public readonly bool SupportsIndexTypeUint8;
         public readonly bool SupportsCustomBorderColor;
diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs
index 04956e36..76de1296 100644
--- a/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs
+++ b/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs
@@ -3,7 +3,7 @@ using System;
 
 namespace Ryujinx.Graphics.Vulkan
 {
-    struct MemoryAllocation : IDisposable
+    readonly struct MemoryAllocation : IDisposable
     {
         private readonly MemoryAllocatorBlockList _owner;
         private readonly MemoryAllocatorBlockList.Block _block;
diff --git a/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs b/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
index 1c008d49..66d339b8 100644
--- a/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
+++ b/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
@@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan
             public ulong Size { get; }
             public bool Mapped => HostPointer != IntPtr.Zero;
 
-            private struct Range : IComparable<Range>
+            private readonly struct Range : IComparable<Range>
             {
                 public ulong Offset { get; }
                 public ulong Size { get; }
diff --git a/Ryujinx.Graphics.Vulkan/StagingBuffer.cs b/Ryujinx.Graphics.Vulkan/StagingBuffer.cs
index df353453..4e3c1dee 100644
--- a/Ryujinx.Graphics.Vulkan/StagingBuffer.cs
+++ b/Ryujinx.Graphics.Vulkan/StagingBuffer.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Vulkan
         private readonly VulkanRenderer _gd;
         private readonly BufferHolder _buffer;
 
-        private struct PendingCopy
+        private readonly struct PendingCopy
         {
             public FenceHolder Fence { get; }
             public int Size { get; }
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs b/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
index 1949df31..9829ae03 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KernelInit.cs
@@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
 {
     static class KernelInit
     {
-        private struct MemoryRegion
+        private readonly struct MemoryRegion
         {
             public ulong Address { get; }
             public ulong Size    { get; }
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs b/Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs
index 098d83d1..d805a4e1 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/OnScopeExit.cs
@@ -2,7 +2,7 @@
 
 namespace Ryujinx.HLE.HOS.Kernel.Common
 {
-    struct OnScopeExit : IDisposable
+    readonly struct OnScopeExit : IDisposable
     {
         private readonly Action _action;
         public OnScopeExit(Action action) => _action = action;
diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
index e28677ff..dc5ad717 100644
--- a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs
@@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
             (MemoryState)0xfffce5d4 //This is invalid, shouldn't be accessed.
         };
 
-        private struct Message
+        private readonly struct Message
         {
             public ulong Address  { get; }
             public ulong Size     { get; }
@@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
                 request.CustomCmdBuffSize) { }
         }
 
-        private struct MessageHeader
+        private readonly struct MessageHeader
         {
             public uint Word0 { get; }
             public uint Word1 { get; }
diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs
index 26c23b3b..c05bb574 100644
--- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessCreationInfo.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.HLE.HOS.Kernel.Process
 {
-    struct ProcessCreationInfo
+    readonly struct ProcessCreationInfo
     {
         public string Name { get; }
 
diff --git a/Ryujinx.HLE/HOS/ModLoader.cs b/Ryujinx.HLE/HOS/ModLoader.cs
index c24f0f74..3b269517 100644
--- a/Ryujinx.HLE/HOS/ModLoader.cs
+++ b/Ryujinx.HLE/HOS/ModLoader.cs
@@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS
         private const string AmsNroPatchDir = "nro_patches";
         private const string AmsKipPatchDir = "kip_patches";
 
-        public struct Mod<T> where T : FileSystemInfo
+        public readonly struct Mod<T> where T : FileSystemInfo
         {
             public readonly string Name;
             public readonly T Path;
diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs
index 8cf4bff1..1793067d 100644
--- a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs
+++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs
@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
 namespace Ryujinx.HLE.HOS.Services.Account.Acc
 {
     [StructLayout(LayoutKind.Sequential)]
-    public struct UserId : IEquatable<UserId>
+    public readonly record struct UserId
     {
         public readonly long High;
         public readonly long Low;
@@ -50,37 +50,12 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
             return High.ToString("x16") + Low.ToString("x16");
         }
 
-        public static bool operator ==(UserId x, UserId y)
-        {
-            return x.Equals(y);
-        }
-
-        public static bool operator !=(UserId x, UserId y)
-        {
-            return !x.Equals(y);
-        }
-
-        public override bool Equals(object obj)
-        {
-            return obj is UserId userId && Equals(userId);
-        }
-
-        public bool Equals(UserId cmpObj)
-        {
-            return Low == cmpObj.Low && High == cmpObj.High;
-        }
-
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(Low, High);
-        }
-
-        public readonly Uid ToLibHacUid()
+        public Uid ToLibHacUid()
         {
             return new Uid((ulong)High, (ulong)Low);
         }
 
-        public readonly UInt128 ToUInt128()
+        public UInt128 ToUInt128()
         {
             return new UInt128((ulong)High, (ulong)Low);
         }
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
index a1ad1c94..0e0fe7f2 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
@@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
         private const ulong SmallRegionLimit = 0x400000000UL; // 16 GiB
         private const ulong DefaultUserSize = 1UL << 37;
 
-        private struct VmRegion
+        private readonly struct VmRegion
         {
             public ulong Start { get; }
             public ulong Limit { get; }
diff --git a/Ryujinx.HLE/HOS/Tamper/OperationBlock.cs b/Ryujinx.HLE/HOS/Tamper/OperationBlock.cs
index db439946..c16c2812 100644
--- a/Ryujinx.HLE/HOS/Tamper/OperationBlock.cs
+++ b/Ryujinx.HLE/HOS/Tamper/OperationBlock.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
 
 namespace Ryujinx.HLE.HOS.Tamper
 {
-    struct OperationBlock
+    readonly struct OperationBlock
     {
         public byte[] BaseInstruction { get; }
         public List<IOperation> Operations { get; }
diff --git a/Ryujinx.HLE/Ui/ThemeColor.cs b/Ryujinx.HLE/Ui/ThemeColor.cs
index 1a42b167..c594c370 100644
--- a/Ryujinx.HLE/Ui/ThemeColor.cs
+++ b/Ryujinx.HLE/Ui/ThemeColor.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.HLE.Ui
 {
-    public struct ThemeColor
+    public readonly struct ThemeColor
     {
         public float A { get; }
         public float R { get; }
diff --git a/Ryujinx.Horizon.Generators/Kernel/SyscallGenerator.cs b/Ryujinx.Horizon.Generators/Kernel/SyscallGenerator.cs
index 2681551b..2562cd46 100644
--- a/Ryujinx.Horizon.Generators/Kernel/SyscallGenerator.cs
+++ b/Ryujinx.Horizon.Generators/Kernel/SyscallGenerator.cs
@@ -40,7 +40,7 @@ namespace Ryujinx.Horizon.Generators.Kernel
             $"{TypeKernelResultName}.InvalidState"
         };
 
-        private struct OutParameter
+        private readonly struct OutParameter
         {
             public readonly string Identifier;
             public readonly bool NeedsSplit;
@@ -104,7 +104,7 @@ namespace Ryujinx.Horizon.Generators.Kernel
             }
         }
 
-        private struct SyscallIdAndName : IComparable<SyscallIdAndName>
+        private readonly struct SyscallIdAndName : IComparable<SyscallIdAndName>
         {
             public readonly int Id;
             public readonly string Name;
diff --git a/Ryujinx.Memory/Range/MemoryRange.cs b/Ryujinx.Memory/Range/MemoryRange.cs
index 33d2439f..7465fbcb 100644
--- a/Ryujinx.Memory/Range/MemoryRange.cs
+++ b/Ryujinx.Memory/Range/MemoryRange.cs
@@ -1,11 +1,9 @@
-using System;
-
-namespace Ryujinx.Memory.Range
+namespace Ryujinx.Memory.Range
 {
     /// <summary>
     /// Range of memory composed of an address and size.
     /// </summary>
-    public struct MemoryRange : IEquatable<MemoryRange>
+    public readonly record struct MemoryRange
     {
         /// <summary>
         /// An empty memory range, with a null address and zero size.
@@ -59,20 +57,5 @@ namespace Ryujinx.Memory.Range
 
             return thisAddress < otherEndAddress && otherAddress < thisEndAddress;
         }
-
-        public override bool Equals(object obj)
-        {
-            return obj is MemoryRange other && Equals(other);
-        }
-
-        public bool Equals(MemoryRange other)
-        {
-            return Address == other.Address && Size == other.Size;
-        }
-
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(Address, Size);
-        }
     }
 }
diff --git a/Ryujinx.Memory/Range/MultiRange.cs b/Ryujinx.Memory/Range/MultiRange.cs
index 21ca5f09..e95af02f 100644
--- a/Ryujinx.Memory/Range/MultiRange.cs
+++ b/Ryujinx.Memory/Range/MultiRange.cs
@@ -6,7 +6,7 @@ namespace Ryujinx.Memory.Range
     /// <summary>
     /// Sequence of physical memory regions that a single non-contiguous virtual memory region maps to.
     /// </summary>
-    public struct MultiRange : IEquatable<MultiRange>
+    public readonly struct MultiRange : IEquatable<MultiRange>
     {
         private readonly MemoryRange _singleRange;
         private readonly MemoryRange[] _ranges;
diff --git a/Ryujinx.Memory/Tracking/BitMap.cs b/Ryujinx.Memory/Tracking/BitMap.cs
index d3f15994..173952f3 100644
--- a/Ryujinx.Memory/Tracking/BitMap.cs
+++ b/Ryujinx.Memory/Tracking/BitMap.cs
@@ -5,7 +5,7 @@ namespace Ryujinx.Memory.Tracking
     /// <summary>
     /// A bitmap that can check or set large ranges of true/false values at once.
     /// </summary>
-    struct BitMap
+    readonly struct BitMap
     {
         public const int IntSize = 64;