diff --git a/src/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs b/src/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs
index 74a9aa049..a2e5b1164 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs
@@ -32,6 +32,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
///
public ref TState State => ref _state.State;
+ ///
+ /// Current shadow state.
+ ///
+ public ref TState ShadowState => ref _shadowState.State;
+
///
/// Creates a new instance of the device state, with shadow state.
///
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs
index a4c4dd106..7d9e1ec02 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs
@@ -1,7 +1,10 @@
using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
+using Ryujinx.Graphics.Gpu.Engine.Threed;
+using Ryujinx.Graphics.Gpu.Engine.Types;
using System;
using System.Collections.Generic;
@@ -15,9 +18,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
private const int ColorLayerCountOffset = 0x818;
private const int ColorStructSize = 0x40;
private const int ZetaLayerCountOffset = 0x1230;
+ private const int UniformBufferBindVertexOffset = 0x2410;
+ private const int FirstVertexOffset = 0x1434;
private const int IndirectIndexedDataEntrySize = 0x14;
+ private const int LogicOpOffset = 0x19c4;
+ private const int ShaderIdScratchOffset = 0x3470;
+ private const int ShaderAddressScratchOffset = 0x3488;
+ private const int UpdateConstantBufferAddressesBase = 0x34a8;
+ private const int UpdateConstantBufferSizesBase = 0x34bc;
+ private const int UpdateConstantBufferAddressCbu = 0x3460;
+
private readonly GPFifoProcessor _processor;
private readonly MacroHLEFunctionName _functionName;
@@ -49,6 +61,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
{
switch (_functionName)
{
+ case MacroHLEFunctionName.BindShaderProgram:
+ BindShaderProgram(state, arg0);
+ break;
case MacroHLEFunctionName.ClearColor:
ClearColor(state, arg0);
break;
@@ -58,6 +73,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
case MacroHLEFunctionName.DrawArraysInstanced:
DrawArraysInstanced(state, arg0);
break;
+ case MacroHLEFunctionName.DrawElements:
+ DrawElements(state, arg0);
+ break;
case MacroHLEFunctionName.DrawElementsInstanced:
DrawElementsInstanced(state, arg0);
break;
@@ -67,6 +85,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
MultiDrawElementsIndirectCount(state, arg0);
break;
+ case MacroHLEFunctionName.UpdateBlendState:
+ UpdateBlendState(state, arg0);
+ break;
+ case MacroHLEFunctionName.UpdateColorMasks:
+ UpdateColorMasks(state, arg0);
+ break;
+ case MacroHLEFunctionName.UpdateUniformBufferState:
+ UpdateUniformBufferState(state, arg0);
+ break;
+ case MacroHLEFunctionName.UpdateUniformBufferStateCbu:
+ UpdateUniformBufferStateCbu(state, arg0);
+ break;
+ case MacroHLEFunctionName.UpdateUniformBufferStateCbuV2:
+ UpdateUniformBufferStateCbuV2(state, arg0);
+ break;
default:
throw new NotImplementedException(_functionName.ToString());
}
@@ -75,6 +108,149 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
Fifo.Clear();
}
+ ///
+ /// Binds a shader program with the index in arg0.
+ ///
+ /// GPU state at the time of the call
+ /// First argument of the call
+ private void BindShaderProgram(IDeviceState state, int arg0)
+ {
+ int scratchOffset = ShaderIdScratchOffset + arg0 * 4;
+
+ int lastId = state.Read(scratchOffset);
+ int id = FetchParam().Word;
+ int offset = FetchParam().Word;
+
+ if (lastId == id)
+ {
+ FetchParam();
+ FetchParam();
+
+ return;
+ }
+
+ _processor.ThreedClass.SetShaderOffset(arg0, (uint)offset);
+
+ // Removes overflow on the method address into the increment portion.
+ // Present in the original macro.
+ int addrMask = unchecked((int)0xfffc0fff) << 2;
+
+ state.Write(scratchOffset & addrMask, id);
+ state.Write((ShaderAddressScratchOffset + arg0 * 4) & addrMask, offset);
+
+ int stage = FetchParam().Word;
+ uint cbAddress = (uint)FetchParam().Word;
+
+ _processor.ThreedClass.UpdateUniformBufferState(65536, cbAddress >> 24, cbAddress << 8);
+
+ int stageOffset = (stage & 0x7f) << 3;
+
+ state.Write((UniformBufferBindVertexOffset + stageOffset * 4) & addrMask, 17);
+ }
+
+ ///
+ /// Updates uniform buffer state for update or bind.
+ ///
+ /// GPU state at the time of the call
+ /// First argument of the call
+ private void UpdateUniformBufferState(IDeviceState state, int arg0)
+ {
+ uint address = (uint)state.Read(UpdateConstantBufferAddressesBase + arg0 * 4);
+ int size = state.Read(UpdateConstantBufferSizesBase + arg0 * 4);
+
+ _processor.ThreedClass.UpdateUniformBufferState(size, address >> 24, address << 8);
+ }
+
+ ///
+ /// Updates uniform buffer state for update.
+ ///
+ /// GPU state at the time of the call
+ /// First argument of the call
+ private void UpdateUniformBufferStateCbu(IDeviceState state, int arg0)
+ {
+ uint address = (uint)state.Read(UpdateConstantBufferAddressCbu);
+
+ UniformBufferState ubState = new()
+ {
+ Address = new()
+ {
+ High = address >> 24,
+ Low = address << 8
+ },
+ Size = 24320,
+ Offset = arg0 << 2
+ };
+
+ _processor.ThreedClass.UpdateUniformBufferState(ubState);
+ }
+
+ ///
+ /// Updates uniform buffer state for update.
+ ///
+ /// GPU state at the time of the call
+ /// First argument of the call
+ private void UpdateUniformBufferStateCbuV2(IDeviceState state, int arg0)
+ {
+ uint address = (uint)state.Read(UpdateConstantBufferAddressCbu);
+
+ UniformBufferState ubState = new()
+ {
+ Address = new()
+ {
+ High = address >> 24,
+ Low = address << 8
+ },
+ Size = 28672,
+ Offset = arg0 << 2
+ };
+
+ _processor.ThreedClass.UpdateUniformBufferState(ubState);
+ }
+
+ ///
+ /// Updates blend enable using the given argument.
+ ///
+ /// GPU state at the time of the call
+ /// First argument of the call
+ private void UpdateBlendState(IDeviceState state, int arg0)
+ {
+ state.Write(LogicOpOffset, 0);
+
+ Array8 enable = new();
+
+ for (int i = 0; i < 8; i++)
+ {
+ enable[i] = new Boolean32((uint)(arg0 >> (i + 8)) & 1);
+ }
+
+ _processor.ThreedClass.UpdateBlendEnable(ref enable);
+ }
+
+ ///
+ /// Updates color masks using the given argument and three pushed arguments.
+ ///
+ /// GPU state at the time of the call
+ /// First argument of the call
+ private void UpdateColorMasks(IDeviceState state, int arg0)
+ {
+ Array8 masks = new();
+
+ int index = 0;
+
+ for (int i = 0; i < 4; i++)
+ {
+ masks[index++] = new RtColorMask((uint)arg0 & 0x1fff);
+ masks[index++] = new RtColorMask(((uint)arg0 >> 16) & 0x1fff);
+
+ if (i != 3)
+ {
+ arg0 = FetchParam().Word;
+ }
+ }
+
+ _processor.ThreedClass.UpdateColorMasks(ref masks);
+ }
+
///
/// Clears one bound color target.
///
@@ -129,6 +305,36 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
indexed: false);
}
+ ///
+ /// Performs a indexed draw.
+ ///
+ /// GPU state at the time of the call
+ /// First argument of the call
+ private void DrawElements(IDeviceState state, int arg0)
+ {
+ var topology = (PrimitiveTopology)arg0;
+
+ var indexAddressHigh = FetchParam();
+ var indexAddressLow = FetchParam();
+ var indexType = FetchParam();
+ var firstIndex = 0;
+ var indexCount = FetchParam();
+
+ _processor.ThreedClass.UpdateIndexBuffer(
+ (uint)indexAddressHigh.Word,
+ (uint)indexAddressLow.Word,
+ (IndexType)indexType.Word);
+
+ _processor.ThreedClass.Draw(
+ topology,
+ indexCount.Word,
+ 1,
+ firstIndex,
+ state.Read(FirstVertexOffset),
+ 0,
+ indexed: true);
+ }
+
///
/// Performs a indexed draw.
///
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs
index 9e71761b4..8dca52262 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLEFunctionName.cs
@@ -6,11 +6,19 @@
enum MacroHLEFunctionName
{
None,
+ BindShaderProgram,
ClearColor,
ClearDepthStencil,
DrawArraysInstanced,
+ DrawElements,
DrawElementsInstanced,
DrawElementsIndirect,
MultiDrawElementsIndirectCount,
+
+ UpdateBlendState,
+ UpdateColorMasks,
+ UpdateUniformBufferState,
+ UpdateUniformBufferStateCbu,
+ UpdateUniformBufferStateCbuV2
}
}
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
index 5630756cb..9a496164d 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs
@@ -46,12 +46,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
private static readonly TableEntry[] _table = new TableEntry[]
{
+ new(MacroHLEFunctionName.BindShaderProgram, new Hash128(0x5d5efb912369f60b, 0x69131ed5019f08ef), 0x68),
new(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28),
new(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24),
new(MacroHLEFunctionName.DrawArraysInstanced, new Hash128(0x197FB416269DBC26, 0x34288C01DDA82202), 0x48),
+ new(MacroHLEFunctionName.DrawElements, new Hash128(0x3D7F32AE6C2702A7, 0x9353C9F41C1A244D), 0x20),
new(MacroHLEFunctionName.DrawElementsInstanced, new Hash128(0x1A501FD3D54EC8E0, 0x6CF570CF79DA74D6), 0x5c),
new(MacroHLEFunctionName.DrawElementsIndirect, new Hash128(0x86A3E8E903AF8F45, 0xD35BBA07C23860A4), 0x7c),
new(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C),
+ new(MacroHLEFunctionName.UpdateBlendState, new Hash128(0x40F6D4E7B08D7640, 0x82167BEEAECB959F), 0x28),
+ new(MacroHLEFunctionName.UpdateColorMasks, new Hash128(0x9EE32420B8441DFD, 0x6E7724759A57333E), 0x24),
+ new(MacroHLEFunctionName.UpdateUniformBufferState, new Hash128(0x8EE66706049CB0B0, 0x51C1CF906EC86F7C), 0x20),
+ new(MacroHLEFunctionName.UpdateUniformBufferStateCbu, new Hash128(0xA4592676A3E581A0, 0xA39E77FE19FE04AC), 0x18),
+ new(MacroHLEFunctionName.UpdateUniformBufferStateCbuV2, new Hash128(0x392FA750489983D4, 0x35BACE455155D2C3), 0x18)
};
///
@@ -62,18 +69,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// True if the host supports the HLE macro, false otherwise
private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name)
{
- if (name == MacroHLEFunctionName.ClearColor ||
- name == MacroHLEFunctionName.ClearDepthStencil ||
- name == MacroHLEFunctionName.DrawArraysInstanced ||
- name == MacroHLEFunctionName.DrawElementsInstanced ||
- name == MacroHLEFunctionName.DrawElementsIndirect)
- {
- return true;
- }
- else if (name == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
+ if (name == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
{
return caps.SupportsIndirectParameters;
}
+ else if (name != MacroHLEFunctionName.None)
+ {
+ return true;
+ }
return false;
}
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs b/src/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs
index ebb0ff33e..b9a5c74a3 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs
@@ -10,4 +10,22 @@ namespace Ryujinx.Graphics.Gpu.Engine
MethodPassthrough = 2,
MethodReplay = 3,
}
+
+ static class SetMmeShadowRamControlModeExtensions
+ {
+ public static bool IsTrack(this SetMmeShadowRamControlMode mode)
+ {
+ return mode == SetMmeShadowRamControlMode.MethodTrack || mode == SetMmeShadowRamControlMode.MethodTrackWithFilter;
+ }
+
+ public static bool IsPassthrough(this SetMmeShadowRamControlMode mode)
+ {
+ return mode == SetMmeShadowRamControlMode.MethodPassthrough;
+ }
+
+ public static bool IsReplay(this SetMmeShadowRamControlMode mode)
+ {
+ return mode == SetMmeShadowRamControlMode.MethodReplay;
+ }
+ }
}
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
index 37e41c51b..1ff821569 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -17,9 +17,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
class StateUpdater
{
public const int ShaderStateIndex = 26;
+ public const int RtColorMaskIndex = 14;
public const int RasterizerStateIndex = 15;
public const int ScissorStateIndex = 16;
public const int VertexBufferStateIndex = 0;
+ public const int BlendStateIndex = 2;
public const int IndexBufferStateIndex = 23;
public const int PrimitiveRestartStateIndex = 12;
public const int RenderTargetStateIndex = 27;
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
index 7bc2970fe..df9d1f5c9 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
@@ -1,12 +1,15 @@
-using Ryujinx.Graphics.Device;
+using Ryujinx.Common.Memory;
+using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
+using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Synchronization;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
@@ -26,6 +29,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private readonly ConstantBufferUpdater _cbUpdater;
private readonly StateUpdater _stateUpdater;
+ private SetMmeShadowRamControlMode ShadowMode => _state.State.SetMmeShadowRamControlMode;
+
///
/// Creates a new instance of the 3D engine class.
///
@@ -228,6 +233,206 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_cbUpdater.Update(data);
}
+ ///
+ /// Test if two 32 byte structs are equal.
+ ///
+ /// Type of the 32-byte struct
+ /// First struct
+ /// Second struct
+ /// True if equal, false otherwise
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool UnsafeEquals32Byte(ref T lhs, ref T rhs) where T : unmanaged
+ {
+ if (Vector256.IsHardwareAccelerated)
+ {
+ return Vector256.EqualsAll(
+ Unsafe.As>(ref lhs),
+ Unsafe.As>(ref rhs)
+ );
+ }
+ else
+ {
+ ref var lhsVec = ref Unsafe.As>(ref lhs);
+ ref var rhsVec = ref Unsafe.As>(ref rhs);
+
+ return Vector128.EqualsAll(lhsVec, rhsVec) &&
+ Vector128.EqualsAll(Unsafe.Add(ref lhsVec, 1), Unsafe.Add(ref rhsVec, 1));
+ }
+ }
+
+ ///
+ /// Updates blend enable. Respects current shadow mode.
+ ///
+ /// Blend enable
+ public void UpdateBlendEnable(ref Array8 enable)
+ {
+ var shadow = ShadowMode;
+ ref var state = ref _state.State.BlendEnable;
+
+ if (shadow.IsReplay())
+ {
+ enable = _state.ShadowState.BlendEnable;
+ }
+
+ if (!UnsafeEquals32Byte(ref enable, ref state))
+ {
+ state = enable;
+
+ _stateUpdater.ForceDirty(StateUpdater.BlendStateIndex);
+ }
+
+ if (shadow.IsTrack())
+ {
+ _state.ShadowState.BlendEnable = enable;
+ }
+ }
+
+ ///
+ /// Updates color masks. Respects current shadow mode.
+ ///
+ /// Color masks
+ public void UpdateColorMasks(ref Array8 masks)
+ {
+ var shadow = ShadowMode;
+ ref var state = ref _state.State.RtColorMask;
+
+ if (shadow.IsReplay())
+ {
+ masks = _state.ShadowState.RtColorMask;
+ }
+
+ if (!UnsafeEquals32Byte(ref masks, ref state))
+ {
+ state = masks;
+
+ _stateUpdater.ForceDirty(StateUpdater.RtColorMaskIndex);
+ }
+
+ if (shadow.IsTrack())
+ {
+ _state.ShadowState.RtColorMask = masks;
+ }
+ }
+
+ ///
+ /// Updates index buffer state for an indexed draw. Respects current shadow mode.
+ ///
+ /// High part of the address
+ /// Low part of the address
+ /// Type of the binding
+ public void UpdateIndexBuffer(uint addrHigh, uint addrLow, IndexType type)
+ {
+ var shadow = ShadowMode;
+ ref var state = ref _state.State.IndexBufferState;
+
+ if (shadow.IsReplay())
+ {
+ ref var shadowState = ref _state.ShadowState.IndexBufferState;
+ addrHigh = shadowState.Address.High;
+ addrLow = shadowState.Address.Low;
+ type = shadowState.Type;
+ }
+
+ if (state.Address.High != addrHigh || state.Address.Low != addrLow || state.Type != type)
+ {
+ state.Address.High = addrHigh;
+ state.Address.Low = addrLow;
+ state.Type = type;
+
+ _stateUpdater.ForceDirty(StateUpdater.IndexBufferStateIndex);
+ }
+
+ if (shadow.IsTrack())
+ {
+ ref var shadowState = ref _state.ShadowState.IndexBufferState;
+ shadowState.Address.High = addrHigh;
+ shadowState.Address.Low = addrLow;
+ shadowState.Type = type;
+ }
+ }
+
+ ///
+ /// Updates uniform buffer state for update or bind. Respects current shadow mode.
+ ///
+ /// Size of the binding
+ /// High part of the addrsss
+ /// Low part of the address
+ public void UpdateUniformBufferState(int size, uint addrHigh, uint addrLow)
+ {
+ var shadow = ShadowMode;
+ ref var state = ref _state.State.UniformBufferState;
+
+ if (shadow.IsReplay())
+ {
+ ref var shadowState = ref _state.ShadowState.UniformBufferState;
+ size = shadowState.Size;
+ addrHigh = shadowState.Address.High;
+ addrLow = shadowState.Address.Low;
+ }
+
+ state.Size = size;
+ state.Address.High = addrHigh;
+ state.Address.Low = addrLow;
+
+ if (shadow.IsTrack())
+ {
+ ref var shadowState = ref _state.ShadowState.UniformBufferState;
+ shadowState.Size = size;
+ shadowState.Address.High = addrHigh;
+ shadowState.Address.Low = addrLow;
+ }
+ }
+
+ ///
+ /// Updates a shader offset. Respects current shadow mode.
+ ///
+ /// Index of the shader to update
+ /// Offset to update with
+ public void SetShaderOffset(int index, uint offset)
+ {
+ var shadow = ShadowMode;
+ ref var shaderState = ref _state.State.ShaderState[index];
+
+ if (shadow.IsReplay())
+ {
+ offset = _state.ShadowState.ShaderState[index].Offset;
+ }
+
+ if (shaderState.Offset != offset)
+ {
+ shaderState.Offset = offset;
+
+ _stateUpdater.ForceDirty(StateUpdater.ShaderStateIndex);
+ }
+
+ if (shadow.IsTrack())
+ {
+ _state.ShadowState.ShaderState[index].Offset = offset;
+ }
+ }
+
+ ///
+ /// Updates uniform buffer state for update. Respects current shadow mode.
+ ///
+ /// Uniform buffer state
+ public void UpdateUniformBufferState(UniformBufferState ubState)
+ {
+ var shadow = ShadowMode;
+ ref var state = ref _state.State.UniformBufferState;
+
+ if (shadow.IsReplay())
+ {
+ ubState = _state.ShadowState.UniformBufferState;
+ }
+
+ state = ubState;
+
+ if (shadow.IsTrack())
+ {
+ _state.ShadowState.UniformBufferState = ubState;
+ }
+ }
+
///
/// Launches the Inline-to-Memory DMA copy operation.
///
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
index f2997678c..45284525c 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
@@ -590,9 +590,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
///
struct RtColorMask
{
-#pragma warning disable CS0649 // Field is never assigned to
public uint Packed;
-#pragma warning restore CS0649
+
+ public RtColorMask(uint packed)
+ {
+ Packed = packed;
+ }
///
/// Unpacks red channel enable.
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs b/src/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs
index 911ad53b4..7293fab9c 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs
@@ -5,9 +5,12 @@
///
readonly struct Boolean32
{
-#pragma warning disable CS0649 // Field is never assigned to
private readonly uint _value;
-#pragma warning restore CS0649
+
+ public Boolean32(uint value)
+ {
+ _value = value;
+ }
public static implicit operator bool(Boolean32 value)
{