aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Engine
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2021-07-11 17:20:40 -0300
committerGitHub <noreply@github.com>2021-07-11 17:20:40 -0300
commit40b21cc3c4d2622bbd4f88d43073341854d9a671 (patch)
tree6e9dc6a42e7c0bae5b03db468481771d5a6937ef /Ryujinx.Graphics.Gpu/Engine
parentb5190f16810eb77388c861d1d1773e19644808db (diff)
Separate GPU engines (part 2/2) (#2440)
* 3D engine now uses DeviceState too, plus new state modification tracking * Remove old methods code * Remove GpuState and friends * Optimize DeviceState, force inline some functions * This change was not supposed to go in * Proper channel initialization * Optimize state read/write methods even more * Fix debug build * Do not dirty state if the write is redundant * The YControl register should dirty either the viewport or front face state too, to update the host origin * Avoid redundant vertex buffer updates * Move state and get rid of the Ryujinx.Graphics.Gpu.State namespace * Comments and nits * Fix rebase * PR feedback * Move changed = false to improve codegen * PR feedback * Carry RyuJIT a bit more
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Engine')
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs38
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs3
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs95
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs15
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs20
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs7
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs176
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs134
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs12
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs6
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs4
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs22
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs4
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs6
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs12
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MethodClear.cs85
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs343
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MethodFirmware.cs12
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MethodIncrementSyncpoint.cs21
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MethodReport.cs131
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs31
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs85
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs88
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Methods.cs1138
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs3
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs13
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs (renamed from Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs)58
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs173
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs410
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs45
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs (renamed from Ryujinx.Graphics.Gpu/Engine/IbStreamer.cs)5
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs222
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs166
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs1044
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs428
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs861
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs6
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs13
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs22
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs17
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs134
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs22
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs37
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs99
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs11
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs20
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs42
48 files changed, 4112 insertions, 2229 deletions
diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
index 5d1fd2c0..ac8b1186 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
@@ -1,9 +1,10 @@
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
+using Ryujinx.Graphics.Gpu.Engine.Threed;
+using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader;
-using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Shader;
using System;
using System.Collections.Generic;
@@ -14,27 +15,34 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
/// <summary>
/// Represents a compute engine class.
/// </summary>
- class ComputeClass : InlineToMemoryClass, IDeviceState
+ class ComputeClass : IDeviceState
{
private readonly GpuContext _context;
private readonly GpuChannel _channel;
+ private readonly ThreedClass _3dEngine;
private readonly DeviceState<ComputeClassState> _state;
+ private readonly InlineToMemoryClass _i2mClass;
+
/// <summary>
/// Creates a new instance of the compute engine class.
/// </summary>
/// <param name="context">GPU context</param>
/// <param name="channel">GPU channel</param>
- public ComputeClass(GpuContext context, GpuChannel channel) : base(context, channel, false)
+ /// <param name="threedEngine">3D engine</param>
+ public ComputeClass(GpuContext context, GpuChannel channel, ThreedClass threedEngine)
{
_context = context;
_channel = channel;
+ _3dEngine = threedEngine;
_state = new DeviceState<ComputeClassState>(new Dictionary<string, RwCallback>
{
{ nameof(ComputeClassState.LaunchDma), new RwCallback(LaunchDma, null) },
{ nameof(ComputeClassState.LoadInlineData), new RwCallback(LoadInlineData, null) },
{ nameof(ComputeClassState.SendSignalingPcasB), new RwCallback(SendSignalingPcasB, null) }
});
+
+ _i2mClass = new InlineToMemoryClass(context, channel, initializeState: false);
}
/// <summary>
@@ -42,22 +50,31 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
/// </summary>
/// <param name="offset">Register byte offset</param>
/// <returns>Data at the specified offset</returns>
- public override int Read(int offset) => _state.Read(offset);
+ public int Read(int offset) => _state.Read(offset);
/// <summary>
/// Writes data to the class registers.
/// </summary>
/// <param name="offset">Register byte offset</param>
/// <param name="data">Data to be written</param>
- public override void Write(int offset, int data) => _state.Write(offset, data);
+ public void Write(int offset, int data) => _state.Write(offset, data);
/// <summary>
/// Launches the Inline-to-Memory DMA copy operation.
/// </summary>
/// <param name="argument">Method call argument</param>
- protected override void LaunchDma(int argument)
+ private void LaunchDma(int argument)
+ {
+ _i2mClass.LaunchDma(ref Unsafe.As<ComputeClassState, InlineToMemoryClassState>(ref _state.State), argument);
+ }
+
+ /// <summary>
+ /// Pushes a word of data to the Inline-to-Memory engine.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void LoadInlineData(int argument)
{
- LaunchDma(ref Unsafe.As<ComputeClassState, InlineToMemoryClassState>(ref _state.State), argument);
+ _i2mClass.LoadInlineData(argument);
}
/// <summary>
@@ -68,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
{
var memoryManager = _channel.MemoryManager;
- _context.Methods.FlushUboDirty(memoryManager);
+ _3dEngine.FlushUboDirty();
uint qmdAddress = _state.State.SendPcasA;
@@ -102,7 +119,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
texturePoolGpuVa,
_state.State.SetTexHeaderPoolCMaximumIndex,
_state.State.SetBindlessTextureConstantBufferSlotSelect,
- false);
+ false,
+ PrimitiveTopology.Points);
ShaderBundle cs = memoryManager.Physical.ShaderCache.GetComputeShader(
_channel,
@@ -207,7 +225,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
_context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth);
- _context.Methods.ForceShaderUpdate();
+ _3dEngine.ForceShaderUpdate();
}
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs
index a1116074..1b20e41c 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeQmd.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Gpu.Engine.Types;
using System;
using System.Runtime.CompilerServices;
diff --git a/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs b/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs
index 451f4963..5581b5cc 100644
--- a/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/ConditionalRenderEnabled.cs
@@ -1,5 +1,8 @@
namespace Ryujinx.Graphics.Gpu.Engine
{
+ /// <summary>
+ /// Conditional rendering enable.
+ /// </summary>
enum ConditionalRenderEnabled
{
False,
diff --git a/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs b/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs
new file mode 100644
index 00000000..3a06bc2a
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs
@@ -0,0 +1,95 @@
+using Ryujinx.Graphics.Device;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Graphics.Gpu.Engine
+{
+ /// <summary>
+ /// State interface with a shadow memory control register.
+ /// </summary>
+ interface IShadowState
+ {
+ /// <summary>
+ /// MME shadow ram control mode.
+ /// </summary>
+ SetMmeShadowRamControlMode SetMmeShadowRamControlMode { get; }
+ }
+
+ /// <summary>
+ /// Represents a device's state, with a additional shadow state.
+ /// </summary>
+ /// <typeparam name="TState">Type of the state</typeparam>
+ class DeviceStateWithShadow<TState> : IDeviceState where TState : unmanaged, IShadowState
+ {
+ private readonly DeviceState<TState> _state;
+ private readonly DeviceState<TState> _shadowState;
+
+ /// <summary>
+ /// Current device state.
+ /// </summary>
+ public ref TState State => ref _state.State;
+
+ /// <summary>
+ /// Creates a new instance of the device state, with shadow state.
+ /// </summary>
+ /// <param name="callbacks">Optional that will be called if a register specified by name is read or written</param>
+ /// <param name="debugLogCallback">Optional callback to be used for debug log messages</param>
+ public DeviceStateWithShadow(IReadOnlyDictionary<string, RwCallback> callbacks = null, Action<string> debugLogCallback = null)
+ {
+ _state = new DeviceState<TState>(callbacks, debugLogCallback);
+ _shadowState = new DeviceState<TState>();
+ }
+
+ /// <summary>
+ /// Reads a value from a register.
+ /// </summary>
+ /// <param name="offset">Register offset in bytes</param>
+ /// <returns>Value stored on the register</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int Read(int offset)
+ {
+ return _state.Read(offset);
+ }
+
+ /// <summary>
+ /// Writes a value to a register.
+ /// </summary>
+ /// <param name="offset">Register offset in bytes</param>
+ /// <param name="value">Value to be written</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Write(int offset, int value)
+ {
+ WriteWithRedundancyCheck(offset, value, out _);
+ }
+
+ /// <summary>
+ /// Writes a value to a register, returning a value indicating if <paramref name="value"/>
+ /// is different from the current value on the register.
+ /// </summary>
+ /// <param name="offset">Register offset in bytes</param>
+ /// <param name="value">Value to be written</param>
+ /// <param name="changed">True if the value was changed, false otherwise</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void WriteWithRedundancyCheck(int offset, int value, out bool changed)
+ {
+ var shadowRamControl = _state.State.SetMmeShadowRamControlMode;
+ if (shadowRamControl == SetMmeShadowRamControlMode.MethodPassthrough || offset < 0x200)
+ {
+ _state.WriteWithRedundancyCheck(offset, value, out changed);
+ }
+ else if (shadowRamControl == SetMmeShadowRamControlMode.MethodTrack ||
+ shadowRamControl == SetMmeShadowRamControlMode.MethodTrackWithFilter)
+ {
+ _shadowState.Write(offset, value);
+ _state.WriteWithRedundancyCheck(offset, value, out changed);
+ }
+ else /* if (shadowRamControl == SetMmeShadowRamControlMode.MethodReplay) */
+ {
+ Debug.Assert(shadowRamControl == SetMmeShadowRamControlMode.MethodReplay);
+ _state.WriteWithRedundancyCheck(offset, _shadowState.Read(offset), out changed);
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs
index 58fa2326..70909168 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs
@@ -1,6 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Graphics.Device;
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Texture;
using System;
using System.Collections.Generic;
@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
{
private readonly GpuContext _context;
private readonly GpuChannel _channel;
+ private readonly ThreedClass _3dEngine;
private readonly DeviceState<DmaClassState> _state;
/// <summary>
@@ -35,10 +36,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
/// </summary>
/// <param name="context">GPU context</param>
/// <param name="channel">GPU channel</param>
- public DmaClass(GpuContext context, GpuChannel channel)
+ /// <param name="threedEngine">3D engine</param>
+ public DmaClass(GpuContext context, GpuChannel channel, ThreedClass threedEngine)
{
_context = context;
_channel = channel;
+ _3dEngine = threedEngine;
_state = new DeviceState<DmaClassState>(new Dictionary<string, RwCallback>
{
{ nameof(DmaClassState.LaunchDma), new RwCallback(LaunchDma, null) }
@@ -69,7 +72,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
/// <param name="xCount">Number of pixels to be copied</param>
/// <param name="yCount">Number of lines to be copied</param>
/// <returns></returns>
- private static bool IsTextureCopyComplete(CopyBufferTexture tex, bool linear, int bpp, int stride, int xCount, int yCount)
+ private static bool IsTextureCopyComplete(DmaTexture tex, bool linear, int bpp, int stride, int xCount, int yCount)
{
if (linear)
{
@@ -116,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
int xCount = (int)_state.State.LineLengthIn;
int yCount = (int)_state.State.LineCount;
- _context.Methods.FlushUboDirty(memoryManager);
+ _3dEngine.FlushUboDirty();
if (copy2D)
{
@@ -125,8 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
int srcBpp = remap ? ((int)_state.State.SetRemapComponentsNumSrcComponents + 1) * componentSize : 1;
int dstBpp = remap ? ((int)_state.State.SetRemapComponentsNumDstComponents + 1) * componentSize : 1;
- var dst = Unsafe.As<uint, CopyBufferTexture>(ref _state.State.SetDstBlockSize);
- var src = Unsafe.As<uint, CopyBufferTexture>(ref _state.State.SetSrcBlockSize);
+ var dst = Unsafe.As<uint, DmaTexture>(ref _state.State.SetDstBlockSize);
+ var src = Unsafe.As<uint, DmaTexture>(ref _state.State.SetSrcBlockSize);
int srcStride = (int)_state.State.PitchIn;
int dstStride = (int)_state.State.PitchOut;
diff --git a/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs
new file mode 100644
index 00000000..6873ff40
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Graphics.Gpu.Engine.Types;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Dma
+{
+ /// <summary>
+ /// Buffer to texture copy parameters.
+ /// </summary>
+ struct DmaTexture
+ {
+#pragma warning disable CS0649
+ public MemoryLayout MemoryLayout;
+ public int Width;
+ public int Height;
+ public int Depth;
+ public int RegionZ;
+ public ushort RegionX;
+ public ushort RegionY;
+#pragma warning restore CS0649
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs
index 75b19c37..28822f4e 100644
--- a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs
@@ -1,6 +1,5 @@
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Engine.MME;
-using Ryujinx.Graphics.Gpu.State;
using System;
using System.Collections.Generic;
using System.Threading;
@@ -150,7 +149,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
/// <param name="argument">Method call argument</param>
public void WaitForIdle(int argument)
{
- _context.Methods.PerformDeferredDraws();
+ _parent.PerformDeferredDraws();
_context.Renderer.Pipeline.Barrier();
_context.CreateHostSyncIfNeeded();
@@ -189,7 +188,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
/// <param name="argument">Method call argument</param>
public void SetMmeShadowRamControl(int argument)
{
- _parent.SetShadowRamControl((ShadowRamControl)argument);
+ _parent.SetShadowRamControl(argument);
}
/// <summary>
@@ -217,7 +216,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
/// </summary>
/// <param name="index">Index of the macro</param>
/// <param name="state">Current GPU state</param>
- public void CallMme(int index, GpuState state)
+ public void CallMme(int index, IDeviceState state)
{
_macros[index].Execute(_macroCode, state);
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs
index c2727f48..dd5e6fe5 100644
--- a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs
@@ -2,9 +2,9 @@
using Ryujinx.Graphics.Gpu.Engine.Compute;
using Ryujinx.Graphics.Gpu.Engine.Dma;
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
+using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Engine.Twod;
using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.State;
using System;
using System.Runtime.CompilerServices;
@@ -18,9 +18,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
private const int MacrosCount = 0x80;
private const int MacroIndexMask = MacrosCount - 1;
- private readonly GpuContext _context;
+ private const int UniformBufferUpdateDataMethodOffset = 0x8e4;
+
private readonly GpuChannel _channel;
+ /// <summary>
+ /// Channel memory manager.
+ /// </summary>
public MemoryManager MemoryManager => _channel.MemoryManager;
/// <summary>
@@ -37,8 +41,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
private DmaState _state;
- private readonly GpuState[] _subChannels;
- private readonly IDeviceState[] _subChannels2;
+ private readonly ThreedClass _3dClass;
+ private readonly ComputeClass _computeClass;
+ private readonly InlineToMemoryClass _i2mClass;
+ private readonly TwodClass _2dClass;
+ private readonly DmaClass _dmaClass;
+
private readonly GPFifoClass _fifoClass;
/// <summary>
@@ -48,29 +56,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
/// <param name="channel">Channel that the GPFIFO processor belongs to</param>
public GPFifoProcessor(GpuContext context, GpuChannel channel)
{
- _context = context;
_channel = channel;
_fifoClass = new GPFifoClass(context, this);
- _subChannels = new GpuState[8];
- _subChannels2 = new IDeviceState[8]
- {
- null,
- new ComputeClass(context, channel),
- new InlineToMemoryClass(context, channel),
- new TwodClass(channel),
- new DmaClass(context, channel),
- null,
- null,
- null
- };
-
- for (int index = 0; index < _subChannels.Length; index++)
- {
- _subChannels[index] = new GpuState(channel, _subChannels2[index]);
-
- _context.Methods.RegisterCallbacks(_subChannels[index]);
- }
+ _3dClass = new ThreedClass(context, channel);
+ _computeClass = new ComputeClass(context, channel, _3dClass);
+ _i2mClass = new InlineToMemoryClass(context, channel);
+ _2dClass = new TwodClass(channel);
+ _dmaClass = new DmaClass(context, channel, _3dClass);
}
/// <summary>
@@ -85,7 +78,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
if (_state.MethodCount != 0)
{
- Send(new MethodParams(_state.Method, command, _state.SubChannel, _state.MethodCount));
+ Send(_state.Method, command, _state.SubChannel, _state.MethodCount <= 1);
if (!_state.NonIncrementing)
{
@@ -121,13 +114,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
_state.NonIncrementing = meth.SecOp == SecOp.NonIncMethod;
break;
case SecOp.ImmdDataMethod:
- Send(new MethodParams(meth.MethodAddress, meth.ImmdData, meth.MethodSubchannel, 1));
+ Send(meth.MethodAddress, meth.ImmdData, meth.MethodSubchannel, true);
break;
}
}
}
- _context.Methods.FlushUboDirty(MemoryManager);
+ _3dClass.FlushUboDirty();
}
/// <summary>
@@ -145,11 +138,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
if (meth.MethodCount < availableCount &&
meth.SecOp == SecOp.NonIncMethod &&
- meth.MethodAddress == (int)MethodOffset.UniformBufferUpdateData)
+ meth.MethodAddress == UniformBufferUpdateDataMethodOffset)
{
- GpuState state = _subChannels[meth.MethodSubchannel];
-
- _context.Methods.UniformBufferUpdate(state, commandBuffer.Slice(offset + 1, meth.MethodCount));
+ _3dClass.ConstantBufferUpdate(commandBuffer.Slice(offset + 1, meth.MethodCount));
return true;
}
@@ -161,67 +152,124 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
/// Sends a uncompressed method for processing by the graphics pipeline.
/// </summary>
/// <param name="meth">Method to be processed</param>
- private void Send(MethodParams meth)
+ private void Send(int offset, int argument, int subChannel, bool isLastCall)
{
- if ((MethodOffset)meth.Method == MethodOffset.BindChannel)
- {
- _subChannels[meth.SubChannel].ClearCallbacks();
-
- _context.Methods.RegisterCallbacks(_subChannels[meth.SubChannel]);
- }
- else if (meth.Method < 0x60)
- {
- // TODO: check if macros are shared between subchannels or not. For now let's assume they are.
- _fifoClass.Write(meth.Method * 4, meth.Argument);
- }
- else if (meth.Method < 0xe00)
+ if (offset < 0x60)
{
- _subChannels[meth.SubChannel].CallMethod(meth);
+ _fifoClass.Write(offset * 4, argument);
}
- else
+ else if (offset < 0xe00)
{
- int macroIndex = (meth.Method >> 1) & MacroIndexMask;
+ offset *= 4;
- if ((meth.Method & 1) != 0)
+ switch (subChannel)
{
- _fifoClass.MmePushArgument(macroIndex, meth.Argument);
+ case 0:
+ _3dClass.Write(offset, argument);
+ break;
+ case 1:
+ _computeClass.Write(offset, argument);
+ break;
+ case 2:
+ _i2mClass.Write(offset, argument);
+ break;
+ case 3:
+ _2dClass.Write(offset, argument);
+ break;
+ case 4:
+ _dmaClass.Write(offset, argument);
+ break;
}
- else
+ }
+ else
+ {
+ IDeviceState state = subChannel switch
{
- _fifoClass.MmeStart(macroIndex, meth.Argument);
- }
+ 0 => _3dClass,
+ 3 => _2dClass,
+ _ => null
+ };
- if (meth.IsLastCall)
+ if (state != null)
{
- _fifoClass.CallMme(macroIndex, _subChannels[meth.SubChannel]);
+ int macroIndex = (offset >> 1) & MacroIndexMask;
+
+ if ((offset & 1) != 0)
+ {
+ _fifoClass.MmePushArgument(macroIndex, argument);
+ }
+ else
+ {
+ _fifoClass.MmeStart(macroIndex, argument);
+ }
- _context.Methods.PerformDeferredDraws();
+ if (isLastCall)
+ {
+ _fifoClass.CallMme(macroIndex, state);
+
+ _3dClass.PerformDeferredDraws();
+ }
}
}
}
/// <summary>
- /// Sets the shadow ram control value of all sub-channels.
+ /// Writes data directly to the state of the specified class.
/// </summary>
- /// <param name="control">New shadow ram control value</param>
- public void SetShadowRamControl(ShadowRamControl control)
+ /// <param name="classId">ID of the class to write the data into</param>
+ /// <param name="offset">State offset in bytes</param>
+ /// <param name="value">Value to be written</param>
+ public void Write(ClassId classId, int offset, int value)
{
- for (int i = 0; i < _subChannels.Length; i++)
+ switch (classId)
{
- _subChannels[i].ShadowRamControl = control;
+ case ClassId.Threed:
+ _3dClass.Write(offset, value);
+ break;
+ case ClassId.Compute:
+ _computeClass.Write(offset, value);
+ break;
+ case ClassId.InlineToMemory:
+ _i2mClass.Write(offset, value);
+ break;
+ case ClassId.Twod:
+ _2dClass.Write(offset, value);
+ break;
+ case ClassId.Dma:
+ _dmaClass.Write(offset, value);
+ break;
+ case ClassId.GPFifo:
+ _fifoClass.Write(offset, value);
+ break;
}
}
/// <summary>
+ /// Sets the shadow ram control value of all sub-channels.
+ /// </summary>
+ /// <param name="control">New shadow ram control value</param>
+ public void SetShadowRamControl(int control)
+ {
+ _3dClass.SetShadowRamControl(control);
+ }
+
+ /// <summary>
/// Forces a full host state update by marking all state as modified,
/// and also requests all GPU resources in use to be rebound.
/// </summary>
public void ForceAllDirty()
{
- for (int index = 0; index < _subChannels.Length; index++)
- {
- _subChannels[index].ForceAllDirty();
- }
+ _3dClass.ForceStateDirty();
+ _channel.BufferManager.Rebind();
+ _channel.TextureManager.Rebind();
+ }
+
+ /// <summary>
+ /// Perform any deferred draws.
+ /// </summary>
+ public void PerformDeferredDraws()
+ {
+ _3dClass.PerformDeferredDraws();
}
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs b/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
deleted file mode 100644
index 3d23b785..00000000
--- a/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.Graphics.Gpu.State;
-using Ryujinx.Graphics.Texture;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
- partial class Methods
- {
- private Inline2MemoryParams _params;
-
- private bool _isLinear;
-
- private int _offset;
- private int _size;
-
- private bool _finished;
-
- private int[] _buffer;
-
- /// <summary>
- /// Launches Inline-to-Memory engine DMA copy.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- public void LaunchDma(GpuState state, int argument)
- {
- _params = state.Get<Inline2MemoryParams>(MethodOffset.I2mParams);
-
- _isLinear = (argument & 1) != 0;
-
- _offset = 0;
- _size = _params.LineLengthIn * _params.LineCount;
-
- int count = BitUtils.DivRoundUp(_size, 4);
-
- if (_buffer == null || _buffer.Length < count)
- {
- _buffer = new int[count];
- }
-
- ulong dstBaseAddress = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
-
- // Trigger read tracking, to flush any managed resources in the destination region.
- state.Channel.MemoryManager.Physical.GetSpan(dstBaseAddress, _size, true);
-
- _finished = false;
- }
-
- /// <summary>
- /// Pushes a word of data to the Inline-to-Memory engine.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- public void LoadInlineData(GpuState state, int argument)
- {
- if (!_finished)
- {
- _buffer[_offset++] = argument;
-
- if (_offset * 4 >= _size)
- {
- FinishTransfer(state);
- }
- }
- }
-
- /// <summary>
- /// Performs actual copy of the inline data after the transfer is finished.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void FinishTransfer(GpuState state)
- {
- Span<byte> data = MemoryMarshal.Cast<int, byte>(_buffer).Slice(0, _size);
-
- if (_isLinear && _params.LineCount == 1)
- {
- ulong address = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
-
- state.Channel.MemoryManager.Physical.Write(address, data);
- }
- else
- {
- var dstCalculator = new OffsetCalculator(
- _params.DstWidth,
- _params.DstHeight,
- _params.DstStride,
- _isLinear,
- _params.DstMemoryLayout.UnpackGobBlocksInY(),
- 1);
-
- int srcOffset = 0;
-
- ulong dstBaseAddress = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
-
- for (int y = _params.DstY; y < _params.DstY + _params.LineCount; y++)
- {
- int x1 = _params.DstX;
- int x2 = _params.DstX + _params.LineLengthIn;
- int x2Trunc = _params.DstX + BitUtils.AlignDown(_params.LineLengthIn, 16);
-
- int x;
-
- for (x = x1; x < x2Trunc; x += 16, srcOffset += 16)
- {
- int dstOffset = dstCalculator.GetOffset(x, y);
-
- ulong dstAddress = dstBaseAddress + (ulong)dstOffset;
-
- Span<byte> pixel = data.Slice(srcOffset, 16);
-
- state.Channel.MemoryManager.Physical.Write(dstAddress, pixel);
- }
-
- for (; x < x2; x++, srcOffset++)
- {
- int dstOffset = dstCalculator.GetOffset(x, y);
-
- ulong dstAddress = dstBaseAddress + (ulong)dstOffset;
-
- Span<byte> pixel = data.Slice(srcOffset, 1);
-
- state.Channel.MemoryManager.Physical.Write(dstAddress, pixel);
- }
- }
- }
-
- _finished = true;
-
- _context.AdvanceSequence();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs b/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs
index 0e7d6fb0..cb4133ec 100644
--- a/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs
@@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
/// <param name="context">GPU context</param>
/// <param name="channel">GPU channel</param>
/// <param name="initializeState">Indicates if the internal state should be initialized. Set to false if part of another engine</param>
- protected InlineToMemoryClass(GpuContext context, GpuChannel channel, bool initializeState)
+ public InlineToMemoryClass(GpuContext context, GpuChannel channel, bool initializeState)
{
_context = context;
_channel = channel;
@@ -70,20 +70,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
/// </summary>
/// <param name="offset">Register byte offset</param>
/// <returns>Data at the specified offset</returns>
- public virtual int Read(int offset) => _state.Read(offset);
+ public int Read(int offset) => _state.Read(offset);
/// <summary>
/// Writes data to the class registers.
/// </summary>
/// <param name="offset">Register byte offset</param>
/// <param name="data">Data to be written</param>
- public virtual void Write(int offset, int data) => _state.Write(offset, data);
+ public void Write(int offset, int data) => _state.Write(offset, data);
/// <summary>
/// Launches Inline-to-Memory engine DMA copy.
/// </summary>
/// <param name="argument">Method call argument</param>
- protected virtual void LaunchDma(int argument)
+ private void LaunchDma(int argument)
{
LaunchDma(ref _state.State, argument);
}
@@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
/// </summary>
/// <param name="state">Current class state</param>
/// <param name="argument">Method call argument</param>
- protected void LaunchDma(ref InlineToMemoryClassState state, int argument)
+ public void LaunchDma(ref InlineToMemoryClassState state, int argument)
{
_isLinear = (argument & 1) != 0;
@@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
/// Pushes a word of data to the Inline-to-Memory engine.
/// </summary>
/// <param name="argument">Method call argument</param>
- protected void LoadInlineData(int argument)
+ public void LoadInlineData(int argument)
{
if (!_finished)
{
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs b/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs
index b056ecc8..b957de08 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/IMacroEE.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
using System;
using System.Collections.Generic;
@@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// <summary>
/// Arguments FIFO.
/// </summary>
- public Queue<int> Fifo { get; }
+ Queue<int> Fifo { get; }
/// <summary>
/// Should execute the GPU Macro code being passed.
@@ -20,6 +20,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// <param name="code">Code to be executed</param>
/// <param name="state">GPU state at the time of the call</param>
/// <param name="arg0">First argument to be passed to the GPU Macro</param>
- void Execute(ReadOnlySpan<int> code, GpuState state, int arg0);
+ void Execute(ReadOnlySpan<int> code, IDeviceState state, int arg0);
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
index 9847f4c0..1a79afb9 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
using System;
namespace Ryujinx.Graphics.Gpu.Engine.MME
@@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// </summary>
/// <param name="code">Program code</param>
/// <param name="state">Current GPU state</param>
- public void Execute(ReadOnlySpan<int> code, GpuState state)
+ public void Execute(ReadOnlySpan<int> code, IDeviceState state)
{
if (_executionPending)
{
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs
index 9a834ca6..0173a7fb 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroInterpreter.cs
@@ -1,5 +1,5 @@
using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
using System;
using System.Collections.Generic;
@@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// <param name="code">Code of the program to execute</param>
/// <param name="state">Current GPU state</param>
/// <param name="arg0">Optional argument passed to the program, 0 if not used</param>
- public void Execute(ReadOnlySpan<int> code, GpuState state, int arg0)
+ public void Execute(ReadOnlySpan<int> code, IDeviceState state, int arg0)
{
Reset();
@@ -55,7 +55,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
FetchOpCode(code);
- while (Step(code, state)) ;
+ while (Step(code, state))
+ {
+ }
// Due to the delay slot, we still need to execute
// one more instruction before we actually exit.
@@ -85,7 +87,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// <param name="code">Program code to execute</param>
/// <param name="state">Current GPU state</param>
/// <returns>True to continue execution, false if the program exited</returns>
- private bool Step(ReadOnlySpan<int> code, GpuState state)
+ private bool Step(ReadOnlySpan<int> code, IDeviceState state)
{
int baseAddr = _pc - 1;
@@ -193,7 +195,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// </summary>
/// <param name="state">Current GPU state</param>
/// <returns>Operation result</returns>
- private int GetAluResult(GpuState state)
+ private int GetAluResult(IDeviceState state)
{
AluOperation op = (AluOperation)(_opCode & 7);
@@ -378,9 +380,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// <param name="state">Current GPU state</param>
/// <param name="reg">Register offset to read</param>
/// <returns>GPU register value</returns>
- private int Read(GpuState state, int reg)
+ private int Read(IDeviceState state, int reg)
{
- return state.Read(reg);
+ return state.Read(reg * 4);
}
/// <summary>
@@ -388,11 +390,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// </summary>
/// <param name="state">Current GPU state</param>
/// <param name="value">Call argument</param>
- private void Send(GpuState state, int value)
+ private void Send(IDeviceState state, int value)
{
- MethodParams meth = new MethodParams(_methAddr, value);
-
- state.CallMethod(meth);
+ state.Write(_methAddr * 4, value);
_methAddr += _methIncr;
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs
index 346ae6cf..f0393dd1 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJit.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
using System;
using System.Collections.Generic;
@@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// <param name="code">Code of the program to execute</param>
/// <param name="state">Current GPU state</param>
/// <param name="arg0">Optional argument passed to the program, 0 if not used</param>
- public void Execute(ReadOnlySpan<int> code, GpuState state, int arg0)
+ public void Execute(ReadOnlySpan<int> code, IDeviceState state, int arg0)
{
if (_execute == null)
{
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs
index e752b9dc..d281a75a 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitCompiler.cs
@@ -1,4 +1,4 @@
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// </summary>
public MacroJitCompiler()
{
- _meth = new DynamicMethod("Macro", typeof(void), new Type[] { typeof(MacroJitContext), typeof(GpuState), typeof(int) });
+ _meth = new DynamicMethod("Macro", typeof(void), new Type[] { typeof(MacroJitContext), typeof(IDeviceState), typeof(int) });
_ilGen = _meth.GetILGenerator();
_gprs = new LocalBuilder[8];
@@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
_ilGen.Emit(OpCodes.Stloc, _gprs[1]);
}
- public delegate void MacroExecute(MacroJitContext context, GpuState state, int arg0);
+ public delegate void MacroExecute(MacroJitContext context, IDeviceState state, int arg0);
/// <summary>
/// Translates a new piece of GPU Macro code into host executable code.
diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs
index ba426dc0..aa31c9ee 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MME/MacroJitContext.cs
@@ -1,5 +1,5 @@
using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.Gpu.State;
+using Ryujinx.Graphics.Device;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gpu.Engine.MME
@@ -36,9 +36,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// <param name="state">Current GPU state</param>
/// <param name="reg">Register offset to read</param>
/// <returns>GPU register value</returns>
- public static int Read(GpuState state, int reg)
+ public static int Read(IDeviceState state, int reg)
{
- return state.Read(reg);
+ return state.Read(reg * 4);
}
/// <summary>
@@ -47,11 +47,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
/// <param name="value">Call argument</param>
/// <param name="state">Current GPU state</param>
/// <param name="methAddr">Address, in words, of the method</param>
- public static void Send(int value, GpuState state, int methAddr)
+ public static void Send(int value, IDeviceState state, int methAddr)
{
- MethodParams meth = new MethodParams(methAddr, value);
-
- state.CallMethod(meth);
+ state.Write(methAddr * 4, value);
}
}
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs b/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs
deleted file mode 100644
index 5f6316dc..00000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
- partial class Methods
- {
- /// <summary>
- /// Clears the current color and depth-stencil buffers.
- /// Which buffers should be cleared is also specified on the argument.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void Clear(GpuState state, int argument)
- {
- ConditionalRenderEnabled renderEnable = GetRenderEnable(state);
-
- if (renderEnable == ConditionalRenderEnabled.False)
- {
- return;
- }
-
- // Scissor and rasterizer discard also affect clears.
- if (state.QueryModified(MethodOffset.ScissorState))
- {
- UpdateScissorState(state);
- }
-
- if (state.QueryModified(MethodOffset.RasterizeEnable))
- {
- UpdateRasterizerState(state);
- }
-
- int index = (argument >> 6) & 0xf;
-
- UpdateRenderTargetState(state, useControl: false, singleUse: index);
-
- state.Channel.TextureManager.UpdateRenderTargets();
-
- bool clearDepth = (argument & 1) != 0;
- bool clearStencil = (argument & 2) != 0;
-
- uint componentMask = (uint)((argument >> 2) & 0xf);
-
- if (componentMask != 0)
- {
- var clearColor = state.Get<ClearColors>(MethodOffset.ClearColors);
-
- ColorF color = new ColorF(
- clearColor.Red,
- clearColor.Green,
- clearColor.Blue,
- clearColor.Alpha);
-
- _context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color);
- }
-
- if (clearDepth || clearStencil)
- {
- float depthValue = state.Get<float>(MethodOffset.ClearDepthValue);
- int stencilValue = state.Get<int> (MethodOffset.ClearStencilValue);
-
- int stencilMask = 0;
-
- if (clearStencil)
- {
- stencilMask = state.Get<StencilTestState>(MethodOffset.StencilTestState).FrontMask;
- }
-
- _context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
- depthValue,
- clearDepth,
- stencilValue,
- stencilMask);
- }
-
- UpdateRenderTargetState(state, useControl: true);
-
- if (renderEnable == ConditionalRenderEnabled.Host)
- {
- _context.Renderer.Pipeline.EndHostConditionalRendering();
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs b/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs
deleted file mode 100644
index fec1cc46..00000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs
+++ /dev/null
@@ -1,343 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Image;
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
- partial class Methods
- {
- private bool _drawIndexed;
-
- private bool _instancedDrawPending;
- private bool _instancedIndexed;
-
- private int _instancedFirstIndex;
- private int _instancedFirstVertex;
- private int _instancedFirstInstance;
- private int _instancedIndexCount;
- private int _instancedDrawStateFirst;
- private int _instancedDrawStateCount;
-
- private int _instanceIndex;
-
- private IbStreamer _ibStreamer;
-
- /// <summary>
- /// Primitive topology of the current draw.
- /// </summary>
- public PrimitiveTopology Topology { get; private set; }
-
- /// <summary>
- /// Finishes the draw call.
- /// This draws geometry on the bound buffers based on the current GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void DrawEnd(GpuState state, int argument)
- {
- var indexBuffer = state.Get<IndexBufferState>(MethodOffset.IndexBufferState);
-
- DrawEnd(state, indexBuffer.First, indexBuffer.Count);
- }
-
- /// <summary>
- /// Finishes the draw call.
- /// This draws geometry on the bound buffers based on the current GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
- /// <param name="indexCount">Number of index buffer elements used on the draw</param>
- private void DrawEnd(GpuState state, int firstIndex, int indexCount)
- {
- ConditionalRenderEnabled renderEnable = GetRenderEnable(state);
-
- if (renderEnable == ConditionalRenderEnabled.False || _instancedDrawPending)
- {
- if (renderEnable == ConditionalRenderEnabled.False)
- {
- PerformDeferredDraws();
- }
-
- _drawIndexed = false;
-
- if (renderEnable == ConditionalRenderEnabled.Host)
- {
- _context.Renderer.Pipeline.EndHostConditionalRendering();
- }
-
- return;
- }
-
- UpdateState(state, firstIndex, indexCount);
-
- bool instanced = _vsUsesInstanceId || _isAnyVbInstanced;
-
- if (instanced)
- {
- _instancedDrawPending = true;
-
- _instancedIndexed = _drawIndexed;
-
- _instancedFirstIndex = firstIndex;
- _instancedFirstVertex = state.Get<int>(MethodOffset.FirstVertex);
- _instancedFirstInstance = state.Get<int>(MethodOffset.FirstInstance);
-
- _instancedIndexCount = indexCount;
-
- var drawState = state.Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);
-
- _instancedDrawStateFirst = drawState.First;
- _instancedDrawStateCount = drawState.Count;
-
- _drawIndexed = false;
-
- if (renderEnable == ConditionalRenderEnabled.Host)
- {
- _context.Renderer.Pipeline.EndHostConditionalRendering();
- }
-
- return;
- }
-
- int firstInstance = state.Get<int>(MethodOffset.FirstInstance);
-
- int inlineIndexCount = _ibStreamer.GetAndResetInlineIndexCount();
-
- if (inlineIndexCount != 0)
- {
- int firstVertex = state.Get<int>(MethodOffset.FirstVertex);
-
- BufferRange br = new BufferRange(_ibStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
-
- state.Channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
-
- _context.Renderer.Pipeline.DrawIndexed(
- inlineIndexCount,
- 1,
- firstIndex,
- firstVertex,
- firstInstance);
- }
- else if (_drawIndexed)
- {
- int firstVertex = state.Get<int>(MethodOffset.FirstVertex);
-
- _context.Renderer.Pipeline.DrawIndexed(
- indexCount,
- 1,
- firstIndex,
- firstVertex,
- firstInstance);
- }
- else
- {
- var drawState = state.Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);
-
- _context.Renderer.Pipeline.Draw(
- drawState.Count,
- 1,
- drawState.First,
- firstInstance);
- }
-
- _drawIndexed = false;
-
- if (renderEnable == ConditionalRenderEnabled.Host)
- {
- _context.Renderer.Pipeline.EndHostConditionalRendering();
- }
- }
-
- /// <summary>
- /// Starts draw.
- /// This sets primitive type and instanced draw parameters.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void DrawBegin(GpuState state, int argument)
- {
- bool incrementInstance = (argument & (1 << 26)) != 0;
- bool resetInstance = (argument & (1 << 27)) == 0;
-
- PrimitiveType type = (PrimitiveType)(argument & 0xffff);
-
- PrimitiveTypeOverride typeOverride = state.Get<PrimitiveTypeOverride>(MethodOffset.PrimitiveTypeOverride);
-
- if (typeOverride != PrimitiveTypeOverride.Invalid)
- {
- DrawBegin(incrementInstance, resetInstance, typeOverride.Convert());
- }
- else
- {
- DrawBegin(incrementInstance, resetInstance, type.Convert());
- }
- }
-
- /// <summary>
- /// Starts draw.
- /// This sets primitive type and instanced draw parameters.
- /// </summary>
- /// <param name="incrementInstance">Indicates if the current instance should be incremented</param>
- /// <param name="resetInstance">Indicates if the current instance should be set to zero</param>
- /// <param name="topology">Primitive topology</param>
- private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveTopology topology)
- {
- if (incrementInstance)
- {
- _instanceIndex++;
- }
- else if (resetInstance)
- {
- PerformDeferredDraws();
-
- _instanceIndex = 0;
- }
-
- _context.Renderer.Pipeline.SetPrimitiveTopology(topology);
-
- Topology = topology;
- }
-
- /// <summary>
- /// Sets the index buffer count.
- /// This also sets internal state that indicates that the next draw is an indexed draw.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void SetIndexBufferCount(GpuState state, int argument)
- {
- _drawIndexed = true;
- }
-
- /// <summary>
- /// Performs a indexed draw with a low number of index buffer elements.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void DrawIndexedSmall(GpuState state, int argument)
- {
- DrawIndexedSmall(state, argument, false);
- }
-
- /// <summary>
- /// Performs a indexed draw with a low number of index buffer elements.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void DrawIndexedSmall2(GpuState state, int argument)
- {
- DrawIndexedSmall(state, argument);
- }
-
- /// <summary>
- /// Performs a indexed draw with a low number of index buffer elements,
- /// while also pre-incrementing the current instance value.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void DrawIndexedSmallIncInstance(GpuState state, int argument)
- {
- DrawIndexedSmall(state, argument, true);
- }
-
- /// <summary>
- /// Performs a indexed draw with a low number of index buffer elements,
- /// while also pre-incrementing the current instance value.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void DrawIndexedSmallIncInstance2(GpuState state, int argument)
- {
- DrawIndexedSmallIncInstance(state, argument);
- }
-
- /// <summary>
- /// Performs a indexed draw with a low number of index buffer elements,
- /// while optionally also pre-incrementing the current instance value.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- /// <param name="instanced">True to increment the current instance value, false otherwise</param>
- private void DrawIndexedSmall(GpuState state, int argument, bool instanced)
- {
- PrimitiveTypeOverride typeOverride = state.Get<PrimitiveTypeOverride>(MethodOffset.PrimitiveTypeOverride);
-
- DrawBegin(instanced, !instanced, typeOverride.Convert());
-
- int firstIndex = argument & 0xffff;
- int indexCount = (argument >> 16) & 0xfff;
-
- bool oldDrawIndexed = _drawIndexed;
-
- _drawIndexed = true;
-
- DrawEnd(state, firstIndex, indexCount);
-
- _drawIndexed = oldDrawIndexed;
- }
-
- /// <summary>
- /// Pushes four 8-bit index buffer elements.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void VbElementU8(GpuState state, int argument)
- {
- _ibStreamer.VbElementU8(_context.Renderer, argument);
- }
-
- /// <summary>
- /// Pushes two 16-bit index buffer elements.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void VbElementU16(GpuState state, int argument)
- {
- _ibStreamer.VbElementU16(_context.Renderer, argument);
- }
-
- /// <summary>
- /// Pushes one 32-bit index buffer element.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void VbElementU32(GpuState state, int argument)
- {
- _ibStreamer.VbElementU32(_context.Renderer, argument);
- }
-
- /// <summary>
- /// Perform any deferred draws.
- /// This is used for instanced draws.
- /// Since each instance is a separate draw, we defer the draw and accumulate the instance count.
- /// Once we detect the last instanced draw, then we perform the host instanced draw,
- /// with the accumulated instance count.
- /// </summary>
- public void PerformDeferredDraws()
- {
- // Perform any pending instanced draw.
- if (_instancedDrawPending)
- {
- _instancedDrawPending = false;
-
- if (_instancedIndexed)
- {
- _context.Renderer.Pipeline.DrawIndexed(
- _instancedIndexCount,
- _instanceIndex + 1,
- _instancedFirstIndex,
- _instancedFirstVertex,
- _instancedFirstInstance);
- }
- else
- {
- _context.Renderer.Pipeline.Draw(
- _instancedDrawStateCount,
- _instanceIndex + 1,
- _instancedDrawStateFirst,
- _instancedFirstInstance);
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodFirmware.cs b/Ryujinx.Graphics.Gpu/Engine/MethodFirmware.cs
deleted file mode 100644
index 25a48af9..00000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodFirmware.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
- partial class Methods
- {
- private void FirmwareCall4(GpuState state, int argument)
- {
- state.Write(0xd00, 1);
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodIncrementSyncpoint.cs b/Ryujinx.Graphics.Gpu/Engine/MethodIncrementSyncpoint.cs
deleted file mode 100644
index 9c22275d..00000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodIncrementSyncpoint.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
- partial class Methods
- {
- /// <summary>
- /// Performs an incrementation on a syncpoint.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- public void IncrementSyncpoint(GpuState state, int argument)
- {
- uint syncpointId = (uint)(argument) & 0xFFFF;
-
- _context.CreateHostSyncIfNeeded();
- _context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result.
- _context.Synchronization.IncrementSyncpoint(syncpointId);
- }
- }
-}
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs b/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
deleted file mode 100644
index 2dd0bbfa..00000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.State;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
- partial class Methods
- {
- private const int NsToTicksFractionNumerator = 384;
- private const int NsToTicksFractionDenominator = 625;
-
- /// <summary>
- /// Writes a GPU counter to guest memory.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void Report(GpuState state, int argument)
- {
- SemaphoreOperation op = (SemaphoreOperation)(argument & 3);
- ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f);
-
- switch (op)
- {
- case SemaphoreOperation.Release: ReleaseSemaphore(state); break;
- case SemaphoreOperation.Counter: ReportCounter(state, type); break;
- }
- }
-
- /// <summary>
- /// Writes (or Releases) a GPU semaphore value to guest memory.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void ReleaseSemaphore(GpuState state)
- {
- var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
-
- state.Channel.MemoryManager.Write(rs.Address.Pack(), rs.Payload);
-
- _context.AdvanceSequence();
- }
-
- /// <summary>
- /// Packed GPU counter data (including GPU timestamp) in memory.
- /// </summary>
- private struct CounterData
- {
- public ulong Counter;
- public ulong Timestamp;
- }
-
- /// <summary>
- /// Writes a GPU counter to guest memory.
- /// This also writes the current timestamp value.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="type">Counter to be written to memory</param>
- private void ReportCounter(GpuState state, ReportCounterType type)
- {
- var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
-
- ulong gpuVa = rs.Address.Pack();
-
- ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
-
- if (GraphicsConfig.FastGpuTime)
- {
- // Divide by some amount to report time as if operations were performed faster than they really are.
- // This can prevent some games from switching to a lower resolution because rendering is too slow.
- ticks /= 256;
- }
-
- ICounterEvent counter = null;
-
- EventHandler<ulong> resultHandler = (object evt, ulong result) =>
- {
- CounterData counterData = new CounterData();
-
- counterData.Counter = result;
- counterData.Timestamp = ticks;
-
- if (counter?.Invalid != true)
- {
- state.Channel.MemoryManager.Write(gpuVa, counterData);
- }
- };
-
- switch (type)
- {
- case ReportCounterType.Zero:
- resultHandler(null, 0);
- break;
- case ReportCounterType.SamplesPassed:
- counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler);
- break;
- case ReportCounterType.PrimitivesGenerated:
- counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler);
- break;
- case ReportCounterType.TransformFeedbackPrimitivesWritten:
- counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler);
- break;
- }
-
- state.Channel.MemoryManager.CounterCache.AddOrUpdate(gpuVa, counter);
- }
-
- /// <summary>
- /// Converts a nanoseconds timestamp value to Maxwell time ticks.
- /// </summary>
- /// <remarks>
- /// The frequency is 614400000 Hz.
- /// </remarks>
- /// <param name="nanoseconds">Timestamp in nanoseconds</param>
- /// <returns>Maxwell ticks</returns>
- private static ulong ConvertNanosecondsToTicks(ulong nanoseconds)
- {
- // We need to divide first to avoid overflows.
- // We fix up the result later by calculating the difference and adding
- // that to the result.
- ulong divided = nanoseconds / NsToTicksFractionDenominator;
-
- ulong rounded = divided * NsToTicksFractionDenominator;
-
- ulong errorBias = (nanoseconds - rounded) * NsToTicksFractionNumerator / NsToTicksFractionDenominator;
-
- return divided * NsToTicksFractionNumerator + errorBias;
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs b/Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs
deleted file mode 100644
index 79f8e1e8..00000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
- partial class Methods
- {
- /// <summary>
- /// Resets the value of an internal GPU counter back to zero.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void ResetCounter(GpuState state, int argument)
- {
- ResetCounterType type = (ResetCounterType)argument;
-
- switch (type)
- {
- case ResetCounterType.SamplesPassed:
- _context.Renderer.ResetCounter(CounterType.SamplesPassed);
- break;
- case ResetCounterType.PrimitivesGenerated:
- _context.Renderer.ResetCounter(CounterType.PrimitivesGenerated);
- break;
- case ResetCounterType.TransformFeedbackPrimitivesWritten:
- _context.Renderer.ResetCounter(CounterType.TransformFeedbackPrimitivesWritten);
- break;
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
deleted file mode 100644
index 0746efa5..00000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using Ryujinx.Graphics.Gpu.State;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
- partial class Methods
- {
- /// <summary>
- /// Binds a uniform buffer for the vertex shader stage.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void UniformBufferBindVertex(GpuState state, int argument)
- {
- UniformBufferBind(state, argument, ShaderType.Vertex);
- }
-
- /// <summary>
- /// Binds a uniform buffer for the tessellation control shader stage.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void UniformBufferBindTessControl(GpuState state, int argument)
- {
- UniformBufferBind(state, argument, ShaderType.TessellationControl);
- }
-
- /// <summary>
- /// Binds a uniform buffer for the tessellation evaluation shader stage.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void UniformBufferBindTessEvaluation(GpuState state, int argument)
- {
- UniformBufferBind(state, argument, ShaderType.TessellationEvaluation);
- }
-
- /// <summary>
- /// Binds a uniform buffer for the geometry shader stage.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void UniformBufferBindGeometry(GpuState state, int argument)
- {
- UniformBufferBind(state, argument, ShaderType.Geometry);
- }
-
- /// <summary>
- /// Binds a uniform buffer for the fragment shader stage.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- private void UniformBufferBindFragment(GpuState state, int argument)
- {
- UniformBufferBind(state, argument, ShaderType.Fragment);
- }
-
- /// <summary>
- ///Binds a uniform buffer for the specified shader stage.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">Method call argument</param>
- /// <param name="type">Shader stage that will access the uniform buffer</param>
- private void UniformBufferBind(GpuState state, int argument, ShaderType type)
- {
- bool enable = (argument & 1) != 0;
-
- int index = (argument >> 4) & 0x1f;
-
- FlushUboDirty(state.Channel.MemoryManager);
-
- if (enable)
- {
- var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
-
- ulong address = uniformBuffer.Address.Pack();
-
- state.Channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, address, (uint)uniformBuffer.Size);
- }
- else
- {
- state.Channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, 0, 0);
- }
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
deleted file mode 100644
index 49c8cda4..00000000
--- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.State;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
- partial class Methods
- {
- // State associated with direct uniform buffer updates.
- // This state is used to attempt to batch together consecutive updates.
- private ulong _ubBeginCpuAddress = 0;
- private ulong _ubFollowUpAddress = 0;
- private ulong _ubByteCount = 0;
-
- /// <summary>
- /// Flushes any queued ubo updates.
- /// </summary>
- /// <param name="memoryManager">GPU memory manager where the uniform buffer is mapped</param>
- public void FlushUboDirty(MemoryManager memoryManager)
- {
- if (_ubFollowUpAddress != 0)
- {
- memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
-
- _ubFollowUpAddress = 0;
- }
- }
-
- /// <summary>
- /// Updates the uniform buffer data with inline data.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="argument">New uniform buffer data word</param>
- private void UniformBufferUpdate(GpuState state, int argument)
- {
- var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
-
- ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
-
- if (_ubFollowUpAddress != address)
- {
- FlushUboDirty(state.Channel.MemoryManager);
-
- _ubByteCount = 0;
- _ubBeginCpuAddress = state.Channel.MemoryManager.Translate(address);
- }
-
- var byteData = MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1));
- state.Channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
-
- _ubFollowUpAddress = address + 4;
- _ubByteCount += 4;
-
- state.SetUniformBufferOffset(uniformBuffer.Offset + 4);
- }
-
- /// <summary>
- /// Updates the uniform buffer data with inline data.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="data">Data to be written to the uniform buffer</param>
- public void UniformBufferUpdate(GpuState state, ReadOnlySpan<int> data)
- {
- var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
-
- ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
-
- ulong size = (ulong)data.Length * 4;
-
- if (_ubFollowUpAddress != address)
- {
- FlushUboDirty(state.Channel.MemoryManager);
-
- _ubByteCount = 0;
- _ubBeginCpuAddress = state.Channel.MemoryManager.Translate(address);
- }
-
- var byteData = MemoryMarshal.Cast<int, byte>(data);
- state.Channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
-
- _ubFollowUpAddress = address + size;
- _ubByteCount += size;
-
- state.SetUniformBufferOffset(uniformBuffer.Offset + data.Length * 4);
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
deleted file mode 100644
index 756d56d9..00000000
--- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs
+++ /dev/null
@@ -1,1138 +0,0 @@
-using Ryujinx.Common.Logging;
-using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Image;
-using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.Shader;
-using Ryujinx.Graphics.Gpu.State;
-using Ryujinx.Graphics.Shader;
-using Ryujinx.Graphics.Texture;
-using System;
-using System.Linq;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.Graphics.Gpu.Engine
-{
- using Texture = Image.Texture;
-
- /// <summary>
- /// GPU method implementations.
- /// </summary>
- partial class Methods
- {
- private readonly GpuContext _context;
- private readonly ShaderProgramInfo[] _currentProgramInfo;
-
- private bool _isAnyVbInstanced;
- private bool _vsUsesInstanceId;
- private byte _vsClipDistancesWritten;
-
- private bool _forceShaderUpdate;
-
- private bool _prevTfEnable;
-
- /// <summary>
- /// Creates a new instance of the GPU methods class.
- /// </summary>
- /// <param name="context">GPU context</param>
- public Methods(GpuContext context)
- {
- _context = context;
-
- _currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
- }
-
- /// <summary>
- /// Register callback for GPU method calls that triggers an action on the GPU.
- /// </summary>
- /// <param name="state">GPU state where the triggers will be registered</param>
- public void RegisterCallbacks(GpuState state)
- {
- state.RegisterCallback(MethodOffset.LaunchDma, LaunchDma);
- state.RegisterCallback(MethodOffset.LoadInlineData, LoadInlineData);
-
- state.RegisterCallback(MethodOffset.SyncpointAction, IncrementSyncpoint);
-
- state.RegisterCallback(MethodOffset.TextureBarrier, TextureBarrier);
- state.RegisterCallback(MethodOffset.TextureBarrierTiled, TextureBarrierTiled);
-
- state.RegisterCallback(MethodOffset.VbElementU8, VbElementU8);
- state.RegisterCallback(MethodOffset.VbElementU16, VbElementU16);
- state.RegisterCallback(MethodOffset.VbElementU32, VbElementU32);
-
- state.RegisterCallback(MethodOffset.ResetCounter, ResetCounter);
-
- state.RegisterCallback(MethodOffset.DrawEnd, DrawEnd);
- state.RegisterCallback(MethodOffset.DrawBegin, DrawBegin);
- state.RegisterCallback(MethodOffset.DrawIndexedSmall, DrawIndexedSmall);
- state.RegisterCallback(MethodOffset.DrawIndexedSmall2, DrawIndexedSmall2);
- state.RegisterCallback(MethodOffset.DrawIndexedSmallIncInstance, DrawIndexedSmallIncInstance);
- state.RegisterCallback(MethodOffset.DrawIndexedSmallIncInstance2, DrawIndexedSmallIncInstance2);
-
- state.RegisterCallback(MethodOffset.IndexBufferCount, SetIndexBufferCount);
-
- state.RegisterCallback(MethodOffset.Clear, Clear);
-
- state.RegisterCallback(MethodOffset.Report, Report);
-
- state.RegisterCallback(MethodOffset.FirmwareCall4, FirmwareCall4);
-
- state.RegisterCallback(MethodOffset.UniformBufferUpdateData, 16, UniformBufferUpdate);
-
- state.RegisterCallback(MethodOffset.UniformBufferBindVertex, UniformBufferBindVertex);
- state.RegisterCallback(MethodOffset.UniformBufferBindTessControl, UniformBufferBindTessControl);
- state.RegisterCallback(MethodOffset.UniformBufferBindTessEvaluation, UniformBufferBindTessEvaluation);
- state.RegisterCallback(MethodOffset.UniformBufferBindGeometry, UniformBufferBindGeometry);
- state.RegisterCallback(MethodOffset.UniformBufferBindFragment, UniformBufferBindFragment);
- }
-
- /// <summary>
- /// Updates host state based on the current guest GPU state.
- /// </summary>
- /// <param name="state">Guest GPU state</param>
- /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
- /// <param name="indexCount">Number of index buffer elements used on the draw</param>
- private void UpdateState(GpuState state, int firstIndex, int indexCount)
- {
- bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable);
-
- if (!tfEnable && _prevTfEnable)
- {
- _context.Renderer.Pipeline.EndTransformFeedback();
- _prevTfEnable = false;
- }
-
- FlushUboDirty(state.Channel.MemoryManager);
-
- // Shaders must be the first one to be updated if modified, because
- // some of the other state depends on information from the currently
- // bound shaders.
- if (state.QueryModified(MethodOffset.ShaderBaseAddress, MethodOffset.ShaderState) || _forceShaderUpdate)
- {
- _forceShaderUpdate = false;
-
- UpdateShaderState(state);
- }
-
- if (state.QueryModified(MethodOffset.TfBufferState))
- {
- UpdateTfBufferState(state);
- }
-
- if (state.QueryModified(MethodOffset.ClipDistanceEnable))
- {
- UpdateUserClipState(state);
- }
-
- if (state.QueryModified(MethodOffset.RasterizeEnable))
- {
- UpdateRasterizerState(state);
- }
-
- if (state.QueryModified(MethodOffset.RtColorState,
- MethodOffset.RtDepthStencilState,
- MethodOffset.RtControl,
- MethodOffset.RtDepthStencilSize,
- MethodOffset.RtDepthStencilEnable))
- {
- UpdateRenderTargetState(state, useControl: true);
- }
-
- if (state.QueryModified(MethodOffset.ScissorState))
- {
- UpdateScissorState(state);
- }
-
- if (state.QueryModified(MethodOffset.ViewVolumeClipControl))
- {
- UpdateDepthClampState(state);
- }
-
- if (state.QueryModified(MethodOffset.AlphaTestEnable,
- MethodOffset.AlphaTestRef,
- MethodOffset.AlphaTestFunc))
- {
- UpdateAlphaTestState(state);
- }
-
- if (state.QueryModified(MethodOffset.DepthTestEnable,
- MethodOffset.DepthWriteEnable,
- MethodOffset.DepthTestFunc))
- {
- UpdateDepthTestState(state);
- }
-
- if (state.QueryModified(MethodOffset.DepthMode,
- MethodOffset.ViewportTransform,
- MethodOffset.ViewportExtents))
- {
- UpdateViewportTransform(state);
- }
-
- if (state.QueryModified(MethodOffset.DepthBiasState,
- MethodOffset.DepthBiasFactor,
- MethodOffset.DepthBiasUnits,
- MethodOffset.DepthBiasClamp))
- {
- UpdateDepthBiasState(state);
- }
-
- if (state.QueryModified(MethodOffset.StencilBackMasks,
- MethodOffset.StencilTestState,
- MethodOffset.StencilBackTestState))
- {
- UpdateStencilTestState(state);
- }
-
- // Pools.
- if (state.QueryModified(MethodOffset.SamplerPoolState, MethodOffset.SamplerIndex))
- {
- UpdateSamplerPoolState(state);
- }
-
- if (state.QueryModified(MethodOffset.TexturePoolState))
- {
- UpdateTexturePoolState(state);
- }
-
- // Rasterizer state.
- if (state.QueryModified(MethodOffset.VertexAttribState))
- {
- UpdateVertexAttribState(state);
- }
-
- if (state.QueryModified(MethodOffset.LineWidthSmooth, MethodOffset.LineSmoothEnable))
- {
- UpdateLineState(state);
- }
-
- if (state.QueryModified(MethodOffset.PointSize,
- MethodOffset.VertexProgramPointSize,
- MethodOffset.PointSpriteEnable,
- MethodOffset.PointCoordReplace))
- {
- UpdatePointState(state);
- }
-
- if (state.QueryModified(MethodOffset.PrimitiveRestartState))
- {
- UpdatePrimitiveRestartState(state);
- }
-
- if (state.QueryModified(MethodOffset.IndexBufferState))
- {
- UpdateIndexBufferState(state, firstIndex, indexCount);
- }
-
- if (state.QueryModified(MethodOffset.VertexBufferDrawState,
- MethodOffset.VertexBufferInstanced,
- MethodOffset.VertexBufferState,
- MethodOffset.VertexBufferEndAddress))
- {
- UpdateVertexBufferState(state);
- }
-
- if (state.QueryModified(MethodOffset.FaceState))
- {
- UpdateFaceState(state);
- }
-
- if (state.QueryModified(MethodOffset.RtColorMaskShared, MethodOffset.RtColorMask))
- {
- UpdateRtColorMask(state);
- }
-
- if (state.QueryModified(MethodOffset.BlendIndependent,
- MethodOffset.BlendConstant,
- MethodOffset.BlendStateCommon,
- MethodOffset.BlendEnableCommon,
- MethodOffset.BlendEnable,
- MethodOffset.BlendState))
- {
- UpdateBlendState(state);
- }
-
- if (state.QueryModified(MethodOffset.LogicOpState))
- {
- UpdateLogicOpState(state);
- }
-
- CommitBindings(state);
-
- if (tfEnable && !_prevTfEnable)
- {
- _context.Renderer.Pipeline.BeginTransformFeedback(Topology);
- _prevTfEnable = true;
- }
- }
-
- /// <summary>
- /// Updates Rasterizer primitive discard state based on guest gpu state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateRasterizerState(GpuState state)
- {
- Boolean32 enable = state.Get<Boolean32>(MethodOffset.RasterizeEnable);
- _context.Renderer.Pipeline.SetRasterizerDiscard(!enable);
- }
-
- /// <summary>
- /// Ensures that the bindings are visible to the host GPU.
- /// Note: this actually performs the binding using the host graphics API.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void CommitBindings(GpuState state)
- {
- UpdateStorageBuffers(state);
-
- state.Channel.TextureManager.CommitGraphicsBindings();
- state.Channel.BufferManager.CommitGraphicsBindings();
- }
-
- /// <summary>
- /// Updates storage buffer bindings.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateStorageBuffers(GpuState state)
- {
- for (int stage = 0; stage < _currentProgramInfo.Length; stage++)
- {
- ShaderProgramInfo info = _currentProgramInfo[stage];
-
- if (info == null)
- {
- continue;
- }
-
- for (int index = 0; index < info.SBuffers.Count; index++)
- {
- BufferDescriptor sb = info.SBuffers[index];
-
- ulong sbDescAddress = state.Channel.BufferManager.GetGraphicsUniformBufferAddress(stage, 0);
-
- int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10;
-
- sbDescAddress += (ulong)sbDescOffset;
-
- SbDescriptor sbDescriptor = state.Channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
-
- state.Channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
- }
- }
- }
-
- /// <summary>
- /// Updates render targets (color and depth-stencil buffers) based on current render target state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="useControl">Use draw buffers information from render target control register</param>
- /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
- private void UpdateRenderTargetState(GpuState state, bool useControl, int singleUse = -1)
- {
- var memoryManager = state.Channel.MemoryManager;
- var rtControl = state.Get<RtControl>(MethodOffset.RtControl);
-
- int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
-
- var msaaMode = state.Get<TextureMsaaMode>(MethodOffset.RtMsaaMode);
-
- int samplesInX = msaaMode.SamplesInX();
- int samplesInY = msaaMode.SamplesInY();
-
- var scissor = state.Get<ScreenScissorState>(MethodOffset.ScreenScissorState);
- Size sizeHint = new Size(scissor.X + scissor.Width, scissor.Y + scissor.Height, 1);
-
- bool changedScale = false;
-
- for (int index = 0; index < Constants.TotalRenderTargets; index++)
- {
- int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index;
-
- var colorState = state.Get<RtColorState>(MethodOffset.RtColorState, rtIndex);
-
- if (index >= count || !IsRtEnabled(colorState))
- {
- changedScale |= state.Channel.TextureManager.SetRenderTargetColor(index, null);
-
- continue;
- }
-
- Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
- memoryManager,
- colorState,
- samplesInX,
- samplesInY,
- sizeHint);
-
- changedScale |= state.Channel.TextureManager.SetRenderTargetColor(index, color);
- }
-
- bool dsEnable = state.Get<Boolean32>(MethodOffset.RtDepthStencilEnable);
-
- Texture depthStencil = null;
-
- if (dsEnable)
- {
- var dsState = state.Get<RtDepthStencilState>(MethodOffset.RtDepthStencilState);
- var dsSize = state.Get<Size3D>(MethodOffset.RtDepthStencilSize);
-
- depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
- memoryManager,
- dsState,
- dsSize,
- samplesInX,
- samplesInY,
- sizeHint);
- }
-
- changedScale |= state.Channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);
-
- if (changedScale)
- {
- state.Channel.TextureManager.UpdateRenderTargetScale(singleUse);
- _context.Renderer.Pipeline.SetRenderTargetScale(state.Channel.TextureManager.RenderTargetScale);
-
- UpdateViewportTransform(state);
- UpdateScissorState(state);
- }
- }
-
- /// <summary>
- /// Checks if a render target color buffer is used.
- /// </summary>
- /// <param name="colorState">Color buffer information</param>
- /// <returns>True if the specified buffer is enabled/used, false otherwise</returns>
- private static bool IsRtEnabled(RtColorState colorState)
- {
- // Colors are disabled by writing 0 to the format.
- return colorState.Format != 0 && colorState.WidthOrStride != 0;
- }
-
- /// <summary>
- /// Updates host scissor test state based on current GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateScissorState(GpuState state)
- {
- for (int index = 0; index < Constants.TotalViewports; index++)
- {
- ScissorState scissor = state.Get<ScissorState>(MethodOffset.ScissorState, index);
-
- bool enable = scissor.Enable && (scissor.X1 != 0 || scissor.Y1 != 0 || scissor.X2 != 0xffff || scissor.Y2 != 0xffff);
-
- if (enable)
- {
- int x = scissor.X1;
- int y = scissor.Y1;
- int width = scissor.X2 - x;
- int height = scissor.Y2 - y;
-
- float scale = state.Channel.TextureManager.RenderTargetScale;
- if (scale != 1f)
- {
- x = (int)(x * scale);
- y = (int)(y * scale);
- width = (int)Math.Ceiling(width * scale);
- height = (int)Math.Ceiling(height * scale);
- }
-
- _context.Renderer.Pipeline.SetScissor(index, true, x, y, width, height);
- }
- else
- {
- _context.Renderer.Pipeline.SetScissor(index, false, 0, 0, 0, 0);
- }
- }
- }
-
- /// <summary>
- /// Updates host depth clamp state based on current GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateDepthClampState(GpuState state)
- {
- ViewVolumeClipControl clip = state.Get<ViewVolumeClipControl>(MethodOffset.ViewVolumeClipControl);
- _context.Renderer.Pipeline.SetDepthClamp((clip & ViewVolumeClipControl.DepthClampDisabled) == 0);
- }
-
- /// <summary>
- /// Updates host alpha test state based on current GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateAlphaTestState(GpuState state)
- {
- _context.Renderer.Pipeline.SetAlphaTest(
- state.Get<Boolean32>(MethodOffset.AlphaTestEnable),
- state.Get<float>(MethodOffset.AlphaTestRef),
- state.Get<CompareOp>(MethodOffset.AlphaTestFunc));
- }
-
- /// <summary>
- /// Updates host depth test state based on current GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateDepthTestState(GpuState state)
- {
- _context.Renderer.Pipeline.SetDepthTest(new DepthTestDescriptor(
- state.Get<Boolean32>(MethodOffset.DepthTestEnable),
- state.Get<Boolean32>(MethodOffset.DepthWriteEnable),
- state.Get<CompareOp>(MethodOffset.DepthTestFunc)));
- }
-
- /// <summary>
- /// Updates host viewport transform and clipping state based on current GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateViewportTransform(GpuState state)
- {
- var yControl = state.Get<YControl> (MethodOffset.YControl);
- var face = state.Get<FaceState>(MethodOffset.FaceState);
-
- UpdateFrontFace(yControl, face.FrontFace);
-
- bool flipY = yControl.HasFlag(YControl.NegateY);
-
- Span<Viewport> viewports = stackalloc Viewport[Constants.TotalViewports];
-
- for (int index = 0; index < Constants.TotalViewports; index++)
- {
- var transform = state.Get<ViewportTransform>(MethodOffset.ViewportTransform, index);
- var extents = state.Get<ViewportExtents> (MethodOffset.ViewportExtents, index);
-
- float scaleX = MathF.Abs(transform.ScaleX);
- float scaleY = transform.ScaleY;
-
- if (flipY)
- {
- scaleY = -scaleY;
- }
-
- if (!_context.Capabilities.SupportsViewportSwizzle && transform.UnpackSwizzleY() == ViewportSwizzle.NegativeY)
- {
- scaleY = -scaleY;
- }
-
- if (index == 0)
- {
- // Try to guess the depth mode being used on the high level API
- // based on current transform.
- // It is setup like so by said APIs:
- // If depth mode is ZeroToOne:
- // TranslateZ = Near
- // ScaleZ = Far - Near
- // If depth mode is MinusOneToOne:
- // TranslateZ = (Near + Far) / 2
- // ScaleZ = (Far - Near) / 2
- // DepthNear/Far are sorted such as that Near is always less than Far.
- DepthMode depthMode = extents.DepthNear != transform.TranslateZ &&
- extents.DepthFar != transform.TranslateZ ? DepthMode.MinusOneToOne : DepthMode.ZeroToOne;
-
- _context.Renderer.Pipeline.SetDepthMode(depthMode);
- }
-
- float x = transform.TranslateX - scaleX;
- float y = transform.TranslateY - scaleY;
-
- float width = scaleX * 2;
- float height = scaleY * 2;
-
- float scale = state.Channel.TextureManager.RenderTargetScale;
- if (scale != 1f)
- {
- x *= scale;
- y *= scale;
- width *= scale;
- height *= scale;
- }
-
- RectangleF region = new RectangleF(x, y, width, height);
-
- ViewportSwizzle swizzleX = transform.UnpackSwizzleX();
- ViewportSwizzle swizzleY = transform.UnpackSwizzleY();
- ViewportSwizzle swizzleZ = transform.UnpackSwizzleZ();
- ViewportSwizzle swizzleW = transform.UnpackSwizzleW();
-
- float depthNear = extents.DepthNear;
- float depthFar = extents.DepthFar;
-
- if (transform.ScaleZ < 0)
- {
- float temp = depthNear;
- depthNear = depthFar;
- depthFar = temp;
- }
-
- viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar);
- }
-
- _context.Renderer.Pipeline.SetViewports(0, viewports);
- }
-
- /// <summary>
- /// Updates host depth bias (also called polygon offset) state based on current GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateDepthBiasState(GpuState state)
- {
- var depthBias = state.Get<DepthBiasState>(MethodOffset.DepthBiasState);
-
- float factor = state.Get<float>(MethodOffset.DepthBiasFactor);
- float units = state.Get<float>(MethodOffset.DepthBiasUnits);
- float clamp = state.Get<float>(MethodOffset.DepthBiasClamp);
-
- PolygonModeMask enables;
-
- enables = (depthBias.PointEnable ? PolygonModeMask.Point : 0);
- enables |= (depthBias.LineEnable ? PolygonModeMask.Line : 0);
- enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0);
-
- _context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp);
- }
-
- /// <summary>
- /// Updates host stencil test state based on current GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateStencilTestState(GpuState state)
- {
- var backMasks = state.Get<StencilBackMasks>(MethodOffset.StencilBackMasks);
- var test = state.Get<StencilTestState>(MethodOffset.StencilTestState);
- var backTest = state.Get<StencilBackTestState>(MethodOffset.StencilBackTestState);
-
- CompareOp backFunc;
- StencilOp backSFail;
- StencilOp backDpPass;
- StencilOp backDpFail;
- int backFuncRef;
- int backFuncMask;
- int backMask;
-
- if (backTest.TwoSided)
- {
- backFunc = backTest.BackFunc;
- backSFail = backTest.BackSFail;
- backDpPass = backTest.BackDpPass;
- backDpFail = backTest.BackDpFail;
- backFuncRef = backMasks.FuncRef;
- backFuncMask = backMasks.FuncMask;
- backMask = backMasks.Mask;
- }
- else
- {
- backFunc = test.FrontFunc;
- backSFail = test.FrontSFail;
- backDpPass = test.FrontDpPass;
- backDpFail = test.FrontDpFail;
- backFuncRef = test.FrontFuncRef;
- backFuncMask = test.FrontFuncMask;
- backMask = test.FrontMask;
- }
-
- _context.Renderer.Pipeline.SetStencilTest(new StencilTestDescriptor(
- test.Enable,
- test.FrontFunc,
- test.FrontSFail,
- test.FrontDpPass,
- test.FrontDpFail,
- test.FrontFuncRef,
- test.FrontFuncMask,
- test.FrontMask,
- backFunc,
- backSFail,
- backDpPass,
- backDpFail,
- backFuncRef,
- backFuncMask,
- backMask));
- }
-
- /// <summary>
- /// Updates current sampler pool address and size based on guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateSamplerPoolState(GpuState state)
- {
- var texturePool = state.Get<PoolState>(MethodOffset.TexturePoolState);
- var samplerPool = state.Get<PoolState>(MethodOffset.SamplerPoolState);
-
- var samplerIndex = state.Get<SamplerIndex>(MethodOffset.SamplerIndex);
-
- int maximumId = samplerIndex == SamplerIndex.ViaHeaderIndex
- ? texturePool.MaximumId
- : samplerPool.MaximumId;
-
- state.Channel.TextureManager.SetGraphicsSamplerPool(samplerPool.Address.Pack(), maximumId, samplerIndex);
- }
-
- /// <summary>
- /// Updates current texture pool address and size based on guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateTexturePoolState(GpuState state)
- {
- var texturePool = state.Get<PoolState>(MethodOffset.TexturePoolState);
-
- state.Channel.TextureManager.SetGraphicsTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);
- state.Channel.TextureManager.SetGraphicsTextureBufferIndex(state.Get<int>(MethodOffset.TextureBufferIndex));
- }
-
- /// <summary>
- /// Updates host vertex attributes based on guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateVertexAttribState(GpuState state)
- {
- Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs];
-
- for (int index = 0; index < Constants.TotalVertexAttribs; index++)
- {
- var vertexAttrib = state.Get<VertexAttribState>(MethodOffset.VertexAttribState, index);
-
- if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
- {
- Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
-
- format = Format.R32G32B32A32Float;
- }
-
- vertexAttribs[index] = new VertexAttribDescriptor(
- vertexAttrib.UnpackBufferIndex(),
- vertexAttrib.UnpackOffset(),
- vertexAttrib.UnpackIsConstant(),
- format);
- }
-
- _context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs);
- }
-
- /// <summary>
- /// Updates host line width based on guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateLineState(GpuState state)
- {
- float width = state.Get<float>(MethodOffset.LineWidthSmooth);
- bool smooth = state.Get<Boolean32>(MethodOffset.LineSmoothEnable);
-
- _context.Renderer.Pipeline.SetLineParameters(width, smooth);
- }
-
- /// <summary>
- /// Updates host point size based on guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdatePointState(GpuState state)
- {
- float size = state.Get<float>(MethodOffset.PointSize);
- bool isProgramPointSize = state.Get<Boolean32>(MethodOffset.VertexProgramPointSize);
- bool enablePointSprite = state.Get<Boolean32>(MethodOffset.PointSpriteEnable);
-
- // TODO: Need to figure out a way to map PointCoordReplace enable bit.
- Origin origin = (state.Get<int>(MethodOffset.PointCoordReplace) & 4) == 0 ? Origin.LowerLeft : Origin.UpperLeft;
-
- _context.Renderer.Pipeline.SetPointParameters(size, isProgramPointSize, enablePointSprite, origin);
- }
-
- /// <summary>
- /// Updates host primitive restart based on guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdatePrimitiveRestartState(GpuState state)
- {
- PrimitiveRestartState primitiveRestart = state.Get<PrimitiveRestartState>(MethodOffset.PrimitiveRestartState);
-
- _context.Renderer.Pipeline.SetPrimitiveRestart(
- primitiveRestart.Enable,
- primitiveRestart.Index);
- }
-
- /// <summary>
- /// Updates host index buffer binding based on guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
- /// <param name="indexCount">Number of index buffer elements used on the draw</param>
- private void UpdateIndexBufferState(GpuState state, int firstIndex, int indexCount)
- {
- var indexBuffer = state.Get<IndexBufferState>(MethodOffset.IndexBufferState);
-
- if (indexCount == 0)
- {
- return;
- }
-
- ulong gpuVa = indexBuffer.Address.Pack();
-
- // Do not use the end address to calculate the size, because
- // the result may be much larger than the real size of the index buffer.
- ulong size = (ulong)(firstIndex + indexCount);
-
- switch (indexBuffer.Type)
- {
- case IndexType.UShort: size *= 2; break;
- case IndexType.UInt: size *= 4; break;
- }
-
- state.Channel.BufferManager.SetIndexBuffer(gpuVa, size, indexBuffer.Type);
-
- // The index buffer affects the vertex buffer size calculation, we
- // need to ensure that they are updated.
- UpdateVertexBufferState(state);
- }
-
- /// <summary>
- /// Updates host vertex buffer bindings based on guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateVertexBufferState(GpuState state)
- {
- _isAnyVbInstanced = false;
-
- for (int index = 0; index < Constants.TotalVertexBuffers; index++)
- {
- var vertexBuffer = state.Get<VertexBufferState>(MethodOffset.VertexBufferState, index);
-
- if (!vertexBuffer.UnpackEnable())
- {
- state.Channel.BufferManager.SetVertexBuffer(index, 0, 0, 0, 0);
-
- continue;
- }
-
- GpuVa endAddress = state.Get<GpuVa>(MethodOffset.VertexBufferEndAddress, index);
-
- ulong address = vertexBuffer.Address.Pack();
-
- int stride = vertexBuffer.UnpackStride();
-
- bool instanced = state.Get<Boolean32>(MethodOffset.VertexBufferInstanced + index);
-
- int divisor = instanced ? vertexBuffer.Divisor : 0;
-
- _isAnyVbInstanced |= divisor != 0;
-
- ulong size;
-
- if (_ibStreamer.HasInlineIndexData || _drawIndexed || stride == 0 || instanced)
- {
- // This size may be (much) larger than the real vertex buffer size.
- // Avoid calculating it this way, unless we don't have any other option.
- size = endAddress.Pack() - address + 1;
- }
- else
- {
- // For non-indexed draws, we can guess the size from the vertex count
- // and stride.
- int firstInstance = state.Get<int>(MethodOffset.FirstInstance);
-
- var drawState = state.Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);
-
- size = (ulong)((firstInstance + drawState.First + drawState.Count) * stride);
- }
-
- state.Channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor);
- }
- }
-
- /// <summary>
- /// Updates host face culling and orientation based on guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateFaceState(GpuState state)
- {
- var yControl = state.Get<YControl> (MethodOffset.YControl);
- var face = state.Get<FaceState>(MethodOffset.FaceState);
-
- _context.Renderer.Pipeline.SetFaceCulling(face.CullEnable, face.CullFace);
-
- UpdateFrontFace(yControl, face.FrontFace);
- }
-
- /// <summary>
- /// Updates the front face based on the current front face and the origin.
- /// </summary>
- /// <param name="yControl">Y control register value, where the origin is located</param>
- /// <param name="frontFace">Front face</param>
- private void UpdateFrontFace(YControl yControl, FrontFace frontFace)
- {
- bool isUpperLeftOrigin = !yControl.HasFlag(YControl.TriangleRastFlip);
-
- if (isUpperLeftOrigin)
- {
- frontFace = frontFace == FrontFace.CounterClockwise ? FrontFace.Clockwise : FrontFace.CounterClockwise;
- }
-
- _context.Renderer.Pipeline.SetFrontFace(frontFace);
- }
-
- /// <summary>
- /// Updates host render target color masks, based on guest GPU state.
- /// This defines which color channels are written to each color buffer.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateRtColorMask(GpuState state)
- {
- bool rtColorMaskShared = state.Get<Boolean32>(MethodOffset.RtColorMaskShared);
-
- Span<uint> componentMasks = stackalloc uint[Constants.TotalRenderTargets];
-
- for (int index = 0; index < Constants.TotalRenderTargets; index++)
- {
- var colorMask = state.Get<RtColorMask>(MethodOffset.RtColorMask, rtColorMaskShared ? 0 : index);
-
- uint componentMask;
-
- componentMask = (colorMask.UnpackRed() ? 1u : 0u);
- componentMask |= (colorMask.UnpackGreen() ? 2u : 0u);
- componentMask |= (colorMask.UnpackBlue() ? 4u : 0u);
- componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u);
-
- componentMasks[index] = componentMask;
- }
-
- _context.Renderer.Pipeline.SetRenderTargetColorMasks(componentMasks);
- }
-
- /// <summary>
- /// Updates host render target color buffer blending state, based on guest state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateBlendState(GpuState state)
- {
- bool blendIndependent = state.Get<Boolean32>(MethodOffset.BlendIndependent);
- ColorF blendConstant = state.Get<ColorF>(MethodOffset.BlendConstant);
-
- for (int index = 0; index < Constants.TotalRenderTargets; index++)
- {
- BlendDescriptor descriptor;
-
- if (blendIndependent)
- {
- bool enable = state.Get<Boolean32> (MethodOffset.BlendEnable, index);
- var blend = state.Get<BlendState>(MethodOffset.BlendState, index);
-
- descriptor = new BlendDescriptor(
- enable,
- blendConstant,
- blend.ColorOp,
- blend.ColorSrcFactor,
- blend.ColorDstFactor,
- blend.AlphaOp,
- blend.AlphaSrcFactor,
- blend.AlphaDstFactor);
- }
- else
- {
- bool enable = state.Get<Boolean32> (MethodOffset.BlendEnable, 0);
- var blend = state.Get<BlendStateCommon>(MethodOffset.BlendStateCommon);
-
- descriptor = new BlendDescriptor(
- enable,
- blendConstant,
- blend.ColorOp,
- blend.ColorSrcFactor,
- blend.ColorDstFactor,
- blend.AlphaOp,
- blend.AlphaSrcFactor,
- blend.AlphaDstFactor);
- }
-
- _context.Renderer.Pipeline.SetBlendState(index, descriptor);
- }
- }
-
- /// <summary>
- /// Updates host logical operation state, based on guest state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- public void UpdateLogicOpState(GpuState state)
- {
- LogicalOpState logicOpState = state.Get<LogicalOpState>(MethodOffset.LogicOpState);
-
- _context.Renderer.Pipeline.SetLogicOpState(logicOpState.Enable, logicOpState.LogicalOp);
- }
-
- /// <summary>
- /// Updates host shaders based on the guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateShaderState(GpuState state)
- {
- ShaderAddresses addresses = new ShaderAddresses();
-
- Span<ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1);
-
- Span<ulong> addressesArray = MemoryMarshal.Cast<ShaderAddresses, ulong>(addressesSpan);
-
- ulong baseAddress = state.Get<GpuVa>(MethodOffset.ShaderBaseAddress).Pack();
-
- for (int index = 0; index < 6; index++)
- {
- var shader = state.Get<ShaderState>(MethodOffset.ShaderState, index);
-
- if (!shader.UnpackEnable() && index != 1)
- {
- continue;
- }
-
- addressesArray[index] = baseAddress + shader.Offset;
- }
-
- ShaderBundle gs = state.Channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(state, addresses);
-
- byte oldVsClipDistancesWritten = _vsClipDistancesWritten;
-
- _vsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false;
- _vsClipDistancesWritten = gs.Shaders[0]?.Info.ClipDistancesWritten ?? 0;
-
- if (oldVsClipDistancesWritten != _vsClipDistancesWritten)
- {
- UpdateUserClipState(state);
- }
-
- int storageBufferBindingsCount = 0;
- int uniformBufferBindingsCount = 0;
-
- for (int stage = 0; stage < Constants.ShaderStages; stage++)
- {
- ShaderProgramInfo info = gs.Shaders[stage]?.Info;
-
- _currentProgramInfo[stage] = info;
-
- if (info == null)
- {
- state.Channel.TextureManager.SetGraphicsTextures(stage, Array.Empty<TextureBindingInfo>());
- state.Channel.TextureManager.SetGraphicsImages(stage, Array.Empty<TextureBindingInfo>());
- state.Channel.BufferManager.SetGraphicsStorageBufferBindings(stage, null);
- state.Channel.BufferManager.SetGraphicsUniformBufferBindings(stage, null);
- continue;
- }
-
- var textureBindings = new TextureBindingInfo[info.Textures.Count];
-
- for (int index = 0; index < info.Textures.Count; index++)
- {
- var descriptor = info.Textures[index];
-
- Target target = ShaderTexture.GetTarget(descriptor.Type);
-
- textureBindings[index] = new TextureBindingInfo(
- target,
- descriptor.Binding,
- descriptor.CbufSlot,
- descriptor.HandleIndex,
- descriptor.Flags);
- }
-
- state.Channel.TextureManager.SetGraphicsTextures(stage, textureBindings);
-
- var imageBindings = new TextureBindingInfo[info.Images.Count];
-
- for (int index = 0; index < info.Images.Count; index++)
- {
- var descriptor = info.Images[index];
-
- Target target = ShaderTexture.GetTarget(descriptor.Type);
- Format format = ShaderTexture.GetFormat(descriptor.Format);
-
- imageBindings[index] = new TextureBindingInfo(
- target,
- format,
- descriptor.Binding,
- descriptor.CbufSlot,
- descriptor.HandleIndex,
- descriptor.Flags);
- }
-
- state.Channel.TextureManager.SetGraphicsImages(stage, imageBindings);
-
- state.Channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
- state.Channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);
-
- if (info.SBuffers.Count != 0)
- {
- storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1);
- }
-
- if (info.CBuffers.Count != 0)
- {
- uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1);
- }
- }
-
- state.Channel.BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount);
- state.Channel.BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount);
-
- _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
- }
-
- /// <summary>
- /// Forces the shaders to be rebound on the next draw.
- /// </summary>
- public void ForceShaderUpdate()
- {
- _forceShaderUpdate = true;
- }
-
- /// <summary>
- /// Updates transform feedback buffer state based on the guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateTfBufferState(GpuState state)
- {
- for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
- {
- TfBufferState tfb = state.Get<TfBufferState>(MethodOffset.TfBufferState, index);
-
- if (!tfb.Enable)
- {
- state.Channel.BufferManager.SetTransformFeedbackBuffer(index, 0, 0);
-
- continue;
- }
-
- state.Channel.BufferManager.SetTransformFeedbackBuffer(index, tfb.Address.Pack(), (uint)tfb.Size);
- }
- }
-
- /// <summary>
- /// Updates user-defined clipping based on the guest GPU state.
- /// </summary>
- /// <param name="state">Current GPU state</param>
- private void UpdateUserClipState(GpuState state)
- {
- int clipMask = state.Get<int>(MethodOffset.ClipDistanceEnable) & _vsClipDistancesWritten;
-
- for (int i = 0; i < Constants.TotalClipDistances; ++i)
- {
- _context.Renderer.Pipeline.SetUserClipDistance(i, (clipMask & (1 << i)) != 0);
- }
- }
-
- /// <summary>
- /// Issues a texture barrier.
- /// This waits until previous texture writes from the GPU to finish, before
- /// performing new operations with said textures.
- /// </summary>
- /// <param name="state">Current GPU state (unused)</param>
- /// <param name="argument">Method call argument (unused)</param>
- private void TextureBarrier(GpuState state, int argument)
- {
- _context.Renderer.Pipeline.TextureBarrier();
- }
-
- /// <summary>
- /// Issues a texture barrier.
- /// This waits until previous texture writes from the GPU to finish, before
- /// performing new operations with said textures.
- /// This performs a per-tile wait, it is only valid if both the previous write
- /// and current access has the same access patterns.
- /// This may be faster than the regular barrier on tile-based rasterizers.
- /// </summary>
- /// <param name="state">Current GPU state (unused)</param>
- /// <param name="argument">Method call argument (unused)</param>
- private void TextureBarrierTiled(GpuState state, int argument)
- {
- _context.Renderer.Pipeline.TextureBarrierTiled();
- }
- }
-} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs b/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs
index df9021e0..a3295616 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MmeShadowScratch.cs
@@ -3,6 +3,9 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Engine
{
+ /// <summary>
+ /// Represents temporary storage used by macros.
+ /// </summary>
[StructLayout(LayoutKind.Sequential, Size = 1024)]
struct MmeShadowScratch
{
diff --git a/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs b/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs
new file mode 100644
index 00000000..060d35ca
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Graphics.Gpu.Engine
+{
+ /// <summary>
+ /// MME shadow RAM control mode.
+ /// </summary>
+ enum SetMmeShadowRamControlMode
+ {
+ MethodTrack = 0,
+ MethodTrackWithFilter = 1,
+ MethodPassthrough = 2,
+ MethodReplay = 3,
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs
index 039ed78e..85f66985 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ConditionalRendering.cs
@@ -1,37 +1,41 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Memory;
-using Ryujinx.Graphics.Gpu.State;
-namespace Ryujinx.Graphics.Gpu.Engine
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
- partial class Methods
+ /// <summary>
+ /// Helper methods used for conditional rendering.
+ /// </summary>
+ static class ConditionalRendering
{
/// <summary>
/// Checks if draws and clears should be performed, according
/// to currently set conditional rendering conditions.
/// </summary>
- /// <param name="state">GPU state</param>
+ /// <param name="context">GPU context</param>
+ /// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
+ /// <param name="address">Conditional rendering buffer address</param>
+ /// <param name="condition">Conditional rendering condition</param>
/// <returns>True if rendering is enabled, false otherwise</returns>
- private ConditionalRenderEnabled GetRenderEnable(GpuState state)
+ public static ConditionalRenderEnabled GetRenderEnable(GpuContext context, MemoryManager memoryManager, GpuVa address, Condition condition)
{
- ConditionState condState = state.Get<ConditionState>(MethodOffset.ConditionState);
-
- switch (condState.Condition)
+ switch (condition)
{
case Condition.Always:
return ConditionalRenderEnabled.True;
case Condition.Never:
return ConditionalRenderEnabled.False;
case Condition.ResultNonZero:
- return CounterNonZero(state, condState.Address.Pack());
+ return CounterNonZero(context, memoryManager, address.Pack());
case Condition.Equal:
- return CounterCompare(state, condState.Address.Pack(), true);
+ return CounterCompare(context, memoryManager, address.Pack(), true);
case Condition.NotEqual:
- return CounterCompare(state, condState.Address.Pack(), false);
+ return CounterCompare(context, memoryManager, address.Pack(), false);
}
- Logger.Warning?.Print(LogClass.Gpu, $"Invalid conditional render condition \"{condState.Condition}\".");
+ Logger.Warning?.Print(LogClass.Gpu, $"Invalid conditional render condition \"{condition}\".");
return ConditionalRenderEnabled.True;
}
@@ -39,54 +43,56 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <summary>
/// Checks if the counter value at a given GPU memory address is non-zero.
/// </summary>
- /// <param name="state">GPU state</param>
+ /// <param name="context">GPU context</param>
+ /// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
/// <param name="gpuVa">GPU virtual address of the counter value</param>
/// <returns>True if the value is not zero, false otherwise. Returns host if handling with host conditional rendering</returns>
- private ConditionalRenderEnabled CounterNonZero(GpuState state, ulong gpuVa)
+ private static ConditionalRenderEnabled CounterNonZero(GpuContext context, MemoryManager memoryManager, ulong gpuVa)
{
- ICounterEvent evt = state.Channel.MemoryManager.CounterCache.FindEvent(gpuVa);
+ ICounterEvent evt = memoryManager.CounterCache.FindEvent(gpuVa);
if (evt == null)
{
return ConditionalRenderEnabled.False;
}
- if (_context.Renderer.Pipeline.TryHostConditionalRendering(evt, 0L, false))
+ if (context.Renderer.Pipeline.TryHostConditionalRendering(evt, 0L, false))
{
return ConditionalRenderEnabled.Host;
}
else
{
evt.Flush();
- return (state.Channel.MemoryManager.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
+ return (memoryManager.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
}
}
/// <summary>
/// Checks if the counter at a given GPU memory address passes a specified equality comparison.
/// </summary>
- /// <param name="state">GPU state</param>
+ /// <param name="context">GPU context</param>
+ /// <param name="memoryManager">Memory manager bound to the channel currently executing</param>
/// <param name="gpuVa">GPU virtual address</param>
/// <param name="isEqual">True to check if the values are equal, false to check if they are not equal</param>
/// <returns>True if the condition is met, false otherwise. Returns host if handling with host conditional rendering</returns>
- private ConditionalRenderEnabled CounterCompare(GpuState state, ulong gpuVa, bool isEqual)
+ private static ConditionalRenderEnabled CounterCompare(GpuContext context, MemoryManager memoryManager, ulong gpuVa, bool isEqual)
{
- ICounterEvent evt = FindEvent(state.Channel.MemoryManager.CounterCache, gpuVa);
- ICounterEvent evt2 = FindEvent(state.Channel.MemoryManager.CounterCache, gpuVa + 16);
+ ICounterEvent evt = FindEvent(memoryManager.CounterCache, gpuVa);
+ ICounterEvent evt2 = FindEvent(memoryManager.CounterCache, gpuVa + 16);
bool useHost;
if (evt != null && evt2 == null)
{
- useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, state.Channel.MemoryManager.Read<ulong>(gpuVa + 16), isEqual);
+ useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt, memoryManager.Read<ulong>(gpuVa + 16), isEqual);
}
else if (evt == null && evt2 != null)
{
- useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, state.Channel.MemoryManager.Read<ulong>(gpuVa), isEqual);
+ useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt2, memoryManager.Read<ulong>(gpuVa), isEqual);
}
else if (evt != null && evt2 != null)
{
- useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, evt2, isEqual);
+ useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt, evt2, isEqual);
}
else
{
@@ -102,8 +108,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
evt?.Flush();
evt2?.Flush();
- ulong x = state.Channel.MemoryManager.Read<ulong>(gpuVa);
- ulong y = state.Channel.MemoryManager.Read<ulong>(gpuVa + 16);
+ ulong x = memoryManager.Read<ulong>(gpuVa);
+ ulong y = memoryManager.Read<ulong>(gpuVa + 16);
return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs
new file mode 100644
index 00000000..f4006ba9
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+ /// <summary>
+ /// Constant buffer updater.
+ /// </summary>
+ class ConstantBufferUpdater
+ {
+ private readonly GpuChannel _channel;
+ private readonly DeviceStateWithShadow<ThreedClassState> _state;
+
+ // State associated with direct uniform buffer updates.
+ // This state is used to attempt to batch together consecutive updates.
+ private ulong _ubBeginCpuAddress = 0;
+ private ulong _ubFollowUpAddress = 0;
+ private ulong _ubByteCount = 0;
+
+ /// <summary>
+ /// Creates a new instance of the constant buffer updater.
+ /// </summary>
+ /// <param name="channel">GPU channel</param>
+ /// <param name="state">Channel state</param>
+ public ConstantBufferUpdater(GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state)
+ {
+ _channel = channel;
+ _state = state;
+ }
+
+ /// <summary>
+ /// Binds a uniform buffer for the vertex shader stage.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void BindVertex(int argument)
+ {
+ Bind(argument, ShaderType.Vertex);
+ }
+
+ /// <summary>
+ /// Binds a uniform buffer for the tessellation control shader stage.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void BindTessControl(int argument)
+ {
+ Bind(argument, ShaderType.TessellationControl);
+ }
+
+ /// <summary>
+ /// Binds a uniform buffer for the tessellation evaluation shader stage.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void BindTessEvaluation(int argument)
+ {
+ Bind(argument, ShaderType.TessellationEvaluation);
+ }
+
+ /// <summary>
+ /// Binds a uniform buffer for the geometry shader stage.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void BindGeometry(int argument)
+ {
+ Bind(argument, ShaderType.Geometry);
+ }
+
+ /// <summary>
+ /// Binds a uniform buffer for the fragment shader stage.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void BindFragment(int argument)
+ {
+ Bind(argument, ShaderType.Fragment);
+ }
+
+ /// <summary>
+ /// Binds a uniform buffer for the specified shader stage.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ /// <param name="type">Shader stage that will access the uniform buffer</param>
+ private void Bind(int argument, ShaderType type)
+ {
+ bool enable = (argument & 1) != 0;
+
+ int index = (argument >> 4) & 0x1f;
+
+ FlushUboDirty();
+
+ if (enable)
+ {
+ var uniformBuffer = _state.State.UniformBufferState;
+
+ ulong address = uniformBuffer.Address.Pack();
+
+ _channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, address, (uint)uniformBuffer.Size);
+ }
+ else
+ {
+ _channel.BufferManager.SetGraphicsUniformBuffer((int)type, index, 0, 0);
+ }
+ }
+
+ /// <summary>
+ /// Flushes any queued UBO updates.
+ /// </summary>
+ public void FlushUboDirty()
+ {
+ if (_ubFollowUpAddress != 0)
+ {
+ var memoryManager = _channel.MemoryManager;
+ memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
+
+ _ubFollowUpAddress = 0;
+ }
+ }
+
+ /// <summary>
+ /// Updates the uniform buffer data with inline data.
+ /// </summary>
+ /// <param name="argument">New uniform buffer data word</param>
+ public void Update(int argument)
+ {
+ var uniformBuffer = _state.State.UniformBufferState;
+
+ ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
+
+ if (_ubFollowUpAddress != address)
+ {
+ FlushUboDirty();
+
+ _ubByteCount = 0;
+ _ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
+ }
+
+ var byteData = MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1));
+ _channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
+
+ _ubFollowUpAddress = address + 4;
+ _ubByteCount += 4;
+
+ _state.State.UniformBufferState.Offset += 4;
+ }
+
+ /// <summary>
+ /// Updates the uniform buffer data with inline data.
+ /// </summary>
+ /// <param name="data">Data to be written to the uniform buffer</param>
+ public void Update(ReadOnlySpan<int> data)
+ {
+ var uniformBuffer = _state.State.UniformBufferState;
+
+ ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
+
+ ulong size = (ulong)data.Length * 4;
+
+ if (_ubFollowUpAddress != address)
+ {
+ FlushUboDirty();
+
+ _ubByteCount = 0;
+ _ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
+ }
+
+ var byteData = MemoryMarshal.Cast<int, byte>(data);
+ _channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
+
+ _ubFollowUpAddress = address + size;
+ _ubByteCount += size;
+
+ _state.State.UniformBufferState.Offset += data.Length * 4;
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
new file mode 100644
index 00000000..d58f175d
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
@@ -0,0 +1,410 @@
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Types;
+using System.Text;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+ /// <summary>
+ /// Draw manager.
+ /// </summary>
+ class DrawManager
+ {
+ private readonly GpuContext _context;
+ private readonly GpuChannel _channel;
+ private readonly DeviceStateWithShadow<ThreedClassState> _state;
+ private readonly DrawState _drawState;
+
+ private bool _instancedDrawPending;
+ private bool _instancedIndexed;
+
+ private int _instancedFirstIndex;
+ private int _instancedFirstVertex;
+ private int _instancedFirstInstance;
+ private int _instancedIndexCount;
+ private int _instancedDrawStateFirst;
+ private int _instancedDrawStateCount;
+
+ private int _instanceIndex;
+
+ /// <summary>
+ /// Creates a new instance of the draw manager.
+ /// </summary>
+ /// <param name="context">GPU context</param>
+ /// <param name="channel">GPU channel</param>
+ /// <param name="state">Channel state</param>
+ /// <param name="drawState">Draw state</param>
+ public DrawManager(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState)
+ {
+ _context = context;
+ _channel = channel;
+ _state = state;
+ _drawState = drawState;
+ }
+
+ /// <summary>
+ /// Pushes four 8-bit index buffer elements.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void VbElementU8(int argument)
+ {
+ _drawState.IbStreamer.VbElementU8(_context.Renderer, argument);
+ }
+
+ /// <summary>
+ /// Pushes two 16-bit index buffer elements.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void VbElementU16(int argument)
+ {
+ _drawState.IbStreamer.VbElementU16(_context.Renderer, argument);
+ }
+
+ /// <summary>
+ /// Pushes one 32-bit index buffer element.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void VbElementU32(int argument)
+ {
+ _drawState.IbStreamer.VbElementU32(_context.Renderer, argument);
+ }
+
+ /// <summary>
+ /// Finishes the draw call.
+ /// This draws geometry on the bound buffers based on the current GPU state.
+ /// </summary>
+ /// <param name="engine">3D engine where this method is being called</param>
+ /// <param name="argument">Method call argument</param>
+ public void DrawEnd(ThreedClass engine, int argument)
+ {
+ DrawEnd(engine, _state.State.IndexBufferState.First, (int)_state.State.IndexBufferCount);
+ }
+
+ /// <summary>
+ /// Finishes the draw call.
+ /// This draws geometry on the bound buffers based on the current GPU state.
+ /// </summary>
+ /// <param name="engine">3D engine where this method is being called</param>
+ /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
+ /// <param name="indexCount">Number of index buffer elements used on the draw</param>
+ private void DrawEnd(ThreedClass engine, int firstIndex, int indexCount)
+ {
+ ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
+ _context,
+ _channel.MemoryManager,
+ _state.State.RenderEnableAddress,
+ _state.State.RenderEnableCondition);
+
+ if (renderEnable == ConditionalRenderEnabled.False || _instancedDrawPending)
+ {
+ if (renderEnable == ConditionalRenderEnabled.False)
+ {
+ PerformDeferredDraws();
+ }
+
+ _drawState.DrawIndexed = false;
+
+ if (renderEnable == ConditionalRenderEnabled.Host)
+ {
+ _context.Renderer.Pipeline.EndHostConditionalRendering();
+ }
+
+ return;
+ }
+
+ _drawState.FirstIndex = firstIndex;
+ _drawState.IndexCount = indexCount;
+
+ engine.UpdateState();
+
+ bool instanced = _drawState.VsUsesInstanceId || _drawState.IsAnyVbInstanced;
+
+ if (instanced)
+ {
+ _instancedDrawPending = true;
+
+ _instancedIndexed = _drawState.DrawIndexed;
+
+ _instancedFirstIndex = firstIndex;
+ _instancedFirstVertex = (int)_state.State.FirstVertex;
+ _instancedFirstInstance = (int)_state.State.FirstInstance;
+
+ _instancedIndexCount = indexCount;
+
+ var drawState = _state.State.VertexBufferDrawState;
+
+ _instancedDrawStateFirst = drawState.First;
+ _instancedDrawStateCount = drawState.Count;
+
+ _drawState.DrawIndexed = false;
+
+ if (renderEnable == ConditionalRenderEnabled.Host)
+ {
+ _context.Renderer.Pipeline.EndHostConditionalRendering();
+ }
+
+ return;
+ }
+
+ int firstInstance = (int)_state.State.FirstInstance;
+
+ int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount();
+
+ if (inlineIndexCount != 0)
+ {
+ int firstVertex = (int)_state.State.FirstVertex;
+
+ BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
+
+ _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
+
+ _context.Renderer.Pipeline.DrawIndexed(inlineIndexCount, 1, firstIndex, firstVertex, firstInstance);
+ }
+ else if (_drawState.DrawIndexed)
+ {
+ int firstVertex = (int)_state.State.FirstVertex;
+
+ _context.Renderer.Pipeline.DrawIndexed(indexCount, 1, firstIndex, firstVertex, firstInstance);
+ }
+ else
+ {
+ var drawState = _state.State.VertexBufferDrawState;
+
+ _context.Renderer.Pipeline.Draw(drawState.Count, 1, drawState.First, firstInstance);
+ }
+
+ _drawState.DrawIndexed = false;
+
+ if (renderEnable == ConditionalRenderEnabled.Host)
+ {
+ _context.Renderer.Pipeline.EndHostConditionalRendering();
+ }
+ }
+
+ /// <summary>
+ /// Starts draw.
+ /// This sets primitive type and instanced draw parameters.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void DrawBegin(int argument)
+ {
+ bool incrementInstance = (argument & (1 << 26)) != 0;
+ bool resetInstance = (argument & (1 << 27)) == 0;
+
+ if (_state.State.PrimitiveTypeOverrideEnable)
+ {
+ PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
+ DrawBegin(incrementInstance, resetInstance, typeOverride.Convert());
+ }
+ else
+ {
+ PrimitiveType type = (PrimitiveType)(argument & 0xffff);
+ DrawBegin(incrementInstance, resetInstance, type.Convert());
+ }
+ }
+
+ /// <summary>
+ /// Starts draw.
+ /// This sets primitive type and instanced draw parameters.
+ /// </summary>
+ /// <param name="incrementInstance">Indicates if the current instance should be incremented</param>
+ /// <param name="resetInstance">Indicates if the current instance should be set to zero</param>
+ /// <param name="topology">Primitive topology</param>
+ private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveTopology topology)
+ {
+ if (incrementInstance)
+ {
+ _instanceIndex++;
+ }
+ else if (resetInstance)
+ {
+ PerformDeferredDraws();
+
+ _instanceIndex = 0;
+ }
+
+ _context.Renderer.Pipeline.SetPrimitiveTopology(topology);
+
+ _drawState.Topology = topology;
+ }
+
+ /// <summary>
+ /// Sets the index buffer count.
+ /// This also sets internal state that indicates that the next draw is an indexed draw.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void SetIndexBufferCount(int argument)
+ {
+ _drawState.DrawIndexed = true;
+ }
+
+ /// <summary>
+ /// Performs a indexed draw with a low number of index buffer elements.
+ /// </summary>
+ /// <param name="engine">3D engine where this method is being called</param>
+ /// <param name="argument">Method call argument</param>
+ public void DrawIndexedSmall(ThreedClass engine, int argument)
+ {
+ DrawIndexedSmall(engine, argument, false);
+ }
+
+ /// <summary>
+ /// Performs a indexed draw with a low number of index buffer elements.
+ /// </summary>
+ /// <param name="engine">3D engine where this method is being called</param>
+ /// <param name="argument">Method call argument</param>
+ public void DrawIndexedSmall2(ThreedClass engine, int argument)
+ {
+ DrawIndexedSmall(engine, argument);
+ }
+
+ /// <summary>
+ /// Performs a indexed draw with a low number of index buffer elements,
+ /// while also pre-incrementing the current instance value.
+ /// </summary>
+ /// <param name="engine">3D engine where this method is being called</param>
+ /// <param name="argument">Method call argument</param>
+ public void DrawIndexedSmallIncInstance(ThreedClass engine, int argument)
+ {
+ DrawIndexedSmall(engine, argument, true);
+ }
+
+ /// <summary>
+ /// Performs a indexed draw with a low number of index buffer elements,
+ /// while also pre-incrementing the current instance value.
+ /// </summary>
+ /// <param name="engine">3D engine where this method is being called</param>
+ /// <param name="argument">Method call argument</param>
+ public void DrawIndexedSmallIncInstance2(ThreedClass engine, int argument)
+ {
+ DrawIndexedSmallIncInstance(engine, argument);
+ }
+
+ /// <summary>
+ /// Performs a indexed draw with a low number of index buffer elements,
+ /// while optionally also pre-incrementing the current instance value.
+ /// </summary>
+ /// <param name="engine">3D engine where this method is being called</param>
+ /// <param name="argument">Method call argument</param>
+ /// <param name="instanced">True to increment the current instance value, false otherwise</param>
+ private void DrawIndexedSmall(ThreedClass engine, int argument, bool instanced)
+ {
+ PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
+
+ DrawBegin(instanced, !instanced, typeOverride.Convert());
+
+ int firstIndex = argument & 0xffff;
+ int indexCount = (argument >> 16) & 0xfff;
+
+ bool oldDrawIndexed = _drawState.DrawIndexed;
+
+ _drawState.DrawIndexed = true;
+
+ DrawEnd(engine, firstIndex, indexCount);
+
+ _drawState.DrawIndexed = oldDrawIndexed;
+ }
+
+ /// <summary>
+ /// Perform any deferred draws.
+ /// This is used for instanced draws.
+ /// Since each instance is a separate draw, we defer the draw and accumulate the instance count.
+ /// Once we detect the last instanced draw, then we perform the host instanced draw,
+ /// with the accumulated instance count.
+ /// </summary>
+ public void PerformDeferredDraws()
+ {
+ // Perform any pending instanced draw.
+ if (_instancedDrawPending)
+ {
+ _instancedDrawPending = false;
+
+ if (_instancedIndexed)
+ {
+ _context.Renderer.Pipeline.DrawIndexed(
+ _instancedIndexCount,
+ _instanceIndex + 1,
+ _instancedFirstIndex,
+ _instancedFirstVertex,
+ _instancedFirstInstance);
+ }
+ else
+ {
+ _context.Renderer.Pipeline.Draw(
+ _instancedDrawStateCount,
+ _instanceIndex + 1,
+ _instancedDrawStateFirst,
+ _instancedFirstInstance);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Clears the current color and depth-stencil buffers.
+ /// Which buffers should be cleared is also specified on the argument.
+ /// </summary>
+ /// <param name="engine">3D engine where this method is being called</param>
+ /// <param name="argument">Method call argument</param>
+ public void Clear(ThreedClass engine, int argument)
+ {
+ ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
+ _context,
+ _channel.MemoryManager,
+ _state.State.RenderEnableAddress,
+ _state.State.RenderEnableCondition);
+
+ if (renderEnable == ConditionalRenderEnabled.False)
+ {
+ return;
+ }
+
+ // Scissor and rasterizer discard also affect clears.
+ engine.UpdateState((1UL << StateUpdater.RasterizerStateIndex) | (1UL << StateUpdater.ScissorStateIndex));
+
+ int index = (argument >> 6) & 0xf;
+
+ engine.UpdateRenderTargetState(useControl: false, singleUse: index);
+
+ _channel.TextureManager.UpdateRenderTargets();
+
+ bool clearDepth = (argument & 1) != 0;
+ bool clearStencil = (argument & 2) != 0;
+
+ uint componentMask = (uint)((argument >> 2) & 0xf);
+
+ if (componentMask != 0)
+ {
+ var clearColor = _state.State.ClearColors;
+
+ ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha);
+
+ _context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color);
+ }
+
+ if (clearDepth || clearStencil)
+ {
+ float depthValue = _state.State.ClearDepthValue;
+ int stencilValue = (int)_state.State.ClearStencilValue;
+
+ int stencilMask = 0;
+
+ if (clearStencil)
+ {
+ stencilMask = _state.State.StencilTestState.FrontMask;
+ }
+
+ _context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
+ depthValue,
+ clearDepth,
+ stencilValue,
+ stencilMask);
+ }
+
+ engine.UpdateRenderTargetState(useControl: true);
+
+ if (renderEnable == ConditionalRenderEnabled.Host)
+ {
+ _context.Renderer.Pipeline.EndHostConditionalRendering();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs
new file mode 100644
index 00000000..ff186acc
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs
@@ -0,0 +1,45 @@
+using Ryujinx.Graphics.GAL;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+ /// <summary>
+ /// Draw state.
+ /// </summary>
+ class DrawState
+ {
+ /// <summary>
+ /// First index to be used for the draw on the index buffer.
+ /// </summary>
+ public int FirstIndex;
+
+ /// <summary>
+ /// Number of indices to be used for the draw on the index buffer.
+ /// </summary>
+ public int IndexCount;
+
+ /// <summary>
+ /// Indicates if the next draw will be a indexed draw.
+ /// </summary>
+ public bool DrawIndexed;
+
+ /// <summary>
+ /// Indicates if any of the currently used vertex shaders reads the instance ID.
+ /// </summary>
+ public bool VsUsesInstanceId;
+
+ /// <summary>
+ /// Indicates if any of the currently used vertex buffers is instanced.
+ /// </summary>
+ public bool IsAnyVbInstanced;
+
+ /// <summary>
+ /// Primitive topology for the next draw.
+ /// </summary>
+ public PrimitiveTopology Topology;
+
+ /// <summary>
+ /// Index buffer data streamer for inline index buffer updates, such as those used in legacy OpenGL.
+ /// </summary>
+ public IbStreamer IbStreamer = new IbStreamer();
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/IbStreamer.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs
index b407c941..96b2ed9c 100644
--- a/Ryujinx.Graphics.Gpu/Engine/IbStreamer.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/IbStreamer.cs
@@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL;
using System;
using System.Runtime.InteropServices;
-namespace Ryujinx.Graphics.Gpu.Engine
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
/// <summary>
/// Holds inline index buffer state.
@@ -15,6 +15,9 @@ namespace Ryujinx.Graphics.Gpu.Engine
private int _inlineIndexBufferSize;
private int _inlineIndexCount;
+ /// <summary>
+ /// Indicates if any index buffer data has been pushed.
+ /// </summary>
public bool HasInlineIndexData => _inlineIndexCount != 0;
/// <summary>
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs
new file mode 100644
index 00000000..37fa51c4
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs
@@ -0,0 +1,222 @@
+using Ryujinx.Common;
+using Ryujinx.Graphics.GAL;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+ /// <summary>
+ /// Semaphore updater.
+ /// </summary>
+ class SemaphoreUpdater
+ {
+ private const int NsToTicksFractionNumerator = 384;
+ private const int NsToTicksFractionDenominator = 625;
+
+ /// <summary>
+ /// GPU semaphore operation.
+ /// </summary>
+ private enum SemaphoreOperation
+ {
+ Release = 0,
+ Acquire = 1,
+ Counter = 2
+ }
+
+ /// <summary>
+ /// Counter type for GPU counter reset.
+ /// </summary>
+ private enum ResetCounterType
+ {
+ SamplesPassed = 1,
+ ZcullStats = 2,
+ TransformFeedbackPrimitivesWritten = 0x10,
+ InputVertices = 0x12,
+ InputPrimitives = 0x13,
+ VertexShaderInvocations = 0x15,
+ TessControlShaderInvocations = 0x16,
+ TessEvaluationShaderInvocations = 0x17,
+ TessEvaluationShaderPrimitives = 0x18,
+ GeometryShaderInvocations = 0x1a,
+ GeometryShaderPrimitives = 0x1b,
+ ClipperInputPrimitives = 0x1c,
+ ClipperOutputPrimitives = 0x1d,
+ FragmentShaderInvocations = 0x1e,
+ PrimitivesGenerated = 0x1f
+ }
+
+ /// <summary>
+ /// Counter type for GPU counter reporting.
+ /// </summary>
+ private enum ReportCounterType
+ {
+ Zero = 0,
+ InputVertices = 1,
+ InputPrimitives = 3,
+ VertexShaderInvocations = 5,
+ GeometryShaderInvocations = 7,
+ GeometryShaderPrimitives = 9,
+ ZcullStats0 = 0xa,
+ TransformFeedbackPrimitivesWritten = 0xb,
+ ZcullStats1 = 0xc,
+ ZcullStats2 = 0xe,
+ ClipperInputPrimitives = 0xf,
+ ZcullStats3 = 0x10,
+ ClipperOutputPrimitives = 0x11,
+ PrimitivesGenerated = 0x12,
+ FragmentShaderInvocations = 0x13,
+ SamplesPassed = 0x15,
+ TransformFeedbackOffset = 0x1a,
+ TessControlShaderInvocations = 0x1b,
+ TessEvaluationShaderInvocations = 0x1d,
+ TessEvaluationShaderPrimitives = 0x1f
+ }
+
+ private readonly GpuContext _context;
+ private readonly GpuChannel _channel;
+ private readonly DeviceStateWithShadow<ThreedClassState> _state;
+
+ /// <summary>
+ /// Creates a new instance of the semaphore updater.
+ /// </summary>
+ /// <param name="context">GPU context</param>
+ /// <param name="channel">GPU channel</param>
+ /// <param name="state">Channel state</param>
+ public SemaphoreUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state)
+ {
+ _context = context;
+ _channel = channel;
+ _state = state;
+ }
+
+ /// <summary>
+ /// Resets the value of an internal GPU counter back to zero.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void ResetCounter(int argument)
+ {
+ ResetCounterType type = (ResetCounterType)argument;
+
+ switch (type)
+ {
+ case ResetCounterType.SamplesPassed:
+ _context.Renderer.ResetCounter(CounterType.SamplesPassed);
+ break;
+ case ResetCounterType.PrimitivesGenerated:
+ _context.Renderer.ResetCounter(CounterType.PrimitivesGenerated);
+ break;
+ case ResetCounterType.TransformFeedbackPrimitivesWritten:
+ _context.Renderer.ResetCounter(CounterType.TransformFeedbackPrimitivesWritten);
+ break;
+ }
+ }
+
+ /// <summary>
+ /// Writes a GPU counter to guest memory.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void Report(int argument)
+ {
+ SemaphoreOperation op = (SemaphoreOperation)(argument & 3);
+ ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f);
+
+ switch (op)
+ {
+ case SemaphoreOperation.Release: ReleaseSemaphore(); break;
+ case SemaphoreOperation.Counter: ReportCounter(type); break;
+ }
+ }
+
+ /// <summary>
+ /// Writes (or Releases) a GPU semaphore value to guest memory.
+ /// </summary>
+ private void ReleaseSemaphore()
+ {
+ _channel.MemoryManager.Write(_state.State.SemaphoreAddress.Pack(), _state.State.SemaphorePayload);
+
+ _context.AdvanceSequence();
+ }
+
+ /// <summary>
+ /// Packed GPU counter data (including GPU timestamp) in memory.
+ /// </summary>
+ private struct CounterData
+ {
+ public ulong Counter;
+ public ulong Timestamp;
+ }
+
+ /// <summary>
+ /// Writes a GPU counter to guest memory.
+ /// This also writes the current timestamp value.
+ /// </summary>
+ /// <param name="type">Counter to be written to memory</param>
+ private void ReportCounter(ReportCounterType type)
+ {
+ ulong gpuVa = _state.State.SemaphoreAddress.Pack();
+
+ ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
+
+ if (GraphicsConfig.FastGpuTime)
+ {
+ // Divide by some amount to report time as if operations were performed faster than they really are.
+ // This can prevent some games from switching to a lower resolution because rendering is too slow.
+ ticks /= 256;
+ }
+
+ ICounterEvent counter = null;
+
+ void resultHandler(object evt, ulong result)
+ {
+ CounterData counterData = new CounterData
+ {
+ Counter = result,
+ Timestamp = ticks
+ };
+
+ if (counter?.Invalid != true)
+ {
+ _channel.MemoryManager.Write(gpuVa, counterData);
+ }
+ }
+
+ switch (type)
+ {
+ case ReportCounterType.Zero:
+ resultHandler(null, 0);
+ break;
+ case ReportCounterType.SamplesPassed:
+ counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler);
+ break;
+ case ReportCounterType.PrimitivesGenerated:
+ counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler);
+ break;
+ case ReportCounterType.TransformFeedbackPrimitivesWritten:
+ counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler);
+ break;
+ }
+
+ _channel.MemoryManager.CounterCache.AddOrUpdate(gpuVa, counter);
+ }
+
+ /// <summary>
+ /// Converts a nanoseconds timestamp value to Maxwell time ticks.
+ /// </summary>
+ /// <remarks>
+ /// The frequency is 614400000 Hz.
+ /// </remarks>
+ /// <param name="nanoseconds">Timestamp in nanoseconds</param>
+ /// <returns>Maxwell ticks</returns>
+ private static ulong ConvertNanosecondsToTicks(ulong nanoseconds)
+ {
+ // We need to divide first to avoid overflows.
+ // We fix up the result later by calculating the difference and adding
+ // that to the result.
+ ulong divided = nanoseconds / NsToTicksFractionDenominator;
+
+ ulong rounded = divided * NsToTicksFractionDenominator;
+
+ ulong errorBias = (nanoseconds - rounded) * NsToTicksFractionNumerator / NsToTicksFractionDenominator;
+
+ return divided * NsToTicksFractionNumerator + errorBias;
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
new file mode 100644
index 00000000..2af7a402
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
@@ -0,0 +1,166 @@
+using Ryujinx.Graphics.Device;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+ /// <summary>
+ /// State update callback entry, with the callback function and associated field names.
+ /// </summary>
+ struct StateUpdateCallbackEntry
+ {
+ /// <summary>
+ /// Callback function, to be called if the register was written as the state needs to be updated.
+ /// </summary>
+ public Action Callback { get; }
+
+ /// <summary>
+ /// Name of the state fields (registers) associated with the callback function.
+ /// </summary>
+ public string[] FieldNames { get; }
+
+ /// <summary>
+ /// Creates a new state update callback entry.
+ /// </summary>
+ /// <param name="callback">Callback function, to be called if the register was written as the state needs to be updated</param>
+ /// <param name="fieldNames">Name of the state fields (registers) associated with the callback function</param>
+ public StateUpdateCallbackEntry(Action callback, params string[] fieldNames)
+ {
+ Callback = callback;
+ FieldNames = fieldNames;
+ }
+ }
+
+ /// <summary>
+ /// GPU state update tracker.
+ /// </summary>
+ /// <typeparam name="TState">State type</typeparam>
+ class StateUpdateTracker<TState>
+ {
+ private const int BlockSize = 0xe00;
+ private const int RegisterSize = sizeof(uint);
+
+ private readonly byte[] _registerToGroupMapping;
+ private readonly Action[] _callbacks;
+ private ulong _dirtyMask;
+
+ /// <summary>
+ /// Creates a new instance of the state update tracker.
+ /// </summary>
+ /// <param name="entries">Update tracker callback entries</param>
+ public StateUpdateTracker(StateUpdateCallbackEntry[] entries)
+ {
+ _registerToGroupMapping = new byte[BlockSize];
+ _callbacks = new Action[entries.Length];
+
+ var fieldToDelegate = new Dictionary<string, int>();
+
+ for (int entryIndex = 0; entryIndex < entries.Length; entryIndex++)
+ {
+ var entry = entries[entryIndex];
+
+ foreach (var fieldName in entry.FieldNames)
+ {
+ fieldToDelegate.Add(fieldName, entryIndex);
+ }
+
+ _callbacks[entryIndex] = entry.Callback;
+ }
+
+ var fields = typeof(TState).GetFields();
+ int offset = 0;
+
+ for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
+ {
+ var field = fields[fieldIndex];
+
+ int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
+
+ if (fieldToDelegate.TryGetValue(field.Name, out int entryIndex))
+ {
+ for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
+ {
+ _registerToGroupMapping[(offset + i) / RegisterSize] = (byte)(entryIndex + 1);
+ }
+ }
+
+ offset += sizeOfField;
+ }
+
+ Debug.Assert(offset == Unsafe.SizeOf<TState>());
+ }
+
+ /// <summary>
+ /// Sets a register as modified.
+ /// </summary>
+ /// <param name="offset">Register offset in bytes</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void SetDirty(int offset)
+ {
+ uint index = (uint)offset / RegisterSize;
+
+ if (index < BlockSize)
+ {
+ int groupIndex = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_registerToGroupMapping), (IntPtr)index);
+ if (groupIndex != 0)
+ {
+ groupIndex--;
+ _dirtyMask |= 1UL << groupIndex;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Forces a register group as dirty, by index.
+ /// </summary>
+ /// <param name="groupIndex">Index of the group to be dirtied</param>
+ public void ForceDirty(int groupIndex)
+ {
+ if ((uint)groupIndex >= _callbacks.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(groupIndex));
+ }
+
+ _dirtyMask |= 1UL << groupIndex;
+ }
+
+ /// <summary>
+ /// Forces all register groups as dirty, triggering a full update on the next call to <see cref="Update"/>.
+ /// </summary>
+ public void SetAllDirty()
+ {
+ Debug.Assert(_callbacks.Length <= sizeof(ulong) * 8);
+ _dirtyMask = ulong.MaxValue >> ((sizeof(ulong) * 8) - _callbacks.Length);
+ }
+
+ /// <summary>
+ /// Check all the groups specified by <paramref name="checkMask"/> for modification, and update if modified.
+ /// </summary>
+ /// <param name="checkMask">Mask, where each bit set corresponds to a group index that should be checked</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Update(ulong checkMask)
+ {
+ ulong mask = _dirtyMask & checkMask;
+ if (mask == 0)
+ {
+ return;
+ }
+
+ do
+ {
+ int groupIndex = BitOperations.TrailingZeroCount(mask);
+
+ _callbacks[groupIndex]();
+
+ mask &= ~(1UL << groupIndex);
+ }
+ while (mask != 0);
+
+ _dirtyMask &= ~checkMask;
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
new file mode 100644
index 00000000..4ff084e9
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -0,0 +1,1044 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Types;
+using Ryujinx.Graphics.Gpu.Image;
+using Ryujinx.Graphics.Gpu.Shader;
+using Ryujinx.Graphics.Shader;
+using Ryujinx.Graphics.Texture;
+using System;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+ /// <summary>
+ /// GPU state updater.
+ /// </summary>
+ class StateUpdater
+ {
+ public const int ShaderStateIndex = 0;
+ public const int RasterizerStateIndex = 1;
+ public const int ScissorStateIndex = 2;
+ public const int VertexBufferStateIndex = 3;
+
+ private readonly GpuContext _context;
+ private readonly GpuChannel _channel;
+ private readonly DeviceStateWithShadow<ThreedClassState> _state;
+ private readonly DrawState _drawState;
+
+ private readonly StateUpdateTracker<ThreedClassState> _updateTracker;
+
+ private readonly ShaderProgramInfo[] _currentProgramInfo;
+
+ private byte _vsClipDistancesWritten;
+
+ private bool _prevDrawIndexed;
+ private bool _prevTfEnable;
+
+ /// <summary>
+ /// Creates a new instance of the state updater.
+ /// </summary>
+ /// <param name="context">GPU context</param>
+ /// <param name="channel">GPU channel</param>
+ /// <param name="state">3D engine state</param>
+ /// <param name="drawState">Draw state</param>
+ public StateUpdater(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState)
+ {
+ _context = context;
+ _channel = channel;
+ _state = state;
+ _drawState = drawState;
+ _currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
+
+ // ShaderState must be the first, as other state updates depends on information from the currently bound shader.
+ // Rasterizer and scissor states are checked by render target clear, their indexes
+ // must be updated on the constants "RasterizerStateIndex" and "ScissorStateIndex" if modified.
+ // The vertex buffer state may be forced dirty when a indexed draw starts, the "VertexBufferStateIndex"
+ // constant must be updated if modified.
+ // The order of the other state updates doesn't matter.
+ _updateTracker = new StateUpdateTracker<ThreedClassState>(new[]
+ {
+ new StateUpdateCallbackEntry(UpdateShaderState,
+ nameof(ThreedClassState.ShaderBaseAddress),
+ nameof(ThreedClassState.ShaderState)),
+
+ new StateUpdateCallbackEntry(UpdateRasterizerState, nameof(ThreedClassState.RasterizeEnable)),
+ new StateUpdateCallbackEntry(UpdateScissorState, nameof(ThreedClassState.ScissorState)),
+
+ new StateUpdateCallbackEntry(UpdateVertexBufferState,
+ nameof(ThreedClassState.VertexBufferDrawState),
+ nameof(ThreedClassState.VertexBufferInstanced),
+ nameof(ThreedClassState.VertexBufferState),
+ nameof(ThreedClassState.VertexBufferEndAddress)),
+
+ new StateUpdateCallbackEntry(UpdateTfBufferState, nameof(ThreedClassState.TfBufferState)),
+ new StateUpdateCallbackEntry(UpdateUserClipState, nameof(ThreedClassState.ClipDistanceEnable)),
+
+ new StateUpdateCallbackEntry(UpdateRenderTargetState,
+ nameof(ThreedClassState.RtColorState),
+ nameof(ThreedClassState.RtDepthStencilState),
+ nameof(ThreedClassState.RtControl),
+ nameof(ThreedClassState.RtDepthStencilSize),
+ nameof(ThreedClassState.RtDepthStencilEnable)),
+
+ new StateUpdateCallbackEntry(UpdateDepthClampState, nameof(ThreedClassState.ViewVolumeClipControl)),
+
+ new StateUpdateCallbackEntry(UpdateAlphaTestState,
+ nameof(ThreedClassState.AlphaTestEnable),
+ nameof(ThreedClassState.AlphaTestRef),
+ nameof(ThreedClassState.AlphaTestFunc)),
+
+ new StateUpdateCallbackEntry(UpdateDepthTestState,
+ nameof(ThreedClassState.DepthTestEnable),
+ nameof(ThreedClassState.DepthWriteEnable),
+ nameof(ThreedClassState.DepthTestFunc)),
+
+ new StateUpdateCallbackEntry(UpdateViewportTransform,
+ nameof(ThreedClassState.DepthMode),
+ nameof(ThreedClassState.ViewportTransform),
+ nameof(ThreedClassState.ViewportExtents),
+ nameof(ThreedClassState.YControl)),
+
+ new StateUpdateCallbackEntry(UpdateDepthBiasState,
+ nameof(ThreedClassState.DepthBiasState),
+ nameof(ThreedClassState.DepthBiasFactor),
+ nameof(ThreedClassState.DepthBiasUnits),
+ nameof(ThreedClassState.DepthBiasClamp)),
+
+ new StateUpdateCallbackEntry(UpdateStencilTestState,
+ nameof(ThreedClassState.StencilBackMasks),
+ nameof(ThreedClassState.StencilTestState),
+ nameof(ThreedClassState.StencilBackTestState)),
+
+ new StateUpdateCallbackEntry(UpdateSamplerPoolState,
+ nameof(ThreedClassState.SamplerPoolState),
+ nameof(ThreedClassState.SamplerIndex)),
+
+ new StateUpdateCallbackEntry(UpdateTexturePoolState, nameof(ThreedClassState.TexturePoolState)),
+ new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)),
+
+ new StateUpdateCallbackEntry(UpdateLineState,
+ nameof(ThreedClassState.LineWidthSmooth),
+ nameof(ThreedClassState.LineSmoothEnable)),
+
+ new StateUpdateCallbackEntry(UpdatePointState,
+ nameof(ThreedClassState.PointSize),
+ nameof(ThreedClassState.VertexProgramPointSize),
+ nameof(ThreedClassState.PointSpriteEnable),
+ nameof(ThreedClassState.PointCoordReplace)),
+
+ new StateUpdateCallbackEntry(UpdatePrimitiveRestartState, nameof(ThreedClassState.PrimitiveRestartState)),
+
+ new StateUpdateCallbackEntry(UpdateIndexBufferState,
+ nameof(ThreedClassState.IndexBufferState),
+ nameof(ThreedClassState.IndexBufferCount)),
+
+ new StateUpdateCallbackEntry(UpdateFaceState, nameof(ThreedClassState.FaceState)),
+
+ new StateUpdateCallbackEntry(UpdateRtColorMask,
+ nameof(ThreedClassState.RtColorMaskShared),
+ nameof(ThreedClassState.RtColorMask)),
+
+ new StateUpdateCallbackEntry(UpdateBlendState,
+ nameof(ThreedClassState.BlendIndependent),
+ nameof(ThreedClassState.BlendConstant),
+ nameof(ThreedClassState.BlendStateCommon),
+ nameof(ThreedClassState.BlendEnableCommon),
+ nameof(ThreedClassState.BlendEnable),
+ nameof(ThreedClassState.BlendState)),
+
+ new StateUpdateCallbackEntry(UpdateLogicOpState, nameof(ThreedClassState.LogicOpState))
+ });
+ }
+
+ /// <summary>
+ /// Sets a register at a specific offset as dirty.
+ /// This must be called if the register value was modified.
+ /// </summary>
+ /// <param name="offset">Register offset</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void SetDirty(int offset)
+ {
+ _updateTracker.SetDirty(offset);
+ }
+
+ /// <summary>
+ /// Force all the guest state to be marked as dirty.
+ /// The next call to <see cref="Update"/> will update all the host state.
+ /// </summary>
+ public void SetAllDirty()
+ {
+ _updateTracker.SetAllDirty();
+ }
+
+ /// <summary>
+ /// Updates host state for any modified guest state, since the last time this function was called.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Update()
+ {
+ // The vertex buffer size is calculated using a different
+ // method when doing indexed draws, so we need to make sure
+ // to update the vertex buffers if we are doing a regular
+ // draw after a indexed one and vice-versa.
+ if (_drawState.DrawIndexed != _prevDrawIndexed)
+ {
+ _updateTracker.ForceDirty(VertexBufferStateIndex);
+ _prevDrawIndexed = _drawState.DrawIndexed;
+ }
+
+ bool tfEnable = _state.State.TfEnable;
+
+ if (!tfEnable && _prevTfEnable)
+ {
+ _context.Renderer.Pipeline.EndTransformFeedback();
+ _prevTfEnable = false;
+ }
+
+ _updateTracker.Update(ulong.MaxValue);
+
+ CommitBindings();
+
+ if (tfEnable && !_prevTfEnable)
+ {
+ _context.Renderer.Pipeline.BeginTransformFeedback(_drawState.Topology);
+ _prevTfEnable = true;
+ }
+ }
+
+ /// <summary>
+ /// Updates the host state for any modified guest state group with the respective bit set on <paramref name="mask"/>.
+ /// </summary>
+ /// <param name="mask">Mask, where each bit set corresponds to a group index that should be checked and updated</param>
+ public void Update(ulong mask)
+ {
+ _updateTracker.Update(mask);
+ }
+
+ /// <summary>
+ /// Ensures that the bindings are visible to the host GPU.
+ /// Note: this actually performs the binding using the host graphics API.
+ /// </summary>
+ private void CommitBindings()
+ {
+ UpdateStorageBuffers();
+
+ _channel.TextureManager.CommitGraphicsBindings();
+ _channel.BufferManager.CommitGraphicsBindings();
+ }
+
+ /// <summary>
+ /// Updates storage buffer bindings.
+ /// </summary>
+ private void UpdateStorageBuffers()
+ {
+ for (int stage = 0; stage < Constants.ShaderStages; stage++)
+ {
+ ShaderProgramInfo info = _currentProgramInfo[stage];
+
+ if (info == null)
+ {
+ continue;
+ }
+
+ for (int index = 0; index < info.SBuffers.Count; index++)
+ {
+ BufferDescriptor sb = info.SBuffers[index];
+
+ ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, 0);
+
+ int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10;
+
+ sbDescAddress += (ulong)sbDescOffset;
+
+ SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
+
+ _channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates transform feedback buffer state based on the guest GPU state.
+ /// </summary>
+ private void UpdateTfBufferState()
+ {
+ for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++)
+ {
+ TfBufferState tfb = _state.State.TfBufferState[index];
+
+ if (!tfb.Enable)
+ {
+ _channel.BufferManager.SetTransformFeedbackBuffer(index, 0, 0);
+
+ continue;
+ }
+
+ _channel.BufferManager.SetTransformFeedbackBuffer(index, tfb.Address.Pack(), (uint)tfb.Size);
+ }
+ }
+
+ /// <summary>
+ /// Updates Rasterizer primitive discard state based on guest gpu state.
+ /// </summary>
+ private void UpdateRasterizerState()
+ {
+ bool enable = _state.State.RasterizeEnable;
+ _context.Renderer.Pipeline.SetRasterizerDiscard(!enable);
+ }
+
+ /// <summary>
+ /// Updates render targets (color and depth-stencil buffers) based on current render target state.
+ /// </summary>
+ private void UpdateRenderTargetState()
+ {
+ UpdateRenderTargetState(true);
+ }
+
+ /// <summary>
+ /// Updates render targets (color and depth-stencil buffers) based on current render target state.
+ /// </summary>
+ /// <param name="useControl">Use draw buffers information from render target control register</param>
+ /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
+ public void UpdateRenderTargetState(bool useControl, int singleUse = -1)
+ {
+ var memoryManager = _channel.MemoryManager;
+ var rtControl = _state.State.RtControl;
+
+ int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
+
+ var msaaMode = _state.State.RtMsaaMode;
+
+ int samplesInX = msaaMode.SamplesInX();
+ int samplesInY = msaaMode.SamplesInY();
+
+ var scissor = _state.State.ScreenScissorState;
+ Size sizeHint = new Size(scissor.X + scissor.Width, scissor.Y + scissor.Height, 1);
+
+ bool changedScale = false;
+
+ for (int index = 0; index < Constants.TotalRenderTargets; index++)
+ {
+ int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index;
+
+ var colorState = _state.State.RtColorState[rtIndex];
+
+ if (index >= count || !IsRtEnabled(colorState))
+ {
+ changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null);
+
+ continue;
+ }
+
+ Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
+ memoryManager,
+ colorState,
+ samplesInX,
+ samplesInY,
+ sizeHint);
+
+ changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color);
+ }
+
+ bool dsEnable = _state.State.RtDepthStencilEnable;
+
+ Image.Texture depthStencil = null;
+
+ if (dsEnable)
+ {
+ var dsState = _state.State.RtDepthStencilState;
+ var dsSize = _state.State.RtDepthStencilSize;
+
+ depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
+ memoryManager,
+ dsState,
+ dsSize,
+ samplesInX,
+ samplesInY,
+ sizeHint);
+ }
+
+ changedScale |= _channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);
+
+ if (changedScale)
+ {
+ _channel.TextureManager.UpdateRenderTargetScale(singleUse);
+ _context.Renderer.Pipeline.SetRenderTargetScale(_channel.TextureManager.RenderTargetScale);
+
+ UpdateViewportTransform();
+ UpdateScissorState();
+ }
+ }
+
+ /// <summary>
+ /// Checks if a render target color buffer is used.
+ /// </summary>
+ /// <param name="colorState">Color buffer information</param>
+ /// <returns>True if the specified buffer is enabled/used, false otherwise</returns>
+ private static bool IsRtEnabled(RtColorState colorState)
+ {
+ // Colors are disabled by writing 0 to the format.
+ return colorState.Format != 0 && colorState.WidthOrStride != 0;
+ }
+
+ /// <summary>
+ /// Updates host scissor test state based on current GPU state.
+ /// </summary>
+ private void UpdateScissorState()
+ {
+ for (int index = 0; index < Constants.TotalViewports; index++)
+ {
+ ScissorState scissor = _state.State.ScissorState[index];
+
+ bool enable = scissor.Enable && (scissor.X1 != 0 || scissor.Y1 != 0 || scissor.X2 != 0xffff || scissor.Y2 != 0xffff);
+
+ if (enable)
+ {
+ int x = scissor.X1;
+ int y = scissor.Y1;
+ int width = scissor.X2 - x;
+ int height = scissor.Y2 - y;
+
+ float scale = _channel.TextureManager.RenderTargetScale;
+ if (scale != 1f)
+ {
+ x = (int)(x * scale);
+ y = (int)(y * scale);
+ width = (int)Math.Ceiling(width * scale);
+ height = (int)Math.Ceiling(height * scale);
+ }
+
+ _context.Renderer.Pipeline.SetScissor(index, true, x, y, width, height);
+ }
+ else
+ {
+ _context.Renderer.Pipeline.SetScissor(index, false, 0, 0, 0, 0);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Updates host depth clamp state based on current GPU state.
+ /// </summary>
+ /// <param name="state">Current GPU state</param>
+ private void UpdateDepthClampState()
+ {
+ ViewVolumeClipControl clip = _state.State.ViewVolumeClipControl;
+ _context.Renderer.Pipeline.SetDepthClamp((clip & ViewVolumeClipControl.DepthClampDisabled) == 0);
+ }
+
+ /// <summary>
+ /// Updates host alpha test state based on current GPU state.
+ /// </summary>
+ private void UpdateAlphaTestState()
+ {
+ _context.Renderer.Pipeline.SetAlphaTest(
+ _state.State.AlphaTestEnable,
+ _state.State.AlphaTestRef,
+ _state.State.AlphaTestFunc);
+ }
+
+ /// <summary>
+ /// Updates host depth test state based on current GPU state.
+ /// </summary>
+ private void UpdateDepthTestState()
+ {
+ _context.Renderer.Pipeline.SetDepthTest(new DepthTestDescriptor(
+ _state.State.DepthTestEnable,
+ _state.State.DepthWriteEnable,
+ _state.State.DepthTestFunc));
+ }
+
+ /// <summary>
+ /// Updates host viewport transform and clipping state based on current GPU state.
+ /// </summary>
+ private void UpdateViewportTransform()
+ {
+ var yControl = _state.State.YControl;
+ var face = _state.State.FaceState;
+
+ UpdateFrontFace(yControl, face.FrontFace);
+
+ bool flipY = yControl.HasFlag(YControl.NegateY);
+
+ Span<Viewport> viewports = stackalloc Viewport[Constants.TotalViewports];
+
+ for (int index = 0; index < Constants.TotalViewports; index++)
+ {
+ var transform = _state.State.ViewportTransform[index];
+ var extents = _state.State.ViewportExtents[index];
+
+ float scaleX = MathF.Abs(transform.ScaleX);
+ float scaleY = transform.ScaleY;
+
+ if (flipY)
+ {
+ scaleY = -scaleY;
+ }
+
+ if (!_context.Capabilities.SupportsViewportSwizzle && transform.UnpackSwizzleY() == ViewportSwizzle.NegativeY)
+ {
+ scaleY = -scaleY;
+ }
+
+ if (index == 0)
+ {
+ // Try to guess the depth mode being used on the high level API
+ // based on current transform.
+ // It is setup like so by said APIs:
+ // If depth mode is ZeroToOne:
+ // TranslateZ = Near
+ // ScaleZ = Far - Near
+ // If depth mode is MinusOneToOne:
+ // TranslateZ = (Near + Far) / 2
+ // ScaleZ = (Far - Near) / 2
+ // DepthNear/Far are sorted such as that Near is always less than Far.
+ DepthMode depthMode = extents.DepthNear != transform.TranslateZ &&
+ extents.DepthFar != transform.TranslateZ ? DepthMode.MinusOneToOne : DepthMode.ZeroToOne;
+
+ _context.Renderer.Pipeline.SetDepthMode(depthMode);
+ }
+
+ float x = transform.TranslateX - scaleX;
+ float y = transform.TranslateY - scaleY;
+
+ float width = scaleX * 2;
+ float height = scaleY * 2;
+
+ float scale = _channel.TextureManager.RenderTargetScale;
+ if (scale != 1f)
+ {
+ x *= scale;
+ y *= scale;
+ width *= scale;
+ height *= scale;
+ }
+
+ RectangleF region = new RectangleF(x, y, width, height);
+
+ ViewportSwizzle swizzleX = transform.UnpackSwizzleX();
+ ViewportSwizzle swizzleY = transform.UnpackSwizzleY();
+ ViewportSwizzle swizzleZ = transform.UnpackSwizzleZ();
+ ViewportSwizzle swizzleW = transform.UnpackSwizzleW();
+
+ float depthNear = extents.DepthNear;
+ float depthFar = extents.DepthFar;
+
+ if (transform.ScaleZ < 0)
+ {
+ float temp = depthNear;
+ depthNear = depthFar;
+ depthFar = temp;
+ }
+
+ viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar);
+ }
+
+ _context.Renderer.Pipeline.SetViewports(0, viewports);
+ }
+
+ /// <summary>
+ /// Updates host depth bias (also called polygon offset) state based on current GPU state.
+ /// </summary>
+ private void UpdateDepthBiasState()
+ {
+ var depthBias = _state.State.DepthBiasState;
+
+ float factor = _state.State.DepthBiasFactor;
+ float units = _state.State.DepthBiasUnits;
+ float clamp = _state.State.DepthBiasClamp;
+
+ PolygonModeMask enables;
+
+ enables = (depthBias.PointEnable ? PolygonModeMask.Point : 0);
+ enables |= (depthBias.LineEnable ? PolygonModeMask.Line : 0);
+ enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0);
+
+ _context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp);
+ }
+
+ /// <summary>
+ /// Updates host stencil test state based on current GPU state.
+ /// </summary>
+ private void UpdateStencilTestState()
+ {
+ var backMasks = _state.State.StencilBackMasks;
+ var test = _state.State.StencilTestState;
+ var backTest = _state.State.StencilBackTestState;
+
+ CompareOp backFunc;
+ StencilOp backSFail;
+ StencilOp backDpPass;
+ StencilOp backDpFail;
+ int backFuncRef;
+ int backFuncMask;
+ int backMask;
+
+ if (backTest.TwoSided)
+ {
+ backFunc = backTest.BackFunc;
+ backSFail = backTest.BackSFail;
+ backDpPass = backTest.BackDpPass;
+ backDpFail = backTest.BackDpFail;
+ backFuncRef = backMasks.FuncRef;
+ backFuncMask = backMasks.FuncMask;
+ backMask = backMasks.Mask;
+ }
+ else
+ {
+ backFunc = test.FrontFunc;
+ backSFail = test.FrontSFail;
+ backDpPass = test.FrontDpPass;
+ backDpFail = test.FrontDpFail;
+ backFuncRef = test.FrontFuncRef;
+ backFuncMask = test.FrontFuncMask;
+ backMask = test.FrontMask;
+ }
+
+ _context.Renderer.Pipeline.SetStencilTest(new StencilTestDescriptor(
+ test.Enable,
+ test.FrontFunc,
+ test.FrontSFail,
+ test.FrontDpPass,
+ test.FrontDpFail,
+ test.FrontFuncRef,
+ test.FrontFuncMask,
+ test.FrontMask,
+ backFunc,
+ backSFail,
+ backDpPass,
+ backDpFail,
+ backFuncRef,
+ backFuncMask,
+ backMask));
+ }
+
+ /// <summary>
+ /// Updates user-defined clipping based on the guest GPU state.
+ /// </summary>
+ private void UpdateUserClipState()
+ {
+ uint clipMask = _state.State.ClipDistanceEnable & _vsClipDistancesWritten;
+
+ for (int i = 0; i < Constants.TotalClipDistances; ++i)
+ {
+ _context.Renderer.Pipeline.SetUserClipDistance(i, (clipMask & (1 << i)) != 0);
+ }
+ }
+
+ /// <summary>
+ /// Updates current sampler pool address and size based on guest GPU state.
+ /// </summary>
+ private void UpdateSamplerPoolState()
+ {
+ var texturePool = _state.State.TexturePoolState;
+ var samplerPool = _state.State.SamplerPoolState;
+
+ var samplerIndex = _state.State.SamplerIndex;
+
+ int maximumId = samplerIndex == SamplerIndex.ViaHeaderIndex
+ ? texturePool.MaximumId
+ : samplerPool.MaximumId;
+
+ _channel.TextureManager.SetGraphicsSamplerPool(samplerPool.Address.Pack(), maximumId, samplerIndex);
+ }
+
+ /// <summary>
+ /// Updates current texture pool address and size based on guest GPU state.
+ /// </summary>
+ private void UpdateTexturePoolState()
+ {
+ var texturePool = _state.State.TexturePoolState;
+
+ _channel.TextureManager.SetGraphicsTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);
+ _channel.TextureManager.SetGraphicsTextureBufferIndex((int)_state.State.TextureBufferIndex);
+ }
+
+ /// <summary>
+ /// Updates host vertex attributes based on guest GPU state.
+ /// </summary>
+ private void UpdateVertexAttribState()
+ {
+ Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs];
+
+ for (int index = 0; index < Constants.TotalVertexAttribs; index++)
+ {
+ var vertexAttrib = _state.State.VertexAttribState[index];
+
+ if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
+ {
+ Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
+
+ format = Format.R32G32B32A32Float;
+ }
+
+ vertexAttribs[index] = new VertexAttribDescriptor(
+ vertexAttrib.UnpackBufferIndex(),
+ vertexAttrib.UnpackOffset(),
+ vertexAttrib.UnpackIsConstant(),
+ format);
+ }
+
+ _context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs);
+ }
+
+ /// <summary>
+ /// Updates host line width based on guest GPU state.
+ /// </summary>
+ private void UpdateLineState()
+ {
+ float width = _state.State.LineWidthSmooth;
+ bool smooth = _state.State.LineSmoothEnable;
+
+ _context.Renderer.Pipeline.SetLineParameters(width, smooth);
+ }
+
+ /// <summary>
+ /// Updates host point size based on guest GPU state.
+ /// </summary>
+ private void UpdatePointState()
+ {
+ float size = _state.State.PointSize;
+ bool isProgramPointSize = _state.State.VertexProgramPointSize;
+ bool enablePointSprite = _state.State.PointSpriteEnable;
+
+ // TODO: Need to figure out a way to map PointCoordReplace enable bit.
+ Origin origin = (_state.State.PointCoordReplace & 4) == 0 ? Origin.LowerLeft : Origin.UpperLeft;
+
+ _context.Renderer.Pipeline.SetPointParameters(size, isProgramPointSize, enablePointSprite, origin);
+ }
+
+ /// <summary>
+ /// Updates host primitive restart based on guest GPU state.
+ /// </summary>
+ private void UpdatePrimitiveRestartState()
+ {
+ PrimitiveRestartState primitiveRestart = _state.State.PrimitiveRestartState;
+
+ _context.Renderer.Pipeline.SetPrimitiveRestart(primitiveRestart.Enable, primitiveRestart.Index);
+ }
+
+ /// <summary>
+ /// Updates host index buffer binding based on guest GPU state.
+ /// </summary>
+ private void UpdateIndexBufferState()
+ {
+ var indexBuffer = _state.State.IndexBufferState;
+
+ if (_drawState.IndexCount == 0)
+ {
+ return;
+ }
+
+ ulong gpuVa = indexBuffer.Address.Pack();
+
+ // Do not use the end address to calculate the size, because
+ // the result may be much larger than the real size of the index buffer.
+ ulong size = (ulong)(_drawState.FirstIndex + _drawState.IndexCount);
+
+ switch (indexBuffer.Type)
+ {
+ case IndexType.UShort: size *= 2; break;
+ case IndexType.UInt: size *= 4; break;
+ }
+
+ _channel.BufferManager.SetIndexBuffer(gpuVa, size, indexBuffer.Type);
+ }
+
+ /// <summary>
+ /// Updates host vertex buffer bindings based on guest GPU state.
+ /// </summary>
+ private void UpdateVertexBufferState()
+ {
+ _drawState.IsAnyVbInstanced = false;
+
+ for (int index = 0; index < Constants.TotalVertexBuffers; index++)
+ {
+ var vertexBuffer = _state.State.VertexBufferState[index];
+
+ if (!vertexBuffer.UnpackEnable())
+ {
+ _channel.BufferManager.SetVertexBuffer(index, 0, 0, 0, 0);
+
+ continue;
+ }
+
+ GpuVa endAddress = _state.State.VertexBufferEndAddress[index];
+
+ ulong address = vertexBuffer.Address.Pack();
+
+ int stride = vertexBuffer.UnpackStride();
+
+ bool instanced = _state.State.VertexBufferInstanced[index];
+
+ int divisor = instanced ? vertexBuffer.Divisor : 0;
+
+ _drawState.IsAnyVbInstanced |= divisor != 0;
+
+ ulong size;
+
+ if (_drawState.IbStreamer.HasInlineIndexData || _drawState.DrawIndexed || stride == 0 || instanced)
+ {
+ // This size may be (much) larger than the real vertex buffer size.
+ // Avoid calculating it this way, unless we don't have any other option.
+ size = endAddress.Pack() - address + 1;
+ }
+ else
+ {
+ // For non-indexed draws, we can guess the size from the vertex count
+ // and stride.
+ int firstInstance = (int)_state.State.FirstInstance;
+
+ var drawState = _state.State.VertexBufferDrawState;
+
+ size = (ulong)((firstInstance + drawState.First + drawState.Count) * stride);
+ }
+
+ _channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor);
+ }
+ }
+
+ /// <summary>
+ /// Updates host face culling and orientation based on guest GPU state.
+ /// </summary>
+ private void UpdateFaceState()
+ {
+ var yControl = _state.State.YControl;
+ var face = _state.State.FaceState;
+
+ _context.Renderer.Pipeline.SetFaceCulling(face.CullEnable, face.CullFace);
+
+ UpdateFrontFace(yControl, face.FrontFace);
+ }
+
+ /// <summary>
+ /// Updates the front face based on the current front face and the origin.
+ /// </summary>
+ /// <param name="yControl">Y control register value, where the origin is located</param>
+ /// <param name="frontFace">Front face</param>
+ private void UpdateFrontFace(YControl yControl, FrontFace frontFace)
+ {
+ bool isUpperLeftOrigin = !yControl.HasFlag(YControl.TriangleRastFlip);
+
+ if (isUpperLeftOrigin)
+ {
+ frontFace = frontFace == FrontFace.CounterClockwise ? FrontFace.Clockwise : FrontFace.CounterClockwise;
+ }
+
+ _context.Renderer.Pipeline.SetFrontFace(frontFace);
+ }
+
+ /// <summary>
+ /// Updates host render target color masks, based on guest GPU state.
+ /// This defines which color channels are written to each color buffer.
+ /// </summary>
+ private void UpdateRtColorMask()
+ {
+ bool rtColorMaskShared = _state.State.RtColorMaskShared;
+
+ Span<uint> componentMasks = stackalloc uint[Constants.TotalRenderTargets];
+
+ for (int index = 0; index < Constants.TotalRenderTargets; index++)
+ {
+ var colorMask = _state.State.RtColorMask[rtColorMaskShared ? 0 : index];
+
+ uint componentMask;
+
+ componentMask = (colorMask.UnpackRed() ? 1u : 0u);
+ componentMask |= (colorMask.UnpackGreen() ? 2u : 0u);
+ componentMask |= (colorMask.UnpackBlue() ? 4u : 0u);
+ componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u);
+
+ componentMasks[index] = componentMask;
+ }
+
+ _context.Renderer.Pipeline.SetRenderTargetColorMasks(componentMasks);
+ }
+
+ /// <summary>
+ /// Updates host render target color buffer blending state, based on guest state.
+ /// </summary>
+ private void UpdateBlendState()
+ {
+ bool blendIndependent = _state.State.BlendIndependent;
+ ColorF blendConstant = _state.State.BlendConstant;
+
+ for (int index = 0; index < Constants.TotalRenderTargets; index++)
+ {
+ BlendDescriptor descriptor;
+
+ if (blendIndependent)
+ {
+ bool enable = _state.State.BlendEnable[index];
+ var blend = _state.State.BlendState[index];
+
+ descriptor = new BlendDescriptor(
+ enable,
+ blendConstant,
+ blend.ColorOp,
+ blend.ColorSrcFactor,
+ blend.ColorDstFactor,
+ blend.AlphaOp,
+ blend.AlphaSrcFactor,
+ blend.AlphaDstFactor);
+ }
+ else
+ {
+ bool enable = _state.State.BlendEnable[0];
+ var blend = _state.State.BlendStateCommon;
+
+ descriptor = new BlendDescriptor(
+ enable,
+ blendConstant,
+ blend.ColorOp,
+ blend.ColorSrcFactor,
+ blend.ColorDstFactor,
+ blend.AlphaOp,
+ blend.AlphaSrcFactor,
+ blend.AlphaDstFactor);
+ }
+
+ _context.Renderer.Pipeline.SetBlendState(index, descriptor);
+ }
+ }
+
+ /// <summary>
+ /// Updates host logical operation state, based on guest state.
+ /// </summary>
+ private void UpdateLogicOpState()
+ {
+ LogicalOpState logicOpState = _state.State.LogicOpState;
+
+ _context.Renderer.Pipeline.SetLogicOpState(logicOpState.Enable, logicOpState.LogicalOp);
+ }
+
+ /// <summary>
+ /// Updates host shaders based on the guest GPU state.
+ /// </summary>
+ private void UpdateShaderState()
+ {
+ ShaderAddresses addresses = new ShaderAddresses();
+
+ Span<ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1);
+
+ Span<ulong> addressesArray = MemoryMarshal.Cast<ShaderAddresses, ulong>(addressesSpan);
+
+ ulong baseAddress = _state.State.ShaderBaseAddress.Pack();
+
+ for (int index = 0; index < 6; index++)
+ {
+ var shader = _state.State.ShaderState[index];
+
+ if (!shader.UnpackEnable() && index != 1)
+ {
+ continue;
+ }
+
+ addressesArray[index] = baseAddress + shader.Offset;
+ }
+
+ GpuAccessorState gas = new GpuAccessorState(
+ _state.State.TexturePoolState.Address.Pack(),
+ _state.State.TexturePoolState.MaximumId,
+ (int)_state.State.TextureBufferIndex,
+ _state.State.EarlyZForce,
+ _drawState.Topology);
+
+ ShaderBundle gs = _channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(ref _state.State, _channel, gas, addresses);
+
+ byte oldVsClipDistancesWritten = _vsClipDistancesWritten;
+
+ _drawState.VsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false;
+ _vsClipDistancesWritten = gs.Shaders[0]?.Info.ClipDistancesWritten ?? 0;
+
+ if (oldVsClipDistancesWritten != _vsClipDistancesWritten)
+ {
+ UpdateUserClipState();
+ }
+
+ int storageBufferBindingsCount = 0;
+ int uniformBufferBindingsCount = 0;
+
+ for (int stage = 0; stage < Constants.ShaderStages; stage++)
+ {
+ ShaderProgramInfo info = gs.Shaders[stage]?.Info;
+
+ _currentProgramInfo[stage] = info;
+
+ if (info == null)
+ {
+ _channel.TextureManager.SetGraphicsTextures(stage, Array.Empty<TextureBindingInfo>());
+ _channel.TextureManager.SetGraphicsImages(stage, Array.Empty<TextureBindingInfo>());
+ _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, null);
+ _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, null);
+ continue;
+ }
+
+ var textureBindings = new TextureBindingInfo[info.Textures.Count];
+
+ for (int index = 0; index < info.Textures.Count; index++)
+ {
+ var descriptor = info.Textures[index];
+
+ Target target = ShaderTexture.GetTarget(descriptor.Type);
+
+ textureBindings[index] = new TextureBindingInfo(
+ target,
+ descriptor.Binding,
+ descriptor.CbufSlot,
+ descriptor.HandleIndex,
+ descriptor.Flags);
+ }
+
+ _channel.TextureManager.SetGraphicsTextures(stage, textureBindings);
+
+ var imageBindings = new TextureBindingInfo[info.Images.Count];
+
+ for (int index = 0; index < info.Images.Count; index++)
+ {
+ var descriptor = info.Images[index];
+
+ Target target = ShaderTexture.GetTarget(descriptor.Type);
+ Format format = ShaderTexture.GetFormat(descriptor.Format);
+
+ imageBindings[index] = new TextureBindingInfo(
+ target,
+ format,
+ descriptor.Binding,
+ descriptor.CbufSlot,
+ descriptor.HandleIndex,
+ descriptor.Flags);
+ }
+
+ _channel.TextureManager.SetGraphicsImages(stage, imageBindings);
+
+ _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers);
+ _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers);
+
+ if (info.SBuffers.Count != 0)
+ {
+ storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1);
+ }
+
+ if (info.CBuffers.Count != 0)
+ {
+ uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1);
+ }
+ }
+
+ _channel.BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount);
+ _channel.BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount);
+
+ _context.Renderer.Pipeline.SetProgram(gs.HostProgram);
+ }
+
+ /// <summary>
+ /// Forces the shaders to be rebound on the next draw.
+ /// </summary>
+ public void ForceShaderUpdate()
+ {
+ _updateTracker.ForceDirty(ShaderStateIndex);
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
new file mode 100644
index 00000000..ad6a1d0e
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
@@ -0,0 +1,428 @@
+using Ryujinx.Graphics.Device;
+using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+ /// <summary>
+ /// Represents a 3D engine class.
+ /// </summary>
+ class ThreedClass : IDeviceState
+ {
+ private readonly GpuContext _context;
+ private readonly DeviceStateWithShadow<ThreedClassState> _state;
+
+ private readonly InlineToMemoryClass _i2mClass;
+ private readonly DrawManager _drawManager;
+ private readonly SemaphoreUpdater _semaphoreUpdater;
+ private readonly ConstantBufferUpdater _cbUpdater;
+ private readonly StateUpdater _stateUpdater;
+
+ /// <summary>
+ /// Creates a new instance of the 3D engine class.
+ /// </summary>
+ /// <param name="context">GPU context</param>
+ /// <param name="channel">GPU channel</param>
+ public ThreedClass(GpuContext context, GpuChannel channel)
+ {
+ _context = context;
+ _state = new DeviceStateWithShadow<ThreedClassState>(new Dictionary<string, RwCallback>
+ {
+ { nameof(ThreedClassState.LaunchDma), new RwCallback(LaunchDma, null) },
+ { nameof(ThreedClassState.LoadInlineData), new RwCallback(LoadInlineData, null) },
+ { nameof(ThreedClassState.SyncpointAction), new RwCallback(IncrementSyncpoint, null) },
+ { nameof(ThreedClassState.TextureBarrier), new RwCallback(TextureBarrier, null) },
+ { nameof(ThreedClassState.TextureBarrierTiled), new RwCallback(TextureBarrierTiled, null) },
+ { nameof(ThreedClassState.VbElementU8), new RwCallback(VbElementU8, null) },
+ { nameof(ThreedClassState.VbElementU16), new RwCallback(VbElementU16, null) },
+ { nameof(ThreedClassState.VbElementU32), new RwCallback(VbElementU32, null) },
+ { nameof(ThreedClassState.ResetCounter), new RwCallback(ResetCounter, null) },
+ { nameof(ThreedClassState.RenderEnableCondition), new RwCallback(null, Zero) },
+ { nameof(ThreedClassState.DrawEnd), new RwCallback(DrawEnd, null) },
+ { nameof(ThreedClassState.DrawBegin), new RwCallback(DrawBegin, null) },
+ { nameof(ThreedClassState.DrawIndexedSmall), new RwCallback(DrawIndexedSmall, null) },
+ { nameof(ThreedClassState.DrawIndexedSmall2), new RwCallback(DrawIndexedSmall2, null) },
+ { nameof(ThreedClassState.DrawIndexedSmallIncInstance), new RwCallback(DrawIndexedSmallIncInstance, null) },
+ { nameof(ThreedClassState.DrawIndexedSmallIncInstance2), new RwCallback(DrawIndexedSmallIncInstance2, null) },
+ { nameof(ThreedClassState.IndexBufferCount), new RwCallback(SetIndexBufferCount, null) },
+ { nameof(ThreedClassState.Clear), new RwCallback(Clear, null) },
+ { nameof(ThreedClassState.SemaphoreControl), new RwCallback(Report, null) },
+ { nameof(ThreedClassState.SetFalcon04), new RwCallback(SetFalcon04, null) },
+ { nameof(ThreedClassState.UniformBufferUpdateData), new RwCallback(ConstantBufferUpdate, null) },
+ { nameof(ThreedClassState.UniformBufferBindVertex), new RwCallback(ConstantBufferBindVertex, null) },
+ { nameof(ThreedClassState.UniformBufferBindTessControl), new RwCallback(ConstantBufferBindTessControl, null) },
+ { nameof(ThreedClassState.UniformBufferBindTessEvaluation), new RwCallback(ConstantBufferBindTessEvaluation, null) },
+ { nameof(ThreedClassState.UniformBufferBindGeometry), new RwCallback(ConstantBufferBindGeometry, null) },
+ { nameof(ThreedClassState.UniformBufferBindFragment), new RwCallback(ConstantBufferBindFragment, null) }
+ });
+
+ _i2mClass = new InlineToMemoryClass(context, channel, initializeState: false);
+
+ var drawState = new DrawState();
+
+ _drawManager = new DrawManager(context, channel, _state, drawState);
+ _semaphoreUpdater = new SemaphoreUpdater(context, channel, _state);
+ _cbUpdater = new ConstantBufferUpdater(channel, _state);
+ _stateUpdater = new StateUpdater(context, channel, _state, drawState);
+
+ // This defaults to "always", even without any register write.
+ // Reads just return 0, regardless of what was set there.
+ _state.State.RenderEnableCondition = Condition.Always;
+ }
+
+ /// <summary>
+ /// Reads data from the class registers.
+ /// </summary>
+ /// <param name="offset">Register byte offset</param>
+ /// <returns>Data at the specified offset</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int Read(int offset) => _state.Read(offset);
+
+ /// <summary>
+ /// Writes data to the class registers.
+ /// </summary>
+ /// <param name="offset">Register byte offset</param>
+ /// <param name="data">Data to be written</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Write(int offset, int data)
+ {
+ _state.WriteWithRedundancyCheck(offset, data, out bool valueChanged);
+
+ if (valueChanged)
+ {
+ _stateUpdater.SetDirty(offset);
+ }
+ }
+
+ /// <summary>
+ /// Sets the shadow ram control value of all sub-channels.
+ /// </summary>
+ /// <param name="control">New shadow ram control value</param>
+ public void SetShadowRamControl(int control)
+ {
+ _state.State.SetMmeShadowRamControl = (uint)control;
+ }
+
+ /// <summary>
+ /// Updates current host state for all registers modified since the last call to this method.
+ /// </summary>
+ public void UpdateState()
+ {
+ _cbUpdater.FlushUboDirty();
+ _stateUpdater.Update();
+ }
+
+ /// <summary>
+ /// Updates current host state for all registers modified since the last call to this method.
+ /// </summary>
+ /// <param name="mask">Mask where each bit set indicates that the respective state group index should be checked</param>
+ public void UpdateState(ulong mask)
+ {
+ _stateUpdater.Update(mask);
+ }
+
+ /// <summary>
+ /// Updates render targets (color and depth-stencil buffers) based on current render target state.
+ /// </summary>
+ /// <param name="useControl">Use draw buffers information from render target control register</param>
+ /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
+ public void UpdateRenderTargetState(bool useControl, int singleUse = -1)
+ {
+ _stateUpdater.UpdateRenderTargetState(useControl, singleUse);
+ }
+
+ /// <summary>
+ /// Marks the entire state as dirty, forcing a full host state update before the next draw.
+ /// </summary>
+ public void ForceStateDirty()
+ {
+ _stateUpdater.SetAllDirty();
+ }
+
+ /// <summary>
+ /// Forces the shaders to be rebound on the next draw.
+ /// </summary>
+ public void ForceShaderUpdate()
+ {
+ _stateUpdater.ForceShaderUpdate();
+ }
+
+ /// <summary>
+ /// Flushes any queued UBO updates.
+ /// </summary>
+ public void FlushUboDirty()
+ {
+ _cbUpdater.FlushUboDirty();
+ }
+
+ /// <summary>
+ /// Perform any deferred draws.
+ /// </summary>
+ public void PerformDeferredDraws()
+ {
+ _drawManager.PerformDeferredDraws();
+ }
+
+ /// <summary>
+ /// Updates the currently bound constant buffer.
+ /// </summary>
+ /// <param name="data">Data to be written to the buffer</param>
+ public void ConstantBufferUpdate(ReadOnlySpan<int> data)
+ {
+ _cbUpdater.Update(data);
+ }
+
+ /// <summary>
+ /// Launches the Inline-to-Memory DMA copy operation.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void LaunchDma(int argument)
+ {
+ _i2mClass.LaunchDma(ref Unsafe.As<ThreedClassState, InlineToMemoryClassState>(ref _state.State), argument);
+ }
+
+ /// <summary>
+ /// Pushes a word of data to the Inline-to-Memory engine.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void LoadInlineData(int argument)
+ {
+ _i2mClass.LoadInlineData(argument);
+ }
+
+ /// <summary>
+ /// Performs an incrementation on a syncpoint.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void IncrementSyncpoint(int argument)
+ {
+ uint syncpointId = (uint)argument & 0xFFFF;
+
+ _context.CreateHostSyncIfNeeded();
+ _context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result.
+ _context.Synchronization.IncrementSyncpoint(syncpointId);
+ }
+
+ /// <summary>
+ /// Issues a texture barrier.
+ /// This waits until previous texture writes from the GPU to finish, before
+ /// performing new operations with said textures.
+ /// </summary>
+ /// <param name="argument">Method call argument (unused)</param>
+ private void TextureBarrier(int argument)
+ {
+ _context.Renderer.Pipeline.TextureBarrier();
+ }
+
+ /// <summary>
+ /// Issues a texture barrier.
+ /// This waits until previous texture writes from the GPU to finish, before
+ /// performing new operations with said textures.
+ /// This performs a per-tile wait, it is only valid if both the previous write
+ /// and current access has the same access patterns.
+ /// This may be faster than the regular barrier on tile-based rasterizers.
+ /// </summary>
+ /// <param name="argument">Method call argument (unused)</param>
+ private void TextureBarrierTiled(int argument)
+ {
+ _context.Renderer.Pipeline.TextureBarrierTiled();
+ }
+
+ /// <summary>
+ /// Pushes four 8-bit index buffer elements.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void VbElementU8(int argument)
+ {
+ _drawManager.VbElementU8(argument);
+ }
+
+ /// <summary>
+ /// Pushes two 16-bit index buffer elements.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void VbElementU16(int argument)
+ {
+ _drawManager.VbElementU16(argument);
+ }
+
+ /// <summary>
+ /// Pushes one 32-bit index buffer element.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void VbElementU32(int argument)
+ {
+ _drawManager.VbElementU32(argument);
+ }
+
+ /// <summary>
+ /// Resets the value of an internal GPU counter back to zero.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void ResetCounter(int argument)
+ {
+ _semaphoreUpdater.ResetCounter(argument);
+ }
+
+ /// <summary>
+ /// Finishes the draw call.
+ /// This draws geometry on the bound buffers based on the current GPU state.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void DrawEnd(int argument)
+ {
+ _drawManager.DrawEnd(this, argument);
+ }
+
+ /// <summary>
+ /// Starts draw.
+ /// This sets primitive type and instanced draw parameters.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void DrawBegin(int argument)
+ {
+ _drawManager.DrawBegin(argument);
+ }
+
+ /// <summary>
+ /// Sets the index buffer count.
+ /// This also sets internal state that indicates that the next draw is an indexed draw.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void SetIndexBufferCount(int argument)
+ {
+ _drawManager.SetIndexBufferCount(argument);
+ }
+
+ /// <summary>
+ /// Performs a indexed draw with a low number of index buffer elements.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void DrawIndexedSmall(int argument)
+ {
+ _drawManager.DrawIndexedSmall(this, argument);
+ }
+
+ /// <summary>
+ /// Performs a indexed draw with a low number of index buffer elements.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void DrawIndexedSmall2(int argument)
+ {
+ _drawManager.DrawIndexedSmall2(this, argument);
+ }
+
+ /// <summary>
+ /// Performs a indexed draw with a low number of index buffer elements,
+ /// while also pre-incrementing the current instance value.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void DrawIndexedSmallIncInstance(int argument)
+ {
+ _drawManager.DrawIndexedSmallIncInstance(this, argument);
+ }
+
+ /// <summary>
+ /// Performs a indexed draw with a low number of index buffer elements,
+ /// while also pre-incrementing the current instance value.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void DrawIndexedSmallIncInstance2(int argument)
+ {
+ _drawManager.DrawIndexedSmallIncInstance2(this, argument);
+ }
+
+ /// <summary>
+ /// Clears the current color and depth-stencil buffers.
+ /// Which buffers should be cleared is also specified on the argument.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void Clear(int argument)
+ {
+ _drawManager.Clear(this, argument);
+ }
+
+ /// <summary>
+ /// Writes a GPU counter to guest memory.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void Report(int argument)
+ {
+ _semaphoreUpdater.Report(argument);
+ }
+
+ /// <summary>
+ /// Performs high-level emulation of Falcon microcode function number "4".
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void SetFalcon04(int argument)
+ {
+ _state.State.SetMmeShadowScratch[0] = 1;
+ }
+
+ /// <summary>
+ /// Updates the uniform buffer data with inline data.
+ /// </summary>
+ /// <param name="argument">New uniform buffer data word</param>
+ private void ConstantBufferUpdate(int argument)
+ {
+ _cbUpdater.Update(argument);
+ }
+
+ /// <summary>
+ /// Binds a uniform buffer for the vertex shader stage.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void ConstantBufferBindVertex(int argument)
+ {
+ _cbUpdater.BindVertex(argument);
+ }
+
+ /// <summary>
+ /// Binds a uniform buffer for the tessellation control shader stage.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void ConstantBufferBindTessControl(int argument)
+ {
+ _cbUpdater.BindTessControl(argument);
+ }
+
+ /// <summary>
+ /// Binds a uniform buffer for the tessellation evaluation shader stage.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void ConstantBufferBindTessEvaluation(int argument)
+ {
+ _cbUpdater.BindTessEvaluation(argument);
+ }
+
+ /// <summary>
+ /// Binds a uniform buffer for the geometry shader stage.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void ConstantBufferBindGeometry(int argument)
+ {
+ _cbUpdater.BindGeometry(argument);
+ }
+
+ /// <summary>
+ /// Binds a uniform buffer for the fragment shader stage.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ private void ConstantBufferBindFragment(int argument)
+ {
+ _cbUpdater.BindFragment(argument);
+ }
+
+ /// <summary>
+ /// Generic register read function that just returns 0.
+ /// </summary>
+ /// <returns>Zero</returns>
+ private static int Zero()
+ {
+ return 0;
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
new file mode 100644
index 00000000..a6392e3d
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
@@ -0,0 +1,861 @@
+using Ryujinx.Common.Memory;
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
+using Ryujinx.Graphics.Gpu.Engine.Types;
+using Ryujinx.Graphics.Gpu.Image;
+using System;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed
+{
+ /// <summary>
+ /// Shader stage name.
+ /// </summary>
+ enum ShaderType
+ {
+ Vertex,
+ TessellationControl,
+ TessellationEvaluation,
+ Geometry,
+ Fragment
+ }
+
+ /// <summary>
+ /// Transform feedback buffer state.
+ /// </summary>
+ struct TfBufferState
+ {
+#pragma warning disable CS0649
+ public Boolean32 Enable;
+ public GpuVa Address;
+ public int Size;
+ public int Offset;
+ public uint Padding0;
+ public uint Padding1;
+ public uint Padding2;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Transform feedback state.
+ /// </summary>
+ struct TfState
+ {
+#pragma warning disable CS0649
+ public int BufferIndex;
+ public int VaryingsCount;
+ public int Stride;
+ public uint Padding;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Render target color buffer state.
+ /// </summary>
+ struct RtColorState
+ {
+#pragma warning disable CS0649
+ public GpuVa Address;
+ public int WidthOrStride;
+ public int Height;
+ public ColorFormat Format;
+ public MemoryLayout MemoryLayout;
+ public int Depth;
+ public int LayerSize;
+ public int BaseLayer;
+ public int Unknown0x24;
+ public int Padding0;
+ public int Padding1;
+ public int Padding2;
+ public int Padding3;
+ public int Padding4;
+ public int Padding5;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Viewport transform parameters, for viewport transformation.
+ /// </summary>
+ struct ViewportTransform
+ {
+#pragma warning disable CS0649
+ public float ScaleX;
+ public float ScaleY;
+ public float ScaleZ;
+ public float TranslateX;
+ public float TranslateY;
+ public float TranslateZ;
+ public uint Swizzle;
+ public uint SubpixelPrecisionBias;
+#pragma warning restore CS0649
+
+ /// <summary>
+ /// Unpacks viewport swizzle of the position X component.
+ /// </summary>
+ /// <returns>Swizzle enum value</returns>
+ public ViewportSwizzle UnpackSwizzleX()
+ {
+ return (ViewportSwizzle)(Swizzle & 7);
+ }
+
+ /// <summary>
+ /// Unpacks viewport swizzle of the position Y component.
+ /// </summary>
+ /// <returns>Swizzle enum value</returns>
+ public ViewportSwizzle UnpackSwizzleY()
+ {
+ return (ViewportSwizzle)((Swizzle >> 4) & 7);
+ }
+
+ /// <summary>
+ /// Unpacks viewport swizzle of the position Z component.
+ /// </summary>
+ /// <returns>Swizzle enum value</returns>
+ public ViewportSwizzle UnpackSwizzleZ()
+ {
+ return (ViewportSwizzle)((Swizzle >> 8) & 7);
+ }
+
+ /// <summary>
+ /// Unpacks viewport swizzle of the position W component.
+ /// </summary>
+ /// <returns>Swizzle enum value</returns>
+ public ViewportSwizzle UnpackSwizzleW()
+ {
+ return (ViewportSwizzle)((Swizzle >> 12) & 7);
+ }
+ }
+
+ /// <summary>
+ /// Viewport extents for viewport clipping, also includes depth range.
+ /// </summary>
+ struct ViewportExtents
+ {
+#pragma warning disable CS0649
+ public ushort X;
+ public ushort Width;
+ public ushort Y;
+ public ushort Height;
+ public float DepthNear;
+ public float DepthFar;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Draw state for non-indexed draws.
+ /// </summary>
+ struct VertexBufferDrawState
+ {
+#pragma warning disable CS0649
+ public int First;
+ public int Count;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Color buffer clear color.
+ /// </summary>
+ struct ClearColors
+ {
+#pragma warning disable CS0649
+ public float Red;
+ public float Green;
+ public float Blue;
+ public float Alpha;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Depth bias (also called polygon offset) parameters.
+ /// </summary>
+ struct DepthBiasState
+ {
+#pragma warning disable CS0649
+ public Boolean32 PointEnable;
+ public Boolean32 LineEnable;
+ public Boolean32 FillEnable;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Scissor state.
+ /// </summary>
+ struct ScissorState
+ {
+#pragma warning disable CS0649
+ public Boolean32 Enable;
+ public ushort X1;
+ public ushort X2;
+ public ushort Y1;
+ public ushort Y2;
+ public uint Padding;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Stencil test masks for back tests.
+ /// </summary>
+ struct StencilBackMasks
+ {
+#pragma warning disable CS0649
+ public int FuncRef;
+ public int Mask;
+ public int FuncMask;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Render target depth-stencil buffer state.
+ /// </summary>
+ struct RtDepthStencilState
+ {
+#pragma warning disable CS0649
+ public GpuVa Address;
+ public ZetaFormat Format;
+ public MemoryLayout MemoryLayout;
+ public int LayerSize;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Screen scissor state.
+ /// </summary>
+ struct ScreenScissorState
+ {
+#pragma warning disable CS0649
+ public ushort X;
+ public ushort Width;
+ public ushort Y;
+ public ushort Height;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Vertex buffer attribute state.
+ /// </summary>
+ struct VertexAttribState
+ {
+#pragma warning disable CS0649
+ public uint Attribute;
+#pragma warning restore CS0649
+
+ /// <summary>
+ /// Unpacks the index of the vertex buffer this attribute belongs to.
+ /// </summary>
+ /// <returns>Vertex buffer index</returns>
+ public int UnpackBufferIndex()
+ {
+ return (int)(Attribute & 0x1f);
+ }
+
+ /// <summary>
+ /// Unpacks the attribute constant flag.
+ /// </summary>
+ /// <returns>True if the attribute is constant, false otherwise</returns>
+ public bool UnpackIsConstant()
+ {
+ return (Attribute & 0x40) != 0;
+ }
+
+ /// <summary>
+ /// Unpacks the offset, in bytes, of the attribute on the vertex buffer.
+ /// </summary>
+ /// <returns>Attribute offset in bytes</returns>
+ public int UnpackOffset()
+ {
+ return (int)((Attribute >> 7) & 0x3fff);
+ }
+
+ /// <summary>
+ /// Unpacks the Maxwell attribute format integer.
+ /// </summary>
+ /// <returns>Attribute format integer</returns>
+ public uint UnpackFormat()
+ {
+ return Attribute & 0x3fe00000;
+ }
+ }
+
+ /// <summary>
+ /// Render target draw buffers control.
+ /// </summary>
+ struct RtControl
+ {
+#pragma warning disable CS0649
+ public uint Packed;
+#pragma warning restore CS0649
+
+ /// <summary>
+ /// Unpacks the number of active draw buffers.
+ /// </summary>
+ /// <returns>Number of active draw buffers</returns>
+ public int UnpackCount()
+ {
+ return (int)(Packed & 0xf);
+ }
+
+ /// <summary>
+ /// Unpacks the color attachment index for a given draw buffer.
+ /// </summary>
+ /// <param name="index">Index of the draw buffer</param>
+ /// <returns>Attachment index</returns>
+ public int UnpackPermutationIndex(int index)
+ {
+ return (int)((Packed >> (4 + index * 3)) & 7);
+ }
+ }
+
+ /// <summary>
+ /// 3D, 2D or 1D texture size.
+ /// </summary>
+ struct Size3D
+ {
+#pragma warning disable CS0649
+ public int Width;
+ public int Height;
+ public int Depth;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Stencil front test state and masks.
+ /// </summary>
+ struct StencilTestState
+ {
+#pragma warning disable CS0649
+ public Boolean32 Enable;
+ public StencilOp FrontSFail;
+ public StencilOp FrontDpFail;
+ public StencilOp FrontDpPass;
+ public CompareOp FrontFunc;
+ public int FrontFuncRef;
+ public int FrontFuncMask;
+ public int FrontMask;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Screen Y control register.
+ /// </summary>
+ [Flags]
+ enum YControl
+ {
+ NegateY = 1 << 0,
+ TriangleRastFlip = 1 << 4
+ }
+
+ /// <summary>
+ /// Condition for conditional rendering.
+ /// </summary>
+ enum Condition
+ {
+ Never,
+ Always,
+ ResultNonZero,
+ Equal,
+ NotEqual
+ }
+
+ /// <summary>
+ /// Texture or sampler pool state.
+ /// </summary>
+ struct PoolState
+ {
+#pragma warning disable CS0649
+ public GpuVa Address;
+ public int MaximumId;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Stencil back test state.
+ /// </summary>
+ struct StencilBackTestState
+ {
+#pragma warning disable CS0649
+ public Boolean32 TwoSided;
+ public StencilOp BackSFail;
+ public StencilOp BackDpFail;
+ public StencilOp BackDpPass;
+ public CompareOp BackFunc;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Primitive restart state.
+ /// </summary>
+ struct PrimitiveRestartState
+ {
+#pragma warning disable CS0649
+ public Boolean32 Enable;
+ public int Index;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// GPU index buffer state.
+ /// This is used on indexed draws.
+ /// </summary>
+ struct IndexBufferState
+ {
+#pragma warning disable CS0649
+ public GpuVa Address;
+ public GpuVa EndAddress;
+ public IndexType Type;
+ public int First;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Face culling and orientation parameters.
+ /// </summary>
+ struct FaceState
+ {
+#pragma warning disable CS0649
+ public Boolean32 CullEnable;
+ public FrontFace FrontFace;
+ public Face CullFace;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// View volume clip control.
+ /// </summary>
+ [Flags]
+ enum ViewVolumeClipControl
+ {
+ ForceDepthRangeZeroToOne = 1 << 0,
+ DepthClampDisabled = 1 << 11
+ }
+
+ /// <summary>
+ /// Logical operation state.
+ /// </summary>
+ struct LogicalOpState
+ {
+#pragma warning disable CS0649
+ public Boolean32 Enable;
+ public LogicalOp LogicalOp;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Render target color buffer mask.
+ /// This defines which color channels are written to the color buffer.
+ /// </summary>
+ struct RtColorMask
+ {
+#pragma warning disable CS0649
+ public uint Packed;
+#pragma warning restore CS0649
+
+ /// <summary>
+ /// Unpacks red channel enable.
+ /// </summary>
+ /// <returns>True to write the new red channel color, false to keep the old value</returns>
+ public bool UnpackRed()
+ {
+ return (Packed & 0x1) != 0;
+ }
+
+ /// <summary>
+ /// Unpacks green channel enable.
+ /// </summary>
+ /// <returns>True to write the new green channel color, false to keep the old value</returns>
+ public bool UnpackGreen()
+ {
+ return (Packed & 0x10) != 0;
+ }
+
+ /// <summary>
+ /// Unpacks blue channel enable.
+ /// </summary>
+ /// <returns>True to write the new blue channel color, false to keep the old value</returns>
+ public bool UnpackBlue()
+ {
+ return (Packed & 0x100) != 0;
+ }
+
+ /// <summary>
+ /// Unpacks alpha channel enable.
+ /// </summary>
+ /// <returns>True to write the new alpha channel color, false to keep the old value</returns>
+ public bool UnpackAlpha()
+ {
+ return (Packed & 0x1000) != 0;
+ }
+ }
+
+ /// <summary>
+ /// Vertex buffer state.
+ /// </summary>
+ struct VertexBufferState
+ {
+#pragma warning disable CS0649
+ public uint Control;
+ public GpuVa Address;
+ public int Divisor;
+#pragma warning restore CS0649
+
+ /// <summary>
+ /// Vertex buffer stride, defined as the number of bytes occupied by each vertex in memory.
+ /// </summary>
+ /// <returns>Vertex buffer stride</returns>
+ public int UnpackStride()
+ {
+ return (int)(Control & 0xfff);
+ }
+
+ /// <summary>
+ /// Vertex buffer enable.
+ /// </summary>
+ /// <returns>True if the vertex buffer is enabled, false otherwise</returns>
+ public bool UnpackEnable()
+ {
+ return (Control & (1 << 12)) != 0;
+ }
+ }
+
+ /// <summary>
+ /// Color buffer blending parameters, shared by all color buffers.
+ /// </summary>
+ struct BlendStateCommon
+ {
+#pragma warning disable CS0649
+ public Boolean32 SeparateAlpha;
+ public BlendOp ColorOp;
+ public BlendFactor ColorSrcFactor;
+ public BlendFactor ColorDstFactor;
+ public BlendOp AlphaOp;
+ public BlendFactor AlphaSrcFactor;
+ public uint Unknown0x1354;
+ public BlendFactor AlphaDstFactor;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Color buffer blending parameters.
+ /// </summary>
+ struct BlendState
+ {
+#pragma warning disable CS0649
+ public Boolean32 SeparateAlpha;
+ public BlendOp ColorOp;
+ public BlendFactor ColorSrcFactor;
+ public BlendFactor ColorDstFactor;
+ public BlendOp AlphaOp;
+ public BlendFactor AlphaSrcFactor;
+ public BlendFactor AlphaDstFactor;
+ public uint Padding;
+#pragma warning restore CS0649
+ }
+
+ /// <summary>
+ /// Graphics shader stage state.
+ /// </summary>
+ struct ShaderState
+ {
+#pragma warning disable CS0649
+ public uint Control;
+ public uint Offset;
+ public uint Unknown0x8;
+ public int MaxRegisters;
+ public ShaderType Type;
+ public uint Unknown0x14;
+ public uint Unknown0x18;
+ public uint Unknown0x1c;
+ public uint Unknown0x20;
+ public uint Unknown0x24;
+ public uint Unknown0x28;
+ public uint Unknown0x2c;
+ public uint Unknown0x30;
+ public uint Unknown0x34;
+ public uint Unknown0x38;
+ public uint Unknown0x3c;
+#pragma warning restore CS0649
+
+ /// <summary>
+ /// Unpacks shader enable information.
+ /// Must be ignored for vertex shaders, those are always enabled.
+ /// </summary>
+ /// <returns>True if the stage is enabled, false otherwise</returns>
+ public bool UnpackEnable()
+ {
+ return (Control & 1) != 0;
+ }
+ }
+
+ /// <summary>
+ /// Uniform buffer state for the uniform buffer currently being modified.
+ /// </summary>
+ struct UniformBufferState
+ {
+#pragma warning disable CS0649
+ public int Size;
+ public GpuVa Address;
+ public int Offset;
+#pragma warning restore CS0649
+ }
+
+ unsafe struct ThreedClassState : IShadowState
+ {
+#pragma warning disable CS0649
+ public uint SetObject;
+ public int SetObjectClassId => (int)((SetObject >> 0) & 0xFFFF);
+ public int SetObjectEngineId => (int)((SetObject >> 16) & 0x1F);
+ public fixed uint Reserved04[63];
+ public uint NoOperation;
+ public uint SetNotifyA;
+ public int SetNotifyAAddressUpper => (int)((SetNotifyA >> 0) & 0xFF);
+ public uint SetNotifyB;
+ public uint Notify;
+ public NotifyType NotifyType => (NotifyType)(Notify);
+ public uint WaitForIdle;
+ public uint LoadMmeInstructionRamPointer;
+ public uint LoadMmeInstructionRam;
+ public uint LoadMmeStartAddressRamPointer;
+ public uint LoadMmeStartAddressRam;
+ public uint SetMmeShadowRamControl;
+ public SetMmeShadowRamControlMode SetMmeShadowRamControlMode => (SetMmeShadowRamControlMode)((SetMmeShadowRamControl >> 0) & 0x3);
+ public fixed uint Reserved128[2];
+ public uint SetGlobalRenderEnableA;
+ public int SetGlobalRenderEnableAOffsetUpper => (int)((SetGlobalRenderEnableA >> 0) & 0xFF);
+ public uint SetGlobalRenderEnableB;
+ public uint SetGlobalRenderEnableC;
+ public int SetGlobalRenderEnableCMode => (int)((SetGlobalRenderEnableC >> 0) & 0x7);
+ public uint SendGoIdle;
+ public uint PmTrigger;
+ public uint PmTriggerWfi;
+ public fixed uint Reserved148[2];
+ public uint SetInstrumentationMethodHeader;
+ public uint SetInstrumentationMethodData;
+ public fixed uint Reserved158[10];
+ public uint LineLengthIn;
+ public uint LineCount;
+ public uint OffsetOutUpper;
+ public int OffsetOutUpperValue => (int)((OffsetOutUpper >> 0) & 0xFF);
+ public uint OffsetOut;
+ public uint PitchOut;
+ public uint SetDstBlockSize;
+ public SetDstBlockSizeWidth SetDstBlockSizeWidth => (SetDstBlockSizeWidth)((SetDstBlockSize >> 0) & 0xF);
+ public SetDstBlockSizeHeight SetDstBlockSizeHeight => (SetDstBlockSizeHeight)((SetDstBlockSize >> 4) & 0xF);
+ public SetDstBlockSizeDepth SetDstBlockSizeDepth => (SetDstBlockSizeDepth)((SetDstBlockSize >> 8) & 0xF);
+ public uint SetDstWidth;
+ public uint SetDstHeight;
+ public uint SetDstDepth;
+ public uint SetDstLayer;
+ public uint SetDstOriginBytesX;
+ public int SetDstOriginBytesXV => (int)((SetDstOriginBytesX >> 0) & 0xFFFFF);
+ public uint SetDstOriginSamplesY;
+ public int SetDstOriginSamplesYV => (int)((SetDstOriginSamplesY >> 0) & 0xFFFF);
+ public uint LaunchDma;
+ public LaunchDmaDstMemoryLayout LaunchDmaDstMemoryLayout => (LaunchDmaDstMemoryLayout)((LaunchDma >> 0) & 0x1);
+ public LaunchDmaCompletionType LaunchDmaCompletionType => (LaunchDmaCompletionType)((LaunchDma >> 4) & 0x3);
+ public LaunchDmaInterruptType LaunchDmaInterruptType => (LaunchDmaInterruptType)((LaunchDma >> 8) & 0x3);
+ public LaunchDmaSemaphoreStructSize LaunchDmaSemaphoreStructSize => (LaunchDmaSemaphoreStructSize)((LaunchDma >> 12) & 0x1);
+ public bool LaunchDmaReductionEnable => (LaunchDma & 0x2) != 0;
+ public LaunchDmaReductionOp LaunchDmaReductionOp => (LaunchDmaReductionOp)((LaunchDma >> 13) & 0x7);
+ public LaunchDmaReductionFormat LaunchDmaReductionFormat => (LaunchDmaReductionFormat)((LaunchDma >> 2) & 0x3);
+ public bool LaunchDmaSysmembarDisable => (LaunchDma & 0x40) != 0;
+ public uint LoadInlineData;
+ public fixed uint Reserved1B8[22];
+ public Boolean32 EarlyZForce;
+ public fixed uint Reserved214[45];
+ public uint SyncpointAction;
+ public fixed uint Reserved2CC[44];
+ public Boolean32 RasterizeEnable;
+ public Array4<TfBufferState> TfBufferState;
+ public fixed uint Reserved400[192];
+ public Array4<TfState> TfState;
+ public fixed uint Reserved740[1];
+ public Boolean32 TfEnable;
+ public fixed uint Reserved748[46];
+ public Array8<RtColorState> RtColorState;
+ public Array16<ViewportTransform> ViewportTransform;
+ public Array16<ViewportExtents> ViewportExtents;
+ public fixed uint ReservedD00[29];
+ public VertexBufferDrawState VertexBufferDrawState;
+ public uint DepthMode;
+ public ClearColors ClearColors;
+ public float ClearDepthValue;
+ public fixed uint ReservedD94[3];
+ public uint ClearStencilValue;
+ public fixed uint ReservedDA4[7];
+ public DepthBiasState DepthBiasState;
+ public fixed uint ReservedDCC[5];
+ public uint TextureBarrier;
+ public fixed uint ReservedDE4[7];
+ public Array16<ScissorState> ScissorState;
+ public fixed uint ReservedF00[21];
+ public StencilBackMasks StencilBackMasks;
+ public fixed uint ReservedF60[5];
+ public uint InvalidateTextures;
+ public fixed uint ReservedF78[1];
+ public uint TextureBarrierTiled;
+ public fixed uint ReservedF80[4];
+ public Boolean32 RtColorMaskShared;
+ public fixed uint ReservedF94[19];
+ public RtDepthStencilState RtDepthStencilState;
+ public ScreenScissorState ScreenScissorState;
+ public fixed uint ReservedFFC[89];
+ public Array16<VertexAttribState> VertexAttribState;
+ public fixed uint Reserved11A0[31];
+ public RtControl RtControl;
+ public fixed uint Reserved1220[2];
+ public Size3D RtDepthStencilSize;
+ public SamplerIndex SamplerIndex;
+ public fixed uint Reserved1238[37];
+ public Boolean32 DepthTestEnable;
+ public fixed uint Reserved12D0[5];
+ public Boolean32 BlendIndependent;
+ public Boolean32 DepthWriteEnable;
+ public Boolean32 AlphaTestEnable;
+ public fixed uint Reserved12F0[5];
+ public uint VbElementU8;
+ public uint Reserved1308;
+ public CompareOp DepthTestFunc;
+ public float AlphaTestRef;
+ public CompareOp AlphaTestFunc;
+ public uint Reserved1318;
+ public ColorF BlendConstant;
+ public fixed uint Reserved132C[4];
+ public BlendStateCommon BlendStateCommon;
+ public Boolean32 BlendEnableCommon;
+ public Array8<Boolean32> BlendEnable;
+ public StencilTestState StencilTestState;
+ public fixed uint Reserved13A0[3];
+ public YControl YControl;
+ public float LineWidthSmooth;
+ public float LineWidthAliased;
+ public fixed uint Reserved13B8[31];
+ public uint FirstVertex;
+ public uint FirstInstance;
+ public fixed uint Reserved143C[53];
+ public uint ClipDistanceEnable;
+ public uint Reserved1514;
+ public float PointSize;
+ public uint Reserved151C;
+ public Boolean32 PointSpriteEnable;
+ public fixed uint Reserved1524[3];
+ public uint ResetCounter;
+ public uint Reserved1534;
+ public Boolean32 RtDepthStencilEnable;
+ public fixed uint Reserved153C[5];
+ public GpuVa RenderEnableAddress;
+ public Condition RenderEnableCondition;
+ public PoolState SamplerPoolState;
+ public uint Reserved1568;
+ public float DepthBiasFactor;
+ public Boolean32 LineSmoothEnable;
+ public PoolState TexturePoolState;
+ public fixed uint Reserved1580[5];
+ public StencilBackTestState StencilBackTestState;
+ public fixed uint Reserved15A8[5];
+ public float DepthBiasUnits;
+ public fixed uint Reserved15C0[4];
+ public TextureMsaaMode RtMsaaMode;
+ public fixed uint Reserved15D4[5];
+ public uint VbElementU32;
+ public uint Reserved15EC;
+ public uint VbElementU16;
+ public fixed uint Reserved15F4[4];
+ public uint PointCoordReplace;
+ public GpuVa ShaderBaseAddress;
+ public uint Reserved1610;
+ public uint DrawEnd;
+ public uint DrawBegin;
+ public fixed uint Reserved161C[10];
+ public PrimitiveRestartState PrimitiveRestartState;
+ public fixed uint Reserved164C[95];
+ public IndexBufferState IndexBufferState;
+ public uint IndexBufferCount;
+ public uint DrawIndexedSmall;
+ public uint DrawIndexedSmall2;
+ public uint Reserved17EC;
+ public uint DrawIndexedSmallIncInstance;
+ public uint DrawIndexedSmallIncInstance2;
+ public fixed uint Reserved17F8[33];
+ public float DepthBiasClamp;
+ public Array16<Boolean32> VertexBufferInstanced;
+ public fixed uint Reserved18C0[20];
+ public Boolean32 VertexProgramPointSize;
+ public uint Reserved1914;
+ public FaceState FaceState;
+ public fixed uint Reserved1924[2];
+ public uint ViewportTransformEnable;
+ public fixed uint Reserved1930[3];
+ public ViewVolumeClipControl ViewVolumeClipControl;
+ public fixed uint Reserved1940[2];
+ public Boolean32 PrimitiveTypeOverrideEnable;
+ public fixed uint Reserved194C[9];
+ public PrimitiveTypeOverride PrimitiveTypeOverride;
+ public fixed uint Reserved1974[20];
+ public LogicalOpState LogicOpState;
+ public uint Reserved19CC;
+ public uint Clear;
+ public fixed uint Reserved19D4[11];
+ public Array8<RtColorMask> RtColorMask;
+ public fixed uint Reserved1A20[56];
+ public GpuVa SemaphoreAddress;
+ public int SemaphorePayload;
+ public uint SemaphoreControl;
+ public fixed uint Reserved1B10[60];
+ public Array16<VertexBufferState> VertexBufferState;
+ public fixed uint Reserved1D00[64];
+ public Array8<BlendState> BlendState;
+ public Array16<GpuVa> VertexBufferEndAddress;
+ public fixed uint Reserved1F80[32];
+ public Array6<ShaderState> ShaderState;
+ public fixed uint Reserved2180[96];
+ public uint SetFalcon00;
+ public uint SetFalcon01;
+ public uint SetFalcon02;
+ public uint SetFalcon03;
+ public uint SetFalcon04;
+ public uint SetFalcon05;
+ public uint SetFalcon06;
+ public uint SetFalcon07;
+ public uint SetFalcon08;
+ public uint SetFalcon09;
+ public uint SetFalcon10;
+ public uint SetFalcon11;
+ public uint SetFalcon12;
+ public uint SetFalcon13;
+ public uint SetFalcon14;
+ public uint SetFalcon15;
+ public uint SetFalcon16;
+ public uint SetFalcon17;
+ public uint SetFalcon18;
+ public uint SetFalcon19;
+ public uint SetFalcon20;
+ public uint SetFalcon21;
+ public uint SetFalcon22;
+ public uint SetFalcon23;
+ public uint SetFalcon24;
+ public uint SetFalcon25;
+ public uint SetFalcon26;
+ public uint SetFalcon27;
+ public uint SetFalcon28;
+ public uint SetFalcon29;
+ public uint SetFalcon30;
+ public uint SetFalcon31;
+ public UniformBufferState UniformBufferState;
+ public Array16<uint> UniformBufferUpdateData;
+ public fixed uint Reserved23D0[16];
+ public uint UniformBufferBindVertex;
+ public fixed uint Reserved2414[7];
+ public uint UniformBufferBindTessControl;
+ public fixed uint Reserved2434[7];
+ public uint UniformBufferBindTessEvaluation;
+ public fixed uint Reserved2454[7];
+ public uint UniformBufferBindGeometry;
+ public fixed uint Reserved2474[7];
+ public uint UniformBufferBindFragment;
+ public fixed uint Reserved2494[93];
+ public uint TextureBufferIndex;
+ public fixed uint Reserved260C[125];
+ public Array4<Array32<uint>> TfVaryingLocations;
+ public fixed uint Reserved2A00[640];
+ public MmeShadowScratch SetMmeShadowScratch;
+#pragma warning restore CS0649
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs
index d4f6d879..3c74c164 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs
@@ -1,7 +1,7 @@
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image;
-using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Texture;
using System;
using System.Collections.Generic;
@@ -52,8 +52,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
{
var memoryManager = _channel.MemoryManager;
- var dstCopyTexture = Unsafe.As<uint, CopyTexture>(ref _state.State.SetDstFormat);
- var srcCopyTexture = Unsafe.As<uint, CopyTexture>(ref _state.State.SetSrcFormat);
+ var dstCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetDstFormat);
+ var srcCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetSrcFormat);
long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac;
long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac;
diff --git a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs
index fdc4204d..46fddb04 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClassState.cs
@@ -14,17 +14,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
}
/// <summary>
- /// MME shadow RAM control mode.
- /// </summary>
- enum SetMmeShadowRamControlMode
- {
- MethodTrack = 0,
- MethodTrackWithFilter = 1,
- MethodPassthrough = 2,
- MethodReplay = 3,
- }
-
- /// <summary>
/// Format of the destination texture.
/// </summary>
enum SetDstFormatV
@@ -506,7 +495,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
/// <summary>
/// 2D class state.
/// </summary>
- unsafe struct TwodClassState
+ unsafe struct TwodClassState : IShadowState
{
#pragma warning disable CS0649
public uint SetObject;
diff --git a/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs
new file mode 100644
index 00000000..c28da094
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs
@@ -0,0 +1,22 @@
+using Ryujinx.Graphics.Gpu.Engine.Types;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Twod
+{
+ /// <summary>
+ /// Texture to texture (with optional resizing) copy parameters.
+ /// </summary>
+ struct TwodTexture
+ {
+#pragma warning disable CS0649
+ public ColorFormat Format;
+ public Boolean32 LinearLayout;
+ public MemoryLayout MemoryLayout;
+ public int Depth;
+ public int Layer;
+ public int Stride;
+ public int Width;
+ public int Height;
+ public GpuVa Address;
+#pragma warning restore CS0649
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs b/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs
new file mode 100644
index 00000000..c982347a
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/Boolean32.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.Graphics.Gpu.Engine.Types
+{
+ /// <summary>
+ /// Boolean value, stored as a 32-bits integer in memory.
+ /// </summary>
+ struct Boolean32
+ {
+#pragma warning disable CS0649
+ private uint _value;
+#pragma warning restore CS0649
+
+ public static implicit operator bool(Boolean32 value)
+ {
+ return (value._value & 1) != 0;
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs b/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs
new file mode 100644
index 00000000..e780ec23
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/ColorFormat.cs
@@ -0,0 +1,134 @@
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Image;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Types
+{
+ /// <summary>
+ /// Color texture format.
+ /// </summary>
+ enum ColorFormat
+ {
+ R32G32B32A32Float = 0xc0,
+ R32G32B32A32Sint = 0xc1,
+ R32G32B32A32Uint = 0xc2,
+ R32G32B32X32Float = 0xc3,
+ R32G32B32X32Sint = 0xc4,
+ R32G32B32X32Uint = 0xc5,
+ R16G16B16X16Unorm = 0xc6,
+ R16G16B16X16Snorm = 0xc7,
+ R16G16B16X16Sint = 0xc8,
+ R16G16B16X16Uint = 0xc9,
+ R16G16B16A16Float = 0xca,
+ R32G32Float = 0xcb,
+ R32G32Sint = 0xcc,
+ R32G32Uint = 0xcd,
+ R16G16B16X16Float = 0xce,
+ B8G8R8A8Unorm = 0xcf,
+ B8G8R8A8Srgb = 0xd0,
+ R10G10B10A2Unorm = 0xd1,
+ R10G10B10A2Uint = 0xd2,
+ R8G8B8A8Unorm = 0xd5,
+ R8G8B8A8Srgb = 0xd6,
+ R8G8B8X8Snorm = 0xd7,
+ R8G8B8X8Sint = 0xd8,
+ R8G8B8X8Uint = 0xd9,
+ R16G16Unorm = 0xda,
+ R16G16Snorm = 0xdb,
+ R16G16Sint = 0xdc,
+ R16G16Uint = 0xdd,
+ R16G16Float = 0xde,
+ R11G11B10Float = 0xe0,
+ R32Sint = 0xe3,
+ R32Uint = 0xe4,
+ R32Float = 0xe5,
+ B8G8R8X8Unorm = 0xe6,
+ B8G8R8X8Srgb = 0xe7,
+ B5G6R5Unorm = 0xe8,
+ B5G5R5A1Unorm = 0xe9,
+ R8G8Unorm = 0xea,
+ R8G8Snorm = 0xeb,
+ R8G8Sint = 0xec,
+ R8G8Uint = 0xed,
+ R16Unorm = 0xee,
+ R16Snorm = 0xef,
+ R16Sint = 0xf0,
+ R16Uint = 0xf1,
+ R16Float = 0xf2,
+ R8Unorm = 0xf3,
+ R8Snorm = 0xf4,
+ R8Sint = 0xf5,
+ R8Uint = 0xf6,
+ B5G5R5X1Unorm = 0xf8,
+ R8G8B8X8Unorm = 0xf9,
+ R8G8B8X8Srgb = 0xfa
+ }
+
+ static class ColorFormatConverter
+ {
+ /// <summary>
+ /// Converts the color texture format to a host compatible format.
+ /// </summary>
+ /// <param name="format">Color format</param>
+ /// <returns>Host compatible format enum value</returns>
+ public static FormatInfo Convert(this ColorFormat format)
+ {
+ return format switch
+ {
+ ColorFormat.R32G32B32A32Float => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4),
+ ColorFormat.R32G32B32A32Sint => new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16, 4),
+ ColorFormat.R32G32B32A32Uint => new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16, 4),
+ ColorFormat.R32G32B32X32Float => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4),
+ ColorFormat.R32G32B32X32Sint => new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16, 4),
+ ColorFormat.R32G32B32X32Uint => new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16, 4),
+ ColorFormat.R16G16B16X16Unorm => new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8, 4),
+ ColorFormat.R16G16B16X16Snorm => new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8, 4),
+ ColorFormat.R16G16B16X16Sint => new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8, 4),
+ ColorFormat.R16G16B16X16Uint => new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8, 4),
+ ColorFormat.R16G16B16A16Float => new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4),
+ ColorFormat.R32G32Float => new FormatInfo(Format.R32G32Float, 1, 1, 8, 2),
+ ColorFormat.R32G32Sint => new FormatInfo(Format.R32G32Sint, 1, 1, 8, 2),
+ ColorFormat.R32G32Uint => new FormatInfo(Format.R32G32Uint, 1, 1, 8, 2),
+ ColorFormat.R16G16B16X16Float => new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4),
+ ColorFormat.B8G8R8A8Unorm => new FormatInfo(Format.B8G8R8A8Unorm, 1, 1, 4, 4),
+ ColorFormat.B8G8R8A8Srgb => new FormatInfo(Format.B8G8R8A8Srgb, 1, 1, 4, 4),
+ ColorFormat.R10G10B10A2Unorm => new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4, 4),
+ ColorFormat.R10G10B10A2Uint => new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4, 4),
+ ColorFormat.R8G8B8A8Unorm => new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4),
+ ColorFormat.R8G8B8A8Srgb => new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4),
+ ColorFormat.R8G8B8X8Snorm => new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4, 4),
+ ColorFormat.R8G8B8X8Sint => new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4, 4),
+ ColorFormat.R8G8B8X8Uint => new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4, 4),
+ ColorFormat.R16G16Unorm => new FormatInfo(Format.R16G16Unorm, 1, 1, 4, 2),
+ ColorFormat.R16G16Snorm => new FormatInfo(Format.R16G16Snorm, 1, 1, 4, 2),
+ ColorFormat.R16G16Sint => new FormatInfo(Format.R16G16Sint, 1, 1, 4, 2),
+ ColorFormat.R16G16Uint => new FormatInfo(Format.R16G16Uint, 1, 1, 4, 2),
+ ColorFormat.R16G16Float => new FormatInfo(Format.R16G16Float, 1, 1, 4, 2),
+ ColorFormat.R11G11B10Float => new FormatInfo(Format.R11G11B10Float, 1, 1, 4, 3),
+ ColorFormat.R32Sint => new FormatInfo(Format.R32Sint, 1, 1, 4, 1),
+ ColorFormat.R32Uint => new FormatInfo(Format.R32Uint, 1, 1, 4, 1),
+ ColorFormat.R32Float => new FormatInfo(Format.R32Float, 1, 1, 4, 1),
+ ColorFormat.B8G8R8X8Unorm => new FormatInfo(Format.B8G8R8A8Unorm, 1, 1, 4, 4),
+ ColorFormat.B8G8R8X8Srgb => new FormatInfo(Format.B8G8R8A8Srgb, 1, 1, 4, 4),
+ ColorFormat.B5G6R5Unorm => new FormatInfo(Format.B5G6R5Unorm, 1, 1, 2, 3),
+ ColorFormat.B5G5R5A1Unorm => new FormatInfo(Format.B5G5R5A1Unorm, 1, 1, 2, 4),
+ ColorFormat.R8G8Unorm => new FormatInfo(Format.R8G8Unorm, 1, 1, 2, 2),
+ ColorFormat.R8G8Snorm => new FormatInfo(Format.R8G8Snorm, 1, 1, 2, 2),
+ ColorFormat.R8G8Sint => new FormatInfo(Format.R8G8Sint, 1, 1, 2, 2),
+ ColorFormat.R8G8Uint => new FormatInfo(Format.R8G8Uint, 1, 1, 2, 2),
+ ColorFormat.R16Unorm => new FormatInfo(Format.R16Unorm, 1, 1, 2, 1),
+ ColorFormat.R16Snorm => new FormatInfo(Format.R16Snorm, 1, 1, 2, 1),
+ ColorFormat.R16Sint => new FormatInfo(Format.R16Sint, 1, 1, 2, 1),
+ ColorFormat.R16Uint => new FormatInfo(Format.R16Uint, 1, 1, 2, 1),
+ ColorFormat.R16Float => new FormatInfo(Format.R16Float, 1, 1, 2, 1),
+ ColorFormat.R8Unorm => new FormatInfo(Format.R8Unorm, 1, 1, 1, 1),
+ ColorFormat.R8Snorm => new FormatInfo(Format.R8Snorm, 1, 1, 1, 1),
+ ColorFormat.R8Sint => new FormatInfo(Format.R8Sint, 1, 1, 1, 1),
+ ColorFormat.R8Uint => new FormatInfo(Format.R8Uint, 1, 1, 1, 1),
+ ColorFormat.B5G5R5X1Unorm => new FormatInfo(Format.B5G5R5X1Unorm, 1, 1, 2, 4),
+ ColorFormat.R8G8B8X8Unorm => new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4),
+ ColorFormat.R8G8B8X8Srgb => new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4),
+ _ => FormatInfo.Default
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs b/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs
new file mode 100644
index 00000000..839faac9
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/GpuVa.cs
@@ -0,0 +1,22 @@
+namespace Ryujinx.Graphics.Gpu.Engine.Types
+{
+ /// <summary>
+ /// Split GPU virtual address.
+ /// </summary>
+ struct GpuVa
+ {
+#pragma warning disable CS0649
+ public uint High;
+ public uint Low;
+#pragma warning restore CS0649
+
+ /// <summary>
+ /// Packs the split address into a 64-bits address value.
+ /// </summary>
+ /// <returns>The 64-bits address value</returns>
+ public ulong Pack()
+ {
+ return Low | ((ulong)High << 32);
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs b/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs
new file mode 100644
index 00000000..6da96bd4
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/MemoryLayout.cs
@@ -0,0 +1,37 @@
+namespace Ryujinx.Graphics.Gpu.Engine.Types
+{
+ /// <summary>
+ /// Memory layout parameters, for block linear textures.
+ /// </summary>
+ struct MemoryLayout
+ {
+#pragma warning disable CS0649
+ public uint Packed;
+#pragma warning restore CS0649
+
+ public int UnpackGobBlocksInX()
+ {
+ return 1 << (int)(Packed & 0xf);
+ }
+
+ public int UnpackGobBlocksInY()
+ {
+ return 1 << (int)((Packed >> 4) & 0xf);
+ }
+
+ public int UnpackGobBlocksInZ()
+ {
+ return 1 << (int)((Packed >> 8) & 0xf);
+ }
+
+ public bool UnpackIsLinear()
+ {
+ return (Packed & 0x1000) != 0;
+ }
+
+ public bool UnpackIsTarget3D()
+ {
+ return (Packed & 0x10000) != 0;
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs b/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs
new file mode 100644
index 00000000..dae63124
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/PrimitiveType.cs
@@ -0,0 +1,99 @@
+using Ryujinx.Graphics.GAL;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Types
+{
+ /// <summary>
+ /// Draw primitive type.
+ /// </summary>
+ enum PrimitiveType
+ {
+ Points,
+ Lines,
+ LineLoop,
+ LineStrip,
+ Triangles,
+ TriangleStrip,
+ TriangleFan,
+ Quads,
+ QuadStrip,
+ Polygon,
+ LinesAdjacency,
+ LineStripAdjacency,
+ TrianglesAdjacency,
+ TriangleStripAdjacency,
+ Patches
+ }
+
+ /// <summary>
+ /// Alternative primitive type that might override <see cref="PrimitiveType"/>.
+ /// </summary>
+ enum PrimitiveTypeOverride
+ {
+ Points = 1,
+ Lines = 2,
+ LineStrip = 3,
+ Triangles = 4,
+ TriangleStrip = 5,
+ TriangleFan = 0x1015,
+ LinesAdjacency = 10,
+ LineStripAdjacency = 11,
+ TrianglesAdjacency = 12,
+ TriangleStripAdjacency = 13,
+ Patches = 14
+ }
+
+ static class PrimitiveTypeConverter
+ {
+ /// <summary>
+ /// Converts the primitive type into something that can be used with the host API.
+ /// </summary>
+ /// <param name="type">The primitive type to convert</param>
+ /// <returns>A host compatible enum value</returns>
+ public static PrimitiveTopology Convert(this PrimitiveType type)
+ {
+ return type switch
+ {
+ PrimitiveType.Points => PrimitiveTopology.Points,
+ PrimitiveType.Lines => PrimitiveTopology.Lines,
+ PrimitiveType.LineLoop => PrimitiveTopology.LineLoop,
+ PrimitiveType.LineStrip => PrimitiveTopology.LineStrip,
+ PrimitiveType.Triangles => PrimitiveTopology.Triangles,
+ PrimitiveType.TriangleStrip => PrimitiveTopology.TriangleStrip,
+ PrimitiveType.TriangleFan => PrimitiveTopology.TriangleFan,
+ PrimitiveType.Quads => PrimitiveTopology.Quads,
+ PrimitiveType.QuadStrip => PrimitiveTopology.QuadStrip,
+ PrimitiveType.Polygon => PrimitiveTopology.Polygon,
+ PrimitiveType.LinesAdjacency => PrimitiveTopology.LinesAdjacency,
+ PrimitiveType.LineStripAdjacency => PrimitiveTopology.LineStripAdjacency,
+ PrimitiveType.TrianglesAdjacency => PrimitiveTopology.TrianglesAdjacency,
+ PrimitiveType.TriangleStripAdjacency => PrimitiveTopology.TriangleStripAdjacency,
+ PrimitiveType.Patches => PrimitiveTopology.Patches,
+ _ => PrimitiveTopology.Triangles
+ };
+ }
+
+ /// <summary>
+ /// Converts the primitive type into something that can be used with the host API.
+ /// </summary>
+ /// <param name="type">The primitive type to convert</param>
+ /// <returns>A host compatible enum value</returns>
+ public static PrimitiveTopology Convert(this PrimitiveTypeOverride type)
+ {
+ return type switch
+ {
+ PrimitiveTypeOverride.Points => PrimitiveTopology.Points,
+ PrimitiveTypeOverride.Lines => PrimitiveTopology.Lines,
+ PrimitiveTypeOverride.LineStrip => PrimitiveTopology.LineStrip,
+ PrimitiveTypeOverride.Triangles => PrimitiveTopology.Triangles,
+ PrimitiveTypeOverride.TriangleStrip => PrimitiveTopology.TriangleStrip,
+ PrimitiveTypeOverride.TriangleFan => PrimitiveTopology.TriangleFan,
+ PrimitiveTypeOverride.LinesAdjacency => PrimitiveTopology.LinesAdjacency,
+ PrimitiveTypeOverride.LineStripAdjacency => PrimitiveTopology.LineStripAdjacency,
+ PrimitiveTypeOverride.TrianglesAdjacency => PrimitiveTopology.TrianglesAdjacency,
+ PrimitiveTypeOverride.TriangleStripAdjacency => PrimitiveTopology.TriangleStripAdjacency,
+ PrimitiveTypeOverride.Patches => PrimitiveTopology.Patches,
+ _ => PrimitiveTopology.Triangles
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs b/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs
new file mode 100644
index 00000000..839a4d0a
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/SamplerIndex.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.Graphics.Gpu.Engine.Types
+{
+ /// <summary>
+ /// Sampler pool indexing mode.
+ /// </summary>
+ enum SamplerIndex
+ {
+ Independently = 0,
+ ViaHeaderIndex = 1
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs b/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs
new file mode 100644
index 00000000..c457dbf9
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/SbDescriptor.cs
@@ -0,0 +1,20 @@
+namespace Ryujinx.Graphics.Gpu.Engine.Types
+{
+ /// <summary>
+ /// Storage buffer address and size information.
+ /// </summary>
+ struct SbDescriptor
+ {
+#pragma warning disable CS0649
+ public uint AddressLow;
+ public uint AddressHigh;
+ public int Size;
+ public int Padding;
+#pragma warning restore CS0649
+
+ public ulong PackAddress()
+ {
+ return AddressLow | ((ulong)AddressHigh << 32);
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs b/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs
new file mode 100644
index 00000000..2de38fd2
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Types/ZetaFormat.cs
@@ -0,0 +1,42 @@
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Image;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Types
+{
+ /// <summary>
+ /// Depth-stencil texture format.
+ /// </summary>
+ enum ZetaFormat
+ {
+ D32Float = 0xa,
+ D16Unorm = 0x13,
+ D24UnormS8Uint = 0x14,
+ D24Unorm = 0x15,
+ S8UintD24Unorm = 0x16,
+ S8Uint = 0x17,
+ D32FloatS8Uint = 0x19
+ }
+
+ static class ZetaFormatConverter
+ {
+ /// <summary>
+ /// Converts the depth-stencil texture format to a host compatible format.
+ /// </summary>
+ /// <param name="format">Depth-stencil format</param>
+ /// <returns>Host compatible format enum value</returns>
+ public static FormatInfo Convert(this ZetaFormat format)
+ {
+ return format switch
+ {
+ ZetaFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
+ ZetaFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
+ ZetaFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
+ ZetaFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 1),
+ ZetaFormat.S8UintD24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
+ ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
+ ZetaFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
+ _ => FormatInfo.Default
+ };
+ }
+ }
+}