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
This commit is contained in:
gdkchan 2021-07-11 17:20:40 -03:00 committed by GitHub
parent b5190f1681
commit 40b21cc3c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
111 changed files with 5262 additions and 4020 deletions

View file

@ -1,10 +0,0 @@
namespace Ryujinx.Graphics.Device
{
public enum AccessControl
{
None = 0,
ReadOnly = 1 << 0,
WriteOnly = 1 << 1,
ReadWrite = ReadOnly | WriteOnly
}
}

View file

@ -1,10 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Device
{
@ -14,28 +12,22 @@ namespace Ryujinx.Graphics.Device
public TState State;
private readonly BitArray _readableRegisters;
private readonly BitArray _writableRegisters;
private uint Size => (uint)(Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize;
private readonly Dictionary<int, Func<int>> _readCallbacks;
private readonly Dictionary<int, Action<int>> _writeCallbacks;
private readonly Func<int>[] _readCallbacks;
private readonly Action<int>[] _writeCallbacks;
private readonly Dictionary<int, string> _fieldNamesForDebug;
private readonly Dictionary<uint, string> _fieldNamesForDebug;
private readonly Action<string> _debugLogCallback;
public DeviceState(IReadOnlyDictionary<string, RwCallback> callbacks = null, Action<string> debugLogCallback = null)
{
int size = (Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize;
_readableRegisters = new BitArray(size);
_writableRegisters = new BitArray(size);
_readCallbacks = new Dictionary<int, Func<int>>();
_writeCallbacks = new Dictionary<int, Action<int>>();
_readCallbacks = new Func<int>[Size];
_writeCallbacks = new Action<int>[Size];
if (debugLogCallback != null)
{
_fieldNamesForDebug = new Dictionary<int, string>();
_fieldNamesForDebug = new Dictionary<uint, string>();
_debugLogCallback = debugLogCallback;
}
@ -45,32 +37,30 @@ namespace Ryujinx.Graphics.Device
for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
{
var field = fields[fieldIndex];
var regAttr = field.GetCustomAttributes<RegisterAttribute>(false).FirstOrDefault();
int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
{
_readableRegisters[(offset + i) / RegisterSize] = regAttr?.AccessControl.HasFlag(AccessControl.ReadOnly) ?? true;
_writableRegisters[(offset + i) / RegisterSize] = regAttr?.AccessControl.HasFlag(AccessControl.WriteOnly) ?? true;
}
int index = (offset + i) / RegisterSize;
if (callbacks != null && callbacks.TryGetValue(field.Name, out var cb))
{
if (cb.Read != null)
if (callbacks != null && callbacks.TryGetValue(field.Name, out var cb))
{
_readCallbacks.Add(offset, cb.Read);
}
if (cb.Read != null)
{
_readCallbacks[index] = cb.Read;
}
if (cb.Write != null)
{
_writeCallbacks.Add(offset, cb.Write);
if (cb.Write != null)
{
_writeCallbacks[index] = cb.Write;
}
}
}
if (debugLogCallback != null)
{
_fieldNamesForDebug.Add(offset, field.Name);
_fieldNamesForDebug.Add((uint)offset, field.Name);
}
offset += sizeOfField;
@ -79,48 +69,71 @@ namespace Ryujinx.Graphics.Device
Debug.Assert(offset == Unsafe.SizeOf<TState>());
}
public virtual int Read(int offset)
public int Read(int offset)
{
if (Check(offset) && _readableRegisters[offset / RegisterSize])
{
int alignedOffset = Align(offset);
uint index = (uint)offset / RegisterSize;
if (_readCallbacks.TryGetValue(alignedOffset, out Func<int> read))
if (index < Size)
{
uint alignedOffset = index * RegisterSize;
var readCallback = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_readCallbacks), (IntPtr)index);
if (readCallback != null)
{
return read();
return readCallback();
}
else
{
return GetRef<int>(alignedOffset);
return GetRefUnchecked<int>(alignedOffset);
}
}
return 0;
}
public virtual void Write(int offset, int data)
public void Write(int offset, int data)
{
if (Check(offset) && _writableRegisters[offset / RegisterSize])
uint index = (uint)offset / RegisterSize;
if (index < Size)
{
int alignedOffset = Align(offset);
uint alignedOffset = index * RegisterSize;
DebugWrite(alignedOffset, data);
if (_fieldNamesForDebug != null && _fieldNamesForDebug.TryGetValue(alignedOffset, out string fieldName))
{
_debugLogCallback($"{typeof(TState).Name}.{fieldName} = 0x{data:X}");
}
GetRefIntAlignedUncheck(index) = data;
GetRef<int>(alignedOffset) = data;
if (_writeCallbacks.TryGetValue(alignedOffset, out Action<int> write))
{
write(data);
}
Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data);
}
}
private bool Check(int offset)
public void WriteWithRedundancyCheck(int offset, int data, out bool changed)
{
return (uint)Align(offset) < Unsafe.SizeOf<TState>();
uint index = (uint)offset / RegisterSize;
if (index < Size)
{
uint alignedOffset = index * RegisterSize;
DebugWrite(alignedOffset, data);
ref var storage = ref GetRefIntAlignedUncheck(index);
changed = storage != data;
storage = data;
Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data);
}
else
{
changed = false;
}
}
[Conditional("DEBUG")]
private void DebugWrite(uint alignedOffset, int data)
{
if (_fieldNamesForDebug != null && _fieldNamesForDebug.TryGetValue(alignedOffset, out string fieldName))
{
_debugLogCallback($"{typeof(TState).Name}.{fieldName} = 0x{data:X}");
}
}
public ref T GetRef<T>(int offset) where T : unmanaged
@ -130,12 +143,19 @@ namespace Ryujinx.Graphics.Device
throw new ArgumentOutOfRangeException(nameof(offset));
}
return ref GetRefUnchecked<T>((uint)offset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ref T GetRefUnchecked<T>(uint offset) where T : unmanaged
{
return ref Unsafe.As<TState, T>(ref Unsafe.AddByteOffset(ref State, (IntPtr)offset));
}
private static int Align(int offset)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ref int GetRefIntAlignedUncheck(ulong index)
{
return offset & ~(RegisterSize - 1);
return ref Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (IntPtr)index);
}
}
}

View file

@ -1,15 +0,0 @@
using System;
namespace Ryujinx.Graphics.Device
{
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class RegisterAttribute : Attribute
{
public AccessControl AccessControl { get; }
public RegisterAttribute(AccessControl ac)
{
AccessControl = ac;
}
}
}

View file

@ -3,7 +3,7 @@ using System.Reflection;
namespace Ryujinx.Graphics.Device
{
static class SizeCalculator
public static class SizeCalculator
{
public static int SizeOf(Type type)
{

View file

@ -3,13 +3,13 @@ namespace Ryujinx.Graphics.Gpu
/// <summary>
/// GPU engine class ID.
/// </summary>
enum ClassId
public enum ClassId
{
Engine2D = 0x902d,
Engine3D = 0xb197,
EngineCompute = 0xb1c0,
EngineInline2Memory = 0xa140,
EngineDma = 0xb0b5,
EngineGpfifo = 0xb06f
Twod = 0x902d,
Threed = 0xb197,
Compute = 0xb1c0,
InlineToMemory = 0xa140,
Dma = 0xb0b5,
GPFifo = 0xb06f
}
}

View file

@ -1,9 +1,10 @@
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Shader;
using System;
using System.Collections.Generic;
@ -14,27 +15,34 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
/// <summary>
/// Represents a compute engine class.
/// </summary>
class ComputeClass : InlineToMemoryClass, IDeviceState
class ComputeClass : IDeviceState
{
private readonly GpuContext _context;
private readonly GpuChannel _channel;
private readonly ThreedClass _3dEngine;
private readonly DeviceState<ComputeClassState> _state;
private readonly InlineToMemoryClass _i2mClass;
/// <summary>
/// Creates a new instance of the compute engine class.
/// </summary>
/// <param name="context">GPU context</param>
/// <param name="channel">GPU channel</param>
public ComputeClass(GpuContext context, GpuChannel channel) : base(context, channel, false)
/// <param name="threedEngine">3D engine</param>
public ComputeClass(GpuContext context, GpuChannel channel, ThreedClass threedEngine)
{
_context = context;
_channel = channel;
_3dEngine = threedEngine;
_state = new DeviceState<ComputeClassState>(new Dictionary<string, RwCallback>
{
{ nameof(ComputeClassState.LaunchDma), new RwCallback(LaunchDma, null) },
{ nameof(ComputeClassState.LoadInlineData), new RwCallback(LoadInlineData, null) },
{ nameof(ComputeClassState.SendSignalingPcasB), new RwCallback(SendSignalingPcasB, null) }
});
_i2mClass = new InlineToMemoryClass(context, channel, initializeState: false);
}
/// <summary>
@ -42,22 +50,31 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
/// </summary>
/// <param name="offset">Register byte offset</param>
/// <returns>Data at the specified offset</returns>
public override int Read(int offset) => _state.Read(offset);
public int Read(int offset) => _state.Read(offset);
/// <summary>
/// Writes data to the class registers.
/// </summary>
/// <param name="offset">Register byte offset</param>
/// <param name="data">Data to be written</param>
public override void Write(int offset, int data) => _state.Write(offset, data);
public void Write(int offset, int data) => _state.Write(offset, data);
/// <summary>
/// Launches the Inline-to-Memory DMA copy operation.
/// </summary>
/// <param name="argument">Method call argument</param>
protected override void LaunchDma(int argument)
private void LaunchDma(int argument)
{
LaunchDma(ref Unsafe.As<ComputeClassState, InlineToMemoryClassState>(ref _state.State), argument);
_i2mClass.LaunchDma(ref Unsafe.As<ComputeClassState, InlineToMemoryClassState>(ref _state.State), argument);
}
/// <summary>
/// Pushes a word of data to the Inline-to-Memory engine.
/// </summary>
/// <param name="argument">Method call argument</param>
private void LoadInlineData(int argument)
{
_i2mClass.LoadInlineData(argument);
}
/// <summary>
@ -68,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
{
var memoryManager = _channel.MemoryManager;
_context.Methods.FlushUboDirty(memoryManager);
_3dEngine.FlushUboDirty();
uint qmdAddress = _state.State.SendPcasA;
@ -102,7 +119,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
texturePoolGpuVa,
_state.State.SetTexHeaderPoolCMaximumIndex,
_state.State.SetBindlessTextureConstantBufferSlotSelect,
false);
false,
PrimitiveTopology.Points);
ShaderBundle cs = memoryManager.Physical.ShaderCache.GetComputeShader(
_channel,
@ -207,7 +225,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
_context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth);
_context.Methods.ForceShaderUpdate();
_3dEngine.ForceShaderUpdate();
}
}
}

View file

@ -1,4 +1,4 @@
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Gpu.Engine.Types;
using System;
using System.Runtime.CompilerServices;

View file

@ -1,5 +1,8 @@
namespace Ryujinx.Graphics.Gpu.Engine
{
/// <summary>
/// Conditional rendering enable.
/// </summary>
enum ConditionalRenderEnabled
{
False,

View file

@ -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);
}
}
}
}

View file

@ -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;

View file

@ -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
}
}

View file

@ -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);
}

View file

@ -2,9 +2,9 @@
using Ryujinx.Graphics.Gpu.Engine.Compute;
using Ryujinx.Graphics.Gpu.Engine.Dma;
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Engine.Twod;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.State;
using System;
using System.Runtime.CompilerServices;
@ -18,9 +18,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
private const int MacrosCount = 0x80;
private const int MacroIndexMask = MacrosCount - 1;
private readonly GpuContext _context;
private const int UniformBufferUpdateDataMethodOffset = 0x8e4;
private readonly GpuChannel _channel;
/// <summary>
/// Channel memory manager.
/// </summary>
public MemoryManager MemoryManager => _channel.MemoryManager;
/// <summary>
@ -37,8 +41,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
private DmaState _state;
private readonly GpuState[] _subChannels;
private readonly IDeviceState[] _subChannels2;
private readonly ThreedClass _3dClass;
private readonly ComputeClass _computeClass;
private readonly InlineToMemoryClass _i2mClass;
private readonly TwodClass _2dClass;
private readonly DmaClass _dmaClass;
private readonly GPFifoClass _fifoClass;
/// <summary>
@ -48,29 +56,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
/// <param name="channel">Channel that the GPFIFO processor belongs to</param>
public GPFifoProcessor(GpuContext context, GpuChannel channel)
{
_context = context;
_channel = channel;
_fifoClass = new GPFifoClass(context, this);
_subChannels = new GpuState[8];
_subChannels2 = new IDeviceState[8]
{
null,
new ComputeClass(context, channel),
new InlineToMemoryClass(context, channel),
new TwodClass(channel),
new DmaClass(context, channel),
null,
null,
null
};
for (int index = 0; index < _subChannels.Length; index++)
{
_subChannels[index] = new GpuState(channel, _subChannels2[index]);
_context.Methods.RegisterCallbacks(_subChannels[index]);
}
_3dClass = new ThreedClass(context, channel);
_computeClass = new ComputeClass(context, channel, _3dClass);
_i2mClass = new InlineToMemoryClass(context, channel);
_2dClass = new TwodClass(channel);
_dmaClass = new DmaClass(context, channel, _3dClass);
}
/// <summary>
@ -85,7 +78,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
if (_state.MethodCount != 0)
{
Send(new MethodParams(_state.Method, command, _state.SubChannel, _state.MethodCount));
Send(_state.Method, command, _state.SubChannel, _state.MethodCount <= 1);
if (!_state.NonIncrementing)
{
@ -121,13 +114,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
_state.NonIncrementing = meth.SecOp == SecOp.NonIncMethod;
break;
case SecOp.ImmdDataMethod:
Send(new MethodParams(meth.MethodAddress, meth.ImmdData, meth.MethodSubchannel, 1));
Send(meth.MethodAddress, meth.ImmdData, meth.MethodSubchannel, true);
break;
}
}
}
_context.Methods.FlushUboDirty(MemoryManager);
_3dClass.FlushUboDirty();
}
/// <summary>
@ -145,11 +138,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
if (meth.MethodCount < availableCount &&
meth.SecOp == SecOp.NonIncMethod &&
meth.MethodAddress == (int)MethodOffset.UniformBufferUpdateData)
meth.MethodAddress == UniformBufferUpdateDataMethodOffset)
{
GpuState state = _subChannels[meth.MethodSubchannel];
_context.Methods.UniformBufferUpdate(state, commandBuffer.Slice(offset + 1, meth.MethodCount));
_3dClass.ConstantBufferUpdate(commandBuffer.Slice(offset + 1, meth.MethodCount));
return true;
}
@ -161,55 +152,105 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
/// Sends a uncompressed method for processing by the graphics pipeline.
/// </summary>
/// <param name="meth">Method to be processed</param>
private void Send(MethodParams meth)
private void Send(int offset, int argument, int subChannel, bool isLastCall)
{
if ((MethodOffset)meth.Method == MethodOffset.BindChannel)
if (offset < 0x60)
{
_subChannels[meth.SubChannel].ClearCallbacks();
_fifoClass.Write(offset * 4, argument);
}
else if (offset < 0xe00)
{
offset *= 4;
_context.Methods.RegisterCallbacks(_subChannels[meth.SubChannel]);
}
else if (meth.Method < 0x60)
{
// TODO: check if macros are shared between subchannels or not. For now let's assume they are.
_fifoClass.Write(meth.Method * 4, meth.Argument);
}
else if (meth.Method < 0xe00)
{
_subChannels[meth.SubChannel].CallMethod(meth);
switch (subChannel)
{
case 0:
_3dClass.Write(offset, argument);
break;
case 1:
_computeClass.Write(offset, argument);
break;
case 2:
_i2mClass.Write(offset, argument);
break;
case 3:
_2dClass.Write(offset, argument);
break;
case 4:
_dmaClass.Write(offset, argument);
break;
}
}
else
{
int macroIndex = (meth.Method >> 1) & MacroIndexMask;
if ((meth.Method & 1) != 0)
IDeviceState state = subChannel switch
{
_fifoClass.MmePushArgument(macroIndex, meth.Argument);
}
else
{
_fifoClass.MmeStart(macroIndex, meth.Argument);
}
0 => _3dClass,
3 => _2dClass,
_ => null
};
if (meth.IsLastCall)
if (state != null)
{
_fifoClass.CallMme(macroIndex, _subChannels[meth.SubChannel]);
int macroIndex = (offset >> 1) & MacroIndexMask;
_context.Methods.PerformDeferredDraws();
if ((offset & 1) != 0)
{
_fifoClass.MmePushArgument(macroIndex, argument);
}
else
{
_fifoClass.MmeStart(macroIndex, argument);
}
if (isLastCall)
{
_fifoClass.CallMme(macroIndex, state);
_3dClass.PerformDeferredDraws();
}
}
}
}
/// <summary>
/// Writes data directly to the state of the specified class.
/// </summary>
/// <param name="classId">ID of the class to write the data into</param>
/// <param name="offset">State offset in bytes</param>
/// <param name="value">Value to be written</param>
public void Write(ClassId classId, int offset, int value)
{
switch (classId)
{
case ClassId.Threed:
_3dClass.Write(offset, value);
break;
case ClassId.Compute:
_computeClass.Write(offset, value);
break;
case ClassId.InlineToMemory:
_i2mClass.Write(offset, value);
break;
case ClassId.Twod:
_2dClass.Write(offset, value);
break;
case ClassId.Dma:
_dmaClass.Write(offset, value);
break;
case ClassId.GPFifo:
_fifoClass.Write(offset, value);
break;
}
}
/// <summary>
/// Sets the shadow ram control value of all sub-channels.
/// </summary>
/// <param name="control">New shadow ram control value</param>
public void SetShadowRamControl(ShadowRamControl control)
public void SetShadowRamControl(int control)
{
for (int i = 0; i < _subChannels.Length; i++)
{
_subChannels[i].ShadowRamControl = control;
}
_3dClass.SetShadowRamControl(control);
}
/// <summary>
@ -218,10 +259,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
/// </summary>
public void ForceAllDirty()
{
for (int index = 0; index < _subChannels.Length; index++)
{
_subChannels[index].ForceAllDirty();
}
_3dClass.ForceStateDirty();
_channel.BufferManager.Rebind();
_channel.TextureManager.Rebind();
}
/// <summary>
/// Perform any deferred draws.
/// </summary>
public void PerformDeferredDraws()
{
_3dClass.PerformDeferredDraws();
}
}
}

View file

@ -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();
}
}
}

View file

@ -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)
{

View file

@ -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);
}
}

View file

@ -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)
{

View file

@ -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;
}

View file

@ -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)
{

View file

@ -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.

View file

@ -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);
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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);
}
}
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -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);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -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
{

View file

@ -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,
}
}

View file

@ -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;
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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();
}
}

View file

@ -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>

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -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;
}
}
}

View file

@ -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
}
}

View file

@ -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;

View file

@ -13,17 +13,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
WriteThenAwaken = 1,
}
/// <summary>
/// MME shadow RAM control mode.
/// </summary>
enum SetMmeShadowRamControlMode
{
MethodTrack = 0,
MethodTrackWithFilter = 1,
MethodPassthrough = 2,
MethodReplay = 3,
}
/// <summary>
/// Format of the destination texture.
/// </summary>
@ -506,7 +495,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
/// <summary>
/// 2D class state.
/// </summary>
unsafe struct TwodClassState
unsafe struct TwodClassState : IShadowState
{
#pragma warning disable CS0649
public uint SetObject;

View file

@ -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
}
}

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu.State
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Boolean value, stored as a 32-bits integer in memory.

View file

@ -1,7 +1,7 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
namespace Ryujinx.Graphics.Gpu.State
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Color texture format.

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu.State
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Split GPU virtual address.

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu.State
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Memory layout parameters, for block linear textures.

View file

@ -1,6 +1,6 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.State
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Draw primitive type.
@ -29,7 +29,6 @@ namespace Ryujinx.Graphics.Gpu.State
/// </summary>
enum PrimitiveTypeOverride
{
Invalid = 0,
Points = 1,
Lines = 2,
LineStrip = 3,

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu.State
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Sampler pool indexing mode.

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gpu.State
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Storage buffer address and size information.

View file

@ -1,7 +1,7 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
namespace Ryujinx.Graphics.Gpu.State
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Depth-stencil texture format.

View file

@ -64,6 +64,17 @@ namespace Ryujinx.Graphics.Gpu
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
}
/// <summary>
/// Writes data directly to the state of the specified class.
/// </summary>
/// <param name="classId">ID of the class to write the data into</param>
/// <param name="offset">State offset in bytes</param>
/// <param name="value">Value to be written</param>
public void Write(ClassId classId, int offset, uint value)
{
_processor.Write(classId, offset, (int)value);
}
/// <summary>
/// Push a GPFIFO entry in the form of a prefetched command buffer.
/// It is intended to be used by nvservices to handle special cases.

View file

@ -1,5 +1,4 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader;
@ -26,11 +25,6 @@ namespace Ryujinx.Graphics.Gpu
/// </summary>
public IRenderer Renderer { get; }
/// <summary>
/// GPU engine methods processing.
/// </summary>
internal Methods Methods { get; }
/// <summary>
/// GPU General Purpose FIFO queue.
/// </summary>
@ -94,8 +88,6 @@ namespace Ryujinx.Graphics.Gpu
{
Renderer = renderer;
Methods = new Methods(this);
GPFifo = new GPFifoDevice(this);
Synchronization = new SynchronizationManager();

View file

@ -1,5 +1,5 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Shader;
using System;

View file

@ -1,8 +1,11 @@
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Dma;
using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Engine.Twod;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Texture;
using Ryujinx.Memory.Range;
using System;
@ -151,7 +154,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>The texture</returns>
public Texture FindOrCreateTexture(
MemoryManager memoryManager,
CopyTexture copyTexture,
TwodTexture copyTexture,
ulong offset,
FormatInfo formatInfo,
bool preferScaling = true,
@ -762,7 +765,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>A matching texture, or null if there is no match</returns>
public Texture FindTexture(
MemoryManager memoryManager,
CopyBufferTexture tex,
DmaTexture tex,
ulong gpuVa,
int bpp,
int stride,

View file

@ -1,5 +1,5 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Gpu.Engine.Types;
using System;
namespace Ryujinx.Graphics.Gpu.Image

View file

@ -1,5 +1,4 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Memory.Range;
using System;
using System.Collections.Generic;

View file

@ -1,52 +0,0 @@
namespace Ryujinx.Graphics
{
/// <summary>
/// Method call parameters.
/// </summary>
struct MethodParams
{
/// <summary>
/// Method offset.
/// </summary>
public int Method { get; }
/// <summary>
/// Method call argument.
/// </summary>
public int Argument { get; }
/// <summary>
/// Sub-channel where the call should be sent.
/// </summary>
public int SubChannel { get; }
/// <summary>
/// For multiple calls to the same method, this is the remaining calls count.
/// </summary>
public int MethodCount { get; }
/// <summary>
/// Indicates if the current call is the last one from a batch of calls to the same method.
/// </summary>
public bool IsLastCall => MethodCount <= 1;
/// <summary>
/// Constructs the method call parameters structure.
/// </summary>
/// <param name="method">Method offset</param>
/// <param name="argument">Method call argument</param>
/// <param name="subChannel">Optional sub-channel where the method should be sent (not required for macro calls)</param>
/// <param name="methodCount">Optional remaining calls count (not required for macro calls)</param>
public MethodParams(
int method,
int argument,
int subChannel = 0,
int methodCount = 0)
{
Method = method;
Argument = argument;
SubChannel = subChannel;
MethodCount = methodCount;
}
}
}

View file

@ -144,27 +144,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Current primitive topology</returns>
public InputTopology QueryPrimitiveTopology()
{
switch (_context.Methods.Topology)
return _state.Topology switch
{
case PrimitiveTopology.Points:
return InputTopology.Points;
case PrimitiveTopology.Lines:
case PrimitiveTopology.LineLoop:
case PrimitiveTopology.LineStrip:
return InputTopology.Lines;
case PrimitiveTopology.LinesAdjacency:
case PrimitiveTopology.LineStripAdjacency:
return InputTopology.LinesAdjacency;
case PrimitiveTopology.Triangles:
case PrimitiveTopology.TriangleStrip:
case PrimitiveTopology.TriangleFan:
return InputTopology.Triangles;
case PrimitiveTopology.TrianglesAdjacency:
case PrimitiveTopology.TriangleStripAdjacency:
return InputTopology.TrianglesAdjacency;
}
return InputTopology.Points;
PrimitiveTopology.Points => InputTopology.Points,
PrimitiveTopology.Lines or
PrimitiveTopology.LineLoop or
PrimitiveTopology.LineStrip => InputTopology.Lines,
PrimitiveTopology.LinesAdjacency or
PrimitiveTopology.LineStripAdjacency => InputTopology.LinesAdjacency,
PrimitiveTopology.Triangles or
PrimitiveTopology.TriangleStrip or
PrimitiveTopology.TriangleFan => InputTopology.Triangles,
PrimitiveTopology.TrianglesAdjacency or
PrimitiveTopology.TriangleStripAdjacency => InputTopology.TrianglesAdjacency,
_ => InputTopology.Points,
};
}
/// <summary>

View file

@ -1,3 +1,5 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.Shader
{
/// <summary>
@ -25,6 +27,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary>
public bool EarlyZForce { get; }
/// <summary>
/// Primitive topology of current draw.
/// </summary>
public PrimitiveTopology Topology { get; }
/// <summary>
/// Creates a new instance of the GPU accessor state.
/// </summary>
@ -32,12 +39,19 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="texturePoolMaximumId">Maximum ID of the texture pool</param>
/// <param name="textureBufferIndex">Constant buffer slot where the texture handles are located</param>
/// <param name="earlyZForce">Early Z force enable</param>
public GpuAccessorState(ulong texturePoolGpuVa, int texturePoolMaximumId, int textureBufferIndex, bool earlyZForce)
/// <param name="topology">Primitive topology</param>
public GpuAccessorState(
ulong texturePoolGpuVa,
int texturePoolMaximumId,
int textureBufferIndex,
bool earlyZForce,
PrimitiveTopology topology)
{
TexturePoolGpuVa = texturePoolGpuVa;
TexturePoolMaximumId = texturePoolMaximumId;
TextureBufferIndex = textureBufferIndex;
EarlyZForce = earlyZForce;
Topology = topology;
}
}
}

View file

@ -1,15 +1,16 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader.Cache;
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@ -593,10 +594,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <remarks>
/// This automatically translates, compiles and adds the code to the cache if not present.
/// </remarks>
/// <param name="state">Current GPU state</param>
/// <param name="state">GPU state</param>
/// <param name="channel">GPU channel</param>
/// <param name="gas">GPU accessor state</param>
/// <param name="addresses">Addresses of the shaders for each stage</param>
/// <returns>Compiled graphics shader code</returns>
public ShaderBundle GetGraphicsShader(GpuState state, ShaderAddresses addresses)
public ShaderBundle GetGraphicsShader(ref ThreedClassState state, GpuChannel channel, GpuAccessorState gas, ShaderAddresses addresses)
{
bool isCached = _gpPrograms.TryGetValue(addresses, out List<ShaderBundle> list);
@ -604,7 +607,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
foreach (ShaderBundle cachedGpShaders in list)
{
if (IsShaderEqual(state.Channel.MemoryManager, cachedGpShaders, addresses))
if (IsShaderEqual(channel.MemoryManager, cachedGpShaders, addresses))
{
return cachedGpShaders;
}
@ -613,7 +616,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
TranslatorContext[] shaderContexts = new TranslatorContext[Constants.ShaderStages + 1];
TransformFeedbackDescriptor[] tfd = GetTransformFeedbackDescriptors(state);
TransformFeedbackDescriptor[] tfd = GetTransformFeedbackDescriptors(ref state);
TranslationFlags flags = DefaultFlags;
@ -626,14 +629,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (addresses.VertexA != 0)
{
shaderContexts[0] = DecodeGraphicsShader(state, counts, flags | TranslationFlags.VertexA, ShaderStage.Vertex, addresses.VertexA);
shaderContexts[0] = DecodeGraphicsShader(channel, gas, counts, flags | TranslationFlags.VertexA, ShaderStage.Vertex, addresses.VertexA);
}
shaderContexts[1] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Vertex, addresses.Vertex);
shaderContexts[2] = DecodeGraphicsShader(state, counts, flags, ShaderStage.TessellationControl, addresses.TessControl);
shaderContexts[3] = DecodeGraphicsShader(state, counts, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation);
shaderContexts[4] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Geometry, addresses.Geometry);
shaderContexts[5] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Fragment, addresses.Fragment);
shaderContexts[1] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Vertex, addresses.Vertex);
shaderContexts[2] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.TessellationControl, addresses.TessControl);
shaderContexts[3] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation);
shaderContexts[4] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Geometry, addresses.Geometry);
shaderContexts[5] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Fragment, addresses.Fragment);
bool isShaderCacheEnabled = _cacheManager != null;
bool isShaderCacheReadOnly = false;
@ -656,7 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
isShaderCacheReadOnly = _cacheManager.IsReadOnly;
// Compute hash and prepare data for shader disk cache comparison.
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(state.Channel.MemoryManager, shaderContexts);
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(channel.MemoryManager, shaderContexts);
programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries, tfd);
}
@ -673,11 +676,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
// The shader isn't currently cached, translate it and compile it.
ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages];
shaders[0] = TranslateShader(state.Channel.MemoryManager, shaderContexts[1], shaderContexts[0]);
shaders[1] = TranslateShader(state.Channel.MemoryManager, shaderContexts[2]);
shaders[2] = TranslateShader(state.Channel.MemoryManager, shaderContexts[3]);
shaders[3] = TranslateShader(state.Channel.MemoryManager, shaderContexts[4]);
shaders[4] = TranslateShader(state.Channel.MemoryManager, shaderContexts[5]);
shaders[0] = TranslateShader(channel.MemoryManager, shaderContexts[1], shaderContexts[0]);
shaders[1] = TranslateShader(channel.MemoryManager, shaderContexts[2]);
shaders[2] = TranslateShader(channel.MemoryManager, shaderContexts[3]);
shaders[3] = TranslateShader(channel.MemoryManager, shaderContexts[4]);
shaders[4] = TranslateShader(channel.MemoryManager, shaderContexts[5]);
List<IShader> hostShaders = new List<IShader>();
@ -733,9 +736,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary>
/// <param name="state">Current GPU state</param>
/// <returns>Four transform feedback descriptors for the enabled TFBs, or null if TFB is disabled</returns>
private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(GpuState state)
private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(ref ThreedClassState state)
{
bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable);
bool tfEnable = state.TfEnable;
if (!tfEnable)
{
@ -746,13 +749,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
{
var tf = state.Get<TfState>(MethodOffset.TfState, i);
var tf = state.TfState[i];
int length = (int)Math.Min((uint)tf.VaryingsCount, 0x80);
var varyingLocations = state.GetSpan(MethodOffset.TfVaryingLocations + i * 0x80, length).ToArray();
var varyingLocations = MemoryMarshal.Cast<uint, byte>(state.TfVaryingLocations[i].ToSpan()).Slice(0, length);
descs[i] = new TransformFeedbackDescriptor(tf.BufferIndex, tf.Stride, varyingLocations);
descs[i] = new TransformFeedbackDescriptor(tf.BufferIndex, tf.Stride, varyingLocations.ToArray());
}
return descs;
@ -871,14 +874,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <remarks>
/// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader.
/// </remarks>
/// <param name="state">Current GPU state</param>
/// <param name="channel">GPU channel</param>
/// <param name="gas">GPU accessor state</param>
/// <param name="counts">Cumulative shader resource counts</param>
/// <param name="flags">Flags that controls shader translation</param>
/// <param name="stage">Shader stage</param>
/// <param name="gpuVa">GPU virtual address of the shader code</param>
/// <returns>The generated translator context</returns>
private TranslatorContext DecodeGraphicsShader(
GpuState state,
GpuChannel channel,
GpuAccessorState gas,
TranslationCounts counts,
TranslationFlags flags,
ShaderStage stage,
@ -889,13 +894,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
return null;
}
GpuAccessorState gas = new GpuAccessorState(
state.Get<PoolState>(MethodOffset.TexturePoolState).Address.Pack(),
state.Get<PoolState>(MethodOffset.TexturePoolState).MaximumId,
state.Get<int>(MethodOffset.TextureBufferIndex),
state.Get<Boolean32>(MethodOffset.EarlyZForce));
GpuAccessor gpuAccessor = new GpuAccessor(_context, state.Channel, gas, (int)stage - 1);
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gas, (int)stage - 1);
var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, flags);
return Translator.CreateContext(gpuVa, gpuAccessor, options, counts);

View file

@ -1,28 +0,0 @@
namespace Ryujinx.Graphics.Gpu
{
/// <summary>
/// Shadow RAM Control setting.
/// </summary>
enum ShadowRamControl
{
/// <summary>
/// Track data writes and store them on shadow RAM.
/// </summary>
Track = 0,
/// <summary>
/// Track data writes and store them on shadow RAM, with filtering.
/// </summary>
TrackWithFilter = 1,
/// <summary>
/// Writes data directly without storing on shadow RAM.
/// </summary>
Passthrough = 2,
/// <summary>
/// Ignore data being written and replace with data on shadow RAM instead.
/// </summary>
Replay = 3
}
}

View file

@ -1,31 +0,0 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Color buffer blending parameters.
/// </summary>
struct BlendState
{
#pragma warning disable CS0649
public Boolean32 SeparateAlpha;
public BlendOp ColorOp;
public BlendFactor ColorSrcFactor;
public BlendFactor ColorDstFactor;
public BlendOp AlphaOp;
public BlendFactor AlphaSrcFactor;
public BlendFactor AlphaDstFactor;
public uint Padding;
#pragma warning restore CS0649
public static BlendState Default = new BlendState
{
ColorOp = BlendOp.Add,
ColorSrcFactor = BlendFactor.One,
ColorDstFactor = BlendFactor.Zero,
AlphaOp = BlendOp.Add,
AlphaSrcFactor = BlendFactor.One,
AlphaDstFactor = BlendFactor.Zero
};
}
}

View file

@ -1,31 +0,0 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Color buffer blending parameters, shared by all color buffers.
/// </summary>
struct BlendStateCommon
{
#pragma warning disable CS0649
public Boolean32 SeparateAlpha;
public BlendOp ColorOp;
public BlendFactor ColorSrcFactor;
public BlendFactor ColorDstFactor;
public BlendOp AlphaOp;
public BlendFactor AlphaSrcFactor;
public uint Unknown0x1354;
public BlendFactor AlphaDstFactor;
#pragma warning restore CS0649
public static BlendStateCommon Default = new BlendStateCommon
{
ColorOp = BlendOp.Add,
ColorSrcFactor = BlendFactor.One,
ColorDstFactor = BlendFactor.Zero,
AlphaOp = BlendOp.Add,
AlphaSrcFactor = BlendFactor.One,
AlphaDstFactor = BlendFactor.Zero
};
}
}

View file

@ -1,15 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Color buffer clear color.
/// </summary>
struct ClearColors
{
#pragma warning disable CS0649
public float Red;
public float Green;
public float Blue;
public float Alpha;
#pragma warning restore CS0649
}
}

View file

@ -1,14 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Condition for conditional rendering.
/// </summary>
enum Condition
{
Never,
Always,
ResultNonZero,
Equal,
NotEqual
}
}

View file

@ -1,13 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Condition parameters for conditional rendering.
/// </summary>
struct ConditionState
{
#pragma warning disable CS0649
public GpuVa Address;
public Condition Condition;
#pragma warning restore CS0649
}
}

View file

@ -1,18 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Buffer to texture copy parameters.
/// </summary>
struct CopyBufferTexture
{
#pragma warning disable CS0649
public MemoryLayout MemoryLayout;
public int Width;
public int Height;
public int Depth;
public int RegionZ;
public ushort RegionX;
public ushort RegionY;
#pragma warning restore CS0649
}
}

View file

@ -1,19 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Texture copy region.
/// </summary>
struct CopyRegion
{
#pragma warning disable CS0649
public int DstX;
public int DstY;
public int DstWidth;
public int DstHeight;
public long SrcWidthRF;
public long SrcHeightRF;
public long SrcXF;
public long SrcYF;
#pragma warning restore CS0649
}
}

View file

@ -1,20 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Texture to texture (with optional resizing) copy parameters.
/// </summary>
struct CopyTexture
{
#pragma warning disable CS0649
public ColorFormat Format;
public Boolean32 LinearLayout;
public MemoryLayout MemoryLayout;
public int Depth;
public int Layer;
public int Stride;
public int Width;
public int Height;
public GpuVa Address;
#pragma warning restore CS0649
}
}

View file

@ -1,14 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Depth bias (also called polygon offset) parameters.
/// </summary>
struct DepthBiasState
{
#pragma warning disable CS0649
public Boolean32 PointEnable;
public Boolean32 LineEnable;
public Boolean32 FillEnable;
#pragma warning restore CS0649
}
}

View file

@ -1,16 +0,0 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Face culling and orientation parameters.
/// </summary>
struct FaceState
{
#pragma warning disable CS0649
public Boolean32 CullEnable;
public FrontFace FrontFace;
public Face CullFace;
#pragma warning restore CS0649
}
}

View file

@ -1,477 +0,0 @@
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Image;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// GPU state.
/// </summary>
class GpuState
{
private const int RegistersCount = 0xe00;
public delegate void MethodCallback(GpuState state, int argument);
private readonly int[] _memory;
private readonly int[] _shadow;
/// <summary>
/// GPU register information.
/// </summary>
private struct Register
{
public MethodCallback Callback;
public MethodOffset BaseOffset;
public int Stride;
public int Count;
public bool Modified;
}
private readonly Register[] _registers;
private readonly IDeviceState _deviceState;
/// <summary>
/// Gets or sets the shadow ram control used for this sub-channel.
/// </summary>
public ShadowRamControl ShadowRamControl { get; set; }
/// <summary>
/// GPU channel for the sub-channel state.
/// </summary>
public GpuChannel Channel { get; }
/// <summary>
/// Creates a new instance of the GPU state.
/// </summary>
/// <param name="channel">Channel that the sub-channel state belongs to</param>
/// <param name="deviceState">Optional device state that will replace the internal backing storage</param>
public GpuState(GpuChannel channel, IDeviceState deviceState = null)
{
Channel = channel;
_deviceState = deviceState;
_memory = new int[RegistersCount];
_shadow = new int[RegistersCount];
_registers = new Register[RegistersCount];
for (int index = 0; index < _registers.Length; index++)
{
_registers[index].BaseOffset = (MethodOffset)index;
_registers[index].Stride = 1;
_registers[index].Count = 1;
_registers[index].Modified = true;
}
foreach (var item in GpuStateTable.Table)
{
int totalRegs = item.Size * item.Count;
for (int regOffset = 0; regOffset < totalRegs; regOffset++)
{
int index = (int)item.Offset + regOffset;
_registers[index].BaseOffset = item.Offset;
_registers[index].Stride = item.Size;
_registers[index].Count = item.Count;
}
}
InitializeDefaultState(_memory);
InitializeDefaultState(_shadow);
}
/// <summary>
/// Calls a GPU method, using this state.
/// </summary>
/// <param name="meth">The GPU method to be called</param>
public void CallMethod(MethodParams meth)
{
int value = meth.Argument;
// Methods < 0x80 shouldn't be affected by shadow RAM at all.
if (meth.Method >= 0x80)
{
ShadowRamControl shadowCtrl = ShadowRamControl;
// TODO: Figure out what TrackWithFilter does, compared to Track.
if (shadowCtrl == ShadowRamControl.Track ||
shadowCtrl == ShadowRamControl.TrackWithFilter)
{
_shadow[meth.Method] = value;
}
else if (shadowCtrl == ShadowRamControl.Replay)
{
value = _shadow[meth.Method];
}
}
if (_deviceState != null)
{
_deviceState.Write(meth.Method * 4, meth.Argument);
}
else
{
Register register = _registers[meth.Method];
if (_memory[meth.Method] != value)
{
_registers[(int)register.BaseOffset].Modified = true;
}
_memory[meth.Method] = value;
register.Callback?.Invoke(this, value);
}
}
/// <summary>
/// Reads data from a GPU register at the given offset.
/// </summary>
/// <param name="offset">Offset to be read</param>
/// <returns>Data at the register</returns>
public int Read(int offset)
{
if (_deviceState != null)
{
return _deviceState.Read(offset * 4);
}
return _memory[offset];
}
/// <summary>
/// Writes data to the GPU register at the given offset.
/// </summary>
/// <param name="offset">Offset to be written</param>
/// <param name="value">Value to be written</param>
public void Write(int offset, int value)
{
_memory[offset] = value;
}
/// <summary>
/// Writes an offset value at the uniform buffer offset register.
/// </summary>
/// <param name="offset">The offset to be written</param>
public void SetUniformBufferOffset(int offset)
{
_memory[(int)MethodOffset.UniformBufferState + 3] = offset;
}
/// <summary>
/// Initializes registers with the default state.
/// </summary>
private void InitializeDefaultState(int[] memory)
{
// Enable Rasterizer
memory[(int)MethodOffset.RasterizeEnable] = 1;
// Depth ranges.
for (int index = 0; index < Constants.TotalViewports; index++)
{
memory[(int)MethodOffset.ViewportExtents + index * 4 + 2] = 0;
memory[(int)MethodOffset.ViewportExtents + index * 4 + 3] = 0x3F800000;
// Set swizzle to +XYZW
memory[(int)MethodOffset.ViewportTransform + index * 8 + 6] = 0x6420;
}
// Viewport transform enable.
memory[(int)MethodOffset.ViewportTransformEnable] = 1;
// Default front stencil mask.
memory[0x4e7] = 0xff;
// Conditional rendering condition.
memory[0x556] = (int)Condition.Always;
// Default color mask.
for (int index = 0; index < Constants.TotalRenderTargets; index++)
{
memory[(int)MethodOffset.RtColorMask + index] = 0x1111;
}
// Default blend states
Set(MethodOffset.BlendStateCommon, BlendStateCommon.Default);
for (int index = 0; index < Constants.TotalRenderTargets; index++)
{
Set(MethodOffset.BlendState, index, BlendState.Default);
}
// Default Point Parameters
memory[(int)MethodOffset.PointSpriteEnable] = 1;
memory[(int)MethodOffset.PointSize] = 0x3F800000; // 1.0f
memory[(int)MethodOffset.PointCoordReplace] = 0x8; // Enable
}
/// <summary>
/// Registers a callback that is called every time a GPU method, or methods are called.
/// </summary>
/// <param name="offset">Offset of the method</param>
/// <param name="count">Word count of the methods region</param>
/// <param name="callback">Calllback to be called</param>
public void RegisterCallback(MethodOffset offset, int count, MethodCallback callback)
{
for (int index = 0; index < count; index++)
{
_registers[(int)offset + index].Callback = callback;
}
}
/// <summary>
/// Registers a callback that is called every time a GPU method is called.
/// </summary>
/// <param name="offset">Offset of the method</param>
/// <param name="callback">Calllback to be called</param>
public void RegisterCallback(MethodOffset offset, MethodCallback callback)
{
_registers[(int)offset].Callback = callback;
}
/// <summary>
/// Clear all registered callbacks.
/// </summary>
public void ClearCallbacks()
{
for (int index = 0; index < _registers.Length; index++)
{
_registers[index].Callback = null;
}
}
/// <summary>
/// Forces a full host state update by marking all state as modified,
/// and also requests all GPU resources in use to be rebound.
/// </summary>
public void ForceAllDirty()
{
for (int index = 0; index < _registers.Length; index++)
{
_registers[index].Modified = true;
}
Channel.BufferManager.Rebind();
Channel.TextureManager.Rebind();
}
/// <summary>
/// Checks if a given register has been modified since the last call to this method.
/// </summary>
/// <param name="offset">Register offset</param>
/// <returns>True if modified, false otherwise</returns>
public bool QueryModified(MethodOffset offset)
{
bool modified = _registers[(int)offset].Modified;
_registers[(int)offset].Modified = false;
return modified;
}
/// <summary>
/// Checks if two registers have been modified since the last call to this method.
/// </summary>
/// <param name="m1">First register offset</param>
/// <param name="m2">Second register offset</param>
/// <returns>True if any register was modified, false otherwise</returns>
public bool QueryModified(MethodOffset m1, MethodOffset m2)
{
bool modified = _registers[(int)m1].Modified ||
_registers[(int)m2].Modified;
_registers[(int)m1].Modified = false;
_registers[(int)m2].Modified = false;
return modified;
}
/// <summary>
/// Checks if three registers have been modified since the last call to this method.
/// </summary>
/// <param name="m1">First register offset</param>
/// <param name="m2">Second register offset</param>
/// <param name="m3">Third register offset</param>
/// <returns>True if any register was modified, false otherwise</returns>
public bool QueryModified(MethodOffset m1, MethodOffset m2, MethodOffset m3)
{
bool modified = _registers[(int)m1].Modified ||
_registers[(int)m2].Modified ||
_registers[(int)m3].Modified;
_registers[(int)m1].Modified = false;
_registers[(int)m2].Modified = false;
_registers[(int)m3].Modified = false;
return modified;
}
/// <summary>
/// Checks if four registers have been modified since the last call to this method.
/// </summary>
/// <param name="m1">First register offset</param>
/// <param name="m2">Second register offset</param>
/// <param name="m3">Third register offset</param>
/// <param name="m4">Fourth register offset</param>
/// <returns>True if any register was modified, false otherwise</returns>
public bool QueryModified(MethodOffset m1, MethodOffset m2, MethodOffset m3, MethodOffset m4)
{
bool modified = _registers[(int)m1].Modified ||
_registers[(int)m2].Modified ||
_registers[(int)m3].Modified ||
_registers[(int)m4].Modified;
_registers[(int)m1].Modified = false;
_registers[(int)m2].Modified = false;
_registers[(int)m3].Modified = false;
_registers[(int)m4].Modified = false;
return modified;
}
/// <summary>
/// Checks if five registers have been modified since the last call to this method.
/// </summary>
/// <param name="m1">First register offset</param>
/// <param name="m2">Second register offset</param>
/// <param name="m3">Third register offset</param>
/// <param name="m4">Fourth register offset</param>
/// <param name="m5">Fifth register offset</param>
/// <returns>True if any register was modified, false otherwise</returns>
public bool QueryModified(
MethodOffset m1,
MethodOffset m2,
MethodOffset m3,
MethodOffset m4,
MethodOffset m5)
{
bool modified = _registers[(int)m1].Modified ||
_registers[(int)m2].Modified ||
_registers[(int)m3].Modified ||
_registers[(int)m4].Modified ||
_registers[(int)m5].Modified;
_registers[(int)m1].Modified = false;
_registers[(int)m2].Modified = false;
_registers[(int)m3].Modified = false;
_registers[(int)m4].Modified = false;
_registers[(int)m5].Modified = false;
return modified;
}
/// <summary>
/// Checks if six registers have been modified since the last call to this method.
/// </summary>
/// <param name="m1">First register offset</param>
/// <param name="m2">Second register offset</param>
/// <param name="m3">Third register offset</param>
/// <param name="m4">Fourth register offset</param>
/// <param name="m5">Fifth register offset</param>
/// <param name="m6">Sixth register offset</param>
/// <returns>True if any register was modified, false otherwise</returns>
public bool QueryModified(
MethodOffset m1,
MethodOffset m2,
MethodOffset m3,
MethodOffset m4,
MethodOffset m5,
MethodOffset m6)
{
bool modified = _registers[(int)m1].Modified ||
_registers[(int)m2].Modified ||
_registers[(int)m3].Modified ||
_registers[(int)m4].Modified ||
_registers[(int)m5].Modified ||
_registers[(int)m6].Modified;
_registers[(int)m1].Modified = false;
_registers[(int)m2].Modified = false;
_registers[(int)m3].Modified = false;
_registers[(int)m4].Modified = false;
_registers[(int)m5].Modified = false;
_registers[(int)m6].Modified = false;
return modified;
}
/// <summary>
/// Gets indexed data from a given register offset.
/// </summary>
/// <typeparam name="T">Type of the data</typeparam>
/// <param name="offset">Register offset</param>
/// <param name="index">Index for indexed data</param>
/// <returns>The data at the specified location</returns>
public T Get<T>(MethodOffset offset, int index) where T : unmanaged
{
Register register = _registers[(int)offset];
if ((uint)index >= register.Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return Get<T>(offset + index * register.Stride);
}
/// <summary>
/// Gets data from a given register offset.
/// </summary>
/// <typeparam name="T">Type of the data</typeparam>
/// <param name="offset">Register offset</param>
/// <returns>The data at the specified location</returns>
public T Get<T>(MethodOffset offset) where T : unmanaged
{
return MemoryMarshal.Cast<int, T>(_memory.AsSpan().Slice((int)offset))[0];
}
/// <summary>
/// Gets a span of the data at a given register offset.
/// </summary>
/// <param name="offset">Register offset</param>
/// <param name="length">Length of the data in bytes</param>
/// <returns>The data at the specified location</returns>
public Span<byte> GetSpan(MethodOffset offset, int length)
{
return MemoryMarshal.Cast<int, byte>(_memory.AsSpan().Slice((int)offset)).Slice(0, length);
}
/// <summary>
/// Sets indexed data to a given register offset.
/// </summary>
/// <typeparam name="T">Type of the data</typeparam>
/// <param name="offset">Register offset</param>
/// <param name="index">Index for indexed data</param>
/// <param name="data">The data to set</param>
public void Set<T>(MethodOffset offset, int index, T data) where T : unmanaged
{
Register register = _registers[(int)offset];
if ((uint)index >= register.Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
Set(offset + index * register.Stride, data);
}
/// <summary>
/// Sets data to a given register offset.
/// </summary>
/// <typeparam name="T">Type of the data</typeparam>
/// <param name="offset">Register offset</param>
/// <param name="data">The data to set</param>
public void Set<T>(MethodOffset offset, T data) where T : unmanaged
{
ReadOnlySpan<int> intSpan = MemoryMarshal.Cast<T, int>(MemoryMarshal.CreateReadOnlySpan(ref data, 1));
intSpan.CopyTo(_memory.AsSpan().Slice((int)offset, intSpan.Length));
}
}
}

View file

@ -1,87 +0,0 @@
using Ryujinx.Graphics.GAL;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// GPU State item sizes table.
/// </summary>
static class GpuStateTable
{
/// <summary>
/// GPU state table item, with size for structures, and count for indexed state data.
/// </summary>
public struct TableItem
{
/// <summary>
/// Offset of the data.
/// </summary>
public MethodOffset Offset { get; }
/// <summary>
/// Size in words.
/// </summary>
public int Size { get; }
/// <summary>
/// Count for indexed data, or 1 if not indexed.
/// </summary>
public int Count { get; }
/// <summary>
/// Constructs the table item structure.
/// </summary>
/// <param name="offset">Data offset</param>
/// <param name="type">Data type</param>
/// <param name="count">Data count, for indexed data</param>
public TableItem(MethodOffset offset, Type type, int count)
{
int sizeInBytes = Marshal.SizeOf(type);
Debug.Assert((sizeInBytes & 3) == 0);
Offset = offset;
Size = sizeInBytes / 4;
Count = count;
}
}
/// <summary>
/// Table of GPU state structure sizes and counts.
/// </summary>
public static TableItem[] Table = new TableItem[]
{
new TableItem(MethodOffset.TfBufferState, typeof(TfBufferState), Constants.TotalTransformFeedbackBuffers),
new TableItem(MethodOffset.TfState, typeof(TfState), Constants.TotalTransformFeedbackBuffers),
new TableItem(MethodOffset.RtColorState, typeof(RtColorState), Constants.TotalRenderTargets),
new TableItem(MethodOffset.ViewportTransform, typeof(ViewportTransform), Constants.TotalViewports),
new TableItem(MethodOffset.ViewportExtents, typeof(ViewportExtents), Constants.TotalViewports),
new TableItem(MethodOffset.VertexBufferDrawState, typeof(VertexBufferDrawState), 1),
new TableItem(MethodOffset.DepthBiasState, typeof(DepthBiasState), 1),
new TableItem(MethodOffset.ScissorState, typeof(ScissorState), Constants.TotalViewports),
new TableItem(MethodOffset.StencilBackMasks, typeof(StencilBackMasks), 1),
new TableItem(MethodOffset.RtDepthStencilState, typeof(RtDepthStencilState), 1),
new TableItem(MethodOffset.VertexAttribState, typeof(VertexAttribState), Constants.TotalVertexAttribs),
new TableItem(MethodOffset.RtDepthStencilSize, typeof(Size3D), 1),
new TableItem(MethodOffset.BlendEnable, typeof(Boolean32), Constants.TotalRenderTargets),
new TableItem(MethodOffset.StencilTestState, typeof(StencilTestState), 1),
new TableItem(MethodOffset.SamplerPoolState, typeof(PoolState), 1),
new TableItem(MethodOffset.TexturePoolState, typeof(PoolState), 1),
new TableItem(MethodOffset.StencilBackTestState, typeof(StencilBackTestState), 1),
new TableItem(MethodOffset.ShaderBaseAddress, typeof(GpuVa), 1),
new TableItem(MethodOffset.PrimitiveRestartState, typeof(PrimitiveRestartState), 1),
new TableItem(MethodOffset.IndexBufferState, typeof(IndexBufferState), 1),
new TableItem(MethodOffset.VertexBufferInstanced, typeof(Boolean32), Constants.TotalVertexBuffers),
new TableItem(MethodOffset.FaceState, typeof(FaceState), 1),
new TableItem(MethodOffset.RtColorMask, typeof(RtColorMask), Constants.TotalRenderTargets),
new TableItem(MethodOffset.VertexBufferState, typeof(VertexBufferState), Constants.TotalVertexBuffers),
new TableItem(MethodOffset.BlendConstant, typeof(ColorF), 1),
new TableItem(MethodOffset.BlendStateCommon, typeof(BlendStateCommon), 1),
new TableItem(MethodOffset.BlendState, typeof(BlendState), Constants.TotalRenderTargets),
new TableItem(MethodOffset.VertexBufferEndAddress, typeof(GpuVa), Constants.TotalVertexBuffers),
new TableItem(MethodOffset.ShaderState, typeof(ShaderState), Constants.ShaderStages + 1),
};
}
}

View file

@ -1,19 +0,0 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// GPU index buffer state.
/// This is used on indexed draws.
/// </summary>
struct IndexBufferState
{
#pragma warning disable CS0649
public GpuVa Address;
public GpuVa EndAddress;
public IndexType Type;
public int First;
public int Count;
#pragma warning restore CS0649
}
}

View file

@ -1,22 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Inline-to-memory copy parameters.
/// </summary>
struct Inline2MemoryParams
{
#pragma warning disable CS0649
public int LineLengthIn;
public int LineCount;
public GpuVa DstAddress;
public int DstStride;
public MemoryLayout DstMemoryLayout;
public int DstWidth;
public int DstHeight;
public int DstDepth;
public int DstZ;
public int DstX;
public int DstY;
#pragma warning restore CS0649
}
}

View file

@ -1,12 +0,0 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.State
{
struct LogicalOpState
{
#pragma warning disable CS0649
public Boolean32 Enable;
public LogicalOp LogicalOp;
#pragma warning restore CS0649
}
}

View file

@ -1,134 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// GPU method offset.
/// </summary>
/// <remarks>
/// This is indexed in 32 bits word.
/// </remarks>
enum MethodOffset
{
BindChannel = 0x0,
I2mParams = 0x60,
LaunchDma = 0x6c,
LoadInlineData = 0x6d,
CopyDstTexture = 0x80,
EarlyZForce = 0x84,
CopySrcTexture = 0x8c,
DispatchParamsAddress = 0xad,
Dispatch = 0xaf,
SyncpointAction = 0xb2,
CopyBuffer = 0xc0,
RasterizeEnable = 0xdf,
TfBufferState = 0xe0,
CopyBufferParams = 0x100,
TfState = 0x1c0,
CopyBufferConstA = 0x1c0,
CopyBufferConstB = 0x1c1,
CopyBufferSwizzle = 0x1c2,
CopyBufferDstTexture = 0x1c3,
CopyBufferSrcTexture = 0x1ca,
TfEnable = 0x1d1,
RtColorState = 0x200,
CopyTextureControl = 0x223,
CopyRegion = 0x22c,
CopyTexture = 0x237,
ViewportTransform = 0x280,
ViewportExtents = 0x300,
VertexBufferDrawState = 0x35d,
DepthMode = 0x35f,
ClearColors = 0x360,
ClearDepthValue = 0x364,
ClearStencilValue = 0x368,
DepthBiasState = 0x370,
TextureBarrier = 0x378,
ScissorState = 0x380,
StencilBackMasks = 0x3d5,
InvalidateTextures = 0x3dd,
TextureBarrierTiled = 0x3df,
RtColorMaskShared = 0x3e4,
RtDepthStencilState = 0x3f8,
ScreenScissorState = 0x3fd,
VertexAttribState = 0x458,
RtControl = 0x487,
RtDepthStencilSize = 0x48a,
SamplerIndex = 0x48d,
DepthTestEnable = 0x4b3,
BlendIndependent = 0x4b9,
DepthWriteEnable = 0x4ba,
AlphaTestEnable = 0x4bb,
VbElementU8 = 0x4c1,
DepthTestFunc = 0x4c3,
AlphaTestRef = 0x4c4,
AlphaTestFunc = 0x4c5,
BlendConstant = 0x4c7,
BlendStateCommon = 0x4cf,
BlendEnableCommon = 0x4d7,
BlendEnable = 0x4d8,
StencilTestState = 0x4e0,
YControl = 0x4eb,
LineWidthSmooth = 0x4ec,
LineWidthAliased = 0x4ed,
FirstVertex = 0x50d,
FirstInstance = 0x50e,
ClipDistanceEnable = 0x544,
PointSize = 0x546,
PointSpriteEnable = 0x548,
ResetCounter = 0x54c,
RtDepthStencilEnable = 0x54e,
ConditionState = 0x554,
SamplerPoolState = 0x557,
DepthBiasFactor = 0x55b,
LineSmoothEnable = 0x55c,
TexturePoolState = 0x55d,
StencilBackTestState = 0x565,
DepthBiasUnits = 0x56f,
RtMsaaMode = 0x574,
VbElementU32 = 0x57a,
VbElementU16 = 0x57c,
PointCoordReplace = 0x581,
ShaderBaseAddress = 0x582,
DrawEnd = 0x585,
DrawBegin = 0x586,
PrimitiveRestartState = 0x591,
IndexBufferState = 0x5f2,
IndexBufferCount = 0x5f8,
DrawIndexedSmall = 0x5f9,
DrawIndexedSmall2 = 0x5fa,
DrawIndexedSmallIncInstance = 0x5fc,
DrawIndexedSmallIncInstance2 = 0x5fd,
DepthBiasClamp = 0x61f,
VertexBufferInstanced = 0x620,
VertexProgramPointSize = 0x644,
FaceState = 0x646,
ViewportTransformEnable = 0x64b,
ViewVolumeClipControl = 0x64f,
PrimitiveTypeOverride = 0x65c,
LogicOpState = 0x671,
Clear = 0x674,
RtColorMask = 0x680,
ReportState = 0x6c0,
Report = 0x6c3,
VertexBufferState = 0x700,
BlendState = 0x780,
VertexBufferEndAddress = 0x7c0,
ShaderState = 0x800,
FirmwareCall0 = 0x8c0,
FirmwareCall1 = 0x8c1,
FirmwareCall2 = 0x8c2,
FirmwareCall3 = 0x8c3,
FirmwareCall4 = 0x8c4,
FirmwareCall5 = 0x8c5,
FirmwareCall6 = 0x8c6,
FirmwareCall7 = 0x8c7,
UniformBufferState = 0x8e0,
UniformBufferUpdateData = 0x8e4,
UniformBufferBindVertex = 0x904,
UniformBufferBindTessControl = 0x90c,
UniformBufferBindTessEvaluation = 0x914,
UniformBufferBindGeometry = 0x91c,
UniformBufferBindFragment = 0x924,
TextureBufferIndex = 0x982,
TfVaryingLocations = 0xa00
}
}

View file

@ -1,13 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Texture or sampler pool state.
/// </summary>
struct PoolState
{
#pragma warning disable CS0649
public GpuVa Address;
public int MaximumId;
#pragma warning restore CS0649
}
}

View file

@ -1,13 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Primitive restart state.
/// </summary>
struct PrimitiveRestartState
{
#pragma warning disable CS0649
public Boolean32 Enable;
public int Index;
#pragma warning restore CS0649
}
}

View file

@ -1,29 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Counter type for GPU counter reporting.
/// </summary>
enum ReportCounterType
{
Zero = 0,
InputVertices = 1,
InputPrimitives = 3,
VertexShaderInvocations = 5,
GeometryShaderInvocations = 7,
GeometryShaderPrimitives = 9,
ZcullStats0 = 0xa,
TransformFeedbackPrimitivesWritten = 0xb,
ZcullStats1 = 0xc,
ZcullStats2 = 0xe,
ClipperInputPrimitives = 0xf,
ZcullStats3 = 0x10,
ClipperOutputPrimitives = 0x11,
PrimitivesGenerated = 0x12,
FragmentShaderInvocations = 0x13,
SamplesPassed = 0x15,
TransformFeedbackOffset = 0x1a,
TessControlShaderInvocations = 0x1b,
TessEvaluationShaderInvocations = 0x1d,
TessEvaluationShaderPrimitives = 0x1f
}
}

View file

@ -1,24 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Counter type for GPU counter reset.
/// </summary>
enum ResetCounterType
{
SamplesPassed = 1,
ZcullStats = 2,
TransformFeedbackPrimitivesWritten = 0x10,
InputVertices = 0x12,
InputPrimitives = 0x13,
VertexShaderInvocations = 0x15,
TessControlShaderInvocations = 0x16,
TessEvaluationShaderInvocations = 0x17,
TessEvaluationShaderPrimitives = 0x18,
GeometryShaderInvocations = 0x1a,
GeometryShaderPrimitives = 0x1b,
ClipperInputPrimitives = 0x1c,
ClipperOutputPrimitives = 0x1d,
FragmentShaderInvocations = 0x1e,
PrimitivesGenerated = 0x1f
}
}

View file

@ -1,49 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Render target color buffer mask.
/// This defines which color channels are written to the color buffer.
/// </summary>
struct RtColorMask
{
#pragma warning disable CS0649
public uint Packed;
#pragma warning restore CS0649
/// <summary>
/// Unpacks red channel enable.
/// </summary>
/// <returns>True to write the new red channel color, false to keep the old value</returns>
public bool UnpackRed()
{
return (Packed & 0x1) != 0;
}
/// <summary>
/// Unpacks green channel enable.
/// </summary>
/// <returns>True to write the new green channel color, false to keep the old value</returns>
public bool UnpackGreen()
{
return (Packed & 0x10) != 0;
}
/// <summary>
/// Unpacks blue channel enable.
/// </summary>
/// <returns>True to write the new blue channel color, false to keep the old value</returns>
public bool UnpackBlue()
{
return (Packed & 0x100) != 0;
}
/// <summary>
/// Unpacks alpha channel enable.
/// </summary>
/// <returns>True to write the new alpha channel color, false to keep the old value</returns>
public bool UnpackAlpha()
{
return (Packed & 0x1000) != 0;
}
}
}

View file

@ -1,26 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Render target color buffer state.
/// </summary>
struct RtColorState
{
#pragma warning disable CS0649
public GpuVa Address;
public int WidthOrStride;
public int Height;
public ColorFormat Format;
public MemoryLayout MemoryLayout;
public int Depth;
public int LayerSize;
public int BaseLayer;
public int Unknown0x24;
public int Padding0;
public int Padding1;
public int Padding2;
public int Padding3;
public int Padding4;
public int Padding5;
#pragma warning restore CS0649
}
}

View file

@ -1,31 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Render target draw buffers control.
/// </summary>
struct RtControl
{
#pragma warning disable CS0649
public uint Packed;
#pragma warning restore CS0649
/// <summary>
/// Unpacks the number of active draw buffers.
/// </summary>
/// <returns>Number of active draw buffers</returns>
public int UnpackCount()
{
return (int)(Packed & 0xf);
}
/// <summary>
/// Unpacks the color attachment index for a given draw buffer.
/// </summary>
/// <param name="index">Index of the draw buffer</param>
/// <returns>Attachment index</returns>
public int UnpackPermutationIndex(int index)
{
return (int)((Packed >> (4 + index * 3)) & 7);
}
}
}

View file

@ -1,15 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Render target depth-stencil buffer state.
/// </summary>
struct RtDepthStencilState
{
#pragma warning disable CS0649
public GpuVa Address;
public ZetaFormat Format;
public MemoryLayout MemoryLayout;
public int LayerSize;
#pragma warning restore CS0649
}
}

View file

@ -1,14 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
struct ScissorState
{
#pragma warning disable CS0649
public Boolean32 Enable;
public ushort X1;
public ushort X2;
public ushort Y1;
public ushort Y2;
public uint Padding;
#pragma warning restore CS0649
}
}

View file

@ -1,12 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
struct ScreenScissorState
{
#pragma warning disable CS0649
public ushort X;
public ushort Width;
public ushort Y;
public ushort Height;
#pragma warning restore CS0649
}
}

View file

@ -1,12 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// GPU semaphore operation.
/// </summary>
enum SemaphoreOperation
{
Release = 0,
Acquire = 1,
Counter = 2
}
}

View file

@ -1,14 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// GPU semaphore state.
/// </summary>
struct SemaphoreState
{
#pragma warning disable CS0649
public GpuVa Address;
public int Payload;
public uint Control;
#pragma warning restore CS0649
}
}

View file

@ -1,37 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Graphics shader stage state.
/// </summary>
struct ShaderState
{
#pragma warning disable CS0649
public uint Control;
public uint Offset;
public uint Unknown0x8;
public int MaxRegisters;
public ShaderType Type;
public uint Unknown0x14;
public uint Unknown0x18;
public uint Unknown0x1c;
public uint Unknown0x20;
public uint Unknown0x24;
public uint Unknown0x28;
public uint Unknown0x2c;
public uint Unknown0x30;
public uint Unknown0x34;
public uint Unknown0x38;
public uint Unknown0x3c;
#pragma warning restore CS0649
/// <summary>
/// Unpacks shader enable information.
/// Must be ignored for vertex shaders, those are always enabled.
/// </summary>
/// <returns>True if the stage is enabled, false otherwise</returns>
public bool UnpackEnable()
{
return (Control & 1) != 0;
}
}
}

View file

@ -1,14 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Shader stage name.
/// </summary>
enum ShaderType
{
Vertex,
TessellationControl,
TessellationEvaluation,
Geometry,
Fragment
}
}

View file

@ -1,14 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// 3D, 2D or 1D texture size.
/// </summary>
struct Size3D
{
#pragma warning disable CS0649
public int Width;
public int Height;
public int Depth;
#pragma warning restore CS0649
}
}

View file

@ -1,14 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Stencil test masks for back tests.
/// </summary>
struct StencilBackMasks
{
#pragma warning disable CS0649
public int FuncRef;
public int Mask;
public int FuncMask;
#pragma warning restore CS0649
}
}

View file

@ -1,18 +0,0 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Stencil back test state.
/// </summary>
struct StencilBackTestState
{
#pragma warning disable CS0649
public Boolean32 TwoSided;
public StencilOp BackSFail;
public StencilOp BackDpFail;
public StencilOp BackDpPass;
public CompareOp BackFunc;
#pragma warning restore CS0649
}
}

View file

@ -1,21 +0,0 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Stencil front test state and masks.
/// </summary>
struct StencilTestState
{
#pragma warning disable CS0649
public Boolean32 Enable;
public StencilOp FrontSFail;
public StencilOp FrontDpFail;
public StencilOp FrontDpPass;
public CompareOp FrontFunc;
public int FrontFuncRef;
public int FrontFuncMask;
public int FrontMask;
#pragma warning restore CS0649
}
}

View file

@ -1,18 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Transform feedback buffer state.
/// </summary>
struct TfBufferState
{
#pragma warning disable CS0649
public Boolean32 Enable;
public GpuVa Address;
public int Size;
public int Offset;
public uint Padding0;
public uint Padding1;
public uint Padding2;
#pragma warning restore CS0649
}
}

View file

@ -1,15 +0,0 @@
namespace Ryujinx.Graphics.Gpu.State
{
/// <summary>
/// Transform feedback state.
/// </summary>
struct TfState
{
#pragma warning disable CS0649
public int BufferIndex;
public int VaryingsCount;
public int Stride;
public uint Padding;
#pragma warning restore CS0649
}
}

Some files were not shown because too many files have changed in this diff Show more