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

@ -0,0 +1,17 @@
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Boolean value, stored as a 32-bits integer in memory.
/// </summary>
struct Boolean32
{
#pragma warning disable CS0649
private uint _value;
#pragma warning restore CS0649
public static implicit operator bool(Boolean32 value)
{
return (value._value & 1) != 0;
}
}
}

View file

@ -0,0 +1,134 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Color texture format.
/// </summary>
enum ColorFormat
{
R32G32B32A32Float = 0xc0,
R32G32B32A32Sint = 0xc1,
R32G32B32A32Uint = 0xc2,
R32G32B32X32Float = 0xc3,
R32G32B32X32Sint = 0xc4,
R32G32B32X32Uint = 0xc5,
R16G16B16X16Unorm = 0xc6,
R16G16B16X16Snorm = 0xc7,
R16G16B16X16Sint = 0xc8,
R16G16B16X16Uint = 0xc9,
R16G16B16A16Float = 0xca,
R32G32Float = 0xcb,
R32G32Sint = 0xcc,
R32G32Uint = 0xcd,
R16G16B16X16Float = 0xce,
B8G8R8A8Unorm = 0xcf,
B8G8R8A8Srgb = 0xd0,
R10G10B10A2Unorm = 0xd1,
R10G10B10A2Uint = 0xd2,
R8G8B8A8Unorm = 0xd5,
R8G8B8A8Srgb = 0xd6,
R8G8B8X8Snorm = 0xd7,
R8G8B8X8Sint = 0xd8,
R8G8B8X8Uint = 0xd9,
R16G16Unorm = 0xda,
R16G16Snorm = 0xdb,
R16G16Sint = 0xdc,
R16G16Uint = 0xdd,
R16G16Float = 0xde,
R11G11B10Float = 0xe0,
R32Sint = 0xe3,
R32Uint = 0xe4,
R32Float = 0xe5,
B8G8R8X8Unorm = 0xe6,
B8G8R8X8Srgb = 0xe7,
B5G6R5Unorm = 0xe8,
B5G5R5A1Unorm = 0xe9,
R8G8Unorm = 0xea,
R8G8Snorm = 0xeb,
R8G8Sint = 0xec,
R8G8Uint = 0xed,
R16Unorm = 0xee,
R16Snorm = 0xef,
R16Sint = 0xf0,
R16Uint = 0xf1,
R16Float = 0xf2,
R8Unorm = 0xf3,
R8Snorm = 0xf4,
R8Sint = 0xf5,
R8Uint = 0xf6,
B5G5R5X1Unorm = 0xf8,
R8G8B8X8Unorm = 0xf9,
R8G8B8X8Srgb = 0xfa
}
static class ColorFormatConverter
{
/// <summary>
/// Converts the color texture format to a host compatible format.
/// </summary>
/// <param name="format">Color format</param>
/// <returns>Host compatible format enum value</returns>
public static FormatInfo Convert(this ColorFormat format)
{
return format switch
{
ColorFormat.R32G32B32A32Float => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4),
ColorFormat.R32G32B32A32Sint => new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16, 4),
ColorFormat.R32G32B32A32Uint => new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16, 4),
ColorFormat.R32G32B32X32Float => new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16, 4),
ColorFormat.R32G32B32X32Sint => new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16, 4),
ColorFormat.R32G32B32X32Uint => new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16, 4),
ColorFormat.R16G16B16X16Unorm => new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8, 4),
ColorFormat.R16G16B16X16Snorm => new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8, 4),
ColorFormat.R16G16B16X16Sint => new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8, 4),
ColorFormat.R16G16B16X16Uint => new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8, 4),
ColorFormat.R16G16B16A16Float => new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4),
ColorFormat.R32G32Float => new FormatInfo(Format.R32G32Float, 1, 1, 8, 2),
ColorFormat.R32G32Sint => new FormatInfo(Format.R32G32Sint, 1, 1, 8, 2),
ColorFormat.R32G32Uint => new FormatInfo(Format.R32G32Uint, 1, 1, 8, 2),
ColorFormat.R16G16B16X16Float => new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8, 4),
ColorFormat.B8G8R8A8Unorm => new FormatInfo(Format.B8G8R8A8Unorm, 1, 1, 4, 4),
ColorFormat.B8G8R8A8Srgb => new FormatInfo(Format.B8G8R8A8Srgb, 1, 1, 4, 4),
ColorFormat.R10G10B10A2Unorm => new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4, 4),
ColorFormat.R10G10B10A2Uint => new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4, 4),
ColorFormat.R8G8B8A8Unorm => new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4),
ColorFormat.R8G8B8A8Srgb => new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4),
ColorFormat.R8G8B8X8Snorm => new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4, 4),
ColorFormat.R8G8B8X8Sint => new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4, 4),
ColorFormat.R8G8B8X8Uint => new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4, 4),
ColorFormat.R16G16Unorm => new FormatInfo(Format.R16G16Unorm, 1, 1, 4, 2),
ColorFormat.R16G16Snorm => new FormatInfo(Format.R16G16Snorm, 1, 1, 4, 2),
ColorFormat.R16G16Sint => new FormatInfo(Format.R16G16Sint, 1, 1, 4, 2),
ColorFormat.R16G16Uint => new FormatInfo(Format.R16G16Uint, 1, 1, 4, 2),
ColorFormat.R16G16Float => new FormatInfo(Format.R16G16Float, 1, 1, 4, 2),
ColorFormat.R11G11B10Float => new FormatInfo(Format.R11G11B10Float, 1, 1, 4, 3),
ColorFormat.R32Sint => new FormatInfo(Format.R32Sint, 1, 1, 4, 1),
ColorFormat.R32Uint => new FormatInfo(Format.R32Uint, 1, 1, 4, 1),
ColorFormat.R32Float => new FormatInfo(Format.R32Float, 1, 1, 4, 1),
ColorFormat.B8G8R8X8Unorm => new FormatInfo(Format.B8G8R8A8Unorm, 1, 1, 4, 4),
ColorFormat.B8G8R8X8Srgb => new FormatInfo(Format.B8G8R8A8Srgb, 1, 1, 4, 4),
ColorFormat.B5G6R5Unorm => new FormatInfo(Format.B5G6R5Unorm, 1, 1, 2, 3),
ColorFormat.B5G5R5A1Unorm => new FormatInfo(Format.B5G5R5A1Unorm, 1, 1, 2, 4),
ColorFormat.R8G8Unorm => new FormatInfo(Format.R8G8Unorm, 1, 1, 2, 2),
ColorFormat.R8G8Snorm => new FormatInfo(Format.R8G8Snorm, 1, 1, 2, 2),
ColorFormat.R8G8Sint => new FormatInfo(Format.R8G8Sint, 1, 1, 2, 2),
ColorFormat.R8G8Uint => new FormatInfo(Format.R8G8Uint, 1, 1, 2, 2),
ColorFormat.R16Unorm => new FormatInfo(Format.R16Unorm, 1, 1, 2, 1),
ColorFormat.R16Snorm => new FormatInfo(Format.R16Snorm, 1, 1, 2, 1),
ColorFormat.R16Sint => new FormatInfo(Format.R16Sint, 1, 1, 2, 1),
ColorFormat.R16Uint => new FormatInfo(Format.R16Uint, 1, 1, 2, 1),
ColorFormat.R16Float => new FormatInfo(Format.R16Float, 1, 1, 2, 1),
ColorFormat.R8Unorm => new FormatInfo(Format.R8Unorm, 1, 1, 1, 1),
ColorFormat.R8Snorm => new FormatInfo(Format.R8Snorm, 1, 1, 1, 1),
ColorFormat.R8Sint => new FormatInfo(Format.R8Sint, 1, 1, 1, 1),
ColorFormat.R8Uint => new FormatInfo(Format.R8Uint, 1, 1, 1, 1),
ColorFormat.B5G5R5X1Unorm => new FormatInfo(Format.B5G5R5X1Unorm, 1, 1, 2, 4),
ColorFormat.R8G8B8X8Unorm => new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4),
ColorFormat.R8G8B8X8Srgb => new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4),
_ => FormatInfo.Default
};
}
}
}

View file

@ -0,0 +1,22 @@
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Split GPU virtual address.
/// </summary>
struct GpuVa
{
#pragma warning disable CS0649
public uint High;
public uint Low;
#pragma warning restore CS0649
/// <summary>
/// Packs the split address into a 64-bits address value.
/// </summary>
/// <returns>The 64-bits address value</returns>
public ulong Pack()
{
return Low | ((ulong)High << 32);
}
}
}

View file

@ -0,0 +1,37 @@
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Memory layout parameters, for block linear textures.
/// </summary>
struct MemoryLayout
{
#pragma warning disable CS0649
public uint Packed;
#pragma warning restore CS0649
public int UnpackGobBlocksInX()
{
return 1 << (int)(Packed & 0xf);
}
public int UnpackGobBlocksInY()
{
return 1 << (int)((Packed >> 4) & 0xf);
}
public int UnpackGobBlocksInZ()
{
return 1 << (int)((Packed >> 8) & 0xf);
}
public bool UnpackIsLinear()
{
return (Packed & 0x1000) != 0;
}
public bool UnpackIsTarget3D()
{
return (Packed & 0x10000) != 0;
}
}
}

View file

@ -0,0 +1,99 @@
using Ryujinx.Graphics.GAL;
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Draw primitive type.
/// </summary>
enum PrimitiveType
{
Points,
Lines,
LineLoop,
LineStrip,
Triangles,
TriangleStrip,
TriangleFan,
Quads,
QuadStrip,
Polygon,
LinesAdjacency,
LineStripAdjacency,
TrianglesAdjacency,
TriangleStripAdjacency,
Patches
}
/// <summary>
/// Alternative primitive type that might override <see cref="PrimitiveType"/>.
/// </summary>
enum PrimitiveTypeOverride
{
Points = 1,
Lines = 2,
LineStrip = 3,
Triangles = 4,
TriangleStrip = 5,
TriangleFan = 0x1015,
LinesAdjacency = 10,
LineStripAdjacency = 11,
TrianglesAdjacency = 12,
TriangleStripAdjacency = 13,
Patches = 14
}
static class PrimitiveTypeConverter
{
/// <summary>
/// Converts the primitive type into something that can be used with the host API.
/// </summary>
/// <param name="type">The primitive type to convert</param>
/// <returns>A host compatible enum value</returns>
public static PrimitiveTopology Convert(this PrimitiveType type)
{
return type switch
{
PrimitiveType.Points => PrimitiveTopology.Points,
PrimitiveType.Lines => PrimitiveTopology.Lines,
PrimitiveType.LineLoop => PrimitiveTopology.LineLoop,
PrimitiveType.LineStrip => PrimitiveTopology.LineStrip,
PrimitiveType.Triangles => PrimitiveTopology.Triangles,
PrimitiveType.TriangleStrip => PrimitiveTopology.TriangleStrip,
PrimitiveType.TriangleFan => PrimitiveTopology.TriangleFan,
PrimitiveType.Quads => PrimitiveTopology.Quads,
PrimitiveType.QuadStrip => PrimitiveTopology.QuadStrip,
PrimitiveType.Polygon => PrimitiveTopology.Polygon,
PrimitiveType.LinesAdjacency => PrimitiveTopology.LinesAdjacency,
PrimitiveType.LineStripAdjacency => PrimitiveTopology.LineStripAdjacency,
PrimitiveType.TrianglesAdjacency => PrimitiveTopology.TrianglesAdjacency,
PrimitiveType.TriangleStripAdjacency => PrimitiveTopology.TriangleStripAdjacency,
PrimitiveType.Patches => PrimitiveTopology.Patches,
_ => PrimitiveTopology.Triangles
};
}
/// <summary>
/// Converts the primitive type into something that can be used with the host API.
/// </summary>
/// <param name="type">The primitive type to convert</param>
/// <returns>A host compatible enum value</returns>
public static PrimitiveTopology Convert(this PrimitiveTypeOverride type)
{
return type switch
{
PrimitiveTypeOverride.Points => PrimitiveTopology.Points,
PrimitiveTypeOverride.Lines => PrimitiveTopology.Lines,
PrimitiveTypeOverride.LineStrip => PrimitiveTopology.LineStrip,
PrimitiveTypeOverride.Triangles => PrimitiveTopology.Triangles,
PrimitiveTypeOverride.TriangleStrip => PrimitiveTopology.TriangleStrip,
PrimitiveTypeOverride.TriangleFan => PrimitiveTopology.TriangleFan,
PrimitiveTypeOverride.LinesAdjacency => PrimitiveTopology.LinesAdjacency,
PrimitiveTypeOverride.LineStripAdjacency => PrimitiveTopology.LineStripAdjacency,
PrimitiveTypeOverride.TrianglesAdjacency => PrimitiveTopology.TrianglesAdjacency,
PrimitiveTypeOverride.TriangleStripAdjacency => PrimitiveTopology.TriangleStripAdjacency,
PrimitiveTypeOverride.Patches => PrimitiveTopology.Patches,
_ => PrimitiveTopology.Triangles
};
}
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Sampler pool indexing mode.
/// </summary>
enum SamplerIndex
{
Independently = 0,
ViaHeaderIndex = 1
}
}

View file

@ -0,0 +1,20 @@
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Storage buffer address and size information.
/// </summary>
struct SbDescriptor
{
#pragma warning disable CS0649
public uint AddressLow;
public uint AddressHigh;
public int Size;
public int Padding;
#pragma warning restore CS0649
public ulong PackAddress()
{
return AddressLow | ((ulong)AddressHigh << 32);
}
}
}

View file

@ -0,0 +1,42 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
namespace Ryujinx.Graphics.Gpu.Engine.Types
{
/// <summary>
/// Depth-stencil texture format.
/// </summary>
enum ZetaFormat
{
D32Float = 0xa,
D16Unorm = 0x13,
D24UnormS8Uint = 0x14,
D24Unorm = 0x15,
S8UintD24Unorm = 0x16,
S8Uint = 0x17,
D32FloatS8Uint = 0x19
}
static class ZetaFormatConverter
{
/// <summary>
/// Converts the depth-stencil texture format to a host compatible format.
/// </summary>
/// <param name="format">Depth-stencil format</param>
/// <returns>Host compatible format enum value</returns>
public static FormatInfo Convert(this ZetaFormat format)
{
return format switch
{
ZetaFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
ZetaFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
ZetaFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
ZetaFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 1),
ZetaFormat.S8UintD24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
ZetaFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
_ => FormatInfo.Default
};
}
}
}