Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
66e7fdb871 | |||
2bb9b33da1 | |||
1080f64df9 | |||
c48a75979f | |||
842cb26ba5 | |||
e235d5e7bb |
16
ARMeilleure/Decoders/OpCode32Mrs.cs
Normal file
16
ARMeilleure/Decoders/OpCode32Mrs.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32Mrs : OpCode32
|
||||
{
|
||||
public bool R { get; }
|
||||
public int Rd { get; }
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32Mrs(inst, address, opCode);
|
||||
|
||||
public OpCode32Mrs(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
|
||||
{
|
||||
R = ((opCode >> 22) & 1) != 0;
|
||||
Rd = (opCode >> 12) & 0xf;
|
||||
}
|
||||
}
|
||||
}
|
@ -704,6 +704,7 @@ namespace ARMeilleure.Decoders
|
||||
SetA32("<<<<00110100xxxxxxxxxxxxxxxxxxxx", InstName.Movt, InstEmit32.Movt, OpCode32AluImm16.Create);
|
||||
SetA32("<<<<1110xxx1xxxxxxxx111xxxx1xxxx", InstName.Mrc, InstEmit32.Mrc, OpCode32System.Create);
|
||||
SetA32("<<<<11000101xxxxxxxx111xxxxxxxxx", InstName.Mrrc, InstEmit32.Mrrc, OpCode32System.Create);
|
||||
SetA32("<<<<00010x001111xxxx000000000000", InstName.Mrs, InstEmit32.Mrs, OpCode32Mrs.Create);
|
||||
SetA32("<<<<00010x10xxxx111100000000xxxx", InstName.Msr, InstEmit32.Msr, OpCode32MsrReg.Create);
|
||||
SetA32("<<<<0000000xxxxx0000xxxx1001xxxx", InstName.Mul, InstEmit32.Mul, OpCode32AluMla.Create);
|
||||
SetA32("<<<<0011111x0000xxxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, OpCode32AluImm.Create);
|
||||
@ -796,6 +797,10 @@ namespace ARMeilleure.Decoders
|
||||
SetA32("111100111x110000xxx0001100x0xxx0", InstName.Aese_V, InstEmit32.Aese_V, OpCode32Simd.Create);
|
||||
SetA32("111100111x110000xxx0001111x0xxx0", InstName.Aesimc_V, InstEmit32.Aesimc_V, OpCode32Simd.Create);
|
||||
SetA32("111100111x110000xxx0001110x0xxx0", InstName.Aesmc_V, InstEmit32.Aesmc_V, OpCode32Simd.Create);
|
||||
SetA32("111100110x00xxx0xxx01100x1x0xxx0", InstName.Sha256h_V, InstEmit32.Sha256h_V, OpCode32SimdReg.Create);
|
||||
SetA32("111100110x01xxx0xxx01100x1x0xxx0", InstName.Sha256h2_V, InstEmit32.Sha256h2_V, OpCode32SimdReg.Create);
|
||||
SetA32("111100111x111010xxx0001111x0xxx0", InstName.Sha256su0_V, InstEmit32.Sha256su0_V, OpCode32Simd.Create);
|
||||
SetA32("111100110x10xxx0xxx01100x1x0xxx0", InstName.Sha256su1_V, InstEmit32.Sha256su1_V, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x0x<<xxxxxxxx0111xxx0xxxx", InstName.Vabd, InstEmit32.Vabd_I, OpCode32SimdReg.Create);
|
||||
SetA32("1111001x1x<<xxxxxxxx0111x0x0xxxx", InstName.Vabdl, InstEmit32.Vabdl_I, OpCode32SimdRegLong.Create);
|
||||
SetA32("<<<<11101x110000xxxx101x11x0xxxx", InstName.Vabs, InstEmit32.Vabs_S, OpCode32SimdS.Create);
|
||||
|
64
ARMeilleure/Instructions/InstEmitSimdHash32.cs
Normal file
64
ARMeilleure/Instructions/InstEmitSimdHash32.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using ARMeilleure.Decoders;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
|
||||
using static ARMeilleure.Instructions.InstEmitHelper;
|
||||
|
||||
namespace ARMeilleure.Instructions
|
||||
{
|
||||
static partial class InstEmit32
|
||||
{
|
||||
#region "Sha256"
|
||||
public static void Sha256h_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand n = GetVecA32(op.Qn);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
|
||||
Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashLower)), d, n, m);
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Sha256h2_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand n = GetVecA32(op.Qn);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
|
||||
Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.HashUpper)), d, n, m);
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Sha256su0_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Simd op = (OpCode32Simd)context.CurrOp;
|
||||
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
|
||||
Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart1)), d, m);
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
|
||||
public static void Sha256su1_V(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
|
||||
|
||||
Operand d = GetVecA32(op.Qd);
|
||||
Operand n = GetVecA32(op.Qn);
|
||||
Operand m = GetVecA32(op.Qm);
|
||||
|
||||
Operand res = context.Call(typeof(SoftFallback).GetMethod(nameof(SoftFallback.Sha256SchedulePart2)), d, n, m);
|
||||
|
||||
context.Copy(GetVecA32(op.Qd), res);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -169,6 +169,31 @@ namespace ARMeilleure.Instructions
|
||||
SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32))));
|
||||
}
|
||||
|
||||
public static void Mrs(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32Mrs op = (OpCode32Mrs)context.CurrOp;
|
||||
|
||||
if (op.R)
|
||||
{
|
||||
throw new NotImplementedException("SPSR");
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand vSh = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
|
||||
Operand cSh = context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag));
|
||||
Operand zSh = context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag));
|
||||
Operand nSh = context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag));
|
||||
Operand qSh = context.ShiftLeft(GetFlag(PState.QFlag), Const((int)PState.QFlag));
|
||||
|
||||
Operand spsr = context.BitwiseOr(context.BitwiseOr(nSh, zSh), context.BitwiseOr(cSh, vSh));
|
||||
spsr = context.BitwiseOr(spsr, qSh);
|
||||
|
||||
// TODO: Remaining flags.
|
||||
|
||||
SetIntA32(context, op.Rd, spsr);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Msr(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32MsrReg op = (OpCode32MsrReg)context.CurrOp;
|
||||
|
@ -11,10 +11,11 @@ namespace Ryujinx.Graphics.GAL
|
||||
|
||||
void ClearBuffer(BufferHandle destination, int offset, int size, uint value);
|
||||
|
||||
void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color);
|
||||
void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color);
|
||||
|
||||
void ClearRenderTargetDepthStencil(
|
||||
int layer,
|
||||
int layerCount,
|
||||
float depthValue,
|
||||
bool depthMask,
|
||||
int stencilValue,
|
||||
|
@ -5,20 +5,22 @@
|
||||
public CommandType CommandType => CommandType.ClearRenderTargetColor;
|
||||
private int _index;
|
||||
private int _layer;
|
||||
private int _layerCount;
|
||||
private uint _componentMask;
|
||||
private ColorF _color;
|
||||
|
||||
public void Set(int index, int layer, uint componentMask, ColorF color)
|
||||
public void Set(int index, int layer, int layerCount, uint componentMask, ColorF color)
|
||||
{
|
||||
_index = index;
|
||||
_layer = layer;
|
||||
_layerCount = layerCount;
|
||||
_componentMask = componentMask;
|
||||
_color = color;
|
||||
}
|
||||
|
||||
public static void Run(ref ClearRenderTargetColorCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.ClearRenderTargetColor(command._index, command._layer, command._componentMask, command._color);
|
||||
renderer.Pipeline.ClearRenderTargetColor(command._index, command._layer, command._layerCount, command._componentMask, command._color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,14 +4,16 @@
|
||||
{
|
||||
public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil;
|
||||
private int _layer;
|
||||
private int _layerCount;
|
||||
private float _depthValue;
|
||||
private bool _depthMask;
|
||||
private int _stencilValue;
|
||||
private int _stencilMask;
|
||||
|
||||
public void Set(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||
public void Set(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||
{
|
||||
_layer = layer;
|
||||
_layerCount = layerCount;
|
||||
_depthValue = depthValue;
|
||||
_depthMask = depthMask;
|
||||
_stencilValue = stencilValue;
|
||||
@ -20,7 +22,7 @@
|
||||
|
||||
public static void Run(ref ClearRenderTargetDepthStencilCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.ClearRenderTargetDepthStencil(command._layer, command._depthValue, command._depthMask, command._stencilValue, command._stencilMask);
|
||||
renderer.Pipeline.ClearRenderTargetDepthStencil(command._layer, command._layerCount, command._depthValue, command._depthMask, command._stencilValue, command._stencilMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,15 +41,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color)
|
||||
public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color)
|
||||
{
|
||||
_renderer.New<ClearRenderTargetColorCommand>().Set(index, layer, componentMask, color);
|
||||
_renderer.New<ClearRenderTargetColorCommand>().Set(index, layer, layerCount, componentMask, color);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||
public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||
{
|
||||
_renderer.New<ClearRenderTargetDepthStencilCommand>().Set(layer, depthValue, depthMask, stencilValue, stencilMask);
|
||||
_renderer.New<ClearRenderTargetDepthStencilCommand>().Set(layer, layerCount, depthValue, depthMask, stencilValue, stencilMask);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@ using Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Resources.Programs;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
@ -12,6 +12,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
/// </summary>
|
||||
class MacroHLE : IMacroEE
|
||||
{
|
||||
private const int ColorLayerCountOffset = 0x818;
|
||||
private const int ColorStructSize = 0x40;
|
||||
private const int ZetaLayerCountOffset = 0x1230;
|
||||
|
||||
private readonly GPFifoProcessor _processor;
|
||||
private readonly MacroHLEFunctionName _functionName;
|
||||
|
||||
@ -45,6 +49,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
{
|
||||
switch (_functionName)
|
||||
{
|
||||
case MacroHLEFunctionName.ClearColor:
|
||||
ClearColor(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.ClearDepthStencil:
|
||||
ClearDepthStencil(state, arg0);
|
||||
break;
|
||||
case MacroHLEFunctionName.MultiDrawElementsIndirectCount:
|
||||
MultiDrawElementsIndirectCount(state, arg0);
|
||||
break;
|
||||
@ -53,6 +63,31 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears one bound color target.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void ClearColor(IDeviceState state, int arg0)
|
||||
{
|
||||
int index = (arg0 >> 6) & 0xf;
|
||||
int layerCount = state.Read(ColorLayerCountOffset + index * ColorStructSize);
|
||||
|
||||
_processor.ThreedClass.Clear(arg0, layerCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current depth-stencil target.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state at the time of the call</param>
|
||||
/// <param name="arg0">First argument of the call</param>
|
||||
private void ClearDepthStencil(IDeviceState state, int arg0)
|
||||
{
|
||||
int layerCount = state.Read(ZetaLayerCountOffset);
|
||||
|
||||
_processor.ThreedClass.Clear(arg0, layerCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indirect multi-draw, with parameters from a GPU buffer.
|
||||
/// </summary>
|
||||
|
@ -6,6 +6,8 @@
|
||||
enum MacroHLEFunctionName
|
||||
{
|
||||
None,
|
||||
ClearColor,
|
||||
ClearDepthStencil,
|
||||
MultiDrawElementsIndirectCount
|
||||
}
|
||||
}
|
||||
|
@ -46,12 +46,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
||||
|
||||
private static readonly TableEntry[] Table = new TableEntry[]
|
||||
{
|
||||
new TableEntry(MacroHLEFunctionName.ClearColor, new Hash128(0xA9FB28D1DC43645A, 0xB177E5D2EAE67FB0), 0x28),
|
||||
new TableEntry(MacroHLEFunctionName.ClearDepthStencil, new Hash128(0x1B96CB77D4879F4F, 0x8557032FE0C965FB), 0x24),
|
||||
new TableEntry(MacroHLEFunctionName.MultiDrawElementsIndirectCount, new Hash128(0x890AF57ED3FB1C37, 0x35D0C95C61F5386F), 0x19C)
|
||||
};
|
||||
|
||||
private static bool IsMacroHLESupported(Capabilities caps, MacroHLEFunctionName name)
|
||||
{
|
||||
if (name == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
|
||||
if (name == MacroHLEFunctionName.ClearColor ||
|
||||
name == MacroHLEFunctionName.ClearDepthStencil)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (name == MacroHLEFunctionName.MultiDrawElementsIndirectCount)
|
||||
{
|
||||
return caps.SupportsIndirectParameters;
|
||||
}
|
||||
|
@ -487,11 +487,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current color and depth-stencil buffers.
|
||||
/// Which buffers should be cleared is also specified on the argument.
|
||||
/// Which buffers should be cleared can also be specified with 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)
|
||||
{
|
||||
Clear(engine, argument, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current color and depth-stencil buffers.
|
||||
/// Which buffers should be cleared can also specified with the arguments.
|
||||
/// </summary>
|
||||
/// <param name="engine">3D engine where this method is being called</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
/// <param name="layerCount">For array and 3D textures, indicates how many layers should be cleared</param>
|
||||
public void Clear(ThreedClass engine, int argument, int layerCount)
|
||||
{
|
||||
ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
|
||||
_context,
|
||||
@ -507,7 +519,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
int index = (argument >> 6) & 0xf;
|
||||
int layer = (argument >> 10) & 0x3ff;
|
||||
|
||||
engine.UpdateRenderTargetState(useControl: false, layered: layer != 0, singleUse: index);
|
||||
engine.UpdateRenderTargetState(useControl: false, layered: layer != 0 || layerCount > 1, singleUse: index);
|
||||
|
||||
// If there is a mismatch on the host clip region and the one explicitly defined by the guest
|
||||
// on the screen scissor state, then we need to force only one texture to be bound to avoid
|
||||
@ -578,7 +590,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
bool clearDepth = (argument & 1) != 0;
|
||||
bool clearStencil = (argument & 2) != 0;
|
||||
|
||||
uint componentMask = (uint)((argument >> 2) & 0xf);
|
||||
|
||||
if (componentMask != 0)
|
||||
@ -587,7 +598,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha);
|
||||
|
||||
_context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, componentMask, color);
|
||||
_context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, layerCount, componentMask, color);
|
||||
}
|
||||
|
||||
if (clearDepth || clearStencil)
|
||||
@ -609,6 +620,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
_context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
|
||||
layer,
|
||||
layerCount,
|
||||
depthValue,
|
||||
clearDepth,
|
||||
stencilValue,
|
||||
|
@ -497,6 +497,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current color and depth-stencil buffers.
|
||||
/// Which buffers should be cleared can also specified with the arguments.
|
||||
/// </summary>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
/// <param name="layerCount">For array and 3D textures, indicates how many layers should be cleared</param>
|
||||
public void Clear(int argument, int layerCount)
|
||||
{
|
||||
_drawManager.Clear(this, argument, layerCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a indirect multi-draw, with parameters from a GPU buffer.
|
||||
/// </summary>
|
||||
|
@ -50,8 +50,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
public int InvalidatedSequence;
|
||||
public Texture CachedTexture;
|
||||
public Sampler CachedSampler;
|
||||
public int ScaleIndex;
|
||||
public TextureUsageFlags UsageFlags;
|
||||
}
|
||||
|
||||
private TextureState[] _textureState;
|
||||
@ -535,14 +533,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
// The texture is already bound.
|
||||
state.CachedTexture.SynchronizeMemory();
|
||||
|
||||
if ((state.ScaleIndex != index || state.UsageFlags != usageFlags) &&
|
||||
if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
|
||||
UpdateScale(state.CachedTexture, usageFlags, index, stage))
|
||||
{
|
||||
ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
|
||||
|
||||
state.Texture = hostTextureRebind;
|
||||
state.ScaleIndex = index;
|
||||
state.UsageFlags = usageFlags;
|
||||
|
||||
_context.Renderer.Pipeline.SetTextureAndSampler(stage, bindingInfo.Binding, hostTextureRebind, state.Sampler);
|
||||
}
|
||||
@ -573,7 +569,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
bool textureOrSamplerChanged = state.Texture != hostTexture || state.Sampler != hostSampler;
|
||||
|
||||
if ((state.ScaleIndex != index || state.UsageFlags != usageFlags || textureOrSamplerChanged) &&
|
||||
if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
|
||||
UpdateScale(texture, usageFlags, index, stage))
|
||||
{
|
||||
hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||
@ -583,9 +579,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
if (textureOrSamplerChanged)
|
||||
{
|
||||
state.Texture = hostTexture;
|
||||
state.ScaleIndex = index;
|
||||
state.UsageFlags = usageFlags;
|
||||
|
||||
state.Sampler = hostSampler;
|
||||
|
||||
_context.Renderer.Pipeline.SetTextureAndSampler(stage, bindingInfo.Binding, hostTexture, hostSampler);
|
||||
@ -666,7 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
cachedTexture?.SignalModified();
|
||||
}
|
||||
|
||||
if ((state.ScaleIndex != scaleIndex || state.UsageFlags != usageFlags) &&
|
||||
if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
|
||||
UpdateScale(state.CachedTexture, usageFlags, scaleIndex, stage))
|
||||
{
|
||||
ITexture hostTextureRebind = state.CachedTexture.GetTargetTexture(bindingInfo.Target);
|
||||
@ -674,8 +667,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
Format format = bindingInfo.Format == 0 ? cachedTexture.Format : bindingInfo.Format;
|
||||
|
||||
state.Texture = hostTextureRebind;
|
||||
state.ScaleIndex = scaleIndex;
|
||||
state.UsageFlags = usageFlags;
|
||||
|
||||
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTextureRebind, format);
|
||||
}
|
||||
@ -713,7 +704,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
texture?.SignalModified();
|
||||
}
|
||||
|
||||
if ((state.ScaleIndex != scaleIndex || state.UsageFlags != usageFlags || state.Texture != hostTexture) &&
|
||||
if ((usageFlags & TextureUsageFlags.NeedsScaleValue) != 0 &&
|
||||
UpdateScale(texture, usageFlags, scaleIndex, stage))
|
||||
{
|
||||
hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
|
||||
@ -722,8 +713,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
if (state.Texture != hostTexture)
|
||||
{
|
||||
state.Texture = hostTexture;
|
||||
state.ScaleIndex = scaleIndex;
|
||||
state.UsageFlags = usageFlags;
|
||||
|
||||
Format format = bindingInfo.Format;
|
||||
|
||||
|
@ -434,7 +434,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
_needsHostRegen = true;
|
||||
}
|
||||
|
||||
_programList.Add(entry.ProgramIndex, (entry.CachedProgram, entry.BinaryCode));
|
||||
// Fetch the binary code from the backend if it isn't already present.
|
||||
byte[] binaryCode = entry.BinaryCode ?? entry.CachedProgram.HostProgram.GetBinary();
|
||||
|
||||
_programList.Add(entry.ProgramIndex, (entry.CachedProgram, binaryCode));
|
||||
SignalCompiled();
|
||||
}
|
||||
else if (entry.IsBinary)
|
||||
@ -502,7 +505,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources, shaderInfo);
|
||||
CachedShaderProgram program = new CachedShaderProgram(hostProgram, compilation.SpecializationState, compilation.Shaders);
|
||||
|
||||
byte[] binaryCode = _context.Capabilities.Api == TargetApi.Vulkan ? ShaderBinarySerializer.Pack(shaderSources) : hostProgram.GetBinary();
|
||||
// Vulkan's binary code is the SPIR-V used for compilation, so it is ready immediately. Other APIs get this after compilation.
|
||||
byte[] binaryCode = _context.Capabilities.Api == TargetApi.Vulkan ? ShaderBinarySerializer.Pack(shaderSources) : null;
|
||||
|
||||
EnqueueForValidation(new ProgramEntry(program, binaryCode, compilation.ProgramIndex, compilation.IsCompute, isBinary: false));
|
||||
}
|
||||
|
@ -145,6 +145,16 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
return format == Format.D16Unorm || format == Format.D32Float;
|
||||
}
|
||||
|
||||
public int GetColorLayerCount(int index)
|
||||
{
|
||||
return _colors[index]?.Info.GetDepthOrLayers() ?? 0;
|
||||
}
|
||||
|
||||
public int GetDepthStencilLayerCount()
|
||||
{
|
||||
return _depthStencil?.Info.GetDepthOrLayers() ?? 0;
|
||||
}
|
||||
|
||||
public void AttachColorLayerForClear(int index, int layer)
|
||||
{
|
||||
TextureView color = _colors[index];
|
||||
|
@ -110,8 +110,10 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
Buffer.Clear(destination, offset, size, value);
|
||||
}
|
||||
|
||||
public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color)
|
||||
public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color)
|
||||
{
|
||||
EnsureFramebuffer();
|
||||
|
||||
GL.ColorMask(
|
||||
index,
|
||||
(componentMask & 1) != 0,
|
||||
@ -119,19 +121,31 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
(componentMask & 4) != 0,
|
||||
(componentMask & 8) != 0);
|
||||
|
||||
_framebuffer.AttachColorLayerForClear(index, layer);
|
||||
|
||||
float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha };
|
||||
|
||||
if (layer != 0 || layerCount != _framebuffer.GetColorLayerCount(index))
|
||||
{
|
||||
for (int l = layer; l < layer + layerCount; l++)
|
||||
{
|
||||
_framebuffer.AttachColorLayerForClear(index, l);
|
||||
|
||||
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
|
||||
}
|
||||
|
||||
_framebuffer.DetachColorLayerForClear(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
|
||||
}
|
||||
|
||||
RestoreComponentMask(index);
|
||||
}
|
||||
|
||||
public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||
public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||
{
|
||||
EnsureFramebuffer();
|
||||
|
||||
bool stencilMaskChanged =
|
||||
stencilMask != 0 &&
|
||||
stencilMask != _stencilFrontMask;
|
||||
@ -148,8 +162,35 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
GL.DepthMask(depthMask);
|
||||
}
|
||||
|
||||
_framebuffer.AttachDepthStencilLayerForClear(layer);
|
||||
if (layer != 0 || layerCount != _framebuffer.GetDepthStencilLayerCount())
|
||||
{
|
||||
for (int l = layer; l < layer + layerCount; l++)
|
||||
{
|
||||
_framebuffer.AttachDepthStencilLayerForClear(l);
|
||||
|
||||
ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask);
|
||||
}
|
||||
|
||||
_framebuffer.DetachDepthStencilLayerForClear();
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask);
|
||||
}
|
||||
|
||||
if (stencilMaskChanged)
|
||||
{
|
||||
GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask);
|
||||
}
|
||||
|
||||
if (depthMaskChanged)
|
||||
{
|
||||
GL.DepthMask(_depthMask);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ClearDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||
{
|
||||
if (depthMask && stencilMask != 0)
|
||||
{
|
||||
GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue);
|
||||
@ -162,18 +203,6 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue);
|
||||
}
|
||||
|
||||
_framebuffer.DetachDepthStencilLayerForClear();
|
||||
|
||||
if (stencilMaskChanged)
|
||||
{
|
||||
GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask);
|
||||
}
|
||||
|
||||
if (depthMaskChanged)
|
||||
{
|
||||
GL.DepthMask(_depthMask);
|
||||
}
|
||||
}
|
||||
|
||||
public void CommandBufferBarrier()
|
||||
|
@ -149,14 +149,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return texture is TextureView view && view.Valid;
|
||||
}
|
||||
|
||||
public ClearRect GetClearRect(Rectangle<int> scissor, int layer)
|
||||
public ClearRect GetClearRect(Rectangle<int> scissor, int layer, int layerCount)
|
||||
{
|
||||
int x = scissor.X;
|
||||
int y = scissor.Y;
|
||||
int width = Math.Min((int)Width - scissor.X, scissor.Width);
|
||||
int height = Math.Min((int)Height - scissor.Y, scissor.Height);
|
||||
|
||||
return new ClearRect(new Rect2D(new Offset2D(x, y), new Extent2D((uint)width, (uint)height)), (uint)layer, 1);
|
||||
return new ClearRect(new Rect2D(new Offset2D(x, y), new Extent2D((uint)width, (uint)height)), (uint)layer, (uint)layerCount);
|
||||
}
|
||||
|
||||
public unsafe Auto<DisposableFramebuffer> Create(Vk api, CommandBufferScoped cbs, Auto<DisposableRenderPass> renderPass)
|
||||
|
@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (clearAlpha)
|
||||
{
|
||||
_pipeline.ClearRenderTargetColor(0, 0, new ColorF(0f, 0f, 0f, 1f));
|
||||
_pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
|
||||
}
|
||||
|
||||
_pipeline.SetViewports(viewports, false);
|
||||
|
@ -162,7 +162,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
size);
|
||||
}
|
||||
|
||||
public unsafe void ClearRenderTargetColor(int index, int layer, ColorF color)
|
||||
public unsafe void ClearRenderTargetColor(int index, int layer, int layerCount, ColorF color)
|
||||
{
|
||||
if (FramebufferParams == null || !FramebufferParams.IsValidColorAttachment(index))
|
||||
{
|
||||
@ -178,12 +178,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha));
|
||||
var attachment = new ClearAttachment(ImageAspectFlags.ImageAspectColorBit, (uint)index, clearValue);
|
||||
var clearRect = FramebufferParams?.GetClearRect(ClearScissor, layer) ?? default;
|
||||
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
|
||||
|
||||
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
||||
}
|
||||
|
||||
public unsafe void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||
public unsafe void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||
{
|
||||
// TODO: Use stencilMask (fully)
|
||||
|
||||
@ -208,7 +208,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
|
||||
var attachment = new ClearAttachment(flags, 0, clearValue);
|
||||
var clearRect = FramebufferParams?.GetClearRect(ClearScissor, layer) ?? default;
|
||||
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
|
||||
|
||||
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_pendingQueryCopies.Clear();
|
||||
}
|
||||
|
||||
public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color)
|
||||
public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color)
|
||||
{
|
||||
if (FramebufferParams == null)
|
||||
{
|
||||
@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearRenderTargetColor(index, layer, color);
|
||||
ClearRenderTargetColor(index, layer, layerCount, color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,6 +235,32 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
|
||||
return GetAddrInfoRequestImpl(context, outputBufferPosition, outputBufferSize, true, optionsBufferPosition, optionsBufferSize);
|
||||
}
|
||||
|
||||
[CommandHipc(14)] // 5.0.0+
|
||||
// ResolverSetOptionRequest(buffer<unknown, 5, 0>, u64 unknown, u64 pid_placeholder, pid) -> (i32 ret, u32 bsd_errno)
|
||||
public ResultCode ResolverSetOptionRequest(ServiceCtx context)
|
||||
{
|
||||
ulong bufferPosition = context.Request.SendBuff[0].Position;
|
||||
ulong bufferSize = context.Request.SendBuff[0].Size;
|
||||
|
||||
ulong unknown = context.RequestData.ReadUInt64();
|
||||
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
|
||||
context.Memory.Read(bufferPosition, buffer);
|
||||
|
||||
// TODO: Parse and use options.
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceSfdnsres, new { unknown });
|
||||
|
||||
NetDbError netDbErrorCode = NetDbError.Success;
|
||||
GaiError errno = GaiError.Success;
|
||||
|
||||
context.ResponseData.Write((int)errno);
|
||||
context.ResponseData.Write((int)netDbErrorCode);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private static ResultCode GetHostByNameRequestImpl(
|
||||
ServiceCtx context,
|
||||
ulong inputBufferPosition,
|
||||
@ -615,7 +641,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
|
||||
{
|
||||
context.ResponseData.Write((int)netDbErrorCode);
|
||||
context.ResponseData.Write((int)errno);
|
||||
context.ResponseData.Write((int)serializedSize);
|
||||
context.ResponseData.Write(serializedSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,6 +171,35 @@ namespace Ryujinx.Tests.Cpu
|
||||
private static readonly bool NoInfs = false;
|
||||
private static readonly bool NoNaNs = false;
|
||||
|
||||
[Test, Pairwise, Description("SHA256SU0.32 <Qd>, <Qm>")]
|
||||
public void Sha256su0_V([Values(0xF3BA03C0u)] uint opcode,
|
||||
[Values(0u)] uint rd,
|
||||
[Values(2u)] uint rm,
|
||||
[Values(0x9BCBBF7443FB4F91ul)] ulong z0,
|
||||
[Values(0x482C58A58CBCBD59ul)] ulong z1,
|
||||
[Values(0xA0099B803625F82Aul)] ulong a0,
|
||||
[Values(0x1AA3B0B4E1AB4C8Cul)] ulong a1,
|
||||
[Values(0x29A44D72598F15F3ul)] ulong resultL,
|
||||
[Values(0x74CED221E2793F07ul)] ulong resultH)
|
||||
{
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
|
||||
V128 v0 = MakeVectorE0E1(z0, z1);
|
||||
V128 v1 = MakeVectorE0E1(a0, a1);
|
||||
|
||||
ExecutionContext context = SingleOpcode(opcode, v0: v0, v1: v1, runUnicorn: false);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(GetVectorE0(context.GetV(0)), Is.EqualTo(resultL));
|
||||
Assert.That(GetVectorE1(context.GetV(0)), Is.EqualTo(resultH));
|
||||
});
|
||||
|
||||
// Unicorn does not yet support hash instructions in A32.
|
||||
// CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise]
|
||||
public void Vabs_Vneg_V_S8_S16_S32([ValueSource("_Vabs_Vneg_V_")] uint opcode,
|
||||
[Range(0u, 3u)] uint rd,
|
||||
|
@ -247,6 +247,108 @@ namespace Ryujinx.Tests.Cpu
|
||||
private static readonly bool NoInfs = false;
|
||||
private static readonly bool NoNaNs = false;
|
||||
|
||||
[Test, Pairwise, Description("SHA256H.32 <Qd>, <Qn>, <Qm>")]
|
||||
public void Sha256h_V([Values(0xF3000C40u)] uint opcode,
|
||||
[Values(0u)] uint rd,
|
||||
[Values(2u)] uint rn,
|
||||
[Values(4u)] uint rm,
|
||||
[Values(0xAEE65C11943FB939ul)] ulong z0,
|
||||
[Values(0xA89A87F110291DA3ul)] ulong z1,
|
||||
[Values(0xE9F766DB7A49EA7Dul)] ulong a0,
|
||||
[Values(0x3053F46B0C2F3507ul)] ulong a1,
|
||||
[Values(0x6E86A473B9D4A778ul)] ulong b0,
|
||||
[Values(0x7BE4F9E638156BB1ul)] ulong b1,
|
||||
[Values(0x1F1DC4A98DA9C132ul)] ulong resultL,
|
||||
[Values(0xDB9A2A7B47031A0Dul)] ulong resultH)
|
||||
{
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
|
||||
V128 v0 = MakeVectorE0E1(z0, z1);
|
||||
V128 v1 = MakeVectorE0E1(a0, a1);
|
||||
V128 v2 = MakeVectorE0E1(b0, b1);
|
||||
|
||||
ExecutionContext context = SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, runUnicorn: false);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(GetVectorE0(context.GetV(0)), Is.EqualTo(resultL));
|
||||
Assert.That(GetVectorE1(context.GetV(0)), Is.EqualTo(resultH));
|
||||
});
|
||||
|
||||
// Unicorn does not yet support hash instructions in A32.
|
||||
// CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("SHA256H2.32 <Qd>, <Qn>, <Qm>")]
|
||||
public void Sha256h2_V([Values(0xF3100C40u)] uint opcode,
|
||||
[Values(0u)] uint rd,
|
||||
[Values(2u)] uint rn,
|
||||
[Values(4u)] uint rm,
|
||||
[Values(0xAEE65C11943FB939ul)] ulong z0,
|
||||
[Values(0xA89A87F110291DA3ul)] ulong z1,
|
||||
[Values(0xE9F766DB7A49EA7Dul)] ulong a0,
|
||||
[Values(0x3053F46B0C2F3507ul)] ulong a1,
|
||||
[Values(0x6E86A473B9D4A778ul)] ulong b0,
|
||||
[Values(0x7BE4F9E638156BB1ul)] ulong b1,
|
||||
[Values(0x0A1177E9D9C9B611ul)] ulong resultL,
|
||||
[Values(0xF5A826404928A515ul)] ulong resultH)
|
||||
{
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
|
||||
V128 v0 = MakeVectorE0E1(z0, z1);
|
||||
V128 v1 = MakeVectorE0E1(a0, a1);
|
||||
V128 v2 = MakeVectorE0E1(b0, b1);
|
||||
|
||||
ExecutionContext context = SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, runUnicorn: false);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(GetVectorE0(context.GetV(0)), Is.EqualTo(resultL));
|
||||
Assert.That(GetVectorE1(context.GetV(0)), Is.EqualTo(resultH));
|
||||
});
|
||||
|
||||
// Unicorn does not yet support hash instructions in A32.
|
||||
// CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Test, Pairwise, Description("SHA256SU1.32 <Qd>, <Qn>, <Qm>")]
|
||||
public void Sha256su1_V([Values(0xF3200C40u)] uint opcode,
|
||||
[Values(0u)] uint rd,
|
||||
[Values(2u)] uint rn,
|
||||
[Values(4u)] uint rm,
|
||||
[Values(0xAEE65C11943FB939ul)] ulong z0,
|
||||
[Values(0xA89A87F110291DA3ul)] ulong z1,
|
||||
[Values(0xE9F766DB7A49EA7Dul)] ulong a0,
|
||||
[Values(0x3053F46B0C2F3507ul)] ulong a1,
|
||||
[Values(0x6E86A473B9D4A778ul)] ulong b0,
|
||||
[Values(0x7BE4F9E638156BB1ul)] ulong b1,
|
||||
[Values(0x9EE69CC896D7DE66ul)] ulong resultL,
|
||||
[Values(0x004A147155573E54ul)] ulong resultH)
|
||||
{
|
||||
opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
|
||||
opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
|
||||
opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
|
||||
|
||||
V128 v0 = MakeVectorE0E1(z0, z1);
|
||||
V128 v1 = MakeVectorE0E1(a0, a1);
|
||||
V128 v2 = MakeVectorE0E1(b0, b1);
|
||||
|
||||
ExecutionContext context = SingleOpcode(opcode, v0: v0, v1: v1, v2: v2, runUnicorn: false);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(GetVectorE0(context.GetV(0)), Is.EqualTo(resultL));
|
||||
Assert.That(GetVectorE1(context.GetV(0)), Is.EqualTo(resultH));
|
||||
});
|
||||
|
||||
// Unicorn does not yet support hash instructions in A32.
|
||||
// CompareAgainstUnicorn();
|
||||
}
|
||||
|
||||
[Explicit]
|
||||
[Test, Pairwise, Description("VADD.f32 V0, V0, V0")]
|
||||
public void Vadd_f32([Values(0u)] uint rd,
|
||||
|
Reference in New Issue
Block a user