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:
parent
b5190f1681
commit
40b21cc3c4
111 changed files with 5262 additions and 4020 deletions
|
@ -1,10 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Device
|
|
||||||
{
|
|
||||||
public enum AccessControl
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
ReadOnly = 1 << 0,
|
|
||||||
WriteOnly = 1 << 1,
|
|
||||||
ReadWrite = ReadOnly | WriteOnly
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Device
|
namespace Ryujinx.Graphics.Device
|
||||||
{
|
{
|
||||||
|
@ -14,28 +12,22 @@ namespace Ryujinx.Graphics.Device
|
||||||
|
|
||||||
public TState State;
|
public TState State;
|
||||||
|
|
||||||
private readonly BitArray _readableRegisters;
|
private uint Size => (uint)(Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize;
|
||||||
private readonly BitArray _writableRegisters;
|
|
||||||
|
|
||||||
private readonly Dictionary<int, Func<int>> _readCallbacks;
|
private readonly Func<int>[] _readCallbacks;
|
||||||
private readonly Dictionary<int, Action<int>> _writeCallbacks;
|
private readonly Action<int>[] _writeCallbacks;
|
||||||
|
|
||||||
private readonly Dictionary<int, string> _fieldNamesForDebug;
|
private readonly Dictionary<uint, string> _fieldNamesForDebug;
|
||||||
private readonly Action<string> _debugLogCallback;
|
private readonly Action<string> _debugLogCallback;
|
||||||
|
|
||||||
public DeviceState(IReadOnlyDictionary<string, RwCallback> callbacks = null, Action<string> debugLogCallback = null)
|
public DeviceState(IReadOnlyDictionary<string, RwCallback> callbacks = null, Action<string> debugLogCallback = null)
|
||||||
{
|
{
|
||||||
int size = (Unsafe.SizeOf<TState>() + RegisterSize - 1) / RegisterSize;
|
_readCallbacks = new Func<int>[Size];
|
||||||
|
_writeCallbacks = new Action<int>[Size];
|
||||||
_readableRegisters = new BitArray(size);
|
|
||||||
_writableRegisters = new BitArray(size);
|
|
||||||
|
|
||||||
_readCallbacks = new Dictionary<int, Func<int>>();
|
|
||||||
_writeCallbacks = new Dictionary<int, Action<int>>();
|
|
||||||
|
|
||||||
if (debugLogCallback != null)
|
if (debugLogCallback != null)
|
||||||
{
|
{
|
||||||
_fieldNamesForDebug = new Dictionary<int, string>();
|
_fieldNamesForDebug = new Dictionary<uint, string>();
|
||||||
_debugLogCallback = debugLogCallback;
|
_debugLogCallback = debugLogCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,32 +37,30 @@ namespace Ryujinx.Graphics.Device
|
||||||
for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
|
for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
|
||||||
{
|
{
|
||||||
var field = fields[fieldIndex];
|
var field = fields[fieldIndex];
|
||||||
var regAttr = field.GetCustomAttributes<RegisterAttribute>(false).FirstOrDefault();
|
|
||||||
|
|
||||||
int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
|
int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
|
||||||
|
|
||||||
for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
|
for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
|
||||||
{
|
{
|
||||||
_readableRegisters[(offset + i) / RegisterSize] = regAttr?.AccessControl.HasFlag(AccessControl.ReadOnly) ?? true;
|
int index = (offset + i) / RegisterSize;
|
||||||
_writableRegisters[(offset + i) / RegisterSize] = regAttr?.AccessControl.HasFlag(AccessControl.WriteOnly) ?? true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callbacks != null && callbacks.TryGetValue(field.Name, out var cb))
|
if (callbacks != null && callbacks.TryGetValue(field.Name, out var cb))
|
||||||
{
|
{
|
||||||
if (cb.Read != null)
|
if (cb.Read != null)
|
||||||
{
|
{
|
||||||
_readCallbacks.Add(offset, cb.Read);
|
_readCallbacks[index] = cb.Read;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cb.Write != null)
|
if (cb.Write != null)
|
||||||
{
|
{
|
||||||
_writeCallbacks.Add(offset, cb.Write);
|
_writeCallbacks[index] = cb.Write;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debugLogCallback != null)
|
if (debugLogCallback != null)
|
||||||
{
|
{
|
||||||
_fieldNamesForDebug.Add(offset, field.Name);
|
_fieldNamesForDebug.Add((uint)offset, field.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += sizeOfField;
|
offset += sizeOfField;
|
||||||
|
@ -79,48 +69,71 @@ namespace Ryujinx.Graphics.Device
|
||||||
Debug.Assert(offset == Unsafe.SizeOf<TState>());
|
Debug.Assert(offset == Unsafe.SizeOf<TState>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual int Read(int offset)
|
public int Read(int offset)
|
||||||
{
|
{
|
||||||
if (Check(offset) && _readableRegisters[offset / RegisterSize])
|
uint index = (uint)offset / RegisterSize;
|
||||||
{
|
|
||||||
int alignedOffset = Align(offset);
|
|
||||||
|
|
||||||
if (_readCallbacks.TryGetValue(alignedOffset, out Func<int> read))
|
if (index < Size)
|
||||||
{
|
{
|
||||||
return read();
|
uint alignedOffset = index * RegisterSize;
|
||||||
|
|
||||||
|
var readCallback = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_readCallbacks), (IntPtr)index);
|
||||||
|
if (readCallback != null)
|
||||||
|
{
|
||||||
|
return readCallback();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return GetRef<int>(alignedOffset);
|
return GetRefUnchecked<int>(alignedOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Write(int offset, int data)
|
public void Write(int offset, int data)
|
||||||
{
|
{
|
||||||
if (Check(offset) && _writableRegisters[offset / RegisterSize])
|
uint index = (uint)offset / RegisterSize;
|
||||||
{
|
|
||||||
int alignedOffset = Align(offset);
|
|
||||||
|
|
||||||
|
if (index < Size)
|
||||||
|
{
|
||||||
|
uint alignedOffset = index * RegisterSize;
|
||||||
|
DebugWrite(alignedOffset, data);
|
||||||
|
|
||||||
|
GetRefIntAlignedUncheck(index) = data;
|
||||||
|
|
||||||
|
Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteWithRedundancyCheck(int offset, int data, out bool changed)
|
||||||
|
{
|
||||||
|
uint index = (uint)offset / RegisterSize;
|
||||||
|
|
||||||
|
if (index < Size)
|
||||||
|
{
|
||||||
|
uint alignedOffset = index * RegisterSize;
|
||||||
|
DebugWrite(alignedOffset, data);
|
||||||
|
|
||||||
|
ref var storage = ref GetRefIntAlignedUncheck(index);
|
||||||
|
changed = storage != data;
|
||||||
|
storage = data;
|
||||||
|
|
||||||
|
Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
changed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("DEBUG")]
|
||||||
|
private void DebugWrite(uint alignedOffset, int data)
|
||||||
|
{
|
||||||
if (_fieldNamesForDebug != null && _fieldNamesForDebug.TryGetValue(alignedOffset, out string fieldName))
|
if (_fieldNamesForDebug != null && _fieldNamesForDebug.TryGetValue(alignedOffset, out string fieldName))
|
||||||
{
|
{
|
||||||
_debugLogCallback($"{typeof(TState).Name}.{fieldName} = 0x{data:X}");
|
_debugLogCallback($"{typeof(TState).Name}.{fieldName} = 0x{data:X}");
|
||||||
}
|
}
|
||||||
|
|
||||||
GetRef<int>(alignedOffset) = data;
|
|
||||||
|
|
||||||
if (_writeCallbacks.TryGetValue(alignedOffset, out Action<int> write))
|
|
||||||
{
|
|
||||||
write(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool Check(int offset)
|
|
||||||
{
|
|
||||||
return (uint)Align(offset) < Unsafe.SizeOf<TState>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref T GetRef<T>(int offset) where T : unmanaged
|
public ref T GetRef<T>(int offset) where T : unmanaged
|
||||||
|
@ -130,12 +143,19 @@ namespace Ryujinx.Graphics.Device
|
||||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ref GetRefUnchecked<T>((uint)offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private ref T GetRefUnchecked<T>(uint offset) where T : unmanaged
|
||||||
|
{
|
||||||
return ref Unsafe.As<TState, T>(ref Unsafe.AddByteOffset(ref State, (IntPtr)offset));
|
return ref Unsafe.As<TState, T>(ref Unsafe.AddByteOffset(ref State, (IntPtr)offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Align(int offset)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private ref int GetRefIntAlignedUncheck(ulong index)
|
||||||
{
|
{
|
||||||
return offset & ~(RegisterSize - 1);
|
return ref Unsafe.Add(ref Unsafe.As<TState, int>(ref State), (IntPtr)index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Device
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
|
|
||||||
public sealed class RegisterAttribute : Attribute
|
|
||||||
{
|
|
||||||
public AccessControl AccessControl { get; }
|
|
||||||
|
|
||||||
public RegisterAttribute(AccessControl ac)
|
|
||||||
{
|
|
||||||
AccessControl = ac;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ using System.Reflection;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Device
|
namespace Ryujinx.Graphics.Device
|
||||||
{
|
{
|
||||||
static class SizeCalculator
|
public static class SizeCalculator
|
||||||
{
|
{
|
||||||
public static int SizeOf(Type type)
|
public static int SizeOf(Type type)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,13 +3,13 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GPU engine class ID.
|
/// GPU engine class ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
enum ClassId
|
public enum ClassId
|
||||||
{
|
{
|
||||||
Engine2D = 0x902d,
|
Twod = 0x902d,
|
||||||
Engine3D = 0xb197,
|
Threed = 0xb197,
|
||||||
EngineCompute = 0xb1c0,
|
Compute = 0xb1c0,
|
||||||
EngineInline2Memory = 0xa140,
|
InlineToMemory = 0xa140,
|
||||||
EngineDma = 0xb0b5,
|
Dma = 0xb0b5,
|
||||||
EngineGpfifo = 0xb06f
|
GPFifo = 0xb06f
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
using Ryujinx.Graphics.Device;
|
using Ryujinx.Graphics.Device;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
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.Image;
|
||||||
using Ryujinx.Graphics.Gpu.Shader;
|
using Ryujinx.Graphics.Gpu.Shader;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -14,27 +15,34 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a compute engine class.
|
/// Represents a compute engine class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class ComputeClass : InlineToMemoryClass, IDeviceState
|
class ComputeClass : IDeviceState
|
||||||
{
|
{
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
private readonly GpuChannel _channel;
|
private readonly GpuChannel _channel;
|
||||||
|
private readonly ThreedClass _3dEngine;
|
||||||
private readonly DeviceState<ComputeClassState> _state;
|
private readonly DeviceState<ComputeClassState> _state;
|
||||||
|
|
||||||
|
private readonly InlineToMemoryClass _i2mClass;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the compute engine class.
|
/// Creates a new instance of the compute engine class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context</param>
|
/// <param name="context">GPU context</param>
|
||||||
/// <param name="channel">GPU channel</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;
|
_context = context;
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
|
_3dEngine = threedEngine;
|
||||||
_state = new DeviceState<ComputeClassState>(new Dictionary<string, RwCallback>
|
_state = new DeviceState<ComputeClassState>(new Dictionary<string, RwCallback>
|
||||||
{
|
{
|
||||||
{ nameof(ComputeClassState.LaunchDma), new RwCallback(LaunchDma, null) },
|
{ nameof(ComputeClassState.LaunchDma), new RwCallback(LaunchDma, null) },
|
||||||
{ nameof(ComputeClassState.LoadInlineData), new RwCallback(LoadInlineData, null) },
|
{ nameof(ComputeClassState.LoadInlineData), new RwCallback(LoadInlineData, null) },
|
||||||
{ nameof(ComputeClassState.SendSignalingPcasB), new RwCallback(SendSignalingPcasB, null) }
|
{ nameof(ComputeClassState.SendSignalingPcasB), new RwCallback(SendSignalingPcasB, null) }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_i2mClass = new InlineToMemoryClass(context, channel, initializeState: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -42,22 +50,31 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="offset">Register byte offset</param>
|
/// <param name="offset">Register byte offset</param>
|
||||||
/// <returns>Data at the specified offset</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Writes data to the class registers.
|
/// Writes data to the class registers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="offset">Register byte offset</param>
|
/// <param name="offset">Register byte offset</param>
|
||||||
/// <param name="data">Data to be written</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>
|
/// <summary>
|
||||||
/// Launches the Inline-to-Memory DMA copy operation.
|
/// Launches the Inline-to-Memory DMA copy operation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="argument">Method call argument</param>
|
/// <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>
|
/// <summary>
|
||||||
|
@ -68,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||||
{
|
{
|
||||||
var memoryManager = _channel.MemoryManager;
|
var memoryManager = _channel.MemoryManager;
|
||||||
|
|
||||||
_context.Methods.FlushUboDirty(memoryManager);
|
_3dEngine.FlushUboDirty();
|
||||||
|
|
||||||
uint qmdAddress = _state.State.SendPcasA;
|
uint qmdAddress = _state.State.SendPcasA;
|
||||||
|
|
||||||
|
@ -102,7 +119,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||||
texturePoolGpuVa,
|
texturePoolGpuVa,
|
||||||
_state.State.SetTexHeaderPoolCMaximumIndex,
|
_state.State.SetTexHeaderPoolCMaximumIndex,
|
||||||
_state.State.SetBindlessTextureConstantBufferSlotSelect,
|
_state.State.SetBindlessTextureConstantBufferSlotSelect,
|
||||||
false);
|
false,
|
||||||
|
PrimitiveTopology.Points);
|
||||||
|
|
||||||
ShaderBundle cs = memoryManager.Physical.ShaderCache.GetComputeShader(
|
ShaderBundle cs = memoryManager.Physical.ShaderCache.GetComputeShader(
|
||||||
_channel,
|
_channel,
|
||||||
|
@ -207,7 +225,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||||
|
|
||||||
_context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth);
|
_context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth);
|
||||||
|
|
||||||
_context.Methods.ForceShaderUpdate();
|
_3dEngine.ForceShaderUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Conditional rendering enable.
|
||||||
|
/// </summary>
|
||||||
enum ConditionalRenderEnabled
|
enum ConditionalRenderEnabled
|
||||||
{
|
{
|
||||||
False,
|
False,
|
||||||
|
|
95
Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs
Normal file
95
Ryujinx.Graphics.Gpu/Engine/DeviceStateWithShadow.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Graphics.Device;
|
using Ryujinx.Graphics.Device;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
||||||
{
|
{
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
private readonly GpuChannel _channel;
|
private readonly GpuChannel _channel;
|
||||||
|
private readonly ThreedClass _3dEngine;
|
||||||
private readonly DeviceState<DmaClassState> _state;
|
private readonly DeviceState<DmaClassState> _state;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -35,10 +36,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context</param>
|
/// <param name="context">GPU context</param>
|
||||||
/// <param name="channel">GPU channel</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;
|
_context = context;
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
|
_3dEngine = threedEngine;
|
||||||
_state = new DeviceState<DmaClassState>(new Dictionary<string, RwCallback>
|
_state = new DeviceState<DmaClassState>(new Dictionary<string, RwCallback>
|
||||||
{
|
{
|
||||||
{ nameof(DmaClassState.LaunchDma), new RwCallback(LaunchDma, null) }
|
{ 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="xCount">Number of pixels to be copied</param>
|
||||||
/// <param name="yCount">Number of lines to be copied</param>
|
/// <param name="yCount">Number of lines to be copied</param>
|
||||||
/// <returns></returns>
|
/// <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)
|
if (linear)
|
||||||
{
|
{
|
||||||
|
@ -116,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
||||||
int xCount = (int)_state.State.LineLengthIn;
|
int xCount = (int)_state.State.LineLengthIn;
|
||||||
int yCount = (int)_state.State.LineCount;
|
int yCount = (int)_state.State.LineCount;
|
||||||
|
|
||||||
_context.Methods.FlushUboDirty(memoryManager);
|
_3dEngine.FlushUboDirty();
|
||||||
|
|
||||||
if (copy2D)
|
if (copy2D)
|
||||||
{
|
{
|
||||||
|
@ -125,8 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
||||||
int srcBpp = remap ? ((int)_state.State.SetRemapComponentsNumSrcComponents + 1) * componentSize : 1;
|
int srcBpp = remap ? ((int)_state.State.SetRemapComponentsNumSrcComponents + 1) * componentSize : 1;
|
||||||
int dstBpp = remap ? ((int)_state.State.SetRemapComponentsNumDstComponents + 1) * componentSize : 1;
|
int dstBpp = remap ? ((int)_state.State.SetRemapComponentsNumDstComponents + 1) * componentSize : 1;
|
||||||
|
|
||||||
var dst = Unsafe.As<uint, CopyBufferTexture>(ref _state.State.SetDstBlockSize);
|
var dst = Unsafe.As<uint, DmaTexture>(ref _state.State.SetDstBlockSize);
|
||||||
var src = Unsafe.As<uint, CopyBufferTexture>(ref _state.State.SetSrcBlockSize);
|
var src = Unsafe.As<uint, DmaTexture>(ref _state.State.SetSrcBlockSize);
|
||||||
|
|
||||||
int srcStride = (int)_state.State.PitchIn;
|
int srcStride = (int)_state.State.PitchIn;
|
||||||
int dstStride = (int)_state.State.PitchOut;
|
int dstStride = (int)_state.State.PitchOut;
|
||||||
|
|
20
Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs
Normal file
20
Ryujinx.Graphics.Gpu/Engine/Dma/DmaTexture.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
using Ryujinx.Graphics.Device;
|
using Ryujinx.Graphics.Device;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.MME;
|
using Ryujinx.Graphics.Gpu.Engine.MME;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -150,7 +149,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||||
/// <param name="argument">Method call argument</param>
|
/// <param name="argument">Method call argument</param>
|
||||||
public void WaitForIdle(int argument)
|
public void WaitForIdle(int argument)
|
||||||
{
|
{
|
||||||
_context.Methods.PerformDeferredDraws();
|
_parent.PerformDeferredDraws();
|
||||||
_context.Renderer.Pipeline.Barrier();
|
_context.Renderer.Pipeline.Barrier();
|
||||||
|
|
||||||
_context.CreateHostSyncIfNeeded();
|
_context.CreateHostSyncIfNeeded();
|
||||||
|
@ -189,7 +188,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||||
/// <param name="argument">Method call argument</param>
|
/// <param name="argument">Method call argument</param>
|
||||||
public void SetMmeShadowRamControl(int argument)
|
public void SetMmeShadowRamControl(int argument)
|
||||||
{
|
{
|
||||||
_parent.SetShadowRamControl((ShadowRamControl)argument);
|
_parent.SetShadowRamControl(argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -217,7 +216,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">Index of the macro</param>
|
/// <param name="index">Index of the macro</param>
|
||||||
/// <param name="state">Current GPU state</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);
|
_macros[index].Execute(_macroCode, state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Compute;
|
using Ryujinx.Graphics.Gpu.Engine.Compute;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Dma;
|
using Ryujinx.Graphics.Gpu.Engine.Dma;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Twod;
|
using Ryujinx.Graphics.Gpu.Engine.Twod;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
@ -18,9 +18,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||||
private const int MacrosCount = 0x80;
|
private const int MacrosCount = 0x80;
|
||||||
private const int MacroIndexMask = MacrosCount - 1;
|
private const int MacroIndexMask = MacrosCount - 1;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private const int UniformBufferUpdateDataMethodOffset = 0x8e4;
|
||||||
|
|
||||||
private readonly GpuChannel _channel;
|
private readonly GpuChannel _channel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Channel memory manager.
|
||||||
|
/// </summary>
|
||||||
public MemoryManager MemoryManager => _channel.MemoryManager;
|
public MemoryManager MemoryManager => _channel.MemoryManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -37,8 +41,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||||
|
|
||||||
private DmaState _state;
|
private DmaState _state;
|
||||||
|
|
||||||
private readonly GpuState[] _subChannels;
|
private readonly ThreedClass _3dClass;
|
||||||
private readonly IDeviceState[] _subChannels2;
|
private readonly ComputeClass _computeClass;
|
||||||
|
private readonly InlineToMemoryClass _i2mClass;
|
||||||
|
private readonly TwodClass _2dClass;
|
||||||
|
private readonly DmaClass _dmaClass;
|
||||||
|
|
||||||
private readonly GPFifoClass _fifoClass;
|
private readonly GPFifoClass _fifoClass;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -48,29 +56,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||||
/// <param name="channel">Channel that the GPFIFO processor belongs to</param>
|
/// <param name="channel">Channel that the GPFIFO processor belongs to</param>
|
||||||
public GPFifoProcessor(GpuContext context, GpuChannel channel)
|
public GPFifoProcessor(GpuContext context, GpuChannel channel)
|
||||||
{
|
{
|
||||||
_context = context;
|
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
|
|
||||||
_fifoClass = new GPFifoClass(context, this);
|
_fifoClass = new GPFifoClass(context, this);
|
||||||
_subChannels = new GpuState[8];
|
_3dClass = new ThreedClass(context, channel);
|
||||||
_subChannels2 = new IDeviceState[8]
|
_computeClass = new ComputeClass(context, channel, _3dClass);
|
||||||
{
|
_i2mClass = new InlineToMemoryClass(context, channel);
|
||||||
null,
|
_2dClass = new TwodClass(channel);
|
||||||
new ComputeClass(context, channel),
|
_dmaClass = new DmaClass(context, channel, _3dClass);
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -85,7 +78,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||||
|
|
||||||
if (_state.MethodCount != 0)
|
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)
|
if (!_state.NonIncrementing)
|
||||||
{
|
{
|
||||||
|
@ -121,13 +114,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||||
_state.NonIncrementing = meth.SecOp == SecOp.NonIncMethod;
|
_state.NonIncrementing = meth.SecOp == SecOp.NonIncMethod;
|
||||||
break;
|
break;
|
||||||
case SecOp.ImmdDataMethod:
|
case SecOp.ImmdDataMethod:
|
||||||
Send(new MethodParams(meth.MethodAddress, meth.ImmdData, meth.MethodSubchannel, 1));
|
Send(meth.MethodAddress, meth.ImmdData, meth.MethodSubchannel, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Methods.FlushUboDirty(MemoryManager);
|
_3dClass.FlushUboDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -145,11 +138,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||||
|
|
||||||
if (meth.MethodCount < availableCount &&
|
if (meth.MethodCount < availableCount &&
|
||||||
meth.SecOp == SecOp.NonIncMethod &&
|
meth.SecOp == SecOp.NonIncMethod &&
|
||||||
meth.MethodAddress == (int)MethodOffset.UniformBufferUpdateData)
|
meth.MethodAddress == UniformBufferUpdateDataMethodOffset)
|
||||||
{
|
{
|
||||||
GpuState state = _subChannels[meth.MethodSubchannel];
|
_3dClass.ConstantBufferUpdate(commandBuffer.Slice(offset + 1, meth.MethodCount));
|
||||||
|
|
||||||
_context.Methods.UniformBufferUpdate(state, commandBuffer.Slice(offset + 1, meth.MethodCount));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -161,55 +152,105 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||||
/// Sends a uncompressed method for processing by the graphics pipeline.
|
/// Sends a uncompressed method for processing by the graphics pipeline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="meth">Method to be processed</param>
|
/// <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]);
|
switch (subChannel)
|
||||||
}
|
|
||||||
else if (meth.Method < 0x60)
|
|
||||||
{
|
{
|
||||||
// TODO: check if macros are shared between subchannels or not. For now let's assume they are.
|
case 0:
|
||||||
_fifoClass.Write(meth.Method * 4, meth.Argument);
|
_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 if (meth.Method < 0xe00)
|
|
||||||
{
|
|
||||||
_subChannels[meth.SubChannel].CallMethod(meth);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int macroIndex = (meth.Method >> 1) & MacroIndexMask;
|
IDeviceState state = subChannel switch
|
||||||
|
|
||||||
if ((meth.Method & 1) != 0)
|
|
||||||
{
|
{
|
||||||
_fifoClass.MmePushArgument(macroIndex, meth.Argument);
|
0 => _3dClass,
|
||||||
|
3 => _2dClass,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (state != null)
|
||||||
|
{
|
||||||
|
int macroIndex = (offset >> 1) & MacroIndexMask;
|
||||||
|
|
||||||
|
if ((offset & 1) != 0)
|
||||||
|
{
|
||||||
|
_fifoClass.MmePushArgument(macroIndex, argument);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_fifoClass.MmeStart(macroIndex, meth.Argument);
|
_fifoClass.MmeStart(macroIndex, argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meth.IsLastCall)
|
if (isLastCall)
|
||||||
{
|
{
|
||||||
_fifoClass.CallMme(macroIndex, _subChannels[meth.SubChannel]);
|
_fifoClass.CallMme(macroIndex, state);
|
||||||
|
|
||||||
_context.Methods.PerformDeferredDraws();
|
_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>
|
/// <summary>
|
||||||
/// Sets the shadow ram control value of all sub-channels.
|
/// Sets the shadow ram control value of all sub-channels.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="control">New shadow ram control value</param>
|
/// <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++)
|
_3dClass.SetShadowRamControl(control);
|
||||||
{
|
|
||||||
_subChannels[i].ShadowRamControl = control;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -218,10 +259,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ForceAllDirty()
|
public void ForceAllDirty()
|
||||||
{
|
{
|
||||||
for (int index = 0; index < _subChannels.Length; index++)
|
_3dClass.ForceStateDirty();
|
||||||
|
_channel.BufferManager.Rebind();
|
||||||
|
_channel.TextureManager.Rebind();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform any deferred draws.
|
||||||
|
/// </summary>
|
||||||
|
public void PerformDeferredDraws()
|
||||||
{
|
{
|
||||||
_subChannels[index].ForceAllDirty();
|
_3dClass.PerformDeferredDraws();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -41,7 +41,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
|
||||||
/// <param name="context">GPU context</param>
|
/// <param name="context">GPU context</param>
|
||||||
/// <param name="channel">GPU channel</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>
|
/// <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;
|
_context = context;
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
|
@ -70,20 +70,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="offset">Register byte offset</param>
|
/// <param name="offset">Register byte offset</param>
|
||||||
/// <returns>Data at the specified offset</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Writes data to the class registers.
|
/// Writes data to the class registers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="offset">Register byte offset</param>
|
/// <param name="offset">Register byte offset</param>
|
||||||
/// <param name="data">Data to be written</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>
|
/// <summary>
|
||||||
/// Launches Inline-to-Memory engine DMA copy.
|
/// Launches Inline-to-Memory engine DMA copy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="argument">Method call argument</param>
|
/// <param name="argument">Method call argument</param>
|
||||||
protected virtual void LaunchDma(int argument)
|
private void LaunchDma(int argument)
|
||||||
{
|
{
|
||||||
LaunchDma(ref _state.State, argument);
|
LaunchDma(ref _state.State, argument);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">Current class state</param>
|
/// <param name="state">Current class state</param>
|
||||||
/// <param name="argument">Method call argument</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;
|
_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.
|
/// Pushes a word of data to the Inline-to-Memory engine.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="argument">Method call argument</param>
|
/// <param name="argument">Method call argument</param>
|
||||||
protected void LoadInlineData(int argument)
|
public void LoadInlineData(int argument)
|
||||||
{
|
{
|
||||||
if (!_finished)
|
if (!_finished)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Device;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Arguments FIFO.
|
/// Arguments FIFO.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Queue<int> Fifo { get; }
|
Queue<int> Fifo { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Should execute the GPU Macro code being passed.
|
/// 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="code">Code to be executed</param>
|
||||||
/// <param name="state">GPU state at the time of the call</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>
|
/// <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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Device;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine.MME
|
namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||||
|
@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="code">Program code</param>
|
/// <param name="code">Program code</param>
|
||||||
/// <param name="state">Current GPU state</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)
|
if (_executionPending)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Device;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
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="code">Code of the program to execute</param>
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="state">Current GPU state</param>
|
||||||
/// <param name="arg0">Optional argument passed to the program, 0 if not used</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();
|
Reset();
|
||||||
|
|
||||||
|
@ -55,7 +55,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||||
|
|
||||||
FetchOpCode(code);
|
FetchOpCode(code);
|
||||||
|
|
||||||
while (Step(code, state)) ;
|
while (Step(code, state))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
// Due to the delay slot, we still need to execute
|
// Due to the delay slot, we still need to execute
|
||||||
// one more instruction before we actually exit.
|
// 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="code">Program code to execute</param>
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="state">Current GPU state</param>
|
||||||
/// <returns>True to continue execution, false if the program exited</returns>
|
/// <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;
|
int baseAddr = _pc - 1;
|
||||||
|
|
||||||
|
@ -193,7 +195,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="state">Current GPU state</param>
|
||||||
/// <returns>Operation result</returns>
|
/// <returns>Operation result</returns>
|
||||||
private int GetAluResult(GpuState state)
|
private int GetAluResult(IDeviceState state)
|
||||||
{
|
{
|
||||||
AluOperation op = (AluOperation)(_opCode & 7);
|
AluOperation op = (AluOperation)(_opCode & 7);
|
||||||
|
|
||||||
|
@ -378,9 +380,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="state">Current GPU state</param>
|
||||||
/// <param name="reg">Register offset to read</param>
|
/// <param name="reg">Register offset to read</param>
|
||||||
/// <returns>GPU register value</returns>
|
/// <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>
|
/// <summary>
|
||||||
|
@ -388,11 +390,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="state">Current GPU state</param>
|
||||||
/// <param name="value">Call argument</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.Write(_methAddr * 4, value);
|
||||||
|
|
||||||
state.CallMethod(meth);
|
|
||||||
|
|
||||||
_methAddr += _methIncr;
|
_methAddr += _methIncr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Device;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
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="code">Code of the program to execute</param>
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="state">Current GPU state</param>
|
||||||
/// <param name="arg0">Optional argument passed to the program, 0 if not used</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)
|
if (_execute == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Device;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MacroJitCompiler()
|
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();
|
_ilGen = _meth.GetILGenerator();
|
||||||
_gprs = new LocalBuilder[8];
|
_gprs = new LocalBuilder[8];
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||||
_ilGen.Emit(OpCodes.Stloc, _gprs[1]);
|
_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>
|
/// <summary>
|
||||||
/// Translates a new piece of GPU Macro code into host executable code.
|
/// Translates a new piece of GPU Macro code into host executable code.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Device;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine.MME
|
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="state">Current GPU state</param>
|
||||||
/// <param name="reg">Register offset to read</param>
|
/// <param name="reg">Register offset to read</param>
|
||||||
/// <returns>GPU register value</returns>
|
/// <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>
|
/// <summary>
|
||||||
|
@ -47,11 +47,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||||
/// <param name="value">Call argument</param>
|
/// <param name="value">Call argument</param>
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="state">Current GPU state</param>
|
||||||
/// <param name="methAddr">Address, in words, of the method</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.Write(methAddr * 4, value);
|
||||||
|
|
||||||
state.CallMethod(meth);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
@ -3,6 +3,9 @@ using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents temporary storage used by macros.
|
||||||
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Sequential, Size = 1024)]
|
[StructLayout(LayoutKind.Sequential, Size = 1024)]
|
||||||
struct MmeShadowScratch
|
struct MmeShadowScratch
|
||||||
{
|
{
|
||||||
|
|
13
Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs
Normal file
13
Ryujinx.Graphics.Gpu/Engine/SetMmeShadowRamControlMode.cs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,37 +1,41 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
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>
|
/// <summary>
|
||||||
/// Checks if draws and clears should be performed, according
|
/// Checks if draws and clears should be performed, according
|
||||||
/// to currently set conditional rendering conditions.
|
/// to currently set conditional rendering conditions.
|
||||||
/// </summary>
|
/// </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>
|
/// <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 (condition)
|
||||||
|
|
||||||
switch (condState.Condition)
|
|
||||||
{
|
{
|
||||||
case Condition.Always:
|
case Condition.Always:
|
||||||
return ConditionalRenderEnabled.True;
|
return ConditionalRenderEnabled.True;
|
||||||
case Condition.Never:
|
case Condition.Never:
|
||||||
return ConditionalRenderEnabled.False;
|
return ConditionalRenderEnabled.False;
|
||||||
case Condition.ResultNonZero:
|
case Condition.ResultNonZero:
|
||||||
return CounterNonZero(state, condState.Address.Pack());
|
return CounterNonZero(context, memoryManager, address.Pack());
|
||||||
case Condition.Equal:
|
case Condition.Equal:
|
||||||
return CounterCompare(state, condState.Address.Pack(), true);
|
return CounterCompare(context, memoryManager, address.Pack(), true);
|
||||||
case Condition.NotEqual:
|
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;
|
return ConditionalRenderEnabled.True;
|
||||||
}
|
}
|
||||||
|
@ -39,54 +43,56 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the counter value at a given GPU memory address is non-zero.
|
/// Checks if the counter value at a given GPU memory address is non-zero.
|
||||||
/// </summary>
|
/// </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>
|
/// <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>
|
/// <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)
|
if (evt == null)
|
||||||
{
|
{
|
||||||
return ConditionalRenderEnabled.False;
|
return ConditionalRenderEnabled.False;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_context.Renderer.Pipeline.TryHostConditionalRendering(evt, 0L, false))
|
if (context.Renderer.Pipeline.TryHostConditionalRendering(evt, 0L, false))
|
||||||
{
|
{
|
||||||
return ConditionalRenderEnabled.Host;
|
return ConditionalRenderEnabled.Host;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
evt.Flush();
|
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>
|
/// <summary>
|
||||||
/// Checks if the counter at a given GPU memory address passes a specified equality comparison.
|
/// Checks if the counter at a given GPU memory address passes a specified equality comparison.
|
||||||
/// </summary>
|
/// </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="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>
|
/// <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>
|
/// <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 evt = FindEvent(memoryManager.CounterCache, gpuVa);
|
||||||
ICounterEvent evt2 = FindEvent(state.Channel.MemoryManager.CounterCache, gpuVa + 16);
|
ICounterEvent evt2 = FindEvent(memoryManager.CounterCache, gpuVa + 16);
|
||||||
|
|
||||||
bool useHost;
|
bool useHost;
|
||||||
|
|
||||||
if (evt != null && evt2 == null)
|
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)
|
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)
|
else if (evt != null && evt2 != null)
|
||||||
{
|
{
|
||||||
useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, evt2, isEqual);
|
useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt, evt2, isEqual);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -102,8 +108,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
evt?.Flush();
|
evt?.Flush();
|
||||||
evt2?.Flush();
|
evt2?.Flush();
|
||||||
|
|
||||||
ulong x = state.Channel.MemoryManager.Read<ulong>(gpuVa);
|
ulong x = memoryManager.Read<ulong>(gpuVa);
|
||||||
ulong y = state.Channel.MemoryManager.Read<ulong>(gpuVa + 16);
|
ulong y = memoryManager.Read<ulong>(gpuVa + 16);
|
||||||
|
|
||||||
return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
|
return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
|
||||||
}
|
}
|
173
Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs
Normal file
173
Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
410
Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
Normal file
410
Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs
Normal file
45
Ryujinx.Graphics.Gpu/Engine/Threed/DrawState.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ using Ryujinx.Graphics.GAL;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine
|
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds inline index buffer state.
|
/// Holds inline index buffer state.
|
||||||
|
@ -15,6 +15,9 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
private int _inlineIndexBufferSize;
|
private int _inlineIndexBufferSize;
|
||||||
private int _inlineIndexCount;
|
private int _inlineIndexCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates if any index buffer data has been pushed.
|
||||||
|
/// </summary>
|
||||||
public bool HasInlineIndexData => _inlineIndexCount != 0;
|
public bool HasInlineIndexData => _inlineIndexCount != 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
222
Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs
Normal file
222
Ryujinx.Graphics.Gpu/Engine/Threed/SemaphoreUpdater.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
166
Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
Normal file
166
Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1044
Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
Normal file
1044
Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
Normal file
File diff suppressed because it is too large
Load diff
428
Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
Normal file
428
Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
861
Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
Normal file
861
Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using Ryujinx.Graphics.Device;
|
using Ryujinx.Graphics.Device;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -52,8 +52,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
||||||
{
|
{
|
||||||
var memoryManager = _channel.MemoryManager;
|
var memoryManager = _channel.MemoryManager;
|
||||||
|
|
||||||
var dstCopyTexture = Unsafe.As<uint, CopyTexture>(ref _state.State.SetDstFormat);
|
var dstCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetDstFormat);
|
||||||
var srcCopyTexture = Unsafe.As<uint, CopyTexture>(ref _state.State.SetSrcFormat);
|
var srcCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetSrcFormat);
|
||||||
|
|
||||||
long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac;
|
long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac;
|
||||||
long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac;
|
long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac;
|
||||||
|
|
|
@ -13,17 +13,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
||||||
WriteThenAwaken = 1,
|
WriteThenAwaken = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// MME shadow RAM control mode.
|
|
||||||
/// </summary>
|
|
||||||
enum SetMmeShadowRamControlMode
|
|
||||||
{
|
|
||||||
MethodTrack = 0,
|
|
||||||
MethodTrackWithFilter = 1,
|
|
||||||
MethodPassthrough = 2,
|
|
||||||
MethodReplay = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Format of the destination texture.
|
/// Format of the destination texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -506,7 +495,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 2D class state.
|
/// 2D class state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
unsafe struct TwodClassState
|
unsafe struct TwodClassState : IShadowState
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649
|
#pragma warning disable CS0649
|
||||||
public uint SetObject;
|
public uint SetObject;
|
||||||
|
|
22
Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs
Normal file
22
Ryujinx.Graphics.Gpu/Engine/Twod/TwodTexture.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Boolean value, stored as a 32-bits integer in memory.
|
/// Boolean value, stored as a 32-bits integer in memory.
|
|
@ -1,7 +1,7 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Color texture format.
|
/// Color texture format.
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Split GPU virtual address.
|
/// Split GPU virtual address.
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Memory layout parameters, for block linear textures.
|
/// Memory layout parameters, for block linear textures.
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draw primitive type.
|
/// Draw primitive type.
|
||||||
|
@ -29,7 +29,6 @@ namespace Ryujinx.Graphics.Gpu.State
|
||||||
/// </summary>
|
/// </summary>
|
||||||
enum PrimitiveTypeOverride
|
enum PrimitiveTypeOverride
|
||||||
{
|
{
|
||||||
Invalid = 0,
|
|
||||||
Points = 1,
|
Points = 1,
|
||||||
Lines = 2,
|
Lines = 2,
|
||||||
LineStrip = 3,
|
LineStrip = 3,
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sampler pool indexing mode.
|
/// Sampler pool indexing mode.
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Storage buffer address and size information.
|
/// Storage buffer address and size information.
|
|
@ -1,7 +1,7 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Depth-stencil texture format.
|
/// Depth-stencil texture format.
|
|
@ -64,6 +64,17 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
|
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes data directly to the state of the specified class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="classId">ID of the class to write the data into</param>
|
||||||
|
/// <param name="offset">State offset in bytes</param>
|
||||||
|
/// <param name="value">Value to be written</param>
|
||||||
|
public void Write(ClassId classId, int offset, uint value)
|
||||||
|
{
|
||||||
|
_processor.Write(classId, offset, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Push a GPFIFO entry in the form of a prefetched command buffer.
|
/// Push a GPFIFO entry in the form of a prefetched command buffer.
|
||||||
/// It is intended to be used by nvservices to handle special cases.
|
/// It is intended to be used by nvservices to handle special cases.
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Engine;
|
|
||||||
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Shader;
|
using Ryujinx.Graphics.Gpu.Shader;
|
||||||
|
@ -26,11 +25,6 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IRenderer Renderer { get; }
|
public IRenderer Renderer { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GPU engine methods processing.
|
|
||||||
/// </summary>
|
|
||||||
internal Methods Methods { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GPU General Purpose FIFO queue.
|
/// GPU General Purpose FIFO queue.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -94,8 +88,6 @@ namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
Renderer = renderer;
|
Renderer = renderer;
|
||||||
|
|
||||||
Methods = new Methods(this);
|
|
||||||
|
|
||||||
GPFifo = new GPFifoDevice(this);
|
GPFifo = new GPFifoDevice(this);
|
||||||
|
|
||||||
Synchronization = new SynchronizationManager();
|
Synchronization = new SynchronizationManager();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Dma;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Twod;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
|
@ -151,7 +154,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <returns>The texture</returns>
|
/// <returns>The texture</returns>
|
||||||
public Texture FindOrCreateTexture(
|
public Texture FindOrCreateTexture(
|
||||||
MemoryManager memoryManager,
|
MemoryManager memoryManager,
|
||||||
CopyTexture copyTexture,
|
TwodTexture copyTexture,
|
||||||
ulong offset,
|
ulong offset,
|
||||||
FormatInfo formatInfo,
|
FormatInfo formatInfo,
|
||||||
bool preferScaling = true,
|
bool preferScaling = true,
|
||||||
|
@ -762,7 +765,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <returns>A matching texture, or null if there is no match</returns>
|
/// <returns>A matching texture, or null if there is no match</returns>
|
||||||
public Texture FindTexture(
|
public Texture FindTexture(
|
||||||
MemoryManager memoryManager,
|
MemoryManager memoryManager,
|
||||||
CopyBufferTexture tex,
|
DmaTexture tex,
|
||||||
ulong gpuVa,
|
ulong gpuVa,
|
||||||
int bpp,
|
int bpp,
|
||||||
int stride,
|
int stride,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
namespace Ryujinx.Graphics
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Method call parameters.
|
|
||||||
/// </summary>
|
|
||||||
struct MethodParams
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Method offset.
|
|
||||||
/// </summary>
|
|
||||||
public int Method { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Method call argument.
|
|
||||||
/// </summary>
|
|
||||||
public int Argument { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sub-channel where the call should be sent.
|
|
||||||
/// </summary>
|
|
||||||
public int SubChannel { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For multiple calls to the same method, this is the remaining calls count.
|
|
||||||
/// </summary>
|
|
||||||
public int MethodCount { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates if the current call is the last one from a batch of calls to the same method.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsLastCall => MethodCount <= 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs the method call parameters structure.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method">Method offset</param>
|
|
||||||
/// <param name="argument">Method call argument</param>
|
|
||||||
/// <param name="subChannel">Optional sub-channel where the method should be sent (not required for macro calls)</param>
|
|
||||||
/// <param name="methodCount">Optional remaining calls count (not required for macro calls)</param>
|
|
||||||
public MethodParams(
|
|
||||||
int method,
|
|
||||||
int argument,
|
|
||||||
int subChannel = 0,
|
|
||||||
int methodCount = 0)
|
|
||||||
{
|
|
||||||
Method = method;
|
|
||||||
Argument = argument;
|
|
||||||
SubChannel = subChannel;
|
|
||||||
MethodCount = methodCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -144,27 +144,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// <returns>Current primitive topology</returns>
|
/// <returns>Current primitive topology</returns>
|
||||||
public InputTopology QueryPrimitiveTopology()
|
public InputTopology QueryPrimitiveTopology()
|
||||||
{
|
{
|
||||||
switch (_context.Methods.Topology)
|
return _state.Topology switch
|
||||||
{
|
{
|
||||||
case PrimitiveTopology.Points:
|
PrimitiveTopology.Points => InputTopology.Points,
|
||||||
return InputTopology.Points;
|
PrimitiveTopology.Lines or
|
||||||
case PrimitiveTopology.Lines:
|
PrimitiveTopology.LineLoop or
|
||||||
case PrimitiveTopology.LineLoop:
|
PrimitiveTopology.LineStrip => InputTopology.Lines,
|
||||||
case PrimitiveTopology.LineStrip:
|
PrimitiveTopology.LinesAdjacency or
|
||||||
return InputTopology.Lines;
|
PrimitiveTopology.LineStripAdjacency => InputTopology.LinesAdjacency,
|
||||||
case PrimitiveTopology.LinesAdjacency:
|
PrimitiveTopology.Triangles or
|
||||||
case PrimitiveTopology.LineStripAdjacency:
|
PrimitiveTopology.TriangleStrip or
|
||||||
return InputTopology.LinesAdjacency;
|
PrimitiveTopology.TriangleFan => InputTopology.Triangles,
|
||||||
case PrimitiveTopology.Triangles:
|
PrimitiveTopology.TrianglesAdjacency or
|
||||||
case PrimitiveTopology.TriangleStrip:
|
PrimitiveTopology.TriangleStripAdjacency => InputTopology.TrianglesAdjacency,
|
||||||
case PrimitiveTopology.TriangleFan:
|
_ => InputTopology.Points,
|
||||||
return InputTopology.Triangles;
|
};
|
||||||
case PrimitiveTopology.TrianglesAdjacency:
|
|
||||||
case PrimitiveTopology.TriangleStripAdjacency:
|
|
||||||
return InputTopology.TrianglesAdjacency;
|
|
||||||
}
|
|
||||||
|
|
||||||
return InputTopology.Points;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Shader
|
namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -25,6 +27,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EarlyZForce { get; }
|
public bool EarlyZForce { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Primitive topology of current draw.
|
||||||
|
/// </summary>
|
||||||
|
public PrimitiveTopology Topology { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the GPU accessor state.
|
/// Creates a new instance of the GPU accessor state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -32,12 +39,19 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// <param name="texturePoolMaximumId">Maximum ID of the texture pool</param>
|
/// <param name="texturePoolMaximumId">Maximum ID of the texture pool</param>
|
||||||
/// <param name="textureBufferIndex">Constant buffer slot where the texture handles are located</param>
|
/// <param name="textureBufferIndex">Constant buffer slot where the texture handles are located</param>
|
||||||
/// <param name="earlyZForce">Early Z force enable</param>
|
/// <param name="earlyZForce">Early Z force enable</param>
|
||||||
public GpuAccessorState(ulong texturePoolGpuVa, int texturePoolMaximumId, int textureBufferIndex, bool earlyZForce)
|
/// <param name="topology">Primitive topology</param>
|
||||||
|
public GpuAccessorState(
|
||||||
|
ulong texturePoolGpuVa,
|
||||||
|
int texturePoolMaximumId,
|
||||||
|
int textureBufferIndex,
|
||||||
|
bool earlyZForce,
|
||||||
|
PrimitiveTopology topology)
|
||||||
{
|
{
|
||||||
TexturePoolGpuVa = texturePoolGpuVa;
|
TexturePoolGpuVa = texturePoolGpuVa;
|
||||||
TexturePoolMaximumId = texturePoolMaximumId;
|
TexturePoolMaximumId = texturePoolMaximumId;
|
||||||
TextureBufferIndex = textureBufferIndex;
|
TextureBufferIndex = textureBufferIndex;
|
||||||
EarlyZForce = earlyZForce;
|
EarlyZForce = earlyZForce;
|
||||||
|
Topology = topology;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Shader.Cache;
|
using Ryujinx.Graphics.Gpu.Shader.Cache;
|
||||||
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
|
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -593,10 +594,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This automatically translates, compiles and adds the code to the cache if not present.
|
/// This automatically translates, compiles and adds the code to the cache if not present.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="state">GPU state</param>
|
||||||
|
/// <param name="channel">GPU channel</param>
|
||||||
|
/// <param name="gas">GPU accessor state</param>
|
||||||
/// <param name="addresses">Addresses of the shaders for each stage</param>
|
/// <param name="addresses">Addresses of the shaders for each stage</param>
|
||||||
/// <returns>Compiled graphics shader code</returns>
|
/// <returns>Compiled graphics shader code</returns>
|
||||||
public ShaderBundle GetGraphicsShader(GpuState state, ShaderAddresses addresses)
|
public ShaderBundle GetGraphicsShader(ref ThreedClassState state, GpuChannel channel, GpuAccessorState gas, ShaderAddresses addresses)
|
||||||
{
|
{
|
||||||
bool isCached = _gpPrograms.TryGetValue(addresses, out List<ShaderBundle> list);
|
bool isCached = _gpPrograms.TryGetValue(addresses, out List<ShaderBundle> list);
|
||||||
|
|
||||||
|
@ -604,7 +607,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
{
|
{
|
||||||
foreach (ShaderBundle cachedGpShaders in list)
|
foreach (ShaderBundle cachedGpShaders in list)
|
||||||
{
|
{
|
||||||
if (IsShaderEqual(state.Channel.MemoryManager, cachedGpShaders, addresses))
|
if (IsShaderEqual(channel.MemoryManager, cachedGpShaders, addresses))
|
||||||
{
|
{
|
||||||
return cachedGpShaders;
|
return cachedGpShaders;
|
||||||
}
|
}
|
||||||
|
@ -613,7 +616,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
|
|
||||||
TranslatorContext[] shaderContexts = new TranslatorContext[Constants.ShaderStages + 1];
|
TranslatorContext[] shaderContexts = new TranslatorContext[Constants.ShaderStages + 1];
|
||||||
|
|
||||||
TransformFeedbackDescriptor[] tfd = GetTransformFeedbackDescriptors(state);
|
TransformFeedbackDescriptor[] tfd = GetTransformFeedbackDescriptors(ref state);
|
||||||
|
|
||||||
TranslationFlags flags = DefaultFlags;
|
TranslationFlags flags = DefaultFlags;
|
||||||
|
|
||||||
|
@ -626,14 +629,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
|
|
||||||
if (addresses.VertexA != 0)
|
if (addresses.VertexA != 0)
|
||||||
{
|
{
|
||||||
shaderContexts[0] = DecodeGraphicsShader(state, counts, flags | TranslationFlags.VertexA, ShaderStage.Vertex, addresses.VertexA);
|
shaderContexts[0] = DecodeGraphicsShader(channel, gas, counts, flags | TranslationFlags.VertexA, ShaderStage.Vertex, addresses.VertexA);
|
||||||
}
|
}
|
||||||
|
|
||||||
shaderContexts[1] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Vertex, addresses.Vertex);
|
shaderContexts[1] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Vertex, addresses.Vertex);
|
||||||
shaderContexts[2] = DecodeGraphicsShader(state, counts, flags, ShaderStage.TessellationControl, addresses.TessControl);
|
shaderContexts[2] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.TessellationControl, addresses.TessControl);
|
||||||
shaderContexts[3] = DecodeGraphicsShader(state, counts, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation);
|
shaderContexts[3] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation);
|
||||||
shaderContexts[4] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Geometry, addresses.Geometry);
|
shaderContexts[4] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Geometry, addresses.Geometry);
|
||||||
shaderContexts[5] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Fragment, addresses.Fragment);
|
shaderContexts[5] = DecodeGraphicsShader(channel, gas, counts, flags, ShaderStage.Fragment, addresses.Fragment);
|
||||||
|
|
||||||
bool isShaderCacheEnabled = _cacheManager != null;
|
bool isShaderCacheEnabled = _cacheManager != null;
|
||||||
bool isShaderCacheReadOnly = false;
|
bool isShaderCacheReadOnly = false;
|
||||||
|
@ -656,7 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
isShaderCacheReadOnly = _cacheManager.IsReadOnly;
|
isShaderCacheReadOnly = _cacheManager.IsReadOnly;
|
||||||
|
|
||||||
// Compute hash and prepare data for shader disk cache comparison.
|
// Compute hash and prepare data for shader disk cache comparison.
|
||||||
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(state.Channel.MemoryManager, shaderContexts);
|
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(channel.MemoryManager, shaderContexts);
|
||||||
programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries, tfd);
|
programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries, tfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,11 +676,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
// The shader isn't currently cached, translate it and compile it.
|
// The shader isn't currently cached, translate it and compile it.
|
||||||
ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages];
|
ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages];
|
||||||
|
|
||||||
shaders[0] = TranslateShader(state.Channel.MemoryManager, shaderContexts[1], shaderContexts[0]);
|
shaders[0] = TranslateShader(channel.MemoryManager, shaderContexts[1], shaderContexts[0]);
|
||||||
shaders[1] = TranslateShader(state.Channel.MemoryManager, shaderContexts[2]);
|
shaders[1] = TranslateShader(channel.MemoryManager, shaderContexts[2]);
|
||||||
shaders[2] = TranslateShader(state.Channel.MemoryManager, shaderContexts[3]);
|
shaders[2] = TranslateShader(channel.MemoryManager, shaderContexts[3]);
|
||||||
shaders[3] = TranslateShader(state.Channel.MemoryManager, shaderContexts[4]);
|
shaders[3] = TranslateShader(channel.MemoryManager, shaderContexts[4]);
|
||||||
shaders[4] = TranslateShader(state.Channel.MemoryManager, shaderContexts[5]);
|
shaders[4] = TranslateShader(channel.MemoryManager, shaderContexts[5]);
|
||||||
|
|
||||||
List<IShader> hostShaders = new List<IShader>();
|
List<IShader> hostShaders = new List<IShader>();
|
||||||
|
|
||||||
|
@ -733,9 +736,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="state">Current GPU state</param>
|
||||||
/// <returns>Four transform feedback descriptors for the enabled TFBs, or null if TFB is disabled</returns>
|
/// <returns>Four transform feedback descriptors for the enabled TFBs, or null if TFB is disabled</returns>
|
||||||
private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(GpuState state)
|
private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(ref ThreedClassState state)
|
||||||
{
|
{
|
||||||
bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable);
|
bool tfEnable = state.TfEnable;
|
||||||
|
|
||||||
if (!tfEnable)
|
if (!tfEnable)
|
||||||
{
|
{
|
||||||
|
@ -746,13 +749,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
|
|
||||||
for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
|
for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++)
|
||||||
{
|
{
|
||||||
var tf = state.Get<TfState>(MethodOffset.TfState, i);
|
var tf = state.TfState[i];
|
||||||
|
|
||||||
int length = (int)Math.Min((uint)tf.VaryingsCount, 0x80);
|
int length = (int)Math.Min((uint)tf.VaryingsCount, 0x80);
|
||||||
|
|
||||||
var varyingLocations = state.GetSpan(MethodOffset.TfVaryingLocations + i * 0x80, length).ToArray();
|
var varyingLocations = MemoryMarshal.Cast<uint, byte>(state.TfVaryingLocations[i].ToSpan()).Slice(0, length);
|
||||||
|
|
||||||
descs[i] = new TransformFeedbackDescriptor(tf.BufferIndex, tf.Stride, varyingLocations);
|
descs[i] = new TransformFeedbackDescriptor(tf.BufferIndex, tf.Stride, varyingLocations.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
return descs;
|
return descs;
|
||||||
|
@ -871,14 +874,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader.
|
/// This will combine the "Vertex A" and "Vertex B" shader stages, if specified, into one shader.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="channel">GPU channel</param>
|
||||||
|
/// <param name="gas">GPU accessor state</param>
|
||||||
/// <param name="counts">Cumulative shader resource counts</param>
|
/// <param name="counts">Cumulative shader resource counts</param>
|
||||||
/// <param name="flags">Flags that controls shader translation</param>
|
/// <param name="flags">Flags that controls shader translation</param>
|
||||||
/// <param name="stage">Shader stage</param>
|
/// <param name="stage">Shader stage</param>
|
||||||
/// <param name="gpuVa">GPU virtual address of the shader code</param>
|
/// <param name="gpuVa">GPU virtual address of the shader code</param>
|
||||||
/// <returns>The generated translator context</returns>
|
/// <returns>The generated translator context</returns>
|
||||||
private TranslatorContext DecodeGraphicsShader(
|
private TranslatorContext DecodeGraphicsShader(
|
||||||
GpuState state,
|
GpuChannel channel,
|
||||||
|
GpuAccessorState gas,
|
||||||
TranslationCounts counts,
|
TranslationCounts counts,
|
||||||
TranslationFlags flags,
|
TranslationFlags flags,
|
||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
|
@ -889,13 +894,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
GpuAccessorState gas = new GpuAccessorState(
|
GpuAccessor gpuAccessor = new GpuAccessor(_context, channel, gas, (int)stage - 1);
|
||||||
state.Get<PoolState>(MethodOffset.TexturePoolState).Address.Pack(),
|
|
||||||
state.Get<PoolState>(MethodOffset.TexturePoolState).MaximumId,
|
|
||||||
state.Get<int>(MethodOffset.TextureBufferIndex),
|
|
||||||
state.Get<Boolean32>(MethodOffset.EarlyZForce));
|
|
||||||
|
|
||||||
GpuAccessor gpuAccessor = new GpuAccessor(_context, state.Channel, gas, (int)stage - 1);
|
|
||||||
|
|
||||||
var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, flags);
|
var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, flags);
|
||||||
return Translator.CreateContext(gpuVa, gpuAccessor, options, counts);
|
return Translator.CreateContext(gpuVa, gpuAccessor, options, counts);
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Shadow RAM Control setting.
|
|
||||||
/// </summary>
|
|
||||||
enum ShadowRamControl
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Track data writes and store them on shadow RAM.
|
|
||||||
/// </summary>
|
|
||||||
Track = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Track data writes and store them on shadow RAM, with filtering.
|
|
||||||
/// </summary>
|
|
||||||
TrackWithFilter = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes data directly without storing on shadow RAM.
|
|
||||||
/// </summary>
|
|
||||||
Passthrough = 2,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ignore data being written and replace with data on shadow RAM instead.
|
|
||||||
/// </summary>
|
|
||||||
Replay = 3
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Color buffer blending parameters.
|
|
||||||
/// </summary>
|
|
||||||
struct BlendState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public Boolean32 SeparateAlpha;
|
|
||||||
public BlendOp ColorOp;
|
|
||||||
public BlendFactor ColorSrcFactor;
|
|
||||||
public BlendFactor ColorDstFactor;
|
|
||||||
public BlendOp AlphaOp;
|
|
||||||
public BlendFactor AlphaSrcFactor;
|
|
||||||
public BlendFactor AlphaDstFactor;
|
|
||||||
public uint Padding;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
|
|
||||||
public static BlendState Default = new BlendState
|
|
||||||
{
|
|
||||||
ColorOp = BlendOp.Add,
|
|
||||||
ColorSrcFactor = BlendFactor.One,
|
|
||||||
ColorDstFactor = BlendFactor.Zero,
|
|
||||||
AlphaOp = BlendOp.Add,
|
|
||||||
AlphaSrcFactor = BlendFactor.One,
|
|
||||||
AlphaDstFactor = BlendFactor.Zero
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Color buffer blending parameters, shared by all color buffers.
|
|
||||||
/// </summary>
|
|
||||||
struct BlendStateCommon
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public Boolean32 SeparateAlpha;
|
|
||||||
public BlendOp ColorOp;
|
|
||||||
public BlendFactor ColorSrcFactor;
|
|
||||||
public BlendFactor ColorDstFactor;
|
|
||||||
public BlendOp AlphaOp;
|
|
||||||
public BlendFactor AlphaSrcFactor;
|
|
||||||
public uint Unknown0x1354;
|
|
||||||
public BlendFactor AlphaDstFactor;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
|
|
||||||
public static BlendStateCommon Default = new BlendStateCommon
|
|
||||||
{
|
|
||||||
ColorOp = BlendOp.Add,
|
|
||||||
ColorSrcFactor = BlendFactor.One,
|
|
||||||
ColorDstFactor = BlendFactor.Zero,
|
|
||||||
AlphaOp = BlendOp.Add,
|
|
||||||
AlphaSrcFactor = BlendFactor.One,
|
|
||||||
AlphaDstFactor = BlendFactor.Zero
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Color buffer clear color.
|
|
||||||
/// </summary>
|
|
||||||
struct ClearColors
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public float Red;
|
|
||||||
public float Green;
|
|
||||||
public float Blue;
|
|
||||||
public float Alpha;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Condition for conditional rendering.
|
|
||||||
/// </summary>
|
|
||||||
enum Condition
|
|
||||||
{
|
|
||||||
Never,
|
|
||||||
Always,
|
|
||||||
ResultNonZero,
|
|
||||||
Equal,
|
|
||||||
NotEqual
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Condition parameters for conditional rendering.
|
|
||||||
/// </summary>
|
|
||||||
struct ConditionState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public GpuVa Address;
|
|
||||||
public Condition Condition;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Buffer to texture copy parameters.
|
|
||||||
/// </summary>
|
|
||||||
struct CopyBufferTexture
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public MemoryLayout MemoryLayout;
|
|
||||||
public int Width;
|
|
||||||
public int Height;
|
|
||||||
public int Depth;
|
|
||||||
public int RegionZ;
|
|
||||||
public ushort RegionX;
|
|
||||||
public ushort RegionY;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Texture copy region.
|
|
||||||
/// </summary>
|
|
||||||
struct CopyRegion
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public int DstX;
|
|
||||||
public int DstY;
|
|
||||||
public int DstWidth;
|
|
||||||
public int DstHeight;
|
|
||||||
public long SrcWidthRF;
|
|
||||||
public long SrcHeightRF;
|
|
||||||
public long SrcXF;
|
|
||||||
public long SrcYF;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Texture to texture (with optional resizing) copy parameters.
|
|
||||||
/// </summary>
|
|
||||||
struct CopyTexture
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public ColorFormat Format;
|
|
||||||
public Boolean32 LinearLayout;
|
|
||||||
public MemoryLayout MemoryLayout;
|
|
||||||
public int Depth;
|
|
||||||
public int Layer;
|
|
||||||
public int Stride;
|
|
||||||
public int Width;
|
|
||||||
public int Height;
|
|
||||||
public GpuVa Address;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Depth bias (also called polygon offset) parameters.
|
|
||||||
/// </summary>
|
|
||||||
struct DepthBiasState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public Boolean32 PointEnable;
|
|
||||||
public Boolean32 LineEnable;
|
|
||||||
public Boolean32 FillEnable;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Face culling and orientation parameters.
|
|
||||||
/// </summary>
|
|
||||||
struct FaceState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public Boolean32 CullEnable;
|
|
||||||
public FrontFace FrontFace;
|
|
||||||
public Face CullFace;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,477 +0,0 @@
|
||||||
using Ryujinx.Graphics.Device;
|
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// GPU state.
|
|
||||||
/// </summary>
|
|
||||||
class GpuState
|
|
||||||
{
|
|
||||||
private const int RegistersCount = 0xe00;
|
|
||||||
|
|
||||||
public delegate void MethodCallback(GpuState state, int argument);
|
|
||||||
|
|
||||||
private readonly int[] _memory;
|
|
||||||
private readonly int[] _shadow;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GPU register information.
|
|
||||||
/// </summary>
|
|
||||||
private struct Register
|
|
||||||
{
|
|
||||||
public MethodCallback Callback;
|
|
||||||
|
|
||||||
public MethodOffset BaseOffset;
|
|
||||||
|
|
||||||
public int Stride;
|
|
||||||
public int Count;
|
|
||||||
|
|
||||||
public bool Modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Register[] _registers;
|
|
||||||
|
|
||||||
private readonly IDeviceState _deviceState;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the shadow ram control used for this sub-channel.
|
|
||||||
/// </summary>
|
|
||||||
public ShadowRamControl ShadowRamControl { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GPU channel for the sub-channel state.
|
|
||||||
/// </summary>
|
|
||||||
public GpuChannel Channel { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of the GPU state.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="channel">Channel that the sub-channel state belongs to</param>
|
|
||||||
/// <param name="deviceState">Optional device state that will replace the internal backing storage</param>
|
|
||||||
public GpuState(GpuChannel channel, IDeviceState deviceState = null)
|
|
||||||
{
|
|
||||||
Channel = channel;
|
|
||||||
_deviceState = deviceState;
|
|
||||||
|
|
||||||
_memory = new int[RegistersCount];
|
|
||||||
_shadow = new int[RegistersCount];
|
|
||||||
|
|
||||||
_registers = new Register[RegistersCount];
|
|
||||||
|
|
||||||
for (int index = 0; index < _registers.Length; index++)
|
|
||||||
{
|
|
||||||
_registers[index].BaseOffset = (MethodOffset)index;
|
|
||||||
_registers[index].Stride = 1;
|
|
||||||
_registers[index].Count = 1;
|
|
||||||
_registers[index].Modified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in GpuStateTable.Table)
|
|
||||||
{
|
|
||||||
int totalRegs = item.Size * item.Count;
|
|
||||||
|
|
||||||
for (int regOffset = 0; regOffset < totalRegs; regOffset++)
|
|
||||||
{
|
|
||||||
int index = (int)item.Offset + regOffset;
|
|
||||||
|
|
||||||
_registers[index].BaseOffset = item.Offset;
|
|
||||||
_registers[index].Stride = item.Size;
|
|
||||||
_registers[index].Count = item.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InitializeDefaultState(_memory);
|
|
||||||
InitializeDefaultState(_shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calls a GPU method, using this state.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="meth">The GPU method to be called</param>
|
|
||||||
public void CallMethod(MethodParams meth)
|
|
||||||
{
|
|
||||||
int value = meth.Argument;
|
|
||||||
|
|
||||||
// Methods < 0x80 shouldn't be affected by shadow RAM at all.
|
|
||||||
if (meth.Method >= 0x80)
|
|
||||||
{
|
|
||||||
ShadowRamControl shadowCtrl = ShadowRamControl;
|
|
||||||
|
|
||||||
// TODO: Figure out what TrackWithFilter does, compared to Track.
|
|
||||||
if (shadowCtrl == ShadowRamControl.Track ||
|
|
||||||
shadowCtrl == ShadowRamControl.TrackWithFilter)
|
|
||||||
{
|
|
||||||
_shadow[meth.Method] = value;
|
|
||||||
}
|
|
||||||
else if (shadowCtrl == ShadowRamControl.Replay)
|
|
||||||
{
|
|
||||||
value = _shadow[meth.Method];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_deviceState != null)
|
|
||||||
{
|
|
||||||
_deviceState.Write(meth.Method * 4, meth.Argument);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Register register = _registers[meth.Method];
|
|
||||||
|
|
||||||
if (_memory[meth.Method] != value)
|
|
||||||
{
|
|
||||||
_registers[(int)register.BaseOffset].Modified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_memory[meth.Method] = value;
|
|
||||||
|
|
||||||
register.Callback?.Invoke(this, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads data from a GPU register at the given offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="offset">Offset to be read</param>
|
|
||||||
/// <returns>Data at the register</returns>
|
|
||||||
public int Read(int offset)
|
|
||||||
{
|
|
||||||
if (_deviceState != null)
|
|
||||||
{
|
|
||||||
return _deviceState.Read(offset * 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _memory[offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes data to the GPU register at the given offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="offset">Offset to be written</param>
|
|
||||||
/// <param name="value">Value to be written</param>
|
|
||||||
public void Write(int offset, int value)
|
|
||||||
{
|
|
||||||
_memory[offset] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes an offset value at the uniform buffer offset register.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="offset">The offset to be written</param>
|
|
||||||
public void SetUniformBufferOffset(int offset)
|
|
||||||
{
|
|
||||||
_memory[(int)MethodOffset.UniformBufferState + 3] = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes registers with the default state.
|
|
||||||
/// </summary>
|
|
||||||
private void InitializeDefaultState(int[] memory)
|
|
||||||
{
|
|
||||||
// Enable Rasterizer
|
|
||||||
memory[(int)MethodOffset.RasterizeEnable] = 1;
|
|
||||||
|
|
||||||
// Depth ranges.
|
|
||||||
for (int index = 0; index < Constants.TotalViewports; index++)
|
|
||||||
{
|
|
||||||
memory[(int)MethodOffset.ViewportExtents + index * 4 + 2] = 0;
|
|
||||||
memory[(int)MethodOffset.ViewportExtents + index * 4 + 3] = 0x3F800000;
|
|
||||||
|
|
||||||
// Set swizzle to +XYZW
|
|
||||||
memory[(int)MethodOffset.ViewportTransform + index * 8 + 6] = 0x6420;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Viewport transform enable.
|
|
||||||
memory[(int)MethodOffset.ViewportTransformEnable] = 1;
|
|
||||||
|
|
||||||
// Default front stencil mask.
|
|
||||||
memory[0x4e7] = 0xff;
|
|
||||||
|
|
||||||
// Conditional rendering condition.
|
|
||||||
memory[0x556] = (int)Condition.Always;
|
|
||||||
|
|
||||||
// Default color mask.
|
|
||||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
|
||||||
{
|
|
||||||
memory[(int)MethodOffset.RtColorMask + index] = 0x1111;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default blend states
|
|
||||||
Set(MethodOffset.BlendStateCommon, BlendStateCommon.Default);
|
|
||||||
|
|
||||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
|
||||||
{
|
|
||||||
Set(MethodOffset.BlendState, index, BlendState.Default);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default Point Parameters
|
|
||||||
memory[(int)MethodOffset.PointSpriteEnable] = 1;
|
|
||||||
memory[(int)MethodOffset.PointSize] = 0x3F800000; // 1.0f
|
|
||||||
memory[(int)MethodOffset.PointCoordReplace] = 0x8; // Enable
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a callback that is called every time a GPU method, or methods are called.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="offset">Offset of the method</param>
|
|
||||||
/// <param name="count">Word count of the methods region</param>
|
|
||||||
/// <param name="callback">Calllback to be called</param>
|
|
||||||
public void RegisterCallback(MethodOffset offset, int count, MethodCallback callback)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < count; index++)
|
|
||||||
{
|
|
||||||
_registers[(int)offset + index].Callback = callback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers a callback that is called every time a GPU method is called.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="offset">Offset of the method</param>
|
|
||||||
/// <param name="callback">Calllback to be called</param>
|
|
||||||
public void RegisterCallback(MethodOffset offset, MethodCallback callback)
|
|
||||||
{
|
|
||||||
_registers[(int)offset].Callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clear all registered callbacks.
|
|
||||||
/// </summary>
|
|
||||||
public void ClearCallbacks()
|
|
||||||
{
|
|
||||||
for (int index = 0; index < _registers.Length; index++)
|
|
||||||
{
|
|
||||||
_registers[index].Callback = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Forces a full host state update by marking all state as modified,
|
|
||||||
/// and also requests all GPU resources in use to be rebound.
|
|
||||||
/// </summary>
|
|
||||||
public void ForceAllDirty()
|
|
||||||
{
|
|
||||||
for (int index = 0; index < _registers.Length; index++)
|
|
||||||
{
|
|
||||||
_registers[index].Modified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Channel.BufferManager.Rebind();
|
|
||||||
Channel.TextureManager.Rebind();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if a given register has been modified since the last call to this method.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="offset">Register offset</param>
|
|
||||||
/// <returns>True if modified, false otherwise</returns>
|
|
||||||
public bool QueryModified(MethodOffset offset)
|
|
||||||
{
|
|
||||||
bool modified = _registers[(int)offset].Modified;
|
|
||||||
|
|
||||||
_registers[(int)offset].Modified = false;
|
|
||||||
|
|
||||||
return modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if two registers have been modified since the last call to this method.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="m1">First register offset</param>
|
|
||||||
/// <param name="m2">Second register offset</param>
|
|
||||||
/// <returns>True if any register was modified, false otherwise</returns>
|
|
||||||
public bool QueryModified(MethodOffset m1, MethodOffset m2)
|
|
||||||
{
|
|
||||||
bool modified = _registers[(int)m1].Modified ||
|
|
||||||
_registers[(int)m2].Modified;
|
|
||||||
|
|
||||||
_registers[(int)m1].Modified = false;
|
|
||||||
_registers[(int)m2].Modified = false;
|
|
||||||
|
|
||||||
return modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if three registers have been modified since the last call to this method.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="m1">First register offset</param>
|
|
||||||
/// <param name="m2">Second register offset</param>
|
|
||||||
/// <param name="m3">Third register offset</param>
|
|
||||||
/// <returns>True if any register was modified, false otherwise</returns>
|
|
||||||
public bool QueryModified(MethodOffset m1, MethodOffset m2, MethodOffset m3)
|
|
||||||
{
|
|
||||||
bool modified = _registers[(int)m1].Modified ||
|
|
||||||
_registers[(int)m2].Modified ||
|
|
||||||
_registers[(int)m3].Modified;
|
|
||||||
|
|
||||||
_registers[(int)m1].Modified = false;
|
|
||||||
_registers[(int)m2].Modified = false;
|
|
||||||
_registers[(int)m3].Modified = false;
|
|
||||||
|
|
||||||
return modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if four registers have been modified since the last call to this method.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="m1">First register offset</param>
|
|
||||||
/// <param name="m2">Second register offset</param>
|
|
||||||
/// <param name="m3">Third register offset</param>
|
|
||||||
/// <param name="m4">Fourth register offset</param>
|
|
||||||
/// <returns>True if any register was modified, false otherwise</returns>
|
|
||||||
public bool QueryModified(MethodOffset m1, MethodOffset m2, MethodOffset m3, MethodOffset m4)
|
|
||||||
{
|
|
||||||
bool modified = _registers[(int)m1].Modified ||
|
|
||||||
_registers[(int)m2].Modified ||
|
|
||||||
_registers[(int)m3].Modified ||
|
|
||||||
_registers[(int)m4].Modified;
|
|
||||||
|
|
||||||
_registers[(int)m1].Modified = false;
|
|
||||||
_registers[(int)m2].Modified = false;
|
|
||||||
_registers[(int)m3].Modified = false;
|
|
||||||
_registers[(int)m4].Modified = false;
|
|
||||||
|
|
||||||
return modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if five registers have been modified since the last call to this method.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="m1">First register offset</param>
|
|
||||||
/// <param name="m2">Second register offset</param>
|
|
||||||
/// <param name="m3">Third register offset</param>
|
|
||||||
/// <param name="m4">Fourth register offset</param>
|
|
||||||
/// <param name="m5">Fifth register offset</param>
|
|
||||||
/// <returns>True if any register was modified, false otherwise</returns>
|
|
||||||
public bool QueryModified(
|
|
||||||
MethodOffset m1,
|
|
||||||
MethodOffset m2,
|
|
||||||
MethodOffset m3,
|
|
||||||
MethodOffset m4,
|
|
||||||
MethodOffset m5)
|
|
||||||
{
|
|
||||||
bool modified = _registers[(int)m1].Modified ||
|
|
||||||
_registers[(int)m2].Modified ||
|
|
||||||
_registers[(int)m3].Modified ||
|
|
||||||
_registers[(int)m4].Modified ||
|
|
||||||
_registers[(int)m5].Modified;
|
|
||||||
|
|
||||||
_registers[(int)m1].Modified = false;
|
|
||||||
_registers[(int)m2].Modified = false;
|
|
||||||
_registers[(int)m3].Modified = false;
|
|
||||||
_registers[(int)m4].Modified = false;
|
|
||||||
_registers[(int)m5].Modified = false;
|
|
||||||
|
|
||||||
return modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if six registers have been modified since the last call to this method.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="m1">First register offset</param>
|
|
||||||
/// <param name="m2">Second register offset</param>
|
|
||||||
/// <param name="m3">Third register offset</param>
|
|
||||||
/// <param name="m4">Fourth register offset</param>
|
|
||||||
/// <param name="m5">Fifth register offset</param>
|
|
||||||
/// <param name="m6">Sixth register offset</param>
|
|
||||||
/// <returns>True if any register was modified, false otherwise</returns>
|
|
||||||
public bool QueryModified(
|
|
||||||
MethodOffset m1,
|
|
||||||
MethodOffset m2,
|
|
||||||
MethodOffset m3,
|
|
||||||
MethodOffset m4,
|
|
||||||
MethodOffset m5,
|
|
||||||
MethodOffset m6)
|
|
||||||
{
|
|
||||||
bool modified = _registers[(int)m1].Modified ||
|
|
||||||
_registers[(int)m2].Modified ||
|
|
||||||
_registers[(int)m3].Modified ||
|
|
||||||
_registers[(int)m4].Modified ||
|
|
||||||
_registers[(int)m5].Modified ||
|
|
||||||
_registers[(int)m6].Modified;
|
|
||||||
|
|
||||||
_registers[(int)m1].Modified = false;
|
|
||||||
_registers[(int)m2].Modified = false;
|
|
||||||
_registers[(int)m3].Modified = false;
|
|
||||||
_registers[(int)m4].Modified = false;
|
|
||||||
_registers[(int)m5].Modified = false;
|
|
||||||
_registers[(int)m6].Modified = false;
|
|
||||||
|
|
||||||
return modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets indexed data from a given register offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of the data</typeparam>
|
|
||||||
/// <param name="offset">Register offset</param>
|
|
||||||
/// <param name="index">Index for indexed data</param>
|
|
||||||
/// <returns>The data at the specified location</returns>
|
|
||||||
public T Get<T>(MethodOffset offset, int index) where T : unmanaged
|
|
||||||
{
|
|
||||||
Register register = _registers[(int)offset];
|
|
||||||
|
|
||||||
if ((uint)index >= register.Count)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Get<T>(offset + index * register.Stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets data from a given register offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of the data</typeparam>
|
|
||||||
/// <param name="offset">Register offset</param>
|
|
||||||
/// <returns>The data at the specified location</returns>
|
|
||||||
public T Get<T>(MethodOffset offset) where T : unmanaged
|
|
||||||
{
|
|
||||||
return MemoryMarshal.Cast<int, T>(_memory.AsSpan().Slice((int)offset))[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a span of the data at a given register offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="offset">Register offset</param>
|
|
||||||
/// <param name="length">Length of the data in bytes</param>
|
|
||||||
/// <returns>The data at the specified location</returns>
|
|
||||||
public Span<byte> GetSpan(MethodOffset offset, int length)
|
|
||||||
{
|
|
||||||
return MemoryMarshal.Cast<int, byte>(_memory.AsSpan().Slice((int)offset)).Slice(0, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets indexed data to a given register offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of the data</typeparam>
|
|
||||||
/// <param name="offset">Register offset</param>
|
|
||||||
/// <param name="index">Index for indexed data</param>
|
|
||||||
/// <param name="data">The data to set</param>
|
|
||||||
public void Set<T>(MethodOffset offset, int index, T data) where T : unmanaged
|
|
||||||
{
|
|
||||||
Register register = _registers[(int)offset];
|
|
||||||
|
|
||||||
if ((uint)index >= register.Count)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
Set(offset + index * register.Stride, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets data to a given register offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of the data</typeparam>
|
|
||||||
/// <param name="offset">Register offset</param>
|
|
||||||
/// <param name="data">The data to set</param>
|
|
||||||
public void Set<T>(MethodOffset offset, T data) where T : unmanaged
|
|
||||||
{
|
|
||||||
ReadOnlySpan<int> intSpan = MemoryMarshal.Cast<T, int>(MemoryMarshal.CreateReadOnlySpan(ref data, 1));
|
|
||||||
intSpan.CopyTo(_memory.AsSpan().Slice((int)offset, intSpan.Length));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// GPU State item sizes table.
|
|
||||||
/// </summary>
|
|
||||||
static class GpuStateTable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// GPU state table item, with size for structures, and count for indexed state data.
|
|
||||||
/// </summary>
|
|
||||||
public struct TableItem
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Offset of the data.
|
|
||||||
/// </summary>
|
|
||||||
public MethodOffset Offset { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Size in words.
|
|
||||||
/// </summary>
|
|
||||||
public int Size { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Count for indexed data, or 1 if not indexed.
|
|
||||||
/// </summary>
|
|
||||||
public int Count { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs the table item structure.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="offset">Data offset</param>
|
|
||||||
/// <param name="type">Data type</param>
|
|
||||||
/// <param name="count">Data count, for indexed data</param>
|
|
||||||
public TableItem(MethodOffset offset, Type type, int count)
|
|
||||||
{
|
|
||||||
int sizeInBytes = Marshal.SizeOf(type);
|
|
||||||
|
|
||||||
Debug.Assert((sizeInBytes & 3) == 0);
|
|
||||||
|
|
||||||
Offset = offset;
|
|
||||||
Size = sizeInBytes / 4;
|
|
||||||
Count = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Table of GPU state structure sizes and counts.
|
|
||||||
/// </summary>
|
|
||||||
public static TableItem[] Table = new TableItem[]
|
|
||||||
{
|
|
||||||
new TableItem(MethodOffset.TfBufferState, typeof(TfBufferState), Constants.TotalTransformFeedbackBuffers),
|
|
||||||
new TableItem(MethodOffset.TfState, typeof(TfState), Constants.TotalTransformFeedbackBuffers),
|
|
||||||
new TableItem(MethodOffset.RtColorState, typeof(RtColorState), Constants.TotalRenderTargets),
|
|
||||||
new TableItem(MethodOffset.ViewportTransform, typeof(ViewportTransform), Constants.TotalViewports),
|
|
||||||
new TableItem(MethodOffset.ViewportExtents, typeof(ViewportExtents), Constants.TotalViewports),
|
|
||||||
new TableItem(MethodOffset.VertexBufferDrawState, typeof(VertexBufferDrawState), 1),
|
|
||||||
new TableItem(MethodOffset.DepthBiasState, typeof(DepthBiasState), 1),
|
|
||||||
new TableItem(MethodOffset.ScissorState, typeof(ScissorState), Constants.TotalViewports),
|
|
||||||
new TableItem(MethodOffset.StencilBackMasks, typeof(StencilBackMasks), 1),
|
|
||||||
new TableItem(MethodOffset.RtDepthStencilState, typeof(RtDepthStencilState), 1),
|
|
||||||
new TableItem(MethodOffset.VertexAttribState, typeof(VertexAttribState), Constants.TotalVertexAttribs),
|
|
||||||
new TableItem(MethodOffset.RtDepthStencilSize, typeof(Size3D), 1),
|
|
||||||
new TableItem(MethodOffset.BlendEnable, typeof(Boolean32), Constants.TotalRenderTargets),
|
|
||||||
new TableItem(MethodOffset.StencilTestState, typeof(StencilTestState), 1),
|
|
||||||
new TableItem(MethodOffset.SamplerPoolState, typeof(PoolState), 1),
|
|
||||||
new TableItem(MethodOffset.TexturePoolState, typeof(PoolState), 1),
|
|
||||||
new TableItem(MethodOffset.StencilBackTestState, typeof(StencilBackTestState), 1),
|
|
||||||
new TableItem(MethodOffset.ShaderBaseAddress, typeof(GpuVa), 1),
|
|
||||||
new TableItem(MethodOffset.PrimitiveRestartState, typeof(PrimitiveRestartState), 1),
|
|
||||||
new TableItem(MethodOffset.IndexBufferState, typeof(IndexBufferState), 1),
|
|
||||||
new TableItem(MethodOffset.VertexBufferInstanced, typeof(Boolean32), Constants.TotalVertexBuffers),
|
|
||||||
new TableItem(MethodOffset.FaceState, typeof(FaceState), 1),
|
|
||||||
new TableItem(MethodOffset.RtColorMask, typeof(RtColorMask), Constants.TotalRenderTargets),
|
|
||||||
new TableItem(MethodOffset.VertexBufferState, typeof(VertexBufferState), Constants.TotalVertexBuffers),
|
|
||||||
new TableItem(MethodOffset.BlendConstant, typeof(ColorF), 1),
|
|
||||||
new TableItem(MethodOffset.BlendStateCommon, typeof(BlendStateCommon), 1),
|
|
||||||
new TableItem(MethodOffset.BlendState, typeof(BlendState), Constants.TotalRenderTargets),
|
|
||||||
new TableItem(MethodOffset.VertexBufferEndAddress, typeof(GpuVa), Constants.TotalVertexBuffers),
|
|
||||||
new TableItem(MethodOffset.ShaderState, typeof(ShaderState), Constants.ShaderStages + 1),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// GPU index buffer state.
|
|
||||||
/// This is used on indexed draws.
|
|
||||||
/// </summary>
|
|
||||||
struct IndexBufferState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public GpuVa Address;
|
|
||||||
public GpuVa EndAddress;
|
|
||||||
public IndexType Type;
|
|
||||||
public int First;
|
|
||||||
public int Count;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Inline-to-memory copy parameters.
|
|
||||||
/// </summary>
|
|
||||||
struct Inline2MemoryParams
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public int LineLengthIn;
|
|
||||||
public int LineCount;
|
|
||||||
public GpuVa DstAddress;
|
|
||||||
public int DstStride;
|
|
||||||
public MemoryLayout DstMemoryLayout;
|
|
||||||
public int DstWidth;
|
|
||||||
public int DstHeight;
|
|
||||||
public int DstDepth;
|
|
||||||
public int DstZ;
|
|
||||||
public int DstX;
|
|
||||||
public int DstY;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
struct LogicalOpState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public Boolean32 Enable;
|
|
||||||
public LogicalOp LogicalOp;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// GPU method offset.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This is indexed in 32 bits word.
|
|
||||||
/// </remarks>
|
|
||||||
enum MethodOffset
|
|
||||||
{
|
|
||||||
BindChannel = 0x0,
|
|
||||||
I2mParams = 0x60,
|
|
||||||
LaunchDma = 0x6c,
|
|
||||||
LoadInlineData = 0x6d,
|
|
||||||
CopyDstTexture = 0x80,
|
|
||||||
EarlyZForce = 0x84,
|
|
||||||
CopySrcTexture = 0x8c,
|
|
||||||
DispatchParamsAddress = 0xad,
|
|
||||||
Dispatch = 0xaf,
|
|
||||||
SyncpointAction = 0xb2,
|
|
||||||
CopyBuffer = 0xc0,
|
|
||||||
RasterizeEnable = 0xdf,
|
|
||||||
TfBufferState = 0xe0,
|
|
||||||
CopyBufferParams = 0x100,
|
|
||||||
TfState = 0x1c0,
|
|
||||||
CopyBufferConstA = 0x1c0,
|
|
||||||
CopyBufferConstB = 0x1c1,
|
|
||||||
CopyBufferSwizzle = 0x1c2,
|
|
||||||
CopyBufferDstTexture = 0x1c3,
|
|
||||||
CopyBufferSrcTexture = 0x1ca,
|
|
||||||
TfEnable = 0x1d1,
|
|
||||||
RtColorState = 0x200,
|
|
||||||
CopyTextureControl = 0x223,
|
|
||||||
CopyRegion = 0x22c,
|
|
||||||
CopyTexture = 0x237,
|
|
||||||
ViewportTransform = 0x280,
|
|
||||||
ViewportExtents = 0x300,
|
|
||||||
VertexBufferDrawState = 0x35d,
|
|
||||||
DepthMode = 0x35f,
|
|
||||||
ClearColors = 0x360,
|
|
||||||
ClearDepthValue = 0x364,
|
|
||||||
ClearStencilValue = 0x368,
|
|
||||||
DepthBiasState = 0x370,
|
|
||||||
TextureBarrier = 0x378,
|
|
||||||
ScissorState = 0x380,
|
|
||||||
StencilBackMasks = 0x3d5,
|
|
||||||
InvalidateTextures = 0x3dd,
|
|
||||||
TextureBarrierTiled = 0x3df,
|
|
||||||
RtColorMaskShared = 0x3e4,
|
|
||||||
RtDepthStencilState = 0x3f8,
|
|
||||||
ScreenScissorState = 0x3fd,
|
|
||||||
VertexAttribState = 0x458,
|
|
||||||
RtControl = 0x487,
|
|
||||||
RtDepthStencilSize = 0x48a,
|
|
||||||
SamplerIndex = 0x48d,
|
|
||||||
DepthTestEnable = 0x4b3,
|
|
||||||
BlendIndependent = 0x4b9,
|
|
||||||
DepthWriteEnable = 0x4ba,
|
|
||||||
AlphaTestEnable = 0x4bb,
|
|
||||||
VbElementU8 = 0x4c1,
|
|
||||||
DepthTestFunc = 0x4c3,
|
|
||||||
AlphaTestRef = 0x4c4,
|
|
||||||
AlphaTestFunc = 0x4c5,
|
|
||||||
BlendConstant = 0x4c7,
|
|
||||||
BlendStateCommon = 0x4cf,
|
|
||||||
BlendEnableCommon = 0x4d7,
|
|
||||||
BlendEnable = 0x4d8,
|
|
||||||
StencilTestState = 0x4e0,
|
|
||||||
YControl = 0x4eb,
|
|
||||||
LineWidthSmooth = 0x4ec,
|
|
||||||
LineWidthAliased = 0x4ed,
|
|
||||||
FirstVertex = 0x50d,
|
|
||||||
FirstInstance = 0x50e,
|
|
||||||
ClipDistanceEnable = 0x544,
|
|
||||||
PointSize = 0x546,
|
|
||||||
PointSpriteEnable = 0x548,
|
|
||||||
ResetCounter = 0x54c,
|
|
||||||
RtDepthStencilEnable = 0x54e,
|
|
||||||
ConditionState = 0x554,
|
|
||||||
SamplerPoolState = 0x557,
|
|
||||||
DepthBiasFactor = 0x55b,
|
|
||||||
LineSmoothEnable = 0x55c,
|
|
||||||
TexturePoolState = 0x55d,
|
|
||||||
StencilBackTestState = 0x565,
|
|
||||||
DepthBiasUnits = 0x56f,
|
|
||||||
RtMsaaMode = 0x574,
|
|
||||||
VbElementU32 = 0x57a,
|
|
||||||
VbElementU16 = 0x57c,
|
|
||||||
PointCoordReplace = 0x581,
|
|
||||||
ShaderBaseAddress = 0x582,
|
|
||||||
DrawEnd = 0x585,
|
|
||||||
DrawBegin = 0x586,
|
|
||||||
PrimitiveRestartState = 0x591,
|
|
||||||
IndexBufferState = 0x5f2,
|
|
||||||
IndexBufferCount = 0x5f8,
|
|
||||||
DrawIndexedSmall = 0x5f9,
|
|
||||||
DrawIndexedSmall2 = 0x5fa,
|
|
||||||
DrawIndexedSmallIncInstance = 0x5fc,
|
|
||||||
DrawIndexedSmallIncInstance2 = 0x5fd,
|
|
||||||
DepthBiasClamp = 0x61f,
|
|
||||||
VertexBufferInstanced = 0x620,
|
|
||||||
VertexProgramPointSize = 0x644,
|
|
||||||
FaceState = 0x646,
|
|
||||||
ViewportTransformEnable = 0x64b,
|
|
||||||
ViewVolumeClipControl = 0x64f,
|
|
||||||
PrimitiveTypeOverride = 0x65c,
|
|
||||||
LogicOpState = 0x671,
|
|
||||||
Clear = 0x674,
|
|
||||||
RtColorMask = 0x680,
|
|
||||||
ReportState = 0x6c0,
|
|
||||||
Report = 0x6c3,
|
|
||||||
VertexBufferState = 0x700,
|
|
||||||
BlendState = 0x780,
|
|
||||||
VertexBufferEndAddress = 0x7c0,
|
|
||||||
ShaderState = 0x800,
|
|
||||||
FirmwareCall0 = 0x8c0,
|
|
||||||
FirmwareCall1 = 0x8c1,
|
|
||||||
FirmwareCall2 = 0x8c2,
|
|
||||||
FirmwareCall3 = 0x8c3,
|
|
||||||
FirmwareCall4 = 0x8c4,
|
|
||||||
FirmwareCall5 = 0x8c5,
|
|
||||||
FirmwareCall6 = 0x8c6,
|
|
||||||
FirmwareCall7 = 0x8c7,
|
|
||||||
UniformBufferState = 0x8e0,
|
|
||||||
UniformBufferUpdateData = 0x8e4,
|
|
||||||
UniformBufferBindVertex = 0x904,
|
|
||||||
UniformBufferBindTessControl = 0x90c,
|
|
||||||
UniformBufferBindTessEvaluation = 0x914,
|
|
||||||
UniformBufferBindGeometry = 0x91c,
|
|
||||||
UniformBufferBindFragment = 0x924,
|
|
||||||
TextureBufferIndex = 0x982,
|
|
||||||
TfVaryingLocations = 0xa00
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Texture or sampler pool state.
|
|
||||||
/// </summary>
|
|
||||||
struct PoolState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public GpuVa Address;
|
|
||||||
public int MaximumId;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Primitive restart state.
|
|
||||||
/// </summary>
|
|
||||||
struct PrimitiveRestartState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public Boolean32 Enable;
|
|
||||||
public int Index;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Counter type for GPU counter reporting.
|
|
||||||
/// </summary>
|
|
||||||
enum ReportCounterType
|
|
||||||
{
|
|
||||||
Zero = 0,
|
|
||||||
InputVertices = 1,
|
|
||||||
InputPrimitives = 3,
|
|
||||||
VertexShaderInvocations = 5,
|
|
||||||
GeometryShaderInvocations = 7,
|
|
||||||
GeometryShaderPrimitives = 9,
|
|
||||||
ZcullStats0 = 0xa,
|
|
||||||
TransformFeedbackPrimitivesWritten = 0xb,
|
|
||||||
ZcullStats1 = 0xc,
|
|
||||||
ZcullStats2 = 0xe,
|
|
||||||
ClipperInputPrimitives = 0xf,
|
|
||||||
ZcullStats3 = 0x10,
|
|
||||||
ClipperOutputPrimitives = 0x11,
|
|
||||||
PrimitivesGenerated = 0x12,
|
|
||||||
FragmentShaderInvocations = 0x13,
|
|
||||||
SamplesPassed = 0x15,
|
|
||||||
TransformFeedbackOffset = 0x1a,
|
|
||||||
TessControlShaderInvocations = 0x1b,
|
|
||||||
TessEvaluationShaderInvocations = 0x1d,
|
|
||||||
TessEvaluationShaderPrimitives = 0x1f
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Counter type for GPU counter reset.
|
|
||||||
/// </summary>
|
|
||||||
enum ResetCounterType
|
|
||||||
{
|
|
||||||
SamplesPassed = 1,
|
|
||||||
ZcullStats = 2,
|
|
||||||
TransformFeedbackPrimitivesWritten = 0x10,
|
|
||||||
InputVertices = 0x12,
|
|
||||||
InputPrimitives = 0x13,
|
|
||||||
VertexShaderInvocations = 0x15,
|
|
||||||
TessControlShaderInvocations = 0x16,
|
|
||||||
TessEvaluationShaderInvocations = 0x17,
|
|
||||||
TessEvaluationShaderPrimitives = 0x18,
|
|
||||||
GeometryShaderInvocations = 0x1a,
|
|
||||||
GeometryShaderPrimitives = 0x1b,
|
|
||||||
ClipperInputPrimitives = 0x1c,
|
|
||||||
ClipperOutputPrimitives = 0x1d,
|
|
||||||
FragmentShaderInvocations = 0x1e,
|
|
||||||
PrimitivesGenerated = 0x1f
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Render target color buffer mask.
|
|
||||||
/// This defines which color channels are written to the color buffer.
|
|
||||||
/// </summary>
|
|
||||||
struct RtColorMask
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public uint Packed;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unpacks red channel enable.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True to write the new red channel color, false to keep the old value</returns>
|
|
||||||
public bool UnpackRed()
|
|
||||||
{
|
|
||||||
return (Packed & 0x1) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unpacks green channel enable.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True to write the new green channel color, false to keep the old value</returns>
|
|
||||||
public bool UnpackGreen()
|
|
||||||
{
|
|
||||||
return (Packed & 0x10) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unpacks blue channel enable.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True to write the new blue channel color, false to keep the old value</returns>
|
|
||||||
public bool UnpackBlue()
|
|
||||||
{
|
|
||||||
return (Packed & 0x100) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unpacks alpha channel enable.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True to write the new alpha channel color, false to keep the old value</returns>
|
|
||||||
public bool UnpackAlpha()
|
|
||||||
{
|
|
||||||
return (Packed & 0x1000) != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Render target color buffer state.
|
|
||||||
/// </summary>
|
|
||||||
struct RtColorState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public GpuVa Address;
|
|
||||||
public int WidthOrStride;
|
|
||||||
public int Height;
|
|
||||||
public ColorFormat Format;
|
|
||||||
public MemoryLayout MemoryLayout;
|
|
||||||
public int Depth;
|
|
||||||
public int LayerSize;
|
|
||||||
public int BaseLayer;
|
|
||||||
public int Unknown0x24;
|
|
||||||
public int Padding0;
|
|
||||||
public int Padding1;
|
|
||||||
public int Padding2;
|
|
||||||
public int Padding3;
|
|
||||||
public int Padding4;
|
|
||||||
public int Padding5;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Render target draw buffers control.
|
|
||||||
/// </summary>
|
|
||||||
struct RtControl
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public uint Packed;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unpacks the number of active draw buffers.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Number of active draw buffers</returns>
|
|
||||||
public int UnpackCount()
|
|
||||||
{
|
|
||||||
return (int)(Packed & 0xf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unpacks the color attachment index for a given draw buffer.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">Index of the draw buffer</param>
|
|
||||||
/// <returns>Attachment index</returns>
|
|
||||||
public int UnpackPermutationIndex(int index)
|
|
||||||
{
|
|
||||||
return (int)((Packed >> (4 + index * 3)) & 7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Render target depth-stencil buffer state.
|
|
||||||
/// </summary>
|
|
||||||
struct RtDepthStencilState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public GpuVa Address;
|
|
||||||
public ZetaFormat Format;
|
|
||||||
public MemoryLayout MemoryLayout;
|
|
||||||
public int LayerSize;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
struct ScissorState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public Boolean32 Enable;
|
|
||||||
public ushort X1;
|
|
||||||
public ushort X2;
|
|
||||||
public ushort Y1;
|
|
||||||
public ushort Y2;
|
|
||||||
public uint Padding;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
struct ScreenScissorState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public ushort X;
|
|
||||||
public ushort Width;
|
|
||||||
public ushort Y;
|
|
||||||
public ushort Height;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// GPU semaphore operation.
|
|
||||||
/// </summary>
|
|
||||||
enum SemaphoreOperation
|
|
||||||
{
|
|
||||||
Release = 0,
|
|
||||||
Acquire = 1,
|
|
||||||
Counter = 2
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// GPU semaphore state.
|
|
||||||
/// </summary>
|
|
||||||
struct SemaphoreState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public GpuVa Address;
|
|
||||||
public int Payload;
|
|
||||||
public uint Control;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Graphics shader stage state.
|
|
||||||
/// </summary>
|
|
||||||
struct ShaderState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public uint Control;
|
|
||||||
public uint Offset;
|
|
||||||
public uint Unknown0x8;
|
|
||||||
public int MaxRegisters;
|
|
||||||
public ShaderType Type;
|
|
||||||
public uint Unknown0x14;
|
|
||||||
public uint Unknown0x18;
|
|
||||||
public uint Unknown0x1c;
|
|
||||||
public uint Unknown0x20;
|
|
||||||
public uint Unknown0x24;
|
|
||||||
public uint Unknown0x28;
|
|
||||||
public uint Unknown0x2c;
|
|
||||||
public uint Unknown0x30;
|
|
||||||
public uint Unknown0x34;
|
|
||||||
public uint Unknown0x38;
|
|
||||||
public uint Unknown0x3c;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unpacks shader enable information.
|
|
||||||
/// Must be ignored for vertex shaders, those are always enabled.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if the stage is enabled, false otherwise</returns>
|
|
||||||
public bool UnpackEnable()
|
|
||||||
{
|
|
||||||
return (Control & 1) != 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Shader stage name.
|
|
||||||
/// </summary>
|
|
||||||
enum ShaderType
|
|
||||||
{
|
|
||||||
Vertex,
|
|
||||||
TessellationControl,
|
|
||||||
TessellationEvaluation,
|
|
||||||
Geometry,
|
|
||||||
Fragment
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 3D, 2D or 1D texture size.
|
|
||||||
/// </summary>
|
|
||||||
struct Size3D
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public int Width;
|
|
||||||
public int Height;
|
|
||||||
public int Depth;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Stencil test masks for back tests.
|
|
||||||
/// </summary>
|
|
||||||
struct StencilBackMasks
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public int FuncRef;
|
|
||||||
public int Mask;
|
|
||||||
public int FuncMask;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Stencil back test state.
|
|
||||||
/// </summary>
|
|
||||||
struct StencilBackTestState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public Boolean32 TwoSided;
|
|
||||||
public StencilOp BackSFail;
|
|
||||||
public StencilOp BackDpFail;
|
|
||||||
public StencilOp BackDpPass;
|
|
||||||
public CompareOp BackFunc;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Stencil front test state and masks.
|
|
||||||
/// </summary>
|
|
||||||
struct StencilTestState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public Boolean32 Enable;
|
|
||||||
public StencilOp FrontSFail;
|
|
||||||
public StencilOp FrontDpFail;
|
|
||||||
public StencilOp FrontDpPass;
|
|
||||||
public CompareOp FrontFunc;
|
|
||||||
public int FrontFuncRef;
|
|
||||||
public int FrontFuncMask;
|
|
||||||
public int FrontMask;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Transform feedback buffer state.
|
|
||||||
/// </summary>
|
|
||||||
struct TfBufferState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public Boolean32 Enable;
|
|
||||||
public GpuVa Address;
|
|
||||||
public int Size;
|
|
||||||
public int Offset;
|
|
||||||
public uint Padding0;
|
|
||||||
public uint Padding1;
|
|
||||||
public uint Padding2;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
namespace Ryujinx.Graphics.Gpu.State
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Transform feedback state.
|
|
||||||
/// </summary>
|
|
||||||
struct TfState
|
|
||||||
{
|
|
||||||
#pragma warning disable CS0649
|
|
||||||
public int BufferIndex;
|
|
||||||
public int VaryingsCount;
|
|
||||||
public int Stride;
|
|
||||||
public uint Padding;
|
|
||||||
#pragma warning restore CS0649
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue