Merge branch 'original_master' into profiling

This commit is contained in:
Andy Adshead 2019-04-25 23:46:52 +01:00
commit e0fc1a7a5f
395 changed files with 25871 additions and 14559 deletions

View file

@ -27,6 +27,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageReference Include="System.Runtime.Intrinsics.Experimental" Version="4.5.0-rc1" />
</ItemGroup>

View file

@ -32,8 +32,6 @@ namespace ChocolArm64
{
translator.ExecuteSubroutine(this, entrypoint);
memory.RemoveMonitor(ThreadState.Core);
WorkFinished?.Invoke(this, EventArgs.Empty);
});
}

View file

@ -8,18 +8,9 @@ namespace ChocolArm64.Decoders
public OpCodeSimdCvt64(Inst inst, long position, int opCode) : base(inst, position, opCode)
{
//TODO:
//Und of Fixed Point variants.
int scale = (opCode >> 10) & 0x3f;
int sf = (opCode >> 31) & 0x1;
/*if (Type != SF && !(Type == 2 && SF == 1))
{
Emitter = AInstEmit.Und;
return;
}*/
FBits = 64 - scale;
RegisterSize = sf != 0

View file

@ -1,14 +0,0 @@
using System;
namespace ChocolArm64.Events
{
public class MemoryAccessEventArgs : EventArgs
{
public long Position { get; private set; }
public MemoryAccessEventArgs(long position)
{
Position = position;
}
}
}

View file

@ -1,13 +0,0 @@
using System;
namespace ChocolArm64.Exceptions
{
public class VmmPageFaultException : Exception
{
private const string ExMsg = "Tried to access unmapped address 0x{0:x16}!";
public VmmPageFaultException() { }
public VmmPageFaultException(long position) : base(string.Format(ExMsg, position)) { }
}
}

View file

@ -51,6 +51,8 @@ namespace ChocolArm64.Instructions
public static void Adds(ILEmitterCtx context)
{
context.TryOptMarkCondWithoutCmp();
EmitAluLoadOpers(context);
context.Emit(OpCodes.Add);

View file

@ -11,21 +11,56 @@ namespace ChocolArm64.Instructions
{
OpCodeBfm64 op = (OpCodeBfm64)context.CurrOp;
EmitBfmLoadRn(context);
if (op.Pos < op.Shift)
{
//BFI.
context.EmitLdintzr(op.Rn);
context.EmitLdintzr(op.Rd);
context.EmitLdc_I(~op.WMask & op.TMask);
int shift = op.GetBitsCount() - op.Shift;
context.Emit(OpCodes.And);
context.Emit(OpCodes.Or);
int width = op.Pos + 1;
context.EmitLdintzr(op.Rd);
context.EmitLdc_I(~op.TMask);
long mask = (long)(ulong.MaxValue >> (64 - width));
context.Emit(OpCodes.And);
context.Emit(OpCodes.Or);
context.EmitLdc_I(mask);
context.EmitStintzr(op.Rd);
context.Emit(OpCodes.And);
context.EmitLsl(shift);
context.EmitLdintzr(op.Rd);
context.EmitLdc_I(~(mask << shift));
context.Emit(OpCodes.And);
context.Emit(OpCodes.Or);
context.EmitStintzr(op.Rd);
}
else
{
//BFXIL.
context.EmitLdintzr(op.Rn);
context.EmitLsr(op.Shift);
int width = op.Pos - op.Shift + 1;
long mask = (long)(ulong.MaxValue >> (64 - width));
context.EmitLdc_I(mask);
context.Emit(OpCodes.And);
context.EmitLdintzr(op.Rd);
context.EmitLdc_I(~mask);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Or);
context.EmitStintzr(op.Rd);
}
}
public static void Sbfm(ILEmitterCtx context)

View file

@ -39,7 +39,6 @@ namespace ChocolArm64.Instructions
context.EmitLdc_I(op.Position + 4);
context.EmitStint(RegisterAlias.Lr);
context.EmitStoreState();
EmitCall(context, op.Imm);
}
@ -60,6 +59,8 @@ namespace ChocolArm64.Instructions
{
OpCodeBReg64 op = (OpCodeBReg64)context.CurrOp;
context.HasIndirectJump = true;
context.EmitStoreState();
context.EmitLdintzr(op.Rn);

View file

@ -65,7 +65,6 @@ namespace ChocolArm64.Instructions
}
context.EmitStint(GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr));
context.EmitStoreState();
//If x is true, then this is a branch with link and exchange.
//In this case we need to swap the mode between Arm <-> Thumb.

View file

@ -11,6 +11,8 @@ namespace ChocolArm64.Instructions
{
if (context.Tier == TranslationTier.Tier0)
{
context.EmitStoreState();
context.TranslateAhead(imm);
context.EmitLdc_I8(imm);
@ -22,6 +24,10 @@ namespace ChocolArm64.Instructions
if (!context.TryOptEmitSubroutineCall())
{
context.HasSlowCall = true;
context.EmitStoreState();
context.TranslateAhead(imm);
context.EmitLdarg(TranslatedSub.StateArgIdx);
@ -32,6 +38,7 @@ namespace ChocolArm64.Instructions
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitLdc_I8(imm);
context.EmitLdc_I4((int)CallType.Call);
context.EmitPrivateCall(typeof(Translator), nameof(Translator.GetOrTranslateSubroutine));
@ -58,20 +65,6 @@ namespace ChocolArm64.Instructions
{
if (context.Tier == TranslationTier.Tier0)
{
context.Emit(OpCodes.Dup);
context.EmitSttmp();
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitFieldLoad(typeof(CpuThreadState).GetField(nameof(CpuThreadState.CurrentTranslator),
BindingFlags.Instance |
BindingFlags.NonPublic));
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitLdtmp();
context.EmitPrivateCall(typeof(Translator), nameof(Translator.TranslateVirtualSubroutine));
context.Emit(OpCodes.Ret);
}
else
@ -85,8 +78,11 @@ namespace ChocolArm64.Instructions
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitLdtmp();
context.EmitLdc_I4(isJump
? (int)CallType.VirtualJump
: (int)CallType.VirtualCall);
context.EmitPrivateCall(typeof(Translator), nameof(Translator.GetOrTranslateVirtualSubroutine));
context.EmitPrivateCall(typeof(Translator), nameof(Translator.GetOrTranslateSubroutine));
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitLdarg(TranslatedSub.MemoryArgIdx);

View file

@ -31,8 +31,6 @@ namespace ChocolArm64.Instructions
{
OpCodeMem64 op = (OpCodeMem64)context.CurrOp;
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
EmitLoadAddress(context);
if (signed && op.Extend64)
@ -69,7 +67,6 @@ namespace ChocolArm64.Instructions
return;
}
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdc_I8(op.Imm);
if (op.Signed)
@ -116,13 +113,10 @@ namespace ChocolArm64.Instructions
}
}
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
EmitLoadAddress(context);
EmitReadAndStore(op.Rt);
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdtmp();
context.EmitLdc_I8(1 << op.Size);
@ -137,8 +131,6 @@ namespace ChocolArm64.Instructions
{
OpCodeMem64 op = (OpCodeMem64)context.CurrOp;
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
EmitLoadAddress(context);
if (op is IOpCodeSimd64)
@ -159,8 +151,6 @@ namespace ChocolArm64.Instructions
{
OpCodeMemPair64 op = (OpCodeMemPair64)context.CurrOp;
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
EmitLoadAddress(context);
if (op is IOpCodeSimd64)
@ -174,7 +164,6 @@ namespace ChocolArm64.Instructions
EmitWriteCall(context, op.Size);
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdtmp();
context.EmitLdc_I8(1 << op.Size);

View file

@ -64,9 +64,7 @@ namespace ChocolArm64.Instructions
{
if ((mask & 1) != 0)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdtmp();
context.EmitLdc_I4(offset);
context.Emit(OpCodes.Add);
@ -129,9 +127,7 @@ namespace ChocolArm64.Instructions
{
if ((mask & 1) != 0)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdtmp();
context.EmitLdc_I4(offset);
context.Emit(OpCodes.Add);
@ -198,8 +194,6 @@ namespace ChocolArm64.Instructions
context.EmitSttmp();
}
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
if (op.Index)
{
context.EmitLdtmp();

View file

@ -23,7 +23,9 @@ namespace ChocolArm64.Instructions
public static void Clrex(ILEmitterCtx context)
{
EmitMemoryCall(context, nameof(MemoryManager.ClearExclusive));
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.ClearExclusiveAddress));
}
public static void Dmb(ILEmitterCtx context) => EmitBarrier(context);
@ -37,12 +39,12 @@ namespace ChocolArm64.Instructions
private static void EmitLdr(ILEmitterCtx context, AccessType accType)
{
EmitLoad(context, accType, false);
EmitLoad(context, accType, pair: false);
}
private static void EmitLdp(ILEmitterCtx context, AccessType accType)
{
EmitLoad(context, accType, true);
EmitLoad(context, accType, pair: true);
}
private static void EmitLoad(ILEmitterCtx context, AccessType accType, bool pair)
@ -57,32 +59,121 @@ namespace ChocolArm64.Instructions
EmitBarrier(context);
}
if (exclusive)
{
EmitMemoryCall(context, nameof(MemoryManager.SetExclusive), op.Rn);
}
context.EmitLdint(op.Rn);
context.EmitSttmp();
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdtmp();
if (exclusive)
{
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitLdtmp();
EmitReadZxCall(context, op.Size);
context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.SetExclusiveAddress));
}
context.EmitStintzr(op.Rt);
void WriteExclusiveValue(string propName)
{
context.Emit(OpCodes.Dup);
if (op.Size < 3)
{
context.Emit(OpCodes.Conv_U8);
}
context.EmitSttmp2();
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitLdtmp2();
context.EmitCallPrivatePropSet(typeof(CpuThreadState), propName);
}
if (pair)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdtmp();
context.EmitLdc_I8(1 << op.Size);
//Exclusive loads should be atomic. For pairwise loads, we need to
//read all the data at once. For a 32-bits pairwise load, we do a
//simple 64-bits load, for a 128-bits load, we need to call a special
//method to read 128-bits atomically.
if (op.Size == 2)
{
context.EmitLdtmp();
context.Emit(OpCodes.Add);
EmitReadZxCall(context, 3);
context.Emit(OpCodes.Dup);
//Mask low half.
context.Emit(OpCodes.Conv_U4);
if (exclusive)
{
WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueLow));
}
context.EmitStintzr(op.Rt);
//Shift high half.
context.EmitLsr(32);
context.Emit(OpCodes.Conv_U4);
if (exclusive)
{
WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueHigh));
}
context.EmitStintzr(op.Rt2);
}
else if (op.Size == 3)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdtmp();
context.EmitPrivateCall(typeof(MemoryManager), nameof(MemoryManager.AtomicReadInt128));
context.Emit(OpCodes.Dup);
//Load low part of the vector.
context.EmitLdc_I4(0);
context.EmitLdc_I4(3);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorExtractIntZx));
if (exclusive)
{
WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueLow));
}
context.EmitStintzr(op.Rt);
//Load high part of the vector.
context.EmitLdc_I4(1);
context.EmitLdc_I4(3);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorExtractIntZx));
if (exclusive)
{
WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueHigh));
}
context.EmitStintzr(op.Rt2);
}
else
{
throw new InvalidOperationException($"Invalid load size of {1 << op.Size} bytes.");
}
}
else
{
//8, 16, 32 or 64-bits (non-pairwise) load.
context.EmitLdtmp();
EmitReadZxCall(context, op.Size);
context.EmitStintzr(op.Rt2);
if (exclusive)
{
WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueLow));
}
context.EmitStintzr(op.Rt);
}
}
@ -99,12 +190,12 @@ namespace ChocolArm64.Instructions
private static void EmitStr(ILEmitterCtx context, AccessType accType)
{
EmitStore(context, accType, false);
EmitStore(context, accType, pair: false);
}
private static void EmitStp(ILEmitterCtx context, AccessType accType)
{
EmitStore(context, accType, true);
EmitStore(context, accType, pair: true);
}
private static void EmitStore(ILEmitterCtx context, AccessType accType, bool pair)
@ -119,66 +210,132 @@ namespace ChocolArm64.Instructions
EmitBarrier(context);
}
ILLabel lblEx = new ILLabel();
ILLabel lblEnd = new ILLabel();
if (exclusive)
{
EmitMemoryCall(context, nameof(MemoryManager.TestExclusive), op.Rn);
ILLabel lblEx = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitLdint(op.Rn);
context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.CheckExclusiveAddress));
context.Emit(OpCodes.Brtrue_S, lblEx);
context.EmitLdc_I8(1);
//Address check failed, set error right away and do not store anything.
context.EmitLdc_I4(1);
context.EmitStintzr(op.Rs);
context.Emit(OpCodes.Br_S, lblEnd);
}
context.Emit(OpCodes.Br, lblEnd);
context.MarkLabel(lblEx);
//Address check passsed.
context.MarkLabel(lblEx);
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdint(op.Rn);
context.EmitLdintzr(op.Rt);
EmitWriteCall(context, op.Size);
if (pair)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdint(op.Rn);
context.EmitLdc_I8(1 << op.Size);
context.Emit(OpCodes.Add);
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitLdintzr(op.Rt2);
context.EmitCallPrivatePropGet(typeof(CpuThreadState), nameof(CpuThreadState.ExclusiveValueLow));
EmitWriteCall(context, op.Size);
}
void EmitCast()
{
//The input should be always int64.
switch (op.Size)
{
case 0: context.Emit(OpCodes.Conv_U1); break;
case 1: context.Emit(OpCodes.Conv_U2); break;
case 2: context.Emit(OpCodes.Conv_U4); break;
}
}
EmitCast();
if (pair)
{
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitCallPrivatePropGet(typeof(CpuThreadState), nameof(CpuThreadState.ExclusiveValueHigh));
EmitCast();
context.EmitLdintzr(op.Rt);
EmitCast();
context.EmitLdintzr(op.Rt2);
EmitCast();
switch (op.Size)
{
case 2: context.EmitPrivateCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchange2xInt32)); break;
case 3: context.EmitPrivateCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt128)); break;
default: throw new InvalidOperationException($"Invalid store size of {1 << op.Size} bytes.");
}
}
else
{
context.EmitLdintzr(op.Rt);
EmitCast();
switch (op.Size)
{
case 0: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeByte)); break;
case 1: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt16)); break;
case 2: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt32)); break;
case 3: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt64)); break;
default: throw new InvalidOperationException($"Invalid store size of {1 << op.Size} bytes.");
}
}
//The value returned is a bool, true if the values compared
//were equal and the new value was written, false otherwise.
//We need to invert this result, as on ARM 1 indicates failure,
//and 0 success on those instructions.
context.EmitLdc_I4(1);
context.Emit(OpCodes.Xor);
context.Emit(OpCodes.Dup);
context.Emit(OpCodes.Conv_U8);
if (exclusive)
{
context.EmitLdc_I8(0);
context.EmitStintzr(op.Rs);
EmitMemoryCall(context, nameof(MemoryManager.ClearExclusiveForStore));
//Only clear the exclusive monitor if the store was successful (Rs = false).
context.Emit(OpCodes.Brtrue_S, lblEnd);
Clrex(context);
context.MarkLabel(lblEnd);
}
context.MarkLabel(lblEnd);
}
private static void EmitMemoryCall(ILEmitterCtx context, string name, int rn = -1)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdarg(TranslatedSub.StateArgIdx);
context.EmitCallPropGet(typeof(CpuThreadState), nameof(CpuThreadState.Core));
if (rn != -1)
else
{
context.EmitLdint(rn);
}
void EmitWriteCall(int rt, long offset)
{
context.EmitLdint(op.Rn);
context.EmitCall(typeof(MemoryManager), name);
if (offset != 0)
{
context.EmitLdc_I8(offset);
context.Emit(OpCodes.Add);
}
context.EmitLdintzr(rt);
InstEmitMemoryHelper.EmitWriteCall(context, op.Size);
}
EmitWriteCall(op.Rt, 0);
if (pair)
{
EmitWriteCall(op.Rt2, 1 << op.Size);
}
}
}
private static void EmitBarrier(ILEmitterCtx context)

View file

@ -1,13 +1,20 @@
using ChocolArm64.Decoders;
using ChocolArm64.Memory;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Reflection.Emit;
using System.Runtime.Intrinsics.X86;
namespace ChocolArm64.Instructions
{
static class InstEmitMemoryHelper
{
private static int _tempIntAddress = ILEmitterCtx.GetIntTempIndex();
private static int _tempIntValue = ILEmitterCtx.GetIntTempIndex();
private static int _tempIntPtAddr = ILEmitterCtx.GetIntTempIndex();
private static int _tempVecValue = ILEmitterCtx.GetVecTempIndex();
private enum Extension
{
Zx,
@ -32,9 +39,10 @@ namespace ChocolArm64.Instructions
private static void EmitReadCall(ILEmitterCtx context, Extension ext, int size)
{
bool isSimd = GetIsSimd(context);
//Save the address into a temp.
context.EmitStint(_tempIntAddress);
string name = null;
bool isSimd = IsSimd(context);
if (size < 0 || size > (isSimd ? 4 : 3))
{
@ -43,28 +51,27 @@ namespace ChocolArm64.Instructions
if (isSimd)
{
switch (size)
if (context.Tier == TranslationTier.Tier0 || !Sse2.IsSupported || size < 2)
{
case 0: name = nameof(MemoryManager.ReadVector8); break;
case 1: name = nameof(MemoryManager.ReadVector16); break;
case 2: name = nameof(MemoryManager.ReadVector32); break;
case 3: name = nameof(MemoryManager.ReadVector64); break;
case 4: name = nameof(MemoryManager.ReadVector128); break;
EmitReadVectorFallback(context, size);
}
else
{
EmitReadVector(context, size);
}
}
else
{
switch (size)
if (context.Tier == TranslationTier.Tier0)
{
case 0: name = nameof(MemoryManager.ReadByte); break;
case 1: name = nameof(MemoryManager.ReadUInt16); break;
case 2: name = nameof(MemoryManager.ReadUInt32); break;
case 3: name = nameof(MemoryManager.ReadUInt64); break;
EmitReadIntFallback(context, size);
}
else
{
EmitReadInt(context, size);
}
}
context.EmitCall(typeof(MemoryManager), name);
if (!isSimd)
{
if (ext == Extension.Sx32 ||
@ -89,50 +96,379 @@ namespace ChocolArm64.Instructions
public static void EmitWriteCall(ILEmitterCtx context, int size)
{
bool isSimd = GetIsSimd(context);
bool isSimd = IsSimd(context);
string name = null;
//Save the value into a temp.
if (isSimd)
{
context.EmitStvec(_tempVecValue);
}
else
{
context.EmitStint(_tempIntValue);
}
//Save the address into a temp.
context.EmitStint(_tempIntAddress);
if (size < 0 || size > (isSimd ? 4 : 3))
{
throw new ArgumentOutOfRangeException(nameof(size));
}
if (size < 3 && !isSimd)
{
context.Emit(OpCodes.Conv_I4);
}
if (isSimd)
{
switch (size)
if (context.Tier == TranslationTier.Tier0 || !Sse2.IsSupported || size < 2)
{
case 0: name = nameof(MemoryManager.WriteVector8); break;
case 1: name = nameof(MemoryManager.WriteVector16); break;
case 2: name = nameof(MemoryManager.WriteVector32); break;
case 3: name = nameof(MemoryManager.WriteVector64); break;
case 4: name = nameof(MemoryManager.WriteVector128); break;
EmitWriteVectorFallback(context, size);
}
else
{
EmitWriteVector(context, size);
}
}
else
{
switch (size)
if (context.Tier == TranslationTier.Tier0)
{
case 0: name = nameof(MemoryManager.WriteByte); break;
case 1: name = nameof(MemoryManager.WriteUInt16); break;
case 2: name = nameof(MemoryManager.WriteUInt32); break;
case 3: name = nameof(MemoryManager.WriteUInt64); break;
EmitWriteIntFallback(context, size);
}
else
{
EmitWriteInt(context, size);
}
}
context.EmitCall(typeof(MemoryManager), name);
}
private static bool GetIsSimd(ILEmitterCtx context)
private static bool IsSimd(ILEmitterCtx context)
{
return context.CurrOp is IOpCodeSimd64 &&
!(context.CurrOp is OpCodeSimdMemMs64 ||
context.CurrOp is OpCodeSimdMemSs64);
}
private static void EmitReadInt(ILEmitterCtx context, int size)
{
EmitAddressCheck(context, size);
ILLabel lblFastPath = new ILLabel();
ILLabel lblSlowPath = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brfalse_S, lblFastPath);
context.MarkLabel(lblSlowPath);
EmitReadIntFallback(context, size);
context.Emit(OpCodes.Br, lblEnd);
context.MarkLabel(lblFastPath);
EmitPtPointerLoad(context, lblSlowPath);
switch (size)
{
case 0: context.Emit(OpCodes.Ldind_U1); break;
case 1: context.Emit(OpCodes.Ldind_U2); break;
case 2: context.Emit(OpCodes.Ldind_U4); break;
case 3: context.Emit(OpCodes.Ldind_I8); break;
}
context.MarkLabel(lblEnd);
}
private static void EmitReadVector(ILEmitterCtx context, int size)
{
EmitAddressCheck(context, size);
ILLabel lblFastPath = new ILLabel();
ILLabel lblSlowPath = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brfalse_S, lblFastPath);
context.MarkLabel(lblSlowPath);
EmitReadVectorFallback(context, size);
context.Emit(OpCodes.Br, lblEnd);
context.MarkLabel(lblFastPath);
EmitPtPointerLoad(context, lblSlowPath);
switch (size)
{
case 2: context.EmitCall(typeof(Sse), nameof(Sse.LoadScalarVector128)); break;
case 3:
{
Type[] types = new Type[] { typeof(double*) };
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.LoadScalarVector128), types));
break;
}
case 4: context.EmitCall(typeof(Sse), nameof(Sse.LoadAlignedVector128)); break;
throw new InvalidOperationException($"Invalid vector load size of {1 << size} bytes.");
}
context.MarkLabel(lblEnd);
}
private static void EmitWriteInt(ILEmitterCtx context, int size)
{
EmitAddressCheck(context, size);
ILLabel lblFastPath = new ILLabel();
ILLabel lblSlowPath = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brfalse_S, lblFastPath);
context.MarkLabel(lblSlowPath);
EmitWriteIntFallback(context, size);
context.Emit(OpCodes.Br, lblEnd);
context.MarkLabel(lblFastPath);
EmitPtPointerLoad(context, lblSlowPath);
context.EmitLdint(_tempIntValue);
if (size < 3)
{
context.Emit(OpCodes.Conv_U4);
}
switch (size)
{
case 0: context.Emit(OpCodes.Stind_I1); break;
case 1: context.Emit(OpCodes.Stind_I2); break;
case 2: context.Emit(OpCodes.Stind_I4); break;
case 3: context.Emit(OpCodes.Stind_I8); break;
}
context.MarkLabel(lblEnd);
}
private static void EmitWriteVector(ILEmitterCtx context, int size)
{
EmitAddressCheck(context, size);
ILLabel lblFastPath = new ILLabel();
ILLabel lblSlowPath = new ILLabel();
ILLabel lblEnd = new ILLabel();
context.Emit(OpCodes.Brfalse_S, lblFastPath);
context.MarkLabel(lblSlowPath);
EmitWriteVectorFallback(context, size);
context.Emit(OpCodes.Br, lblEnd);
context.MarkLabel(lblFastPath);
EmitPtPointerLoad(context, lblSlowPath);
context.EmitLdvec(_tempVecValue);
switch (size)
{
case 2: context.EmitCall(typeof(Sse), nameof(Sse.StoreScalar)); break;
case 3: context.EmitCall(typeof(Sse2), nameof(Sse2.StoreScalar)); break;
case 4: context.EmitCall(typeof(Sse), nameof(Sse.StoreAligned)); break;
default: throw new InvalidOperationException($"Invalid vector store size of {1 << size} bytes.");
}
context.MarkLabel(lblEnd);
}
private static void EmitAddressCheck(ILEmitterCtx context, int size)
{
long addressCheckMask = ~(context.Memory.AddressSpaceSize - 1);
addressCheckMask |= (1u << size) - 1;
context.EmitLdint(_tempIntAddress);
context.EmitLdc_I(addressCheckMask);
context.Emit(OpCodes.And);
}
private static void EmitPtPointerLoad(ILEmitterCtx context, ILLabel lblFallbackPath)
{
context.EmitLdc_I8(context.Memory.PageTable.ToInt64());
context.Emit(OpCodes.Conv_I);
int bit = MemoryManager.PageBits;
do
{
context.EmitLdint(_tempIntAddress);
if (context.CurrOp.RegisterSize == RegisterSize.Int32)
{
context.Emit(OpCodes.Conv_U8);
}
context.EmitLsr(bit);
bit += context.Memory.PtLevelBits;
if (bit < context.Memory.AddressSpaceBits)
{
context.EmitLdc_I8(context.Memory.PtLevelMask);
context.Emit(OpCodes.And);
}
context.EmitLdc_I8(IntPtr.Size);
context.Emit(OpCodes.Mul);
context.Emit(OpCodes.Conv_I);
context.Emit(OpCodes.Add);
context.Emit(OpCodes.Ldind_I);
}
while (bit < context.Memory.AddressSpaceBits);
if (!context.Memory.HasWriteWatchSupport)
{
context.Emit(OpCodes.Conv_U8);
context.EmitStint(_tempIntPtAddr);
context.EmitLdint(_tempIntPtAddr);
context.EmitLdc_I8(MemoryManager.PteFlagsMask);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Brtrue, lblFallbackPath);
context.EmitLdint(_tempIntPtAddr);
context.Emit(OpCodes.Conv_I);
}
context.EmitLdint(_tempIntAddress);
context.EmitLdc_I(MemoryManager.PageMask);
context.Emit(OpCodes.And);
context.Emit(OpCodes.Conv_I);
context.Emit(OpCodes.Add);
}
private static void EmitReadIntFallback(ILEmitterCtx context, int size)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdint(_tempIntAddress);
if (context.CurrOp.RegisterSize == RegisterSize.Int32)
{
context.Emit(OpCodes.Conv_U8);
}
string fallbackMethodName = null;
switch (size)
{
case 0: fallbackMethodName = nameof(MemoryManager.ReadByte); break;
case 1: fallbackMethodName = nameof(MemoryManager.ReadUInt16); break;
case 2: fallbackMethodName = nameof(MemoryManager.ReadUInt32); break;
case 3: fallbackMethodName = nameof(MemoryManager.ReadUInt64); break;
}
context.EmitCall(typeof(MemoryManager), fallbackMethodName);
}
private static void EmitReadVectorFallback(ILEmitterCtx context, int size)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdint(_tempIntAddress);
if (context.CurrOp.RegisterSize == RegisterSize.Int32)
{
context.Emit(OpCodes.Conv_U8);
}
string fallbackMethodName = null;
switch (size)
{
case 0: fallbackMethodName = nameof(MemoryManager.ReadVector8); break;
case 1: fallbackMethodName = nameof(MemoryManager.ReadVector16); break;
case 2: fallbackMethodName = nameof(MemoryManager.ReadVector32); break;
case 3: fallbackMethodName = nameof(MemoryManager.ReadVector64); break;
case 4: fallbackMethodName = nameof(MemoryManager.ReadVector128); break;
}
context.EmitCall(typeof(MemoryManager), fallbackMethodName);
}
private static void EmitWriteIntFallback(ILEmitterCtx context, int size)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdint(_tempIntAddress);
if (context.CurrOp.RegisterSize == RegisterSize.Int32)
{
context.Emit(OpCodes.Conv_U8);
}
context.EmitLdint(_tempIntValue);
if (size < 3)
{
context.Emit(OpCodes.Conv_U4);
}
string fallbackMethodName = null;
switch (size)
{
case 0: fallbackMethodName = nameof(MemoryManager.WriteByte); break;
case 1: fallbackMethodName = nameof(MemoryManager.WriteUInt16); break;
case 2: fallbackMethodName = nameof(MemoryManager.WriteUInt32); break;
case 3: fallbackMethodName = nameof(MemoryManager.WriteUInt64); break;
}
context.EmitCall(typeof(MemoryManager), fallbackMethodName);
}
private static void EmitWriteVectorFallback(ILEmitterCtx context, int size)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdint(_tempIntAddress);
if (context.CurrOp.RegisterSize == RegisterSize.Int32)
{
context.Emit(OpCodes.Conv_U8);
}
context.EmitLdvec(_tempVecValue);
string fallbackMethodName = null;
switch (size)
{
case 0: fallbackMethodName = nameof(MemoryManager.WriteVector8); break;
case 1: fallbackMethodName = nameof(MemoryManager.WriteVector16); break;
case 2: fallbackMethodName = nameof(MemoryManager.WriteVector32); break;
case 3: fallbackMethodName = nameof(MemoryManager.WriteVector64); break;
case 4: fallbackMethodName = nameof(MemoryManager.WriteVector128); break;
}
context.EmitCall(typeof(MemoryManager), fallbackMethodName);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -20,19 +20,32 @@ namespace ChocolArm64.Instructions
public static void Cmeq_V(ILEmitterCtx context)
{
if (context.CurrOp is OpCodeSimdReg64 op)
if (Optimizations.UseSse41)
{
if (op.Size < 3 && Optimizations.UseSse2)
OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp;
Type[] typesCmp = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] };
Type typeSse = op.Size != 3 ? typeof(Sse2) : typeof(Sse41);
context.EmitLdvec(op.Rn);
if (op is OpCodeSimdReg64 binOp)
{
EmitSse2Op(context, nameof(Sse2.CompareEqual));
}
else if (op.Size == 3 && Optimizations.UseSse41)
{
EmitSse41Op(context, nameof(Sse41.CompareEqual));
context.EmitLdvec(binOp.Rm);
}
else
{
EmitCmpOp(context, OpCodes.Beq_S, scalar: false);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
}
context.EmitCall(typeSse.GetMethod(nameof(Sse2.CompareEqual), typesCmp));
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
}
else
@ -48,7 +61,45 @@ namespace ChocolArm64.Instructions
public static void Cmge_V(ILEmitterCtx context)
{
EmitCmpOp(context, OpCodes.Bge_S, scalar: false);
if (Optimizations.UseSse42)
{
OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp;
Type[] typesCmp = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] };
Type[] typesAnt = new Type[] { typeof(Vector128<long>), typeof(Vector128<long>) };
Type[] typesSav = new Type[] { typeof(long) };
Type typeSse = op.Size != 3 ? typeof(Sse2) : typeof(Sse42);
if (op is OpCodeSimdReg64 binOp)
{
context.EmitLdvec(binOp.Rm);
}
else
{
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
}
context.EmitLdvec(op.Rn);
context.EmitCall(typeSse.GetMethod(nameof(Sse2.CompareGreaterThan), typesCmp));
context.EmitLdc_I8(-1L);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAnt));
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
}
else
{
EmitCmpOp(context, OpCodes.Bge_S, scalar: false);
}
}
public static void Cmgt_S(ILEmitterCtx context)
@ -58,19 +109,32 @@ namespace ChocolArm64.Instructions
public static void Cmgt_V(ILEmitterCtx context)
{
if (context.CurrOp is OpCodeSimdReg64 op)
if (Optimizations.UseSse42)
{
if (op.Size < 3 && Optimizations.UseSse2)
OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp;
Type[] typesCmp = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] };
Type typeSse = op.Size != 3 ? typeof(Sse2) : typeof(Sse42);
context.EmitLdvec(op.Rn);
if (op is OpCodeSimdReg64 binOp)
{
EmitSse2Op(context, nameof(Sse2.CompareGreaterThan));
}
else if (op.Size == 3 && Optimizations.UseSse42)
{
EmitSse42Op(context, nameof(Sse42.CompareGreaterThan));
context.EmitLdvec(binOp.Rm);
}
else
{
EmitCmpOp(context, OpCodes.Bgt_S, scalar: false);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
}
context.EmitCall(typeSse.GetMethod(nameof(Sse2.CompareGreaterThan), typesCmp));
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
}
else
@ -86,7 +150,42 @@ namespace ChocolArm64.Instructions
public static void Cmhi_V(ILEmitterCtx context)
{
EmitCmpOp(context, OpCodes.Bgt_Un_S, scalar: false);
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
if (Optimizations.UseSse41 && op.Size < 3)
{
Type[] typesMax = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] };
Type[] typesCmp = new Type[] { VectorIntTypesPerSizeLog2 [op.Size], VectorIntTypesPerSizeLog2 [op.Size] };
Type[] typesAnt = new Type[] { typeof(Vector128<long>), typeof(Vector128<long>) };
Type[] typesSav = new Type[] { typeof(long) };
Type typeSse = op.Size == 0 ? typeof(Sse2) : typeof(Sse41);
context.EmitLdvec(op.Rm);
context.EmitLdvec(op.Rn);
context.EmitCall(typeSse.GetMethod(nameof(Sse2.Max), typesMax));
context.EmitLdvec(op.Rm);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareEqual), typesCmp));
context.EmitLdc_I8(-1L);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAnt));
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
}
else
{
EmitCmpOp(context, OpCodes.Bgt_Un_S, scalar: false);
}
}
public static void Cmhs_S(ILEmitterCtx context)
@ -96,7 +195,35 @@ namespace ChocolArm64.Instructions
public static void Cmhs_V(ILEmitterCtx context)
{
EmitCmpOp(context, OpCodes.Bge_Un_S, scalar: false);
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
if (Optimizations.UseSse41 && op.Size < 3)
{
Type[] typesMax = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] };
Type[] typesCmp = new Type[] { VectorIntTypesPerSizeLog2 [op.Size], VectorIntTypesPerSizeLog2 [op.Size] };
Type typeSse = op.Size == 0 ? typeof(Sse2) : typeof(Sse41);
context.EmitLdvec(op.Rn);
context.EmitLdvec(op.Rm);
context.EmitCall(typeSse.GetMethod(nameof(Sse2.Max), typesMax));
context.EmitLdvec(op.Rn);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareEqual), typesCmp));
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
}
else
{
EmitCmpOp(context, OpCodes.Bge_Un_S, scalar: false);
}
}
public static void Cmle_S(ILEmitterCtx context)
@ -106,7 +233,37 @@ namespace ChocolArm64.Instructions
public static void Cmle_V(ILEmitterCtx context)
{
EmitCmpOp(context, OpCodes.Ble_S, scalar: false);
if (Optimizations.UseSse42)
{
OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp;
Type[] typesCmp = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] };
Type[] typesAnt = new Type[] { typeof(Vector128<long>), typeof(Vector128<long>) };
Type[] typesSav = new Type[] { typeof(long) };
Type typeSse = op.Size != 3 ? typeof(Sse2) : typeof(Sse42);
context.EmitLdvec(op.Rn);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitCall(typeSse.GetMethod(nameof(Sse2.CompareGreaterThan), typesCmp));
context.EmitLdc_I8(-1L);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAnt));
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
}
else
{
EmitCmpOp(context, OpCodes.Ble_S, scalar: false);
}
}
public static void Cmlt_S(ILEmitterCtx context)
@ -116,7 +273,30 @@ namespace ChocolArm64.Instructions
public static void Cmlt_V(ILEmitterCtx context)
{
EmitCmpOp(context, OpCodes.Blt_S, scalar: false);
if (Optimizations.UseSse42)
{
OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp;
Type[] typesCmp = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] };
Type typeSse = op.Size != 3 ? typeof(Sse2) : typeof(Sse42);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitLdvec(op.Rn);
context.EmitCall(typeSse.GetMethod(nameof(Sse2.CompareGreaterThan), typesCmp));
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
}
else
{
EmitCmpOp(context, OpCodes.Blt_S, scalar: false);
}
}
public static void Cmtst_S(ILEmitterCtx context)
@ -318,9 +498,6 @@ namespace ChocolArm64.Instructions
context.EmitLdvec(op.Rn);
context.Emit(OpCodes.Dup);
context.EmitStvectmp();
if (cmpWithZero)
{
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
@ -330,8 +507,8 @@ namespace ChocolArm64.Instructions
context.EmitLdvec(op.Rm);
}
context.Emit(OpCodes.Dup);
context.EmitStvectmp2();
context.EmitStvectmp();
context.EmitLdvectmp();
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareOrderedScalar), typesCmp));
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
@ -340,18 +517,18 @@ namespace ChocolArm64.Instructions
context.Emit(OpCodes.Brtrue_S, lblNaN);
context.EmitLdc_I4(0);
context.Emit(OpCodes.Ldc_I4_0);
context.EmitLdvec(op.Rn);
context.EmitLdvectmp();
context.EmitLdvectmp2();
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareGreaterThanOrEqualOrderedScalar), typesCmp));
context.EmitLdvec(op.Rn);
context.EmitLdvectmp();
context.EmitLdvectmp2();
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareEqualOrderedScalar), typesCmp));
context.EmitLdvec(op.Rn);
context.EmitLdvectmp();
context.EmitLdvectmp2();
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareLessThanOrderedScalar), typesCmp));
context.EmitStflg((int)PState.NBit);
@ -363,10 +540,10 @@ namespace ChocolArm64.Instructions
context.MarkLabel(lblNaN);
context.EmitLdc_I4(1);
context.Emit(OpCodes.Dup);
context.EmitLdc_I4(0);
context.Emit(OpCodes.Dup);
context.Emit(OpCodes.Ldc_I4_1);
context.Emit(OpCodes.Ldc_I4_1);
context.Emit(OpCodes.Ldc_I4_0);
context.Emit(OpCodes.Ldc_I4_0);
context.EmitStflg((int)PState.NBit);
context.EmitStflg((int)PState.ZBit);
@ -382,42 +559,39 @@ namespace ChocolArm64.Instructions
ILLabel lblNaN = new ILLabel();
ILLabel lblEnd = new ILLabel();
EmitLdvecWithCastToDouble(context, op.Rn);
context.Emit(OpCodes.Dup);
context.EmitStvectmp();
context.EmitLdvec(op.Rn);
if (cmpWithZero)
{
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleZero));
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
}
else
{
EmitLdvecWithCastToDouble(context, op.Rm);
context.EmitLdvec(op.Rm);
}
context.Emit(OpCodes.Dup);
context.EmitStvectmp2();
context.EmitStvectmp();
context.EmitLdvectmp();
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareOrderedScalar), typesCmp));
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleZero));
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareEqualOrderedScalar), typesCmp));
context.Emit(OpCodes.Brtrue_S, lblNaN);
context.EmitLdc_I4(0);
context.Emit(OpCodes.Ldc_I4_0);
context.EmitLdvec(op.Rn);
context.EmitLdvectmp();
context.EmitLdvectmp2();
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareGreaterThanOrEqualOrderedScalar), typesCmp));
context.EmitLdvec(op.Rn);
context.EmitLdvectmp();
context.EmitLdvectmp2();
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareEqualOrderedScalar), typesCmp));
context.EmitLdvec(op.Rn);
context.EmitLdvectmp();
context.EmitLdvectmp2();
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareLessThanOrderedScalar), typesCmp));
context.EmitStflg((int)PState.NBit);
@ -429,10 +603,10 @@ namespace ChocolArm64.Instructions
context.MarkLabel(lblNaN);
context.EmitLdc_I4(1);
context.Emit(OpCodes.Dup);
context.EmitLdc_I4(0);
context.Emit(OpCodes.Dup);
context.Emit(OpCodes.Ldc_I4_1);
context.Emit(OpCodes.Ldc_I4_1);
context.Emit(OpCodes.Ldc_I4_0);
context.Emit(OpCodes.Ldc_I4_0);
context.EmitStflg((int)PState.NBit);
context.EmitStflg((int)PState.ZBit);
@ -656,26 +830,26 @@ namespace ChocolArm64.Instructions
if (!isLeOrLt)
{
EmitLdvecWithCastToDouble(context, op.Rn);
context.EmitLdvec(op.Rn);
}
if (op is OpCodeSimdReg64 binOp)
{
EmitLdvecWithCastToDouble(context, binOp.Rm);
context.EmitLdvec(binOp.Rm);
}
else
{
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleZero));
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
}
if (isLeOrLt)
{
EmitLdvecWithCastToDouble(context, op.Rn);
context.EmitLdvec(op.Rn);
}
context.EmitCall(typeof(Sse2).GetMethod(name, types));
EmitStvecWithCastFromDouble(context, op.Rd);
context.EmitStvec(op.Rd);
if (scalar)
{

File diff suppressed because it is too large Load diff

View file

@ -86,13 +86,13 @@ namespace ChocolArm64.Instructions
{
OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp;
EmitLdvecWithSignedCast(context, op.Rn, op.Size);
context.EmitLdvec(op.Rn);
Type baseType = VectorIntTypesPerSizeLog2[op.Size];
if (op is OpCodeSimdReg64 binOp)
{
EmitLdvecWithSignedCast(context, binOp.Rm, op.Size);
context.EmitLdvec(binOp.Rm);
context.EmitCall(type.GetMethod(name, new Type[] { baseType, baseType }));
}
@ -101,7 +101,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(type.GetMethod(name, new Type[] { baseType }));
}
EmitStvecWithSignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -109,80 +109,6 @@ namespace ChocolArm64.Instructions
}
}
public static void EmitLdvecWithSignedCast(ILEmitterCtx context, int reg, int size)
{
context.EmitLdvec(reg);
switch (size)
{
case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToSByte)); break;
case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToInt16)); break;
case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToInt32)); break;
case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToInt64)); break;
default: throw new ArgumentOutOfRangeException(nameof(size));
}
}
public static void EmitLdvecWithCastToDouble(ILEmitterCtx context, int reg)
{
context.EmitLdvec(reg);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToDouble));
}
public static void EmitStvecWithCastFromDouble(ILEmitterCtx context, int reg)
{
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleToSingle));
context.EmitStvec(reg);
}
public static void EmitLdvecWithUnsignedCast(ILEmitterCtx context, int reg, int size)
{
context.EmitLdvec(reg);
switch (size)
{
case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToByte)); break;
case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToUInt16)); break;
case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToUInt32)); break;
case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToUInt64)); break;
default: throw new ArgumentOutOfRangeException(nameof(size));
}
}
public static void EmitStvecWithSignedCast(ILEmitterCtx context, int reg, int size)
{
switch (size)
{
case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSByteToSingle)); break;
case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt16ToSingle)); break;
case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt32ToSingle)); break;
case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt64ToSingle)); break;
default: throw new ArgumentOutOfRangeException(nameof(size));
}
context.EmitStvec(reg);
}
public static void EmitStvecWithUnsignedCast(ILEmitterCtx context, int reg, int size)
{
switch (size)
{
case 0: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorByteToSingle)); break;
case 1: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorUInt16ToSingle)); break;
case 2: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorUInt32ToSingle)); break;
case 3: VectorHelper.EmitCall(context, nameof(VectorHelper.VectorUInt64ToSingle)); break;
default: throw new ArgumentOutOfRangeException(nameof(size));
}
context.EmitStvec(reg);
}
public static void EmitScalarSseOrSse2OpF(ILEmitterCtx context, string name)
{
EmitSseOrSse2OpF(context, name, true);
@ -199,17 +125,7 @@ namespace ChocolArm64.Instructions
int sizeF = op.Size & 1;
void Ldvec(int reg)
{
context.EmitLdvec(reg);
if (sizeF == 1)
{
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleToDouble));
}
}
Ldvec(op.Rn);
context.EmitLdvec(op.Rn);
Type type;
Type baseType;
@ -227,7 +143,7 @@ namespace ChocolArm64.Instructions
if (op is OpCodeSimdReg64 binOp)
{
Ldvec(binOp.Rm);
context.EmitLdvec(binOp.Rm);
context.EmitCall(type.GetMethod(name, new Type[] { baseType, baseType }));
}
@ -236,11 +152,6 @@ namespace ChocolArm64.Instructions
context.EmitCall(type.GetMethod(name, new Type[] { baseType }));
}
if (sizeF == 1)
{
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleToSingle));
}
context.EmitStvec(op.Rd);
if (scalar)
@ -681,12 +592,9 @@ namespace ChocolArm64.Instructions
emit();
EmitVectorInsertTmp(context, index, op.Size);
EmitVectorInsert(context, op.Rd, index, op.Size);
}
context.EmitLdvectmp();
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
@ -964,8 +872,8 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.UnpackLow), types));
context.Emit(OpCodes.Dup);
context.EmitStvectmp();
context.EmitLdvectmp();
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
@ -987,20 +895,13 @@ namespace ChocolArm64.Instructions
Type[] types = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
context.EmitLdvec(op.Rn);
context.Emit(OpCodes.Dup);
context.EmitStvectmp();
context.EmitLdvec(op.Rm);
context.Emit(OpCodes.Dup);
context.EmitStvectmp2();
context.EmitLdc_I4(2 << 6 | 0 << 4 | 2 << 2 | 0 << 0);
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Shuffle), typesSfl));
context.EmitLdvectmp();
context.EmitLdvectmp2();
context.EmitLdvec(op.Rn);
context.EmitLdvec(op.Rm);
context.EmitLdc_I4(3 << 6 | 1 << 4 | 3 << 2 | 1 << 0);
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Shuffle), typesSfl));
@ -1014,26 +915,19 @@ namespace ChocolArm64.Instructions
{
Type[] types = new Type[] { typeof(Vector128<double>), typeof(Vector128<double>) };
EmitLdvecWithCastToDouble(context, op.Rn);
context.Emit(OpCodes.Dup);
context.EmitStvectmp();
EmitLdvecWithCastToDouble(context, op.Rm);
context.Emit(OpCodes.Dup);
context.EmitStvectmp2();
context.EmitLdvec(op.Rn);
context.EmitLdvec(op.Rm);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackLow), types));
context.EmitLdvectmp();
context.EmitLdvectmp2();
context.EmitLdvec(op.Rn);
context.EmitLdvec(op.Rm);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackHigh), types));
context.EmitCall(typeof(Sse2).GetMethod(name, types));
EmitStvecWithCastFromDouble(context, op.Rd);
context.EmitStvec(op.Rd);
}
}
@ -1074,11 +968,6 @@ namespace ChocolArm64.Instructions
int bytes = op.GetBitsCount() >> 3;
int elems = !scalar ? bytes >> op.Size : 1;
if (scalar)
{
EmitVectorZeroLowerTmp(context);
}
for (int index = 0; index < elems; index++)
{
EmitVectorExtractSx(context, op.Rn, index, op.Size);
@ -1094,13 +983,15 @@ namespace ChocolArm64.Instructions
EmitUnarySignedSatQAbsOrNeg(context);
}
EmitVectorInsertTmp(context, index, op.Size);
if (scalar)
{
EmitVectorZeroAll(context, op.Rd);
}
EmitVectorInsert(context, op.Rd, index, op.Size);
}
context.EmitLdvectmp();
context.EmitStvec(op.Rd);
if ((op.RegisterSize == RegisterSize.Simd64) || scalar)
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
@ -1141,11 +1032,6 @@ namespace ChocolArm64.Instructions
int bytes = op.GetBitsCount() >> 3;
int elems = !scalar ? bytes >> op.Size : 1;
if (scalar)
{
EmitVectorZeroLowerTmp(context);
}
if (add || sub)
{
for (int index = 0; index < elems; index++)
@ -1171,7 +1057,12 @@ namespace ChocolArm64.Instructions
}
}
EmitVectorInsertTmp(context, index, op.Size);
if (scalar)
{
EmitVectorZeroAll(context, op.Rd);
}
EmitVectorInsert(context, op.Rd, index, op.Size);
}
}
else if (accumulate)
@ -1192,7 +1083,12 @@ namespace ChocolArm64.Instructions
EmitBinarySatQAccumulate(context, signed);
}
EmitVectorInsertTmp(context, index, op.Size);
if (scalar)
{
EmitVectorZeroAll(context, op.Rd);
}
EmitVectorInsert(context, op.Rd, index, op.Size);
}
}
else
@ -1206,14 +1102,16 @@ namespace ChocolArm64.Instructions
EmitSatQ(context, op.Size, true, signed);
EmitVectorInsertTmp(context, index, op.Size);
if (scalar)
{
EmitVectorZeroAll(context, op.Rd);
}
EmitVectorInsert(context, op.Rd, index, op.Size);
}
}
context.EmitLdvectmp();
context.EmitStvec(op.Rd);
if ((op.RegisterSize == RegisterSize.Simd64) || scalar)
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
@ -1277,13 +1175,9 @@ namespace ChocolArm64.Instructions
}
// TSrc (16bit, 32bit, 64bit; signed, unsigned) > TDst (8bit, 16bit, 32bit; signed, unsigned).
public static void EmitSatQ(
ILEmitterCtx context,
int sizeDst,
bool signedSrc,
bool signedDst)
public static void EmitSatQ(ILEmitterCtx context, int sizeDst, bool signedSrc, bool signedDst)
{
if (sizeDst > 2)
if ((uint)sizeDst > 2u)
{
throw new ArgumentOutOfRangeException(nameof(sizeDst));
}
@ -1473,16 +1367,16 @@ namespace ChocolArm64.Instructions
{
if (Optimizations.UseSse)
{
//TODO: Use Sse2.MoveScalar once it is fixed,
//as of the time of writing it just crashes the JIT (SDK 2.1.503).
// TODO: Use Sse2.MoveScalar once it is fixed (in .NET Core 3.0),
// as of the time of writing it just crashes the JIT.
/*Type[] typesMov = new Type[] { typeof(Vector128<ulong>) };
EmitLdvecWithUnsignedCast(context, reg, 3);
context.EmitLdvec(reg);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MoveScalar), typesMov));
EmitStvecWithUnsignedCast(context, reg, 3);*/
context.EmitStvec(reg);*/
context.EmitLdvec(reg);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));

View file

@ -30,14 +30,14 @@ namespace ChocolArm64.Instructions
{
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
Type[] typesAndNot = new Type[] { typeof(Vector128<byte>), typeof(Vector128<byte>) };
Type[] typesAnt = new Type[] { typeof(Vector128<byte>), typeof(Vector128<byte>) };
EmitLdvecWithUnsignedCast(context, op.Rm, 0);
EmitLdvecWithUnsignedCast(context, op.Rn, 0);
context.EmitLdvec(op.Rm);
context.EmitLdvec(op.Rn);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndNot));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAnt));
EmitStvecWithUnsignedCast(context, op.Rd, 0);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -79,20 +79,20 @@ namespace ChocolArm64.Instructions
if (Optimizations.UseSse2)
{
Type[] typesXorAndNot = new Type[] { typeof(Vector128<byte>), typeof(Vector128<byte>) };
Type[] typesXorAnd = new Type[] { typeof(Vector128<byte>), typeof(Vector128<byte>) };
string nameAndNot = notRm ? nameof(Sse2.AndNot) : nameof(Sse2.And);
string nameAnd = notRm ? nameof(Sse2.AndNot) : nameof(Sse2.And);
EmitLdvecWithUnsignedCast(context, op.Rd, 0);
EmitLdvecWithUnsignedCast(context, op.Rm, 0);
EmitLdvecWithUnsignedCast(context, op.Rn, 0);
EmitLdvecWithUnsignedCast(context, op.Rd, 0);
context.EmitLdvec(op.Rd);
context.EmitLdvec(op.Rm);
context.EmitLdvec(op.Rn);
context.EmitLdvec(op.Rd);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXorAndNot));
context.EmitCall(typeof(Sse2).GetMethod(nameAndNot, typesXorAndNot));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXorAndNot));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXorAnd));
context.EmitCall(typeof(Sse2).GetMethod(nameAnd, typesXorAnd));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXorAnd));
EmitStvecWithUnsignedCast(context, op.Rd, 0);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -120,7 +120,6 @@ namespace ChocolArm64.Instructions
}
context.Emit(OpCodes.And);
context.Emit(OpCodes.Xor);
EmitVectorInsert(context, op.Rd, index, 3);
@ -141,20 +140,18 @@ namespace ChocolArm64.Instructions
Type[] typesXorAnd = new Type[] { typeof(Vector128<byte>), typeof(Vector128<byte>) };
EmitLdvecWithUnsignedCast(context, op.Rm, 0);
context.Emit(OpCodes.Dup);
EmitLdvecWithUnsignedCast(context, op.Rn, 0);
context.EmitLdvec(op.Rm);
context.EmitLdvec(op.Rm);
context.EmitLdvec(op.Rn);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXorAnd));
EmitLdvecWithUnsignedCast(context, op.Rd, 0);
context.EmitLdvec(op.Rd);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesXorAnd));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXorAnd));
EmitStvecWithUnsignedCast(context, op.Rd, 0);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -196,17 +193,17 @@ namespace ChocolArm64.Instructions
{
OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp;
Type[] typesSav = new Type[] { typeof(byte) };
Type[] typesAndNot = new Type[] { typeof(Vector128<byte>), typeof(Vector128<byte>) };
Type[] typesSav = new Type[] { typeof(long) };
Type[] typesAnt = new Type[] { typeof(Vector128<long>), typeof(Vector128<long>) };
EmitLdvecWithUnsignedCast(context, op.Rn, 0);
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(byte.MaxValue);
context.EmitLdc_I8(-1L);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndNot));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAnt));
EmitStvecWithUnsignedCast(context, op.Rd, 0);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -225,19 +222,19 @@ namespace ChocolArm64.Instructions
{
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
Type[] typesSav = new Type[] { typeof(byte) };
Type[] typesAndNotOr = new Type[] { typeof(Vector128<byte>), typeof(Vector128<byte>) };
Type[] typesSav = new Type[] { typeof(long) };
Type[] typesAntOr = new Type[] { typeof(Vector128<long>), typeof(Vector128<long>) };
EmitLdvecWithUnsignedCast(context, op.Rn, 0);
EmitLdvecWithUnsignedCast(context, op.Rm, 0);
context.EmitLdvec(op.Rn);
context.EmitLdvec(op.Rm);
context.EmitLdc_I4(byte.MaxValue);
context.EmitLdc_I8(-1L);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndNotOr));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), typesAndNotOr));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAntOr));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), typesAntOr));
EmitStvecWithUnsignedCast(context, op.Rd, 0);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -305,7 +302,7 @@ namespace ChocolArm64.Instructions
Type[] typesSve = new Type[] { typeof(long), typeof(long) };
Type[] typesSfl = new Type[] { typeof(Vector128<sbyte>), typeof(Vector128<sbyte>) };
EmitLdvecWithSignedCast(context, op.Rn, 0); // value
context.EmitLdvec(op.Rn); // value
context.EmitLdc_I8(14L << 56 | 15L << 48 | 12L << 40 | 13L << 32 | 10L << 24 | 11L << 16 | 08L << 8 | 09L << 0); // maskE1
context.EmitLdc_I8(06L << 56 | 07L << 48 | 04L << 40 | 05L << 32 | 02L << 24 | 03L << 16 | 00L << 8 | 01L << 0); // maskE0
@ -314,7 +311,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), typesSfl));
EmitStvecWithSignedCast(context, op.Rd, 0);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -336,7 +333,7 @@ namespace ChocolArm64.Instructions
Type[] typesSve = new Type[] { typeof(long), typeof(long) };
Type[] typesSfl = new Type[] { typeof(Vector128<sbyte>), typeof(Vector128<sbyte>) };
EmitLdvecWithSignedCast(context, op.Rn, op.Size); // value
context.EmitLdvec(op.Rn); // value
if (op.Size == 0)
{
@ -353,7 +350,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), typesSfl));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -375,7 +372,7 @@ namespace ChocolArm64.Instructions
Type[] typesSve = new Type[] { typeof(long), typeof(long) };
Type[] typesSfl = new Type[] { typeof(Vector128<sbyte>), typeof(Vector128<sbyte>) };
EmitLdvecWithSignedCast(context, op.Rn, op.Size); // value
context.EmitLdvec(op.Rn); // value
if (op.Size == 0)
{
@ -397,7 +394,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), typesSfl));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{

View file

@ -45,7 +45,6 @@ namespace ChocolArm64.Instructions
if (isLoad)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdint(op.Rn);
context.EmitLdc_I8(offset);
@ -62,7 +61,6 @@ namespace ChocolArm64.Instructions
}
else
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdint(op.Rn);
context.EmitLdc_I8(offset);
@ -90,7 +88,6 @@ namespace ChocolArm64.Instructions
void EmitMemAddress()
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdint(op.Rn);
context.EmitLdc_I8(offset);

View file

@ -59,7 +59,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav));
EmitStvecWithUnsignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
}
else
{
@ -108,7 +108,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav));
EmitStvecWithUnsignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
}
else
{
@ -138,7 +138,7 @@ namespace ChocolArm64.Instructions
Type[] typesShs = new Type[] { typeof(Vector128<byte>), typeof(byte) };
Type[] typesOr = new Type[] { typeof(Vector128<byte>), typeof(Vector128<byte>) };
EmitLdvecWithUnsignedCast(context, op.Rn, 0);
context.EmitLdvec(op.Rn);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -150,7 +150,7 @@ namespace ChocolArm64.Instructions
context.EmitLdc_I4(op.Imm4);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesShs));
EmitLdvecWithUnsignedCast(context, op.Rm, 0);
context.EmitLdvec(op.Rm);
context.EmitLdc_I4((op.RegisterSize == RegisterSize.Simd64 ? 8 : 16) - op.Imm4);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical128BitLane), typesShs));
@ -164,7 +164,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), typesOr));
EmitStvecWithUnsignedCast(context, op.Rd, 0);
context.EmitStvec(op.Rd);
}
else
{
@ -318,12 +318,26 @@ namespace ChocolArm64.Instructions
public static void Movi_V(ILEmitterCtx context)
{
EmitVectorImmUnaryOp(context, () => { });
if (Optimizations.UseSse2)
{
EmitMoviMvni(context, not: false);
}
else
{
EmitVectorImmUnaryOp(context, () => { });
}
}
public static void Mvni_V(ILEmitterCtx context)
{
EmitVectorImmUnaryOp(context, () => context.Emit(OpCodes.Not));
if (Optimizations.UseSse2)
{
EmitMoviMvni(context, not: true);
}
else
{
EmitVectorImmUnaryOp(context, () => context.Emit(OpCodes.Not));
}
}
public static void Smov_S(ILEmitterCtx context)
@ -341,35 +355,94 @@ namespace ChocolArm64.Instructions
{
OpCodeSimdTbl64 op = (OpCodeSimdTbl64)context.CurrOp;
context.EmitLdvec(op.Rm);
for (int index = 0; index < op.Size; index++)
if (Optimizations.UseSsse3)
{
context.EmitLdvec((op.Rn + index) & 0x1f);
}
Type[] typesCmpSflSub = new Type[] { typeof(Vector128<sbyte>), typeof(Vector128<sbyte>) };
Type[] typesOr = new Type[] { typeof(Vector128<long>), typeof(Vector128<long>) };
Type[] typesSav = new Type[] { typeof(long) };
switch (op.Size)
context.EmitLdvec(op.Rn);
context.EmitLdvec(op.Rm);
context.EmitLdc_I8(0x0F0F0F0F0F0F0F0FL);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav));
context.EmitStvectmp2();
context.EmitLdvectmp2();
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareGreaterThan), typesCmpSflSub));
context.EmitLdvec(op.Rm);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), typesOr));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), typesCmpSflSub));
for (int index = 1; index < op.Size; index++)
{
context.EmitLdvec((op.Rn + index) & 0x1F);
context.EmitLdvec(op.Rm);
context.EmitLdc_I8(0x1010101010101010L * index);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesCmpSflSub));
context.EmitStvectmp();
context.EmitLdvectmp();
context.EmitLdvectmp2();
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareGreaterThan), typesCmpSflSub));
context.EmitLdvectmp();
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), typesOr));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), typesCmpSflSub));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), typesOr));
}
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
}
else
{
case 1: VectorHelper.EmitCall(context,
nameof(VectorHelper.Tbl1_V64),
nameof(VectorHelper.Tbl1_V128)); break;
context.EmitLdvec(op.Rm);
case 2: VectorHelper.EmitCall(context,
nameof(VectorHelper.Tbl2_V64),
nameof(VectorHelper.Tbl2_V128)); break;
for (int index = 0; index < op.Size; index++)
{
context.EmitLdvec((op.Rn + index) & 0x1F);
}
case 3: VectorHelper.EmitCall(context,
nameof(VectorHelper.Tbl3_V64),
nameof(VectorHelper.Tbl3_V128)); break;
switch (op.Size)
{
case 1: VectorHelper.EmitCall(context,
nameof(VectorHelper.Tbl1_V64),
nameof(VectorHelper.Tbl1_V128)); break;
case 4: VectorHelper.EmitCall(context,
nameof(VectorHelper.Tbl4_V64),
nameof(VectorHelper.Tbl4_V128)); break;
case 2: VectorHelper.EmitCall(context,
nameof(VectorHelper.Tbl2_V64),
nameof(VectorHelper.Tbl2_V128)); break;
default: throw new InvalidOperationException();
case 3: VectorHelper.EmitCall(context,
nameof(VectorHelper.Tbl3_V64),
nameof(VectorHelper.Tbl3_V128)); break;
case 4: VectorHelper.EmitCall(context,
nameof(VectorHelper.Tbl4_V64),
nameof(VectorHelper.Tbl4_V128)); break;
default: throw new InvalidOperationException();
}
context.EmitStvec(op.Rd);
}
context.EmitStvec(op.Rd);
}
public static void Trn1_V(ILEmitterCtx context)
@ -418,7 +491,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh)));
EmitLdvecWithSignedCast(context, op.Rn, 0); // value
context.EmitLdvec(op.Rn); // value
context.EmitLdc_I8(_masksE0_TrnUzpXtn[op.Size]); // mask
context.Emit(OpCodes.Dup); // mask
@ -480,6 +553,38 @@ namespace ChocolArm64.Instructions
}
}
private static void EmitMoviMvni(ILEmitterCtx context, bool not)
{
OpCodeSimdImm64 op = (OpCodeSimdImm64)context.CurrOp;
Type[] typesSav = new Type[] { UIntTypesPerSizeLog2[op.Size] };
long imm = op.Imm;
if (not)
{
imm = ~imm;
}
if (op.Size < 3)
{
context.EmitLdc_I4((int)imm);
}
else
{
context.EmitLdc_I8(imm);
}
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav));
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
EmitVectorZeroUpper(context, op.Rd);
}
}
private static void EmitVectorTranspose(ILEmitterCtx context, int part)
{
OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
@ -492,7 +597,7 @@ namespace ChocolArm64.Instructions
? nameof(Sse2.UnpackLow)
: nameof(Sse2.UnpackHigh);
EmitLdvecWithSignedCast(context, op.Rn, op.Size); // value
context.EmitLdvec(op.Rn); // value
if (op.Size < 3)
{
@ -504,7 +609,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
EmitLdvecWithSignedCast(context, op.Rm, op.Size); // value
context.EmitLdvec(op.Rm); // value
if (op.Size < 3)
{
@ -518,7 +623,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(op.Size)));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
}
else
{
@ -560,7 +665,7 @@ namespace ChocolArm64.Instructions
if (op.RegisterSize == RegisterSize.Simd128)
{
EmitLdvecWithSignedCast(context, op.Rn, op.Size); // value
context.EmitLdvec(op.Rn); // value
if (op.Size < 3)
{
@ -572,7 +677,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
EmitLdvecWithSignedCast(context, op.Rm, op.Size); // value
context.EmitLdvec(op.Rm); // value
if (op.Size < 3)
{
@ -586,12 +691,12 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3)));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
}
else
{
EmitLdvecWithSignedCast(context, op.Rn, op.Size);
EmitLdvecWithSignedCast(context, op.Rm, op.Size);
context.EmitLdvec(op.Rn);
context.EmitLdvec(op.Rm);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackLow), GetTypesSflUpk(op.Size))); // value
@ -605,11 +710,11 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), GetTypesSflUpk(0)));
}
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt64Zero));
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3)));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
}
}
else
@ -648,8 +753,8 @@ namespace ChocolArm64.Instructions
? nameof(Sse2.UnpackLow)
: nameof(Sse2.UnpackHigh);
EmitLdvecWithSignedCast(context, op.Rn, op.Size);
EmitLdvecWithSignedCast(context, op.Rm, op.Size);
context.EmitLdvec(op.Rn);
context.EmitLdvec(op.Rm);
if (op.RegisterSize == RegisterSize.Simd128)
{
@ -658,12 +763,12 @@ namespace ChocolArm64.Instructions
else
{
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackLow), GetTypesSflUpk(op.Size)));
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorInt64Zero));
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitCall(typeof(Sse2).GetMethod(nameUpk, GetTypesSflUpk(3)));
}
EmitStvecWithSignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
}
else
{

View file

@ -5,6 +5,7 @@ using ChocolArm64.State;
using ChocolArm64.Translation;
using System;
using System.Reflection.Emit;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using static ChocolArm64.Instructions.InstEmitSimdHelper;
@ -13,9 +14,65 @@ namespace ChocolArm64.Instructions
{
static partial class InstEmit
{
#region "Masks"
private static readonly long[] _masks_RshrnShrn = new long[]
{
14L << 56 | 12L << 48 | 10L << 40 | 08L << 32 | 06L << 24 | 04L << 16 | 02L << 8 | 00L << 0,
13L << 56 | 12L << 48 | 09L << 40 | 08L << 32 | 05L << 24 | 04L << 16 | 01L << 8 | 00L << 0,
11L << 56 | 10L << 48 | 09L << 40 | 08L << 32 | 03L << 24 | 02L << 16 | 01L << 8 | 00L << 0
};
#endregion
public static void Rshrn_V(ILEmitterCtx context)
{
EmitVectorShrImmNarrowOpZx(context, round: true);
if (Optimizations.UseSsse3)
{
OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp;
Type[] typesAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size + 1], VectorUIntTypesPerSizeLog2[op.Size + 1] };
Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size + 1], typeof(byte) };
Type[] typesSfl = new Type[] { typeof(Vector128<sbyte>), typeof(Vector128<sbyte>) };
Type[] typesSav = new Type[] { UIntTypesPerSizeLog2[op.Size + 1] };
Type[] typesSve = new Type[] { typeof(long), typeof(long) };
string nameMov = op.RegisterSize == RegisterSize.Simd128
? nameof(Sse.MoveLowToHigh)
: nameof(Sse.MoveHighToLow);
int shift = GetImmShr(op);
long roundConst = 1L << (shift - 1);
context.EmitLdvec(op.Rd);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh)));
context.EmitLdvec(op.Rn);
context.EmitLdc_I8(roundConst);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd));
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrl)); // value
context.EmitLdc_I8(_masks_RshrnShrn[op.Size]); // mask
context.Emit(OpCodes.Dup); // mask
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), typesSfl));
context.EmitCall(typeof(Sse).GetMethod(nameMov));
context.EmitStvec(op.Rd);
}
else
{
EmitVectorShrImmNarrowOpZx(context, round: true);
}
}
public static void Shl_S(ILEmitterCtx context)
@ -42,12 +99,12 @@ namespace ChocolArm64.Instructions
{
Type[] typesSll = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) };
EmitLdvecWithUnsignedCast(context, op.Rn, op.Size);
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesSll));
EmitStvecWithUnsignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -80,19 +137,20 @@ namespace ChocolArm64.Instructions
nameof(Sse41.ConvertToVector128Int32),
nameof(Sse41.ConvertToVector128Int64) };
int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0;
context.EmitLdvec(op.Rn);
EmitLdvecWithUnsignedCast(context, op.Rn, op.Size);
context.EmitLdc_I4(numBytes);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSll));
if (op.RegisterSize == RegisterSize.Simd128)
{
context.Emit(OpCodes.Ldc_I4_8);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSll));
}
context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt));
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesSll));
EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1);
context.EmitStvec(op.Rd);
}
else
{
@ -102,7 +160,45 @@ namespace ChocolArm64.Instructions
public static void Shrn_V(ILEmitterCtx context)
{
EmitVectorShrImmNarrowOpZx(context, round: false);
if (Optimizations.UseSsse3)
{
OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp;
Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size + 1], typeof(byte) };
Type[] typesSfl = new Type[] { typeof(Vector128<sbyte>), typeof(Vector128<sbyte>) };
Type[] typesSve = new Type[] { typeof(long), typeof(long) };
string nameMov = op.RegisterSize == RegisterSize.Simd128
? nameof(Sse.MoveLowToHigh)
: nameof(Sse.MoveHighToLow);
int shift = GetImmShr(op);
context.EmitLdvec(op.Rd);
VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh)));
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrl)); // value
context.EmitLdc_I8(_masks_RshrnShrn[op.Size]); // mask
context.Emit(OpCodes.Dup); // mask
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSve));
context.EmitCall(typeof(Ssse3).GetMethod(nameof(Ssse3.Shuffle), typesSfl));
context.EmitCall(typeof(Sse).GetMethod(nameMov));
context.EmitStvec(op.Rd);
}
else
{
EmitVectorShrImmNarrowOpZx(context, round: false);
}
}
public static void Sli_V(ILEmitterCtx context)
@ -271,8 +367,7 @@ namespace ChocolArm64.Instructions
{
OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp;
if (Optimizations.UseSse2 && op.Size > 0
&& op.Size < 3)
if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3)
{
Type[] typesShs = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) };
Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] };
@ -280,10 +375,7 @@ namespace ChocolArm64.Instructions
int shift = GetImmShr(op);
int eSize = 8 << op.Size;
EmitLdvecWithSignedCast(context, op.Rn, op.Size);
context.Emit(OpCodes.Dup);
context.EmitStvectmp();
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(eSize - shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs));
@ -291,14 +383,14 @@ namespace ChocolArm64.Instructions
context.EmitLdc_I4(eSize - 1);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs));
context.EmitLdvectmp();
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesShs));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -320,8 +412,7 @@ namespace ChocolArm64.Instructions
{
OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp;
if (Optimizations.UseSse2 && op.Size > 0
&& op.Size < 3)
if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3)
{
Type[] typesShs = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) };
Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] };
@ -329,11 +420,8 @@ namespace ChocolArm64.Instructions
int shift = GetImmShr(op);
int eSize = 8 << op.Size;
EmitLdvecWithSignedCast(context, op.Rd, op.Size);
EmitLdvecWithSignedCast(context, op.Rn, op.Size);
context.Emit(OpCodes.Dup);
context.EmitStvectmp();
context.EmitLdvec(op.Rd);
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(eSize - shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs));
@ -341,7 +429,7 @@ namespace ChocolArm64.Instructions
context.EmitLdc_I4(eSize - 1);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs));
context.EmitLdvectmp();
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesShs));
@ -349,7 +437,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -403,19 +491,23 @@ namespace ChocolArm64.Instructions
nameof(Sse41.ConvertToVector128Int32),
nameof(Sse41.ConvertToVector128Int64) };
int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0;
context.EmitLdvec(op.Rn);
EmitLdvecWithSignedCast(context, op.Rn, op.Size);
context.EmitLdc_I4(numBytes);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSll));
if (op.RegisterSize == RegisterSize.Simd128)
{
context.Emit(OpCodes.Ldc_I4_8);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSll));
}
context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt));
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesSll));
if (shift != 0)
{
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesSll));
}
EmitStvecWithSignedCast(context, op.Rd, op.Size + 1);
context.EmitStvec(op.Rd);
}
else
{
@ -432,17 +524,16 @@ namespace ChocolArm64.Instructions
{
OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp;
if (Optimizations.UseSse2 && op.Size > 0
&& op.Size < 3)
if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3)
{
Type[] typesSra = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) };
EmitLdvecWithSignedCast(context, op.Rn, op.Size);
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(GetImmShr(op));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesSra));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -464,21 +555,20 @@ namespace ChocolArm64.Instructions
{
OpCodeSimdShImm64 op = (OpCodeSimdShImm64)context.CurrOp;
if (Optimizations.UseSse2 && op.Size > 0
&& op.Size < 3)
if (Optimizations.UseSse2 && op.Size > 0 && op.Size < 3)
{
Type[] typesSra = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) };
Type[] typesAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] };
EmitLdvecWithSignedCast(context, op.Rd, op.Size);
EmitLdvecWithSignedCast(context, op.Rn, op.Size);
context.EmitLdvec(op.Rd);
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(GetImmShr(op));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesSra));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd));
EmitStvecWithSignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -610,10 +700,7 @@ namespace ChocolArm64.Instructions
int shift = GetImmShr(op);
int eSize = 8 << op.Size;
EmitLdvecWithUnsignedCast(context, op.Rn, op.Size);
context.Emit(OpCodes.Dup);
context.EmitStvectmp();
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(eSize - shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs));
@ -621,14 +708,14 @@ namespace ChocolArm64.Instructions
context.EmitLdc_I4(eSize - 1);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs));
context.EmitLdvectmp();
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd));
EmitStvecWithUnsignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -658,11 +745,8 @@ namespace ChocolArm64.Instructions
int shift = GetImmShr(op);
int eSize = 8 << op.Size;
EmitLdvecWithUnsignedCast(context, op.Rd, op.Size);
EmitLdvecWithUnsignedCast(context, op.Rn, op.Size);
context.Emit(OpCodes.Dup);
context.EmitStvectmp();
context.EmitLdvec(op.Rd);
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(eSize - shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs));
@ -670,7 +754,7 @@ namespace ChocolArm64.Instructions
context.EmitLdc_I4(eSize - 1);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs));
context.EmitLdvectmp();
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs));
@ -678,7 +762,7 @@ namespace ChocolArm64.Instructions
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd));
EmitStvecWithUnsignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -732,19 +816,23 @@ namespace ChocolArm64.Instructions
nameof(Sse41.ConvertToVector128Int32),
nameof(Sse41.ConvertToVector128Int64) };
int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0;
context.EmitLdvec(op.Rn);
EmitLdvecWithUnsignedCast(context, op.Rn, op.Size);
context.EmitLdc_I4(numBytes);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSll));
if (op.RegisterSize == RegisterSize.Simd128)
{
context.Emit(OpCodes.Ldc_I4_8);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSll));
}
context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt));
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesSll));
if (shift != 0)
{
context.EmitLdc_I4(shift);
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesSll));
}
EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1);
context.EmitStvec(op.Rd);
}
else
{
@ -765,12 +853,12 @@ namespace ChocolArm64.Instructions
{
Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) };
EmitLdvecWithUnsignedCast(context, op.Rn, op.Size);
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(GetImmShr(op));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrl));
EmitStvecWithUnsignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -797,15 +885,15 @@ namespace ChocolArm64.Instructions
Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) };
Type[] typesAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] };
EmitLdvecWithUnsignedCast(context, op.Rd, op.Size);
EmitLdvecWithUnsignedCast(context, op.Rn, op.Size);
context.EmitLdvec(op.Rd);
context.EmitLdvec(op.Rn);
context.EmitLdc_I4(GetImmShr(op));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrl));
context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd));
EmitStvecWithUnsignedCast(context, op.Rd, op.Size);
context.EmitStvec(op.Rd);
if (op.RegisterSize == RegisterSize.Simd64)
{
@ -899,12 +987,9 @@ namespace ChocolArm64.Instructions
context.Emit(OpCodes.Add);
}
EmitVectorInsertTmp(context, index, op.Size);
EmitVectorInsert(context, op.Rd, index, op.Size);
}
context.EmitLdvectmp();
context.EmitStvec(op.Rd);
if ((op.RegisterSize == RegisterSize.Simd64) || scalar)
{
EmitVectorZeroUpper(context, op.Rd);
@ -1044,11 +1129,7 @@ namespace ChocolArm64.Instructions
}
// dst64 = (Int(src64, signed) + roundConst) >> shift;
private static void EmitShrImm64(
ILEmitterCtx context,
bool signed,
long roundConst,
int shift)
private static void EmitShrImm64(ILEmitterCtx context, bool signed, long roundConst, int shift)
{
context.EmitLdc_I8(roundConst);
context.EmitLdc_I4(shift);

View file

@ -102,7 +102,6 @@ namespace ChocolArm64.Instructions
//DC ZVA
for (int offs = 0; offs < (4 << CpuThreadState.DczSizeLog2); offs += 8)
{
context.EmitLdarg(TranslatedSub.MemoryArgIdx);
context.EmitLdintzr(op.Rt);
context.EmitLdc_I(offs);

View file

@ -26,8 +26,8 @@ namespace ChocolArm64.Instructions
{
if (float.IsNaN(value)) return 0;
return value > int.MaxValue ? int.MaxValue :
value < int.MinValue ? int.MinValue : (int)value;
return value >= int.MaxValue ? int.MaxValue :
value <= int.MinValue ? int.MinValue : (int)value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -35,8 +35,8 @@ namespace ChocolArm64.Instructions
{
if (float.IsNaN(value)) return 0;
return value > long.MaxValue ? long.MaxValue :
value < long.MinValue ? long.MinValue : (long)value;
return value >= long.MaxValue ? long.MaxValue :
value <= long.MinValue ? long.MinValue : (long)value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -44,8 +44,8 @@ namespace ChocolArm64.Instructions
{
if (float.IsNaN(value)) return 0;
return value > uint.MaxValue ? uint.MaxValue :
value < uint.MinValue ? uint.MinValue : (uint)value;
return value >= uint.MaxValue ? uint.MaxValue :
value <= uint.MinValue ? uint.MinValue : (uint)value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -53,8 +53,8 @@ namespace ChocolArm64.Instructions
{
if (float.IsNaN(value)) return 0;
return value > ulong.MaxValue ? ulong.MaxValue :
value < ulong.MinValue ? ulong.MinValue : (ulong)value;
return value >= ulong.MaxValue ? ulong.MaxValue :
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -62,8 +62,8 @@ namespace ChocolArm64.Instructions
{
if (double.IsNaN(value)) return 0;
return value > int.MaxValue ? int.MaxValue :
value < int.MinValue ? int.MinValue : (int)value;
return value >= int.MaxValue ? int.MaxValue :
value <= int.MinValue ? int.MinValue : (int)value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -71,8 +71,8 @@ namespace ChocolArm64.Instructions
{
if (double.IsNaN(value)) return 0;
return value > long.MaxValue ? long.MaxValue :
value < long.MinValue ? long.MinValue : (long)value;
return value >= long.MaxValue ? long.MaxValue :
value <= long.MinValue ? long.MinValue : (long)value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -80,8 +80,8 @@ namespace ChocolArm64.Instructions
{
if (double.IsNaN(value)) return 0;
return value > uint.MaxValue ? uint.MaxValue :
value < uint.MinValue ? uint.MinValue : (uint)value;
return value >= uint.MaxValue ? uint.MaxValue :
value <= uint.MinValue ? uint.MinValue : (uint)value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -89,8 +89,8 @@ namespace ChocolArm64.Instructions
{
if (double.IsNaN(value)) return 0;
return value > ulong.MaxValue ? ulong.MaxValue :
value < ulong.MinValue ? ulong.MinValue : (ulong)value;
return value >= ulong.MaxValue ? ulong.MaxValue :
value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
}
public static double Round(double value, CpuThreadState state)
@ -500,50 +500,6 @@ namespace ChocolArm64.Instructions
return Sse41.Insert(vector, value, 0b1110);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<sbyte> VectorSByteZero()
{
if (Sse2.IsSupported)
{
return Sse2.SetZeroVector128<sbyte>();
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<short> VectorInt16Zero()
{
if (Sse2.IsSupported)
{
return Sse2.SetZeroVector128<short>();
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<int> VectorInt32Zero()
{
if (Sse2.IsSupported)
{
return Sse2.SetZeroVector128<int>();
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<long> VectorInt64Zero()
{
if (Sse2.IsSupported)
{
return Sse2.SetZeroVector128<long>();
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorSingleZero()
{
@ -554,214 +510,5 @@ namespace ChocolArm64.Instructions
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<double> VectorDoubleZero()
{
if (Sse2.IsSupported)
{
return Sse2.SetZeroVector128<double>();
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<sbyte> VectorSingleToSByte(Vector128<float> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, sbyte>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<short> VectorSingleToInt16(Vector128<float> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, short>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<int> VectorSingleToInt32(Vector128<float> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, int>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<long> VectorSingleToInt64(Vector128<float> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, long>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<byte> VectorSingleToByte(Vector128<float> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, byte>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<ushort> VectorSingleToUInt16(Vector128<float> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, ushort>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<uint> VectorSingleToUInt32(Vector128<float> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, uint>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<ulong> VectorSingleToUInt64(Vector128<float> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, ulong>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<double> VectorSingleToDouble(Vector128<float> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<float, double>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorSByteToSingle(Vector128<sbyte> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<sbyte, float>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorInt16ToSingle(Vector128<short> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<short, float>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorInt32ToSingle(Vector128<int> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<int, float>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorInt64ToSingle(Vector128<long> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<long, float>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorByteToSingle(Vector128<byte> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<byte, float>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorUInt16ToSingle(Vector128<ushort> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<ushort, float>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorUInt32ToSingle(Vector128<uint> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<uint, float>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorUInt64ToSingle(Vector128<ulong> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<ulong, float>(vector);
}
throw new PlatformNotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector128<float> VectorDoubleToSingle(Vector128<double> vector)
{
if (Sse.IsSupported)
{
return Sse.StaticCast<double, float>(vector);
}
throw new PlatformNotSupportedException();
}
}
}

View file

@ -0,0 +1,151 @@
using System;
using System.Runtime.InteropServices;
namespace ChocolArm64.Memory
{
static class CompareExchange128
{
private struct Int128
{
public ulong Low { get; }
public ulong High { get; }
public Int128(ulong low, ulong high)
{
Low = low;
High = high;
}
}
private delegate Int128 InterlockedCompareExchange(IntPtr address, Int128 expected, Int128 desired);
private delegate int GetCpuId();
private static InterlockedCompareExchange _interlockedCompareExchange;
static CompareExchange128()
{
if (RuntimeInformation.OSArchitecture != Architecture.X64 || !IsCmpxchg16bSupported())
{
throw new PlatformNotSupportedException();
}
byte[] interlockedCompareExchange128Code;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
interlockedCompareExchange128Code = new byte[]
{
0x53, // push rbx
0x49, 0x8b, 0x00, // mov rax, [r8]
0x49, 0x8b, 0x19, // mov rbx, [r9]
0x49, 0x89, 0xca, // mov r10, rcx
0x49, 0x89, 0xd3, // mov r11, rdx
0x49, 0x8b, 0x49, 0x08, // mov rcx, [r9+8]
0x49, 0x8b, 0x50, 0x08, // mov rdx, [r8+8]
0xf0, 0x49, 0x0f, 0xc7, 0x0b, // lock cmpxchg16b [r11]
0x49, 0x89, 0x02, // mov [r10], rax
0x4c, 0x89, 0xd0, // mov rax, r10
0x49, 0x89, 0x52, 0x08, // mov [r10+8], rdx
0x5b, // pop rbx
0xc3 // ret
};
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
interlockedCompareExchange128Code = new byte[]
{
0x53, // push rbx
0x49, 0x89, 0xd1, // mov r9, rdx
0x48, 0x89, 0xcb, // mov rbx, rcx
0x48, 0x89, 0xf0, // mov rax, rsi
0x4c, 0x89, 0xca, // mov rdx, r9
0x4c, 0x89, 0xc1, // mov rcx, r8
0xf0, 0x48, 0x0f, 0xc7, 0x0f, // lock cmpxchg16b [rdi]
0x5b, // pop rbx
0xc3 // ret
};
}
else
{
throw new PlatformNotSupportedException();
}
IntPtr funcPtr = MapCodeAsExecutable(interlockedCompareExchange128Code);
_interlockedCompareExchange = Marshal.GetDelegateForFunctionPointer<InterlockedCompareExchange>(funcPtr);
}
private static bool IsCmpxchg16bSupported()
{
byte[] getCpuIdCode = new byte[]
{
0x53, // push rbx
0xb8, 0x01, 0x00, 0x00, 0x00, // mov eax, 0x1
0x0f, 0xa2, // cpuid
0x89, 0xc8, // mov eax, ecx
0x5b, // pop rbx
0xc3 // ret
};
IntPtr funcPtr = MapCodeAsExecutable(getCpuIdCode);
GetCpuId getCpuId = Marshal.GetDelegateForFunctionPointer<GetCpuId>(funcPtr);
int cpuId = getCpuId();
MemoryManagement.Free(funcPtr);
return (cpuId & (1 << 13)) != 0;
}
private static IntPtr MapCodeAsExecutable(byte[] code)
{
ulong codeLength = (ulong)code.Length;
IntPtr funcPtr = MemoryManagement.Allocate(codeLength);
unsafe
{
fixed (byte* codePtr = code)
{
byte* dest = (byte*)funcPtr;
long size = (long)codeLength;
Buffer.MemoryCopy(codePtr, dest, size, size);
}
}
MemoryManagement.Reprotect(funcPtr, codeLength, MemoryProtection.Execute);
return funcPtr;
}
public static bool InterlockedCompareExchange128(
IntPtr address,
ulong expectedLow,
ulong expectedHigh,
ulong desiredLow,
ulong desiredHigh)
{
Int128 expected = new Int128(expectedLow, expectedHigh);
Int128 desired = new Int128(desiredLow, desiredHigh);
Int128 old = _interlockedCompareExchange(address, expected, desired);
return old.Low == expected.Low && old.High == expected.High;
}
public static void InterlockedRead128(IntPtr address, out ulong low, out ulong high)
{
Int128 zero = new Int128(0, 0);
Int128 old = _interlockedCompareExchange(address, zero, zero);
low = old.Low;
high = old.High;
}
}
}

View file

@ -0,0 +1,114 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ChocolArm64.Memory
{
public static class MemoryManagement
{
public static bool HasWriteWatchSupport => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public static IntPtr Allocate(ulong size)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
IntPtr sizeNint = new IntPtr((long)size);
return MemoryManagementWindows.Allocate(sizeNint);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return MemoryManagementUnix.Allocate(size);
}
else
{
throw new PlatformNotSupportedException();
}
}
public static IntPtr AllocateWriteTracked(ulong size)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
IntPtr sizeNint = new IntPtr((long)size);
return MemoryManagementWindows.AllocateWriteTracked(sizeNint);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return MemoryManagementUnix.Allocate(size);
}
else
{
throw new PlatformNotSupportedException();
}
}
public static void Reprotect(IntPtr address, ulong size, MemoryProtection permission)
{
bool result;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
IntPtr sizeNint = new IntPtr((long)size);
result = MemoryManagementWindows.Reprotect(address, sizeNint, permission);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
result = MemoryManagementUnix.Reprotect(address, size, permission);
}
else
{
throw new PlatformNotSupportedException();
}
if (!result)
{
throw new MemoryProtectionException(permission);
}
}
public static bool Free(IntPtr address)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return MemoryManagementWindows.Free(address);
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return MemoryManagementUnix.Free(address);
}
else
{
throw new PlatformNotSupportedException();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool GetModifiedPages(
IntPtr address,
IntPtr size,
IntPtr[] addresses,
out ulong count)
{
//This is only supported on windows, but returning
//false (failed) is also valid for platforms without
//write tracking support on the OS.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return MemoryManagementWindows.GetModifiedPages(address, size, addresses, out count);
}
else
{
count = 0;
return false;
}
}
}
}

View file

@ -0,0 +1,70 @@
using Mono.Unix.Native;
using System;
namespace ChocolArm64.Memory
{
static class MemoryManagementUnix
{
public static IntPtr Allocate(ulong size)
{
ulong pageSize = (ulong)Syscall.sysconf(SysconfName._SC_PAGESIZE);
const MmapProts prot = MmapProts.PROT_READ | MmapProts.PROT_WRITE;
const MmapFlags flags = MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS;
IntPtr ptr = Syscall.mmap(IntPtr.Zero, size + pageSize, prot, flags, -1, 0);
if (ptr == IntPtr.Zero)
{
throw new OutOfMemoryException();
}
unsafe
{
ptr = new IntPtr(ptr.ToInt64() + (long)pageSize);
*((ulong*)ptr - 1) = size;
}
return ptr;
}
public static bool Reprotect(IntPtr address, ulong size, Memory.MemoryProtection protection)
{
MmapProts prot = GetProtection(protection);
return Syscall.mprotect(address, size, prot) == 0;
}
private static MmapProts GetProtection(Memory.MemoryProtection protection)
{
switch (protection)
{
case Memory.MemoryProtection.None: return MmapProts.PROT_NONE;
case Memory.MemoryProtection.Read: return MmapProts.PROT_READ;
case Memory.MemoryProtection.ReadAndWrite: return MmapProts.PROT_READ | MmapProts.PROT_WRITE;
case Memory.MemoryProtection.ReadAndExecute: return MmapProts.PROT_READ | MmapProts.PROT_EXEC;
case Memory.MemoryProtection.Execute: return MmapProts.PROT_EXEC;
default: throw new ArgumentException($"Invalid permission \"{protection}\".");
}
}
public static bool Free(IntPtr address)
{
ulong pageSize = (ulong)Syscall.sysconf(SysconfName._SC_PAGESIZE);
ulong size;
unsafe
{
size = *((ulong*)address - 1);
address = new IntPtr(address.ToInt64() - (long)pageSize);
}
return Syscall.munmap(address, size + pageSize) == 0;
}
}
}

View file

@ -0,0 +1,155 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ChocolArm64.Memory
{
static class MemoryManagementWindows
{
[Flags]
private enum AllocationType : uint
{
Commit = 0x1000,
Reserve = 0x2000,
Decommit = 0x4000,
Release = 0x8000,
Reset = 0x80000,
Physical = 0x400000,
TopDown = 0x100000,
WriteWatch = 0x200000,
LargePages = 0x20000000
}
[Flags]
private enum MemoryProtection : uint
{
NoAccess = 0x01,
ReadOnly = 0x02,
ReadWrite = 0x04,
WriteCopy = 0x08,
Execute = 0x10,
ExecuteRead = 0x20,
ExecuteReadWrite = 0x40,
ExecuteWriteCopy = 0x80,
GuardModifierflag = 0x100,
NoCacheModifierflag = 0x200,
WriteCombineModifierflag = 0x400
}
private enum WriteWatchFlags : uint
{
None = 0,
Reset = 1
}
[DllImport("kernel32.dll")]
private static extern IntPtr VirtualAlloc(
IntPtr lpAddress,
IntPtr dwSize,
AllocationType flAllocationType,
MemoryProtection flProtect);
[DllImport("kernel32.dll")]
private static extern bool VirtualProtect(
IntPtr lpAddress,
IntPtr dwSize,
MemoryProtection flNewProtect,
out MemoryProtection lpflOldProtect);
[DllImport("kernel32.dll")]
private static extern bool VirtualFree(
IntPtr lpAddress,
IntPtr dwSize,
AllocationType dwFreeType);
[DllImport("kernel32.dll")]
private static extern int GetWriteWatch(
WriteWatchFlags dwFlags,
IntPtr lpBaseAddress,
IntPtr dwRegionSize,
IntPtr[] lpAddresses,
ref ulong lpdwCount,
out uint lpdwGranularity);
public static IntPtr Allocate(IntPtr size)
{
const AllocationType flags =
AllocationType.Reserve |
AllocationType.Commit;
IntPtr ptr = VirtualAlloc(IntPtr.Zero, size, flags, MemoryProtection.ReadWrite);
if (ptr == IntPtr.Zero)
{
throw new OutOfMemoryException();
}
return ptr;
}
public static IntPtr AllocateWriteTracked(IntPtr size)
{
const AllocationType flags =
AllocationType.Reserve |
AllocationType.Commit |
AllocationType.WriteWatch;
IntPtr ptr = VirtualAlloc(IntPtr.Zero, size, flags, MemoryProtection.ReadWrite);
if (ptr == IntPtr.Zero)
{
throw new OutOfMemoryException();
}
return ptr;
}
public static bool Reprotect(IntPtr address, IntPtr size, Memory.MemoryProtection protection)
{
MemoryProtection prot = GetProtection(protection);
return VirtualProtect(address, size, prot, out _);
}
private static MemoryProtection GetProtection(Memory.MemoryProtection protection)
{
switch (protection)
{
case Memory.MemoryProtection.None: return MemoryProtection.NoAccess;
case Memory.MemoryProtection.Read: return MemoryProtection.ReadOnly;
case Memory.MemoryProtection.ReadAndWrite: return MemoryProtection.ReadWrite;
case Memory.MemoryProtection.ReadAndExecute: return MemoryProtection.ExecuteRead;
case Memory.MemoryProtection.Execute: return MemoryProtection.Execute;
default: throw new ArgumentException($"Invalid permission \"{protection}\".");
}
}
public static bool Free(IntPtr address)
{
return VirtualFree(address, IntPtr.Zero, AllocationType.Release);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool GetModifiedPages(
IntPtr address,
IntPtr size,
IntPtr[] addresses,
out ulong count)
{
ulong pagesCount = (ulong)addresses.Length;
int result = GetWriteWatch(
WriteWatchFlags.Reset,
address,
size,
addresses,
ref pagesCount,
out uint granularity);
count = pagesCount;
return result == 0;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,16 @@
using System;
namespace ChocolArm64.Memory
{
[Flags]
public enum MemoryProtection
{
None = 0,
Read = 1 << 0,
Write = 1 << 1,
Execute = 1 << 2,
ReadAndWrite = Read | Write,
ReadAndExecute = Read | Execute
}
}

View file

@ -0,0 +1,10 @@
using System;
namespace ChocolArm64.Memory
{
class MemoryProtectionException : Exception
{
public MemoryProtectionException(MemoryProtection protection) :
base($"Failed to set memory protection to \"{protection}\".") { }
}
}

View file

@ -310,15 +310,17 @@ namespace ChocolArm64
SetA64("x00111100x101000000000xxxxxxxxxx", InstEmit.Fcvtps_Gp, typeof(OpCodeSimdCvt64));
SetA64("x00111100x101001000000xxxxxxxxxx", InstEmit.Fcvtpu_Gp, typeof(OpCodeSimdCvt64));
SetA64("x00111100x111000000000xxxxxxxxxx", InstEmit.Fcvtzs_Gp, typeof(OpCodeSimdCvt64));
SetA64("x00111100x011000xxxxxxxxxxxxxxxx", InstEmit.Fcvtzs_Gp_Fixed, typeof(OpCodeSimdCvt64));
SetA64(">00111100x011000>xxxxxxxxxxxxxxx", InstEmit.Fcvtzs_Gp_Fixed, typeof(OpCodeSimdCvt64));
SetA64("010111101x100001101110xxxxxxxxxx", InstEmit.Fcvtzs_S, typeof(OpCodeSimd64));
SetA64("0>0011101<100001101110xxxxxxxxxx", InstEmit.Fcvtzs_V, typeof(OpCodeSimd64));
SetA64("0x0011110>>xxxxx111111xxxxxxxxxx", InstEmit.Fcvtzs_V, typeof(OpCodeSimdShImm64));
SetA64("0x001111001xxxxx111111xxxxxxxxxx", InstEmit.Fcvtzs_V_Fixed, typeof(OpCodeSimdShImm64));
SetA64("0100111101xxxxxx111111xxxxxxxxxx", InstEmit.Fcvtzs_V_Fixed, typeof(OpCodeSimdShImm64));
SetA64("x00111100x111001000000xxxxxxxxxx", InstEmit.Fcvtzu_Gp, typeof(OpCodeSimdCvt64));
SetA64("x00111100x011001xxxxxxxxxxxxxxxx", InstEmit.Fcvtzu_Gp_Fixed, typeof(OpCodeSimdCvt64));
SetA64(">00111100x011001>xxxxxxxxxxxxxxx", InstEmit.Fcvtzu_Gp_Fixed, typeof(OpCodeSimdCvt64));
SetA64("011111101x100001101110xxxxxxxxxx", InstEmit.Fcvtzu_S, typeof(OpCodeSimd64));
SetA64("0>1011101<100001101110xxxxxxxxxx", InstEmit.Fcvtzu_V, typeof(OpCodeSimd64));
SetA64("0x1011110>>xxxxx111111xxxxxxxxxx", InstEmit.Fcvtzu_V, typeof(OpCodeSimdShImm64));
SetA64("0x101111001xxxxx111111xxxxxxxxxx", InstEmit.Fcvtzu_V_Fixed, typeof(OpCodeSimdShImm64));
SetA64("0110111101xxxxxx111111xxxxxxxxxx", InstEmit.Fcvtzu_V_Fixed, typeof(OpCodeSimdShImm64));
SetA64("000111100x1xxxxx000110xxxxxxxxxx", InstEmit.Fdiv_S, typeof(OpCodeSimdReg64));
SetA64("0>1011100<1xxxxx111111xxxxxxxxxx", InstEmit.Fdiv_V, typeof(OpCodeSimdReg64));
SetA64("000111110x0xxxxx0xxxxxxxxxxxxxxx", InstEmit.Fmadd_S, typeof(OpCodeSimdReg64));
@ -434,8 +436,11 @@ namespace ChocolArm64
SetA64("0x001110<<100000001010xxxxxxxxxx", InstEmit.Saddlp_V, typeof(OpCodeSimd64));
SetA64("0x001110<<1xxxxx000100xxxxxxxxxx", InstEmit.Saddw_V, typeof(OpCodeSimdReg64));
SetA64("x00111100x100010000000xxxxxxxxxx", InstEmit.Scvtf_Gp, typeof(OpCodeSimdCvt64));
SetA64(">00111100x000010>xxxxxxxxxxxxxxx", InstEmit.Scvtf_Gp_Fixed, typeof(OpCodeSimdCvt64));
SetA64("010111100x100001110110xxxxxxxxxx", InstEmit.Scvtf_S, typeof(OpCodeSimd64));
SetA64("0>0011100<100001110110xxxxxxxxxx", InstEmit.Scvtf_V, typeof(OpCodeSimd64));
SetA64("0x001111001xxxxx111001xxxxxxxxxx", InstEmit.Scvtf_V_Fixed, typeof(OpCodeSimdShImm64));
SetA64("0100111101xxxxxx111001xxxxxxxxxx", InstEmit.Scvtf_V_Fixed, typeof(OpCodeSimdShImm64));
SetA64("01011110000xxxxx000000xxxxxxxxxx", InstEmit.Sha1c_V, typeof(OpCodeSimdReg64));
SetA64("0101111000101000000010xxxxxxxxxx", InstEmit.Sha1h_V, typeof(OpCodeSimd64));
SetA64("01011110000xxxxx001000xxxxxxxxxx", InstEmit.Sha1m_V, typeof(OpCodeSimdReg64));
@ -542,8 +547,11 @@ namespace ChocolArm64
SetA64("01101110<<110000001110xxxxxxxxxx", InstEmit.Uaddlv_V, typeof(OpCodeSimd64));
SetA64("0x101110<<1xxxxx000100xxxxxxxxxx", InstEmit.Uaddw_V, typeof(OpCodeSimdReg64));
SetA64("x00111100x100011000000xxxxxxxxxx", InstEmit.Ucvtf_Gp, typeof(OpCodeSimdCvt64));
SetA64(">00111100x000011>xxxxxxxxxxxxxxx", InstEmit.Ucvtf_Gp_Fixed, typeof(OpCodeSimdCvt64));
SetA64("011111100x100001110110xxxxxxxxxx", InstEmit.Ucvtf_S, typeof(OpCodeSimd64));
SetA64("0>1011100<100001110110xxxxxxxxxx", InstEmit.Ucvtf_V, typeof(OpCodeSimd64));
SetA64("0x101111001xxxxx111001xxxxxxxxxx", InstEmit.Ucvtf_V_Fixed, typeof(OpCodeSimdShImm64));
SetA64("0110111101xxxxxx111001xxxxxxxxxx", InstEmit.Ucvtf_V_Fixed, typeof(OpCodeSimdShImm64));
SetA64("0x101110<<1xxxxx000001xxxxxxxxxx", InstEmit.Uhadd_V, typeof(OpCodeSimdReg64));
SetA64("0x101110<<1xxxxx001001xxxxxxxxxx", InstEmit.Uhsub_V, typeof(OpCodeSimdReg64));
SetA64("0x101110<<1xxxxx011001xxxxxxxxxx", InstEmit.Umax_V, typeof(OpCodeSimdReg64));

View file

@ -2,21 +2,23 @@ using System.Runtime.Intrinsics.X86;
public static class Optimizations
{
internal static bool FastFP = true;
public static bool AssumeStrictAbiCompliance { get; set; }
private static bool _useAllSseIfAvailable = true;
public static bool FastFP { get; set; } = true;
private static bool _useSseIfAvailable = true;
private static bool _useSse2IfAvailable = true;
private static bool _useSse3IfAvailable = true;
private static bool _useSsse3IfAvailable = true;
private static bool _useSse41IfAvailable = true;
private static bool _useSse42IfAvailable = true;
private const bool UseAllSseIfAvailable = true;
internal static bool UseSse = (_useAllSseIfAvailable && _useSseIfAvailable) && Sse.IsSupported;
internal static bool UseSse2 = (_useAllSseIfAvailable && _useSse2IfAvailable) && Sse2.IsSupported;
internal static bool UseSse3 = (_useAllSseIfAvailable && _useSse3IfAvailable) && Sse3.IsSupported;
internal static bool UseSsse3 = (_useAllSseIfAvailable && _useSsse3IfAvailable) && Ssse3.IsSupported;
internal static bool UseSse41 = (_useAllSseIfAvailable && _useSse41IfAvailable) && Sse41.IsSupported;
internal static bool UseSse42 = (_useAllSseIfAvailable && _useSse42IfAvailable) && Sse42.IsSupported;
}
public static bool UseSseIfAvailable { get; set; } = UseAllSseIfAvailable;
public static bool UseSse2IfAvailable { get; set; } = UseAllSseIfAvailable;
public static bool UseSse3IfAvailable { get; set; } = UseAllSseIfAvailable;
public static bool UseSsse3IfAvailable { get; set; } = UseAllSseIfAvailable;
public static bool UseSse41IfAvailable { get; set; } = UseAllSseIfAvailable;
public static bool UseSse42IfAvailable { get; set; } = UseAllSseIfAvailable;
internal static bool UseSse => UseSseIfAvailable && Sse.IsSupported;
internal static bool UseSse2 => UseSse2IfAvailable && Sse2.IsSupported;
internal static bool UseSse3 => UseSse3IfAvailable && Sse3.IsSupported;
internal static bool UseSsse3 => UseSsse3IfAvailable && Ssse3.IsSupported;
internal static bool UseSse41 => UseSse41IfAvailable && Sse41.IsSupported;
internal static bool UseSse42 => UseSse42IfAvailable && Sse42.IsSupported;
}

View file

@ -37,7 +37,6 @@ namespace ChocolArm64.State
public int ElrHyp;
public bool Running { get; set; }
public int Core { get; set; }
private bool _interrupted;
@ -85,6 +84,16 @@ namespace ChocolArm64.State
internal Translator CurrentTranslator;
private ulong _exclusiveAddress;
internal ulong ExclusiveValueLow { get; set; }
internal ulong ExclusiveValueHigh { get; set; }
public CpuThreadState()
{
ClearExclusiveAddress();
}
static CpuThreadState()
{
_hostTickFreq = 1.0 / Stopwatch.Frequency;
@ -94,6 +103,26 @@ namespace ChocolArm64.State
_tickCounter.Start();
}
internal void SetExclusiveAddress(ulong address)
{
_exclusiveAddress = GetMaskedExclusiveAddress(address);
}
internal bool CheckExclusiveAddress(ulong address)
{
return GetMaskedExclusiveAddress(address) == _exclusiveAddress;
}
internal void ClearExclusiveAddress()
{
_exclusiveAddress = ulong.MaxValue;
}
private ulong GetMaskedExclusiveAddress(ulong address)
{
return address & ~((4UL << ErgSizeLog2) - 1);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool Synchronize(int bbWeight)
{

View file

@ -0,0 +1,9 @@
namespace ChocolArm64.Translation
{
enum CallType
{
Call,
VirtualCall,
VirtualJump
}
}

View file

@ -4,13 +4,13 @@ namespace ChocolArm64.Translation
{
class ILBlock : IILEmit
{
public long IntInputs { get; private set; }
public long IntOutputs { get; private set; }
public long IntAwOutputs { get; private set; }
public long IntInputs { get; private set; }
public long IntOutputs { get; private set; }
private long _intAwOutputs;
public long VecInputs { get; private set; }
public long VecOutputs { get; private set; }
public long VecAwOutputs { get; private set; }
public long VecInputs { get; private set; }
public long VecOutputs { get; private set; }
private long _vecAwOutputs;
public bool HasStateStore { get; private set; }
@ -34,25 +34,25 @@ namespace ChocolArm64.Translation
//opcodes emitted by each ARM instruction.
//We can only consider the new outputs for doing input elimination
//after all the CIL opcodes used by the instruction being emitted.
IntAwOutputs = IntOutputs;
VecAwOutputs = VecOutputs;
_intAwOutputs = IntOutputs;
_vecAwOutputs = VecOutputs;
}
else if (emitter is ILOpCodeLoad ld && ILMethodBuilder.IsRegIndex(ld.Index))
{
switch (ld.IoType)
switch (ld.VarType)
{
case IoType.Flag: IntInputs |= ((1L << ld.Index) << 32) & ~IntAwOutputs; break;
case IoType.Int: IntInputs |= (1L << ld.Index) & ~IntAwOutputs; break;
case IoType.Vector: VecInputs |= (1L << ld.Index) & ~VecAwOutputs; break;
case VarType.Flag: IntInputs |= ((1L << ld.Index) << 32) & ~_intAwOutputs; break;
case VarType.Int: IntInputs |= (1L << ld.Index) & ~_intAwOutputs; break;
case VarType.Vector: VecInputs |= (1L << ld.Index) & ~_vecAwOutputs; break;
}
}
else if (emitter is ILOpCodeStore st && ILMethodBuilder.IsRegIndex(st.Index))
{
switch (st.IoType)
switch (st.VarType)
{
case IoType.Flag: IntOutputs |= (1L << st.Index) << 32; break;
case IoType.Int: IntOutputs |= 1L << st.Index; break;
case IoType.Vector: VecOutputs |= 1L << st.Index; break;
case VarType.Flag: IntOutputs |= (1L << st.Index) << 32; break;
case VarType.Int: IntOutputs |= 1L << st.Index; break;
case VarType.Vector: VecOutputs |= 1L << st.Index; break;
}
}
else if (emitter is ILOpCodeStoreState)

View file

@ -1,5 +1,6 @@
using ChocolArm64.Decoders;
using ChocolArm64.Instructions;
using ChocolArm64.Memory;
using ChocolArm64.State;
using System;
using System.Collections.Generic;
@ -10,6 +11,8 @@ namespace ChocolArm64.Translation
{
class ILEmitterCtx
{
public MemoryManager Memory { get; }
private TranslatorCache _cache;
private TranslatorQueue _queue;
@ -28,6 +31,10 @@ namespace ChocolArm64.Translation
public Aarch32Mode Mode { get; } = Aarch32Mode.User; //TODO
public bool HasIndirectJump { get; set; }
public bool HasSlowCall { get; set; }
private Dictionary<Block, ILBlock> _visitedBlocks;
private Queue<Block> _branchTargets;
@ -43,18 +50,35 @@ namespace ChocolArm64.Translation
//values needed by some functions, since IL doesn't have a swap instruction.
//You can use any value here as long it doesn't conflict with the indices
//for the other registers. Any value >= 64 or < 0 will do.
private const int IntTmpIndex = -1;
private const int RorTmpIndex = -2;
private const int CmpOptTmp1Index = -3;
private const int CmpOptTmp2Index = -4;
private const int VecTmp1Index = -5;
private const int VecTmp2Index = -6;
private const int ReservedLocalsCount = 64;
public ILEmitterCtx(TranslatorCache cache, TranslatorQueue queue, TranslationTier tier, Block graph)
private const int RorTmpIndex = ReservedLocalsCount + 0;
private const int CmpOptTmp1Index = ReservedLocalsCount + 1;
private const int CmpOptTmp2Index = ReservedLocalsCount + 2;
private const int IntGpTmp1Index = ReservedLocalsCount + 3;
private const int IntGpTmp2Index = ReservedLocalsCount + 4;
private const int UserIntTempStart = ReservedLocalsCount + 5;
//Vectors are part of another "set" of locals.
private const int VecGpTmp1Index = ReservedLocalsCount + 0;
private const int VecGpTmp2Index = ReservedLocalsCount + 1;
private const int VecGpTmp3Index = ReservedLocalsCount + 2;
private const int UserVecTempStart = ReservedLocalsCount + 3;
private static int _userIntTempCount;
private static int _userVecTempCount;
public ILEmitterCtx(
MemoryManager memory,
TranslatorCache cache,
TranslatorQueue queue,
TranslationTier tier,
Block graph)
{
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
_queue = queue ?? throw new ArgumentNullException(nameof(queue));
_currBlock = graph ?? throw new ArgumentNullException(nameof(graph));
Memory = memory ?? throw new ArgumentNullException(nameof(memory));
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
_queue = queue ?? throw new ArgumentNullException(nameof(queue));
_currBlock = graph ?? throw new ArgumentNullException(nameof(graph));
Tier = tier;
@ -72,7 +96,22 @@ namespace ChocolArm64.Translation
ResetBlockState();
AdvanceOpCode();
if (AdvanceOpCode())
{
EmitSynchronization();
_ilBlock.Add(new ILOpCodeLoadState(_ilBlock, isSubEntry: true));
}
}
public static int GetIntTempIndex()
{
return UserIntTempStart + _userIntTempCount++;
}
public static int GetVecTempIndex()
{
return UserVecTempStart + _userVecTempCount++;
}
public ILBlock[] GetILBlocks()
@ -98,10 +137,18 @@ namespace ChocolArm64.Translation
return;
}
if (_opcIndex == 0)
int opcIndex = _opcIndex;
if (opcIndex == 0)
{
MarkLabel(GetLabel(_currBlock.Position));
}
bool isLastOp = opcIndex == CurrBlock.OpCodes.Count - 1;
if (isLastOp && CurrBlock.Branch != null &&
(ulong)CurrBlock.Branch.Position <= (ulong)CurrBlock.Position)
{
EmitSynchronization();
}
@ -132,7 +179,7 @@ namespace ChocolArm64.Translation
//of the next instruction to be executed (in the case that the condition
//is false, and the branch was not taken, as all basic blocks should end with
//some kind of branch).
if (CurrOp == CurrBlock.GetLastOp() && CurrBlock.Next == null)
if (isLastOp && CurrBlock.Next == null)
{
EmitStoreState();
EmitLdc_I8(CurrOp.Position + CurrOp.OpCodeSizeInBytes);
@ -144,7 +191,7 @@ namespace ChocolArm64.Translation
_ilBlock.Add(new ILBarrier());
}
private Condition GetInverseCond(Condition cond)
private static Condition GetInverseCond(Condition cond)
{
//Bit 0 of all conditions is basically a negation bit, so
//inverting this bit has the effect of inverting the condition.
@ -256,32 +303,43 @@ namespace ChocolArm64.Translation
return;
}
_queue.Enqueue(new TranslatorQueueItem(position, mode, TranslationTier.Tier1));
_queue.Enqueue(position, mode, TranslationTier.Tier1, isComplete: true);
}
public bool TryOptEmitSubroutineCall()
{
//Calls should always have a next block, unless
//we're translating a single basic block.
if (_currBlock.Next == null)
{
return false;
}
if (CurrOp.Emitter != InstEmit.Bl)
if (!(CurrOp is IOpCodeBImm op))
{
return false;
}
if (!_cache.TryGetSubroutine(((OpCodeBImmAl64)CurrOp).Imm, out TranslatedSub subroutine))
if (!_cache.TryGetSubroutine(op.Imm, out TranslatedSub sub))
{
return false;
}
//It's not worth to call a Tier0 method, because
//it contains slow code, rather than the entire function.
if (sub.Tier == TranslationTier.Tier0)
{
return false;
}
EmitStoreState(sub);
for (int index = 0; index < TranslatedSub.FixedArgTypes.Length; index++)
{
EmitLdarg(index);
}
EmitCall(subroutine.Method);
EmitCall(sub.Method);
return true;
}
@ -292,8 +350,8 @@ namespace ChocolArm64.Translation
InstEmitAluHelper.EmitAluLoadOpers(this);
Stloc(CmpOptTmp2Index, IoType.Int);
Stloc(CmpOptTmp1Index, IoType.Int);
Stloc(CmpOptTmp2Index, VarType.Int);
Stloc(CmpOptTmp1Index, VarType.Int);
}
private Dictionary<Condition, OpCode> _branchOps = new Dictionary<Condition, OpCode>()
@ -312,19 +370,57 @@ namespace ChocolArm64.Translation
public void EmitCondBranch(ILLabel target, Condition cond)
{
if (_optOpLastCompare != null &&
_optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond))
{
if (_optOpLastCompare.Emitter == InstEmit.Subs)
{
Ldloc(CmpOptTmp1Index, VarType.Int, _optOpLastCompare.RegisterSize);
Ldloc(CmpOptTmp2Index, VarType.Int, _optOpLastCompare.RegisterSize);
Emit(_branchOps[cond], target);
return;
}
else if (_optOpLastCompare.Emitter == InstEmit.Adds && cond != Condition.GeUn
&& cond != Condition.LtUn
&& cond != Condition.GtUn
&& cond != Condition.LeUn)
{
//There are several limitations that needs to be taken into account for CMN comparisons:
//* The unsigned comparisons are not valid, as they depend on the
//carry flag value, and they will have different values for addition and
//subtraction. For addition, it's carry, and for subtraction, it's borrow.
//So, we need to make sure we're not doing a unsigned compare for the CMN case.
//* We can only do the optimization for the immediate variants,
//because when the second operand value is exactly INT_MIN, we can't
//negate the value as theres no positive counterpart.
//Such invalid values can't be encoded on the immediate encodings.
if (_optOpLastCompare is IOpCodeAluImm64 op)
{
Ldloc(CmpOptTmp1Index, VarType.Int, _optOpLastCompare.RegisterSize);
if (_optOpLastCompare.RegisterSize == RegisterSize.Int32)
{
EmitLdc_I4((int)-op.Imm);
}
else
{
EmitLdc_I8(-op.Imm);
}
Emit(_branchOps[cond], target);
return;
}
}
}
OpCode ilOp;
int intCond = (int)cond;
if (_optOpLastCompare != null &&
_optOpLastCompare == _optOpLastFlagSet && _branchOps.ContainsKey(cond))
{
Ldloc(CmpOptTmp1Index, IoType.Int, _optOpLastCompare.RegisterSize);
Ldloc(CmpOptTmp2Index, IoType.Int, _optOpLastCompare.RegisterSize);
ilOp = _branchOps[cond];
}
else if (intCond < 14)
if (intCond < 14)
{
int condTrue = intCond >> 1;
@ -424,14 +520,14 @@ namespace ChocolArm64.Translation
{
if (amount > 0)
{
Stloc(RorTmpIndex, IoType.Int);
Ldloc(RorTmpIndex, IoType.Int);
Stloc(RorTmpIndex, VarType.Int);
Ldloc(RorTmpIndex, VarType.Int);
EmitLdc_I4(amount);
Emit(OpCodes.Shr_Un);
Ldloc(RorTmpIndex, IoType.Int);
Ldloc(RorTmpIndex, VarType.Int);
EmitLdc_I4(CurrOp.GetBitsCount() - amount);
@ -479,7 +575,7 @@ namespace ChocolArm64.Translation
public void EmitLdarg(int index)
{
_ilBlock.Add(new ILOpCodeLoad(index, IoType.Arg));
_ilBlock.Add(new ILOpCodeLoad(index, VarType.Arg));
}
public void EmitLdintzr(int index)
@ -521,22 +617,33 @@ namespace ChocolArm64.Translation
_ilBlock.Add(new ILOpCodeStoreState(_ilBlock));
}
public void EmitLdtmp() => EmitLdint(IntTmpIndex);
public void EmitSttmp() => EmitStint(IntTmpIndex);
private void EmitStoreState(TranslatedSub callSub)
{
_ilBlock.Add(new ILOpCodeStoreState(_ilBlock, callSub));
}
public void EmitLdvectmp() => EmitLdvec(VecTmp1Index);
public void EmitStvectmp() => EmitStvec(VecTmp1Index);
public void EmitLdtmp() => EmitLdint(IntGpTmp1Index);
public void EmitSttmp() => EmitStint(IntGpTmp1Index);
public void EmitLdvectmp2() => EmitLdvec(VecTmp2Index);
public void EmitStvectmp2() => EmitStvec(VecTmp2Index);
public void EmitLdtmp2() => EmitLdint(IntGpTmp2Index);
public void EmitSttmp2() => EmitStint(IntGpTmp2Index);
public void EmitLdint(int index) => Ldloc(index, IoType.Int);
public void EmitStint(int index) => Stloc(index, IoType.Int);
public void EmitLdvectmp() => EmitLdvec(VecGpTmp1Index);
public void EmitStvectmp() => EmitStvec(VecGpTmp1Index);
public void EmitLdvec(int index) => Ldloc(index, IoType.Vector);
public void EmitStvec(int index) => Stloc(index, IoType.Vector);
public void EmitLdvectmp2() => EmitLdvec(VecGpTmp2Index);
public void EmitStvectmp2() => EmitStvec(VecGpTmp2Index);
public void EmitLdflg(int index) => Ldloc(index, IoType.Flag);
public void EmitLdvectmp3() => EmitLdvec(VecGpTmp3Index);
public void EmitStvectmp3() => EmitStvec(VecGpTmp3Index);
public void EmitLdint(int index) => Ldloc(index, VarType.Int);
public void EmitStint(int index) => Stloc(index, VarType.Int);
public void EmitLdvec(int index) => Ldloc(index, VarType.Vector);
public void EmitStvec(int index) => Stloc(index, VarType.Vector);
public void EmitLdflg(int index) => Ldloc(index, VarType.Flag);
public void EmitStflg(int index)
{
//Set this only if any of the NZCV flag bits were modified.
@ -549,52 +656,32 @@ namespace ChocolArm64.Translation
_optOpLastFlagSet = CurrOp;
}
Stloc(index, IoType.Flag);
Stloc(index, VarType.Flag);
}
private void Ldloc(int index, IoType ioType)
private void Ldloc(int index, VarType varType)
{
_ilBlock.Add(new ILOpCodeLoad(index, ioType, CurrOp.RegisterSize));
_ilBlock.Add(new ILOpCodeLoad(index, varType, CurrOp.RegisterSize));
}
private void Ldloc(int index, IoType ioType, RegisterSize registerSize)
private void Ldloc(int index, VarType varType, RegisterSize registerSize)
{
_ilBlock.Add(new ILOpCodeLoad(index, ioType, registerSize));
_ilBlock.Add(new ILOpCodeLoad(index, varType, registerSize));
}
private void Stloc(int index, IoType ioType)
private void Stloc(int index, VarType varType)
{
_ilBlock.Add(new ILOpCodeStore(index, ioType, CurrOp.RegisterSize));
_ilBlock.Add(new ILOpCodeStore(index, varType, CurrOp.RegisterSize));
}
public void EmitCallPropGet(Type objType, string propName)
{
if (objType == null)
{
throw new ArgumentNullException(nameof(objType));
}
if (propName == null)
{
throw new ArgumentNullException(nameof(propName));
}
EmitCall(objType.GetMethod($"get_{propName}"));
EmitCall(objType, $"get_{propName}");
}
public void EmitCallPropSet(Type objType, string propName)
{
if (objType == null)
{
throw new ArgumentNullException(nameof(objType));
}
if (propName == null)
{
throw new ArgumentNullException(nameof(propName));
}
EmitCall(objType.GetMethod($"set_{propName}"));
EmitCall(objType, $"set_{propName}");
}
public void EmitCall(Type objType, string mthdName)
@ -612,6 +699,16 @@ namespace ChocolArm64.Translation
EmitCall(objType.GetMethod(mthdName));
}
public void EmitCallPrivatePropGet(Type objType, string propName)
{
EmitPrivateCall(objType, $"get_{propName}");
}
public void EmitCallPrivatePropSet(Type objType, string propName)
{
EmitPrivateCall(objType, $"set_{propName}");
}
public void EmitPrivateCall(Type objType, string mthdName)
{
if (objType == null)

View file

@ -6,7 +6,7 @@ namespace ChocolArm64.Translation
{
private bool _hasLabel;
private Label _lbl;
private Label _label;
public void Emit(ILMethodBuilder context)
{
@ -17,12 +17,12 @@ namespace ChocolArm64.Translation
{
if (!_hasLabel)
{
_lbl = context.Generator.DefineLabel();
_label = context.Generator.DefineLabel();
_hasLabel = true;
}
return _lbl;
return _label;
}
}
}

View file

@ -8,7 +8,10 @@ namespace ChocolArm64.Translation
{
class ILMethodBuilder
{
public LocalAlloc LocalAlloc { get; private set; }
private const int RegsCount = 32;
private const int RegsMask = RegsCount - 1;
public RegisterUsage RegUsage { get; private set; }
public ILGenerator Generator { get; private set; }
@ -18,29 +21,47 @@ namespace ChocolArm64.Translation
private string _subName;
public bool IsAarch64 { get; }
public bool IsSubComplete { get; }
private int _localsCount;
public ILMethodBuilder(ILBlock[] ilBlocks, string subName)
public ILMethodBuilder(
ILBlock[] ilBlocks,
string subName,
bool isAarch64,
bool isSubComplete = false)
{
_ilBlocks = ilBlocks;
_subName = subName;
_ilBlocks = ilBlocks;
_subName = subName;
IsAarch64 = isAarch64;
IsSubComplete = isSubComplete;
}
public TranslatedSub GetSubroutine(TranslationTier tier)
public TranslatedSub GetSubroutine(TranslationTier tier, bool isWorthOptimizing)
{
LocalAlloc = new LocalAlloc(_ilBlocks, _ilBlocks[0]);
RegUsage = new RegisterUsage();
RegUsage.BuildUses(_ilBlocks[0]);
DynamicMethod method = new DynamicMethod(_subName, typeof(long), TranslatedSub.FixedArgTypes);
Generator = method.GetILGenerator();
long intNiRegsMask = RegUsage.GetIntNotInputs(_ilBlocks[0]);
long vecNiRegsMask = RegUsage.GetVecNotInputs(_ilBlocks[0]);
TranslatedSub subroutine = new TranslatedSub(method, tier);
TranslatedSub subroutine = new TranslatedSub(
method,
intNiRegsMask,
vecNiRegsMask,
tier,
isWorthOptimizing);
_locals = new Dictionary<Register, int>();
_localsCount = 0;
new ILOpCodeLoadState(_ilBlocks[0]).Emit(this);
Generator = method.GetILGenerator();
foreach (ILBlock ilBlock in _ilBlocks)
{
@ -80,13 +101,13 @@ namespace ChocolArm64.Translation
public static Register GetRegFromBit(int bit, RegisterType baseType)
{
if (bit < 32)
if (bit < RegsCount)
{
return new Register(bit, baseType);
}
else if (baseType == RegisterType.Int)
{
return new Register(bit & 0x1f, RegisterType.Flag);
return new Register(bit & RegsMask, RegisterType.Flag);
}
else
{
@ -96,7 +117,7 @@ namespace ChocolArm64.Translation
public static bool IsRegIndex(int index)
{
return (uint)index < 32;
return (uint)index < RegsCount;
}
}
}

View file

@ -4,16 +4,16 @@ namespace ChocolArm64.Translation
{
struct ILOpCode : IILEmit
{
private OpCode _ilOp;
public OpCode ILOp { get; }
public ILOpCode(OpCode ilOp)
{
_ilOp = ilOp;
ILOp = ilOp;
}
public void Emit(ILMethodBuilder context)
{
context.Generator.Emit(_ilOp);
context.Generator.Emit(ILOp);
}
}
}

View file

@ -4,18 +4,18 @@ namespace ChocolArm64.Translation
{
struct ILOpCodeBranch : IILEmit
{
private OpCode _ilOp;
private ILLabel _label;
public OpCode ILOp { get; }
public ILLabel Label { get; }
public ILOpCodeBranch(OpCode ilOp, ILLabel label)
{
_ilOp = ilOp;
_label = label;
ILOp = ilOp;
Label = label;
}
public void Emit(ILMethodBuilder context)
{
context.Generator.Emit(_ilOp, _label.GetLabel(context));
context.Generator.Emit(ILOp, Label.GetLabel(context));
}
}
}

View file

@ -5,9 +5,9 @@ namespace ChocolArm64.Translation
{
struct ILOpCodeCall : IILEmit
{
public MethodInfo Info { get; private set; }
public MethodInfo Info { get; }
public bool IsVirtual { get; private set; }
public bool IsVirtual { get; }
public ILOpCodeCall(MethodInfo info, bool isVirtual)
{

View file

@ -16,6 +16,8 @@ namespace ChocolArm64.Translation
private ImmVal _value;
public long Value => _value.I8;
private enum ConstType
{
Int32,

View file

@ -5,28 +5,28 @@ namespace ChocolArm64.Translation
{
struct ILOpCodeLoad : IILEmit
{
public int Index { get; private set; }
public int Index { get; }
public IoType IoType { get; private set; }
public VarType VarType { get; }
public RegisterSize RegisterSize { get; private set; }
public RegisterSize RegisterSize { get; }
public ILOpCodeLoad(int index, IoType ioType, RegisterSize registerSize = 0)
public ILOpCodeLoad(int index, VarType varType, RegisterSize registerSize = 0)
{
Index = index;
IoType = ioType;
VarType = varType;
RegisterSize = registerSize;
}
public void Emit(ILMethodBuilder context)
{
switch (IoType)
switch (VarType)
{
case IoType.Arg: context.Generator.EmitLdarg(Index); break;
case VarType.Arg: context.Generator.EmitLdarg(Index); break;
case IoType.Flag: EmitLdloc(context, Index, RegisterType.Flag); break;
case IoType.Int: EmitLdloc(context, Index, RegisterType.Int); break;
case IoType.Vector: EmitLdloc(context, Index, RegisterType.Vector); break;
case VarType.Flag: EmitLdloc(context, Index, RegisterType.Flag); break;
case VarType.Int: EmitLdloc(context, Index, RegisterType.Int); break;
case VarType.Vector: EmitLdloc(context, Index, RegisterType.Vector); break;
}
}

View file

@ -5,7 +5,7 @@ namespace ChocolArm64.Translation
{
struct ILOpCodeLoadField : IILEmit
{
public FieldInfo Info { get; private set; }
public FieldInfo Info { get; }
public ILOpCodeLoadField(FieldInfo info)
{

View file

@ -7,15 +7,24 @@ namespace ChocolArm64.Translation
{
private ILBlock _block;
public ILOpCodeLoadState(ILBlock block)
private bool _isSubEntry;
public ILOpCodeLoadState(ILBlock block, bool isSubEntry = false)
{
_block = block;
_block = block;
_isSubEntry = isSubEntry;
}
public void Emit(ILMethodBuilder context)
{
long intInputs = context.LocalAlloc.GetIntInputs(_block);
long vecInputs = context.LocalAlloc.GetVecInputs(_block);
long intInputs = context.RegUsage.GetIntInputs(_block);
long vecInputs = context.RegUsage.GetVecInputs(_block);
if (Optimizations.AssumeStrictAbiCompliance && context.IsSubComplete)
{
intInputs = RegisterUsage.ClearCallerSavedIntRegs(intInputs, context.IsAarch64);
vecInputs = RegisterUsage.ClearCallerSavedVecRegs(vecInputs, context.IsAarch64);
}
LoadLocals(context, intInputs, RegisterType.Int);
LoadLocals(context, vecInputs, RegisterType.Vector);

View file

@ -2,16 +2,16 @@ namespace ChocolArm64.Translation
{
struct ILOpCodeLog : IILEmit
{
private string _text;
public string Text { get; }
public ILOpCodeLog(string text)
{
_text = text;
Text = text;
}
public void Emit(ILMethodBuilder context)
{
context.Generator.EmitWriteLine(_text);
context.Generator.EmitWriteLine(Text);
}
}
}

View file

@ -5,28 +5,28 @@ namespace ChocolArm64.Translation
{
struct ILOpCodeStore : IILEmit
{
public int Index { get; private set; }
public int Index { get; }
public IoType IoType { get; private set; }
public VarType VarType { get; }
public RegisterSize RegisterSize { get; private set; }
public RegisterSize RegisterSize { get; }
public ILOpCodeStore(int index, IoType ioType, RegisterSize registerSize = 0)
public ILOpCodeStore(int index, VarType varType, RegisterSize registerSize = 0)
{
Index = index;
IoType = ioType;
VarType = varType;
RegisterSize = registerSize;
}
public void Emit(ILMethodBuilder context)
{
switch (IoType)
switch (VarType)
{
case IoType.Arg: context.Generator.EmitStarg(Index); break;
case VarType.Arg: context.Generator.EmitStarg(Index); break;
case IoType.Flag: EmitStloc(context, Index, RegisterType.Flag); break;
case IoType.Int: EmitStloc(context, Index, RegisterType.Int); break;
case IoType.Vector: EmitStloc(context, Index, RegisterType.Vector); break;
case VarType.Flag: EmitStloc(context, Index, RegisterType.Flag); break;
case VarType.Int: EmitStloc(context, Index, RegisterType.Int); break;
case VarType.Vector: EmitStloc(context, Index, RegisterType.Vector); break;
}
}

View file

@ -7,15 +7,33 @@ namespace ChocolArm64.Translation
{
private ILBlock _block;
public ILOpCodeStoreState(ILBlock block)
private TranslatedSub _callSub;
public ILOpCodeStoreState(ILBlock block, TranslatedSub callSub = null)
{
_block = block;
_block = block;
_callSub = callSub;
}
public void Emit(ILMethodBuilder context)
{
long intOutputs = context.LocalAlloc.GetIntOutputs(_block);
long vecOutputs = context.LocalAlloc.GetVecOutputs(_block);
long intOutputs = context.RegUsage.GetIntOutputs(_block);
long vecOutputs = context.RegUsage.GetVecOutputs(_block);
if (Optimizations.AssumeStrictAbiCompliance && context.IsSubComplete)
{
intOutputs = RegisterUsage.ClearCallerSavedIntRegs(intOutputs, context.IsAarch64);
vecOutputs = RegisterUsage.ClearCallerSavedVecRegs(vecOutputs, context.IsAarch64);
}
if (_callSub != null)
{
//Those register are assigned on the callee function, without
//reading it's value first. We don't need to write them because
//they are not going to be read on the callee.
intOutputs &= ~_callSub.IntNiRegsMask;
vecOutputs &= ~_callSub.VecNiRegsMask;
}
StoreLocals(context, intOutputs, RegisterType.Int);
StoreLocals(context, vecOutputs, RegisterType.Vector);

View file

@ -3,8 +3,13 @@ using System.Collections.Generic;
namespace ChocolArm64.Translation
{
class LocalAlloc
class RegisterUsage
{
public const long CallerSavedIntRegistersMask = 0x7fL << 9;
public const long PStateNzcvFlagsMask = 0xfL << 60;
public const long CallerSavedVecRegistersMask = 0xffffL << 16;
private class PathIo
{
private Dictionary<ILBlock, long> _allInputs;
@ -18,31 +23,30 @@ namespace ChocolArm64.Translation
_cmnOutputs = new Dictionary<ILBlock, long>();
}
public PathIo(ILBlock root, long inputs, long outputs) : this()
public void Set(ILBlock entry, long inputs, long outputs)
{
Set(root, inputs, outputs);
}
public void Set(ILBlock root, long inputs, long outputs)
{
if (!_allInputs.TryAdd(root, inputs))
if (!_allInputs.TryAdd(entry, inputs))
{
_allInputs[root] |= inputs;
_allInputs[entry] |= inputs;
}
if (!_cmnOutputs.TryAdd(root, outputs))
if (!_cmnOutputs.TryAdd(entry, outputs))
{
_cmnOutputs[root] &= outputs;
_cmnOutputs[entry] &= outputs;
}
_allOutputs |= outputs;
}
public long GetInputs(ILBlock root)
public long GetInputs(ILBlock entry)
{
if (_allInputs.TryGetValue(root, out long inputs))
if (_allInputs.TryGetValue(entry, out long inputs))
{
return inputs | (_allOutputs & ~_cmnOutputs[root]);
//We also need to read the registers that may not be written
//by all paths that can reach a exit point, to ensure that
//the local variable will not remain uninitialized depending
//on the flow path taken.
return inputs | (_allOutputs & ~_cmnOutputs[entry]);
}
return 0;
@ -57,15 +61,38 @@ namespace ChocolArm64.Translation
private Dictionary<ILBlock, PathIo> _intPaths;
private Dictionary<ILBlock, PathIo> _vecPaths;
private struct BlockIo
private struct BlockIo : IEquatable<BlockIo>
{
public ILBlock Block;
public ILBlock Entry;
public ILBlock Block { get; }
public ILBlock Entry { get; }
public long IntInputs;
public long VecInputs;
public long IntOutputs;
public long VecOutputs;
public long IntInputs { get; set; }
public long VecInputs { get; set; }
public long IntOutputs { get; set; }
public long VecOutputs { get; set; }
public BlockIo(ILBlock block, ILBlock entry)
{
Block = block;
Entry = entry;
IntInputs = IntOutputs = 0;
VecInputs = VecOutputs = 0;
}
public BlockIo(
ILBlock block,
ILBlock entry,
long intInputs,
long vecInputs,
long intOutputs,
long vecOutputs) : this(block, entry)
{
IntInputs = intInputs;
VecInputs = vecInputs;
IntOutputs = intOutputs;
VecOutputs = vecOutputs;
}
public override bool Equals(object obj)
{
@ -74,6 +101,11 @@ namespace ChocolArm64.Translation
return false;
}
return Equals(other);
}
public bool Equals(BlockIo other)
{
return other.Block == Block &&
other.Entry == Entry &&
other.IntInputs == IntInputs &&
@ -98,25 +130,13 @@ namespace ChocolArm64.Translation
}
}
private const int MaxOptGraphLength = 40;
public LocalAlloc(ILBlock[] graph, ILBlock entry)
public RegisterUsage()
{
_intPaths = new Dictionary<ILBlock, PathIo>();
_vecPaths = new Dictionary<ILBlock, PathIo>();
if (graph.Length > 1 &&
graph.Length < MaxOptGraphLength)
{
InitializeOptimal(graph, entry);
}
else
{
InitializeFast(graph);
}
}
private void InitializeOptimal(ILBlock[] graph, ILBlock entry)
public void BuildUses(ILBlock entry)
{
//This will go through all possible paths on the graph,
//and store all inputs/outputs for each block. A register
@ -124,7 +144,7 @@ namespace ChocolArm64.Translation
//When a block can be reached by more than one path, then the
//output from all paths needs to be set for this block, and
//only outputs present in all of the parent blocks can be considered
//when doing input elimination. Each block chain have a entry, that's where
//when doing input elimination. Each block chain has a entry, that's where
//the code starts executing. They are present on the subroutine start point,
//and on call return points too (address written to X30 by BL).
HashSet<BlockIo> visited = new HashSet<BlockIo>();
@ -133,19 +153,13 @@ namespace ChocolArm64.Translation
void Enqueue(BlockIo block)
{
if (!visited.Contains(block))
if (visited.Add(block))
{
unvisited.Enqueue(block);
visited.Add(block);
}
}
Enqueue(new BlockIo()
{
Block = entry,
Entry = entry
});
Enqueue(new BlockIo(entry, entry));
while (unvisited.Count > 0)
{
@ -177,19 +191,21 @@ namespace ChocolArm64.Translation
void EnqueueFromCurrent(ILBlock block, bool retTarget)
{
BlockIo blockIo = new BlockIo() { Block = block };
BlockIo blockIo;
if (retTarget)
{
blockIo.Entry = block;
blockIo = new BlockIo(block, block);
}
else
{
blockIo.Entry = current.Entry;
blockIo.IntInputs = current.IntInputs;
blockIo.VecInputs = current.VecInputs;
blockIo.IntOutputs = current.IntOutputs;
blockIo.VecOutputs = current.VecOutputs;
blockIo = new BlockIo(
block,
current.Entry,
current.IntInputs,
current.VecInputs,
current.IntOutputs,
current.VecOutputs);
}
Enqueue(blockIo);
@ -207,54 +223,63 @@ namespace ChocolArm64.Translation
}
}
private void InitializeFast(ILBlock[] graph)
{
//This is WAY faster than InitializeOptimal, but results in
//unneeded loads and stores, so the resulting code will be slower.
long intInputs = 0, intOutputs = 0;
long vecInputs = 0, vecOutputs = 0;
public long GetIntInputs(ILBlock entry) => GetInputsImpl(entry, _intPaths.Values);
public long GetVecInputs(ILBlock entry) => GetInputsImpl(entry, _vecPaths.Values);
foreach (ILBlock block in graph)
{
intInputs |= block.IntInputs;
intOutputs |= block.IntOutputs;
vecInputs |= block.VecInputs;
vecOutputs |= block.VecOutputs;
}
//It's possible that not all code paths writes to those output registers,
//in those cases if we attempt to write an output registers that was
//not written, we will be just writing zero and messing up the old register value.
//So we just need to ensure that all outputs are loaded.
if (graph.Length > 1)
{
intInputs |= intOutputs;
vecInputs |= vecOutputs;
}
foreach (ILBlock block in graph)
{
_intPaths.Add(block, new PathIo(block, intInputs, intOutputs));
_vecPaths.Add(block, new PathIo(block, vecInputs, vecOutputs));
}
}
public long GetIntInputs(ILBlock root) => GetInputsImpl(root, _intPaths.Values);
public long GetVecInputs(ILBlock root) => GetInputsImpl(root, _vecPaths.Values);
private long GetInputsImpl(ILBlock root, IEnumerable<PathIo> values)
private long GetInputsImpl(ILBlock entry, IEnumerable<PathIo> values)
{
long inputs = 0;
foreach (PathIo path in values)
{
inputs |= path.GetInputs(root);
inputs |= path.GetInputs(entry);
}
return inputs;
}
public long GetIntNotInputs(ILBlock entry) => GetNotInputsImpl(entry, _intPaths.Values);
public long GetVecNotInputs(ILBlock entry) => GetNotInputsImpl(entry, _vecPaths.Values);
private long GetNotInputsImpl(ILBlock entry, IEnumerable<PathIo> values)
{
//Returns a mask with registers that are written to
//before being read. Only those registers that are
//written in all paths, and is not read before being
//written to on those paths, should be set on the mask.
long mask = -1L;
foreach (PathIo path in values)
{
mask &= path.GetOutputs() & ~path.GetInputs(entry);
}
return mask;
}
public long GetIntOutputs(ILBlock block) => _intPaths[block].GetOutputs();
public long GetVecOutputs(ILBlock block) => _vecPaths[block].GetOutputs();
public static long ClearCallerSavedIntRegs(long mask, bool isAarch64)
{
//TODO: ARM32 support.
if (isAarch64)
{
mask &= ~(CallerSavedIntRegistersMask | PStateNzcvFlagsMask);
}
return mask;
}
public static long ClearCallerSavedVecRegs(long mask, bool isAarch64)
{
//TODO: ARM32 support.
if (isAarch64)
{
mask &= ~CallerSavedVecRegistersMask;
}
return mask;
}
}
}

View file

@ -10,21 +10,41 @@ namespace ChocolArm64.Translation
class TranslatedSub
{
//This is the minimum amount of calls needed for the method
//to be retranslated with higher quality code. It's only worth
//doing that for hot code.
private const int MinCallCountForOpt = 30;
public ArmSubroutine Delegate { get; private set; }
public static int StateArgIdx { get; private set; }
public static int MemoryArgIdx { get; private set; }
public static int StateArgIdx { get; }
public static int MemoryArgIdx { get; }
public static Type[] FixedArgTypes { get; private set; }
public static Type[] FixedArgTypes { get; }
public DynamicMethod Method { get; private set; }
public DynamicMethod Method { get; }
public TranslationTier Tier { get; private set; }
public TranslationTier Tier { get; }
public TranslatedSub(DynamicMethod method, TranslationTier tier)
public long IntNiRegsMask { get; }
public long VecNiRegsMask { get; }
private bool _isWorthOptimizing;
private int _callCount;
public TranslatedSub(
DynamicMethod method,
long intNiRegsMask,
long vecNiRegsMask,
TranslationTier tier,
bool isWorthOptimizing)
{
Method = method ?? throw new ArgumentNullException(nameof(method));;
Tier = tier;
Method = method ?? throw new ArgumentNullException(nameof(method));;
IntNiRegsMask = intNiRegsMask;
VecNiRegsMask = vecNiRegsMask;
_isWorthOptimizing = isWorthOptimizing;
Tier = tier;
}
static TranslatedSub()
@ -61,5 +81,24 @@ namespace ChocolArm64.Translation
{
return Delegate(threadState, memory);
}
public bool IsWorthOptimizing()
{
if (!_isWorthOptimizing)
{
return false;
}
if (_callCount++ < MinCallCountForOpt)
{
return false;
}
//Only return true once, so that it is
//added to the queue only once.
_isWorthOptimizing = false;
return true;
}
}
}

View file

@ -63,48 +63,36 @@ namespace ChocolArm64.Translation
CpuTrace?.Invoke(this, new CpuTraceEventArgs(position));
}
TranslatedSub subroutine = GetOrTranslateSubroutine(state, position);
if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
{
sub = TranslateLowCq(position, state.GetExecutionMode());
}
position = subroutine.Execute(state, _memory);
position = sub.Execute(state, _memory);
}
while (position != 0 && state.Running);
state.CurrentTranslator = null;
}
internal void TranslateVirtualSubroutine(CpuThreadState state, long position)
{
if (!_cache.TryGetSubroutine(position, out TranslatedSub sub) || sub.Tier == TranslationTier.Tier0)
{
_queue.Enqueue(new TranslatorQueueItem(position, state.GetExecutionMode(), TranslationTier.Tier1));
}
}
internal ArmSubroutine GetOrTranslateVirtualSubroutine(CpuThreadState state, long position)
internal ArmSubroutine GetOrTranslateSubroutine(CpuThreadState state, long position, CallType cs)
{
if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
{
sub = TranslateLowCq(position, state.GetExecutionMode());
}
if (sub.Tier == TranslationTier.Tier0)
if (sub.IsWorthOptimizing())
{
_queue.Enqueue(new TranslatorQueueItem(position, state.GetExecutionMode(), TranslationTier.Tier1));
bool isComplete = cs == CallType.Call ||
cs == CallType.VirtualCall;
_queue.Enqueue(position, state.GetExecutionMode(), TranslationTier.Tier1, isComplete);
}
return sub.Delegate;
}
internal TranslatedSub GetOrTranslateSubroutine(CpuThreadState state, long position)
{
if (!_cache.TryGetSubroutine(position, out TranslatedSub subroutine))
{
subroutine = TranslateLowCq(position, state.GetExecutionMode());
}
return subroutine;
}
private void TranslateQueuedSubs()
{
while (_threadCount != 0)
@ -124,7 +112,7 @@ namespace ChocolArm64.Translation
}
else
{
TranslateHighCq(item.Position, item.Mode);
TranslateHighCq(item.Position, item.Mode, item.IsComplete);
}
}
else
@ -138,30 +126,36 @@ namespace ChocolArm64.Translation
{
Block block = Decoder.DecodeBasicBlock(_memory, position, mode);
ILEmitterCtx context = new ILEmitterCtx(_cache, _queue, TranslationTier.Tier0, block);
ILEmitterCtx context = new ILEmitterCtx(_memory, _cache, _queue, TranslationTier.Tier0, block);
string subName = GetSubroutineName(position);
ILMethodBuilder ilMthdBuilder = new ILMethodBuilder(context.GetILBlocks(), subName);
bool isAarch64 = mode == ExecutionMode.Aarch64;
TranslatedSub subroutine = ilMthdBuilder.GetSubroutine(TranslationTier.Tier0);
ILMethodBuilder ilMthdBuilder = new ILMethodBuilder(context.GetILBlocks(), subName, isAarch64);
TranslatedSub subroutine = ilMthdBuilder.GetSubroutine(TranslationTier.Tier0, isWorthOptimizing: true);
return _cache.GetOrAdd(position, subroutine, block.OpCodes.Count);
}
private void TranslateHighCq(long position, ExecutionMode mode)
private TranslatedSub TranslateHighCq(long position, ExecutionMode mode, bool isComplete)
{
Block graph = Decoder.DecodeSubroutine(_memory, position, mode);
ILEmitterCtx context = new ILEmitterCtx(_cache, _queue, TranslationTier.Tier1, graph);
ILEmitterCtx context = new ILEmitterCtx(_memory, _cache, _queue, TranslationTier.Tier1, graph);
ILBlock[] ilBlocks = context.GetILBlocks();
string subName = GetSubroutineName(position);
ILMethodBuilder ilMthdBuilder = new ILMethodBuilder(ilBlocks, subName);
bool isAarch64 = mode == ExecutionMode.Aarch64;
TranslatedSub subroutine = ilMthdBuilder.GetSubroutine(TranslationTier.Tier1);
isComplete &= !context.HasIndirectJump;
ILMethodBuilder ilMthdBuilder = new ILMethodBuilder(ilBlocks, subName, isAarch64, isComplete);
TranslatedSub subroutine = ilMthdBuilder.GetSubroutine(TranslationTier.Tier1, context.HasSlowCall);
int ilOpCount = 0;
@ -170,9 +164,11 @@ namespace ChocolArm64.Translation
ilOpCount += ilBlock.Count;
}
ForceAheadOfTimeCompilation(subroutine);
_cache.AddOrUpdate(position, subroutine, ilOpCount);
ForceAheadOfTimeCompilation(subroutine);
return subroutine;
}
private string GetSubroutineName(long position)

View file

@ -1,3 +1,4 @@
using ChocolArm64.State;
using System.Collections.Concurrent;
using System.Threading;
@ -5,10 +6,6 @@ namespace ChocolArm64.Translation
{
class TranslatorQueue
{
//This is the maximum number of functions to be translated that the queue can hold.
//The value may need some tuning to find the sweet spot.
private const int MaxQueueSize = 1024;
private ConcurrentStack<TranslatorQueueItem>[] _translationQueue;
private ManualResetEvent _queueDataReceivedEvent;
@ -27,14 +24,11 @@ namespace ChocolArm64.Translation
_queueDataReceivedEvent = new ManualResetEvent(false);
}
public void Enqueue(TranslatorQueueItem item)
public void Enqueue(long position, ExecutionMode mode, TranslationTier tier, bool isComplete)
{
ConcurrentStack<TranslatorQueueItem> queue = _translationQueue[(int)item.Tier];
TranslatorQueueItem item = new TranslatorQueueItem(position, mode, tier, isComplete);
if (queue.Count >= MaxQueueSize)
{
queue.TryPop(out _);
}
ConcurrentStack<TranslatorQueueItem> queue = _translationQueue[(int)tier];
queue.Push(item);

View file

@ -10,11 +10,18 @@ namespace ChocolArm64.Translation
public TranslationTier Tier { get; }
public TranslatorQueueItem(long position, ExecutionMode mode, TranslationTier tier)
public bool IsComplete { get; }
public TranslatorQueueItem(
long position,
ExecutionMode mode,
TranslationTier tier,
bool isComplete = false)
{
Position = position;
Mode = mode;
Tier = tier;
Position = position;
Mode = mode;
Tier = tier;
IsComplete = isComplete;
}
}
}

View file

@ -1,6 +1,6 @@
namespace ChocolArm64.Translation
{
enum IoType
enum VarType
{
Arg,
Flag,

View file

@ -34,6 +34,11 @@ namespace Ryujinx.Common
return value & -(long)size;
}
public static int DivRoundUp(int value, int dividend)
{
return (value + dividend - 1) / dividend;
}
public static ulong DivRoundUp(ulong value, uint dividend)
{
return (value + dividend - 1) / dividend;
@ -44,6 +49,24 @@ namespace Ryujinx.Common
return (value + dividend - 1) / dividend;
}
public static int Pow2RoundUp(int value)
{
value--;
value |= (value >> 1);
value |= (value >> 2);
value |= (value >> 4);
value |= (value >> 8);
value |= (value >> 16);
return ++value;
}
public static int Pow2RoundDown(int value)
{
return IsPowerOfTwo32(value) ? value : Pow2RoundUp(value) >> 1;
}
public static bool IsPowerOfTwo32(int value)
{
return value != 0 && (value & (value - 1)) == 0;
@ -77,7 +100,7 @@ namespace Ryujinx.Common
do
{
nibbleIdx -= 4;
preCount = ClzNibbleTbl[(value >> nibbleIdx) & 0b1111];
preCount = ClzNibbleTbl[(int)(value >> nibbleIdx) & 0b1111];
count += preCount;
}
while (preCount == 4);
@ -85,6 +108,18 @@ namespace Ryujinx.Common
return (ulong)count;
}
public static int CountTrailingZeros32(int value)
{
int count = 0;
while (((value >> count) & 1) == 0)
{
count++;
}
return count;
}
public static long ReverseBits64(long value)
{
return (long)ReverseBits64((ulong)value);
@ -101,4 +136,4 @@ namespace Ryujinx.Common
return (value >> 32) | (value << 32);
}
}
}
}

View file

@ -8,41 +8,41 @@ namespace Ryujinx.Graphics
private const int MethSetMethod = 0x10;
private const int MethSetData = 0x11;
private NvGpu Gpu;
private NvGpu _gpu;
public CdmaProcessor(NvGpu Gpu)
public CdmaProcessor(NvGpu gpu)
{
this.Gpu = Gpu;
_gpu = gpu;
}
public void PushCommands(NvGpuVmm Vmm, int[] CmdBuffer)
public void PushCommands(NvGpuVmm vmm, int[] cmdBuffer)
{
List<ChCommand> Commands = new List<ChCommand>();
List<ChCommand> commands = new List<ChCommand>();
ChClassId CurrentClass = 0;
ChClassId currentClass = 0;
for (int Index = 0; Index < CmdBuffer.Length; Index++)
for (int index = 0; index < cmdBuffer.Length; index++)
{
int Cmd = CmdBuffer[Index];
int cmd = cmdBuffer[index];
int Value = (Cmd >> 0) & 0xffff;
int MethodOffset = (Cmd >> 16) & 0xfff;
int value = (cmd >> 0) & 0xffff;
int methodOffset = (cmd >> 16) & 0xfff;
ChSubmissionMode SubmissionMode = (ChSubmissionMode)((Cmd >> 28) & 0xf);
ChSubmissionMode submissionMode = (ChSubmissionMode)((cmd >> 28) & 0xf);
switch (SubmissionMode)
switch (submissionMode)
{
case ChSubmissionMode.SetClass: CurrentClass = (ChClassId)(Value >> 6); break;
case ChSubmissionMode.SetClass: currentClass = (ChClassId)(value >> 6); break;
case ChSubmissionMode.Incrementing:
{
int Count = Value;
int count = value;
for (int ArgIdx = 0; ArgIdx < Count; ArgIdx++)
for (int argIdx = 0; argIdx < count; argIdx++)
{
int Argument = CmdBuffer[++Index];
int argument = cmdBuffer[++index];
Commands.Add(new ChCommand(CurrentClass, MethodOffset + ArgIdx, Argument));
commands.Add(new ChCommand(currentClass, methodOffset + argIdx, argument));
}
break;
@ -50,44 +50,44 @@ namespace Ryujinx.Graphics
case ChSubmissionMode.NonIncrementing:
{
int Count = Value;
int count = value;
int[] Arguments = new int[Count];
int[] arguments = new int[count];
for (int ArgIdx = 0; ArgIdx < Count; ArgIdx++)
for (int argIdx = 0; argIdx < count; argIdx++)
{
Arguments[ArgIdx] = CmdBuffer[++Index];
arguments[argIdx] = cmdBuffer[++index];
}
Commands.Add(new ChCommand(CurrentClass, MethodOffset, Arguments));
commands.Add(new ChCommand(currentClass, methodOffset, arguments));
break;
}
}
}
ProcessCommands(Vmm, Commands.ToArray());
ProcessCommands(vmm, commands.ToArray());
}
private void ProcessCommands(NvGpuVmm Vmm, ChCommand[] Commands)
private void ProcessCommands(NvGpuVmm vmm, ChCommand[] commands)
{
int MethodOffset = 0;
int methodOffset = 0;
foreach (ChCommand Command in Commands)
foreach (ChCommand command in commands)
{
switch (Command.MethodOffset)
switch (command.MethodOffset)
{
case MethSetMethod: MethodOffset = Command.Arguments[0]; break;
case MethSetMethod: methodOffset = command.Arguments[0]; break;
case MethSetData:
{
if (Command.ClassId == ChClassId.NvDec)
if (command.ClassId == ChClassId.NvDec)
{
Gpu.VideoDecoder.Process(Vmm, MethodOffset, Command.Arguments);
_gpu.VideoDecoder.Process(vmm, methodOffset, command.Arguments);
}
else if (Command.ClassId == ChClassId.GraphicsVic)
else if (command.ClassId == ChClassId.GraphicsVic)
{
Gpu.VideoImageComposer.Process(Vmm, MethodOffset, Command.Arguments);
_gpu.VideoImageComposer.Process(vmm, methodOffset, command.Arguments);
}
break;

View file

@ -2,7 +2,7 @@ namespace Ryujinx.Graphics
{
enum ChClassId
{
Host1x = 0x1,
Host1X = 0x1,
VideoEncodeMpeg = 0x20,
VideoEncodeNvEnc = 0x21,
VideoStreamingVi = 0x30,
@ -10,7 +10,7 @@ namespace Ryujinx.Graphics
VideoStreamingIspB = 0x34,
VideoStreamingViI2c = 0x36,
GraphicsVic = 0x5d,
Graphics3d = 0x60,
Graphics3D = 0x60,
GraphicsGpu = 0x61,
Tsec = 0xe0,
TsecB = 0xe1,

View file

@ -8,11 +8,11 @@ namespace Ryujinx.Graphics
public int[] Arguments { get; private set; }
public ChCommand(ChClassId ClassId, int MethodOffset, params int[] Arguments)
public ChCommand(ChClassId classId, int methodOffset, params int[] arguments)
{
this.ClassId = ClassId;
this.MethodOffset = MethodOffset;
this.Arguments = Arguments;
ClassId = classId;
MethodOffset = methodOffset;
Arguments = arguments;
}
}
}

View file

@ -0,0 +1,14 @@
namespace Ryujinx.Graphics
{
public enum DepthCompareFunc
{
Never = 0,
Less = 1,
Equal = 2,
LEqual = 3,
Greater = 4,
NotEqual = 5,
GEqual = 6,
Always = 7
}
}

View file

@ -6,10 +6,10 @@ namespace Ryujinx.Graphics
{
public class DmaPusher
{
private ConcurrentQueue<(NvGpuVmm, long)> IbBuffer;
private ConcurrentQueue<(NvGpuVmm, long)> _ibBuffer;
private long DmaPut;
private long DmaGet;
private long _dmaPut;
private long _dmaGet;
private struct DmaState
{
@ -21,43 +21,43 @@ namespace Ryujinx.Graphics
public int LengthPending;
}
private DmaState State;
private DmaState _state;
private bool SliEnable;
private bool SliActive;
private bool _sliEnable;
private bool _sliActive;
private bool IbEnable;
private bool NonMain;
private bool _ibEnable;
private bool _nonMain;
private long DmaMGet;
private long _dmaMGet;
private NvGpuVmm Vmm;
private NvGpuVmm _vmm;
private NvGpu Gpu;
private NvGpu _gpu;
private AutoResetEvent Event;
private AutoResetEvent _event;
public DmaPusher(NvGpu Gpu)
public DmaPusher(NvGpu gpu)
{
this.Gpu = Gpu;
_gpu = gpu;
IbBuffer = new ConcurrentQueue<(NvGpuVmm, long)>();
_ibBuffer = new ConcurrentQueue<(NvGpuVmm, long)>();
IbEnable = true;
_ibEnable = true;
Event = new AutoResetEvent(false);
_event = new AutoResetEvent(false);
}
public void Push(NvGpuVmm Vmm, long Entry)
public void Push(NvGpuVmm vmm, long entry)
{
IbBuffer.Enqueue((Vmm, Entry));
_ibBuffer.Enqueue((vmm, entry));
Event.Set();
_event.Set();
}
public bool WaitForCommands()
{
return Event.WaitOne(8);
return _event.WaitOne(8);
}
public void DispatchCalls()
@ -67,101 +67,101 @@ namespace Ryujinx.Graphics
private bool Step()
{
if (DmaGet != DmaPut)
if (_dmaGet != _dmaPut)
{
int Word = Vmm.ReadInt32(DmaGet);
int word = _vmm.ReadInt32(_dmaGet);
DmaGet += 4;
_dmaGet += 4;
if (!NonMain)
if (!_nonMain)
{
DmaMGet = DmaGet;
_dmaMGet = _dmaGet;
}
if (State.LengthPending != 0)
if (_state.LengthPending != 0)
{
State.LengthPending = 0;
State.MethodCount = Word & 0xffffff;
_state.LengthPending = 0;
_state.MethodCount = word & 0xffffff;
}
else if (State.MethodCount != 0)
else if (_state.MethodCount != 0)
{
if (!SliEnable || SliActive)
if (!_sliEnable || _sliActive)
{
CallMethod(Word);
CallMethod(word);
}
if (!State.NonIncrementing)
if (!_state.NonIncrementing)
{
State.Method++;
_state.Method++;
}
if (State.IncrementOnce)
if (_state.IncrementOnce)
{
State.NonIncrementing = true;
_state.NonIncrementing = true;
}
State.MethodCount--;
_state.MethodCount--;
}
else
{
int SumissionMode = (Word >> 29) & 7;
int sumissionMode = (word >> 29) & 7;
switch (SumissionMode)
switch (sumissionMode)
{
case 1:
//Incrementing.
SetNonImmediateState(Word);
SetNonImmediateState(word);
State.NonIncrementing = false;
State.IncrementOnce = false;
_state.NonIncrementing = false;
_state.IncrementOnce = false;
break;
case 3:
//Non-incrementing.
SetNonImmediateState(Word);
SetNonImmediateState(word);
State.NonIncrementing = true;
State.IncrementOnce = false;
_state.NonIncrementing = true;
_state.IncrementOnce = false;
break;
case 4:
//Immediate.
State.Method = (Word >> 0) & 0x1fff;
State.SubChannel = (Word >> 13) & 7;
State.NonIncrementing = true;
State.IncrementOnce = false;
_state.Method = (word >> 0) & 0x1fff;
_state.SubChannel = (word >> 13) & 7;
_state.NonIncrementing = true;
_state.IncrementOnce = false;
CallMethod((Word >> 16) & 0x1fff);
CallMethod((word >> 16) & 0x1fff);
break;
case 5:
//Increment-once.
SetNonImmediateState(Word);
SetNonImmediateState(word);
State.NonIncrementing = false;
State.IncrementOnce = true;
_state.NonIncrementing = false;
_state.IncrementOnce = true;
break;
}
}
}
else if (IbEnable && IbBuffer.TryDequeue(out (NvGpuVmm Vmm, long Entry) Tuple))
else if (_ibEnable && _ibBuffer.TryDequeue(out (NvGpuVmm Vmm, long Entry) tuple))
{
this.Vmm = Tuple.Vmm;
_vmm = tuple.Vmm;
long Entry = Tuple.Entry;
long entry = tuple.Entry;
int Length = (int)(Entry >> 42) & 0x1fffff;
int length = (int)(entry >> 42) & 0x1fffff;
DmaGet = Entry & 0xfffffffffc;
DmaPut = DmaGet + Length * 4;
_dmaGet = entry & 0xfffffffffc;
_dmaPut = _dmaGet + length * 4;
NonMain = (Entry & (1L << 41)) != 0;
_nonMain = (entry & (1L << 41)) != 0;
Gpu.ResourceManager.ClearPbCache();
_gpu.ResourceManager.ClearPbCache();
}
else
{
@ -171,20 +171,20 @@ namespace Ryujinx.Graphics
return true;
}
private void SetNonImmediateState(int Word)
private void SetNonImmediateState(int word)
{
State.Method = (Word >> 0) & 0x1fff;
State.SubChannel = (Word >> 13) & 7;
State.MethodCount = (Word >> 16) & 0x1fff;
_state.Method = (word >> 0) & 0x1fff;
_state.SubChannel = (word >> 13) & 7;
_state.MethodCount = (word >> 16) & 0x1fff;
}
private void CallMethod(int Argument)
private void CallMethod(int argument)
{
Gpu.Fifo.CallMethod(Vmm, new GpuMethodCall(
State.Method,
Argument,
State.SubChannel,
State.MethodCount));
_gpu.Fifo.CallMethod(_vmm, new GpuMethodCall(
_state.Method,
argument,
_state.SubChannel,
_state.MethodCount));
}
}
}

View file

@ -5,15 +5,15 @@ namespace Ryujinx.Graphics.Gal
{
static class EmbeddedResource
{
public static string GetString(string Name)
public static string GetString(string name)
{
Assembly Asm = typeof(EmbeddedResource).Assembly;
Assembly asm = typeof(EmbeddedResource).Assembly;
using (Stream ResStream = Asm.GetManifestResourceStream(Name))
using (Stream resStream = asm.GetManifestResourceStream(name))
{
StreamReader Reader = new StreamReader(ResStream);
StreamReader reader = new StreamReader(resStream);
return Reader.ReadToEnd();
return reader.ReadToEnd();
}
}
}

View file

@ -8,15 +8,15 @@ namespace Ryujinx.Graphics.Gal
public float Alpha { get; private set; }
public GalColorF(
float Red,
float Green,
float Blue,
float Alpha)
float red,
float green,
float blue,
float alpha)
{
this.Red = Red;
this.Green = Green;
this.Blue = Blue;
this.Alpha = Alpha;
Red = red;
Green = green;
Blue = blue;
Alpha = alpha;
}
}
}

View file

@ -2,7 +2,7 @@
{
public enum GalFrontFace
{
CW = 0x900,
CCW = 0x901
Cw = 0x900,
Ccw = 0x901
}
}

View file

@ -6,9 +6,15 @@ namespace Ryujinx.Graphics.Gal
{
public int Width;
public int Height;
// FIXME: separate layer and depth
public int Depth;
public int LayerCount;
public int TileWidth;
public int GobBlockHeight;
public int GobBlockDepth;
public int Pitch;
public int MaxMipmapLevel;
public GalImageFormat Format;
public GalMemoryLayout Layout;
@ -16,48 +22,66 @@ namespace Ryujinx.Graphics.Gal
public GalTextureSource YSource;
public GalTextureSource ZSource;
public GalTextureSource WSource;
public GalTextureTarget TextureTarget;
public GalImage(
int Width,
int Height,
int TileWidth,
int GobBlockHeight,
GalMemoryLayout Layout,
GalImageFormat Format,
GalTextureSource XSource = GalTextureSource.Red,
GalTextureSource YSource = GalTextureSource.Green,
GalTextureSource ZSource = GalTextureSource.Blue,
GalTextureSource WSource = GalTextureSource.Alpha)
int width,
int height,
int depth,
int layerCount,
int tileWidth,
int gobBlockHeight,
int gobBlockDepth,
GalMemoryLayout layout,
GalImageFormat format,
GalTextureTarget textureTarget,
int maxMipmapLevel = 1,
GalTextureSource xSource = GalTextureSource.Red,
GalTextureSource ySource = GalTextureSource.Green,
GalTextureSource zSource = GalTextureSource.Blue,
GalTextureSource wSource = GalTextureSource.Alpha)
{
this.Width = Width;
this.Height = Height;
this.TileWidth = TileWidth;
this.GobBlockHeight = GobBlockHeight;
this.Layout = Layout;
this.Format = Format;
this.XSource = XSource;
this.YSource = YSource;
this.ZSource = ZSource;
this.WSource = WSource;
Width = width;
Height = height;
LayerCount = layerCount;
Depth = depth;
TileWidth = tileWidth;
GobBlockHeight = gobBlockHeight;
GobBlockDepth = gobBlockDepth;
Layout = layout;
Format = format;
MaxMipmapLevel = maxMipmapLevel;
XSource = xSource;
YSource = ySource;
ZSource = zSource;
WSource = wSource;
TextureTarget = textureTarget;
Pitch = ImageUtils.GetPitch(Format, Width);
Pitch = ImageUtils.GetPitch(format, width);
}
public bool SizeMatches(GalImage Image)
public bool SizeMatches(GalImage image, bool ignoreLayer = false)
{
if (ImageUtils.GetBytesPerPixel(Format) !=
ImageUtils.GetBytesPerPixel(Image.Format))
ImageUtils.GetBytesPerPixel(image.Format))
{
return false;
}
if (ImageUtils.GetAlignedWidth(this) !=
ImageUtils.GetAlignedWidth(Image))
ImageUtils.GetAlignedWidth(image))
{
return false;
}
return Height == Image.Height;
bool result = Height == image.Height && Depth == image.Depth;
if (!ignoreLayer)
{
result = result && LayerCount == image.LayerCount;
}
return result;
}
}
}

View file

@ -22,23 +22,23 @@ namespace Ryujinx.Graphics.Gal
Astc2D12x12,
Astc2DEnd,
RGBA4,
RGB565,
BGR565,
BGR5A1,
RGB5A1,
Rgba4,
Rgb565,
Bgr565,
Bgr5A1,
Rgb5A1,
R8,
RG8,
RGBX8,
RGBA8,
BGRA8,
RGB10A2,
Rg8,
Rgbx8,
Rgba8,
Bgra8,
Rgb10A2,
R16,
RG16,
RGBA16,
Rg16,
Rgba16,
R32,
RG32,
RGBA32,
Rg32,
Rgba32,
R11G11B10,
D16,
D24,

View file

@ -2,7 +2,7 @@
{
public struct ColorMaskState
{
private static readonly ColorMaskState _Default = new ColorMaskState()
private static readonly ColorMaskState DefaultBackingField = new ColorMaskState()
{
Red = true,
Green = true,
@ -10,7 +10,7 @@
Alpha = true
};
public static ColorMaskState Default => _Default;
public static ColorMaskState Default => DefaultBackingField;
public bool Red;
public bool Green;
@ -20,7 +20,7 @@
public struct BlendState
{
private static readonly BlendState _Default = new BlendState()
private static readonly BlendState DefaultBackingField = new BlendState()
{
Enabled = false,
SeparateAlpha = false,
@ -32,7 +32,7 @@
FuncDstAlpha = GalBlendFactor.Zero
};
public static BlendState Default => _Default;
public static BlendState Default => DefaultBackingField;
public bool Enabled;
public bool SeparateAlpha;
@ -111,9 +111,9 @@
{
ConstBufferKeys = new long[Stages][];
for (int Stage = 0; Stage < Stages; Stage++)
for (int stage = 0; stage < Stages; stage++)
{
ConstBufferKeys[Stage] = new long[ConstBuffersPerStage];
ConstBufferKeys[stage] = new long[ConstBuffersPerStage];
}
Blends = new BlendState[RenderTargetsCount];

View file

@ -4,48 +4,48 @@
{
Bitmap = 0x1c,
Unknown1D = 0x1d,
RGBA32Float = 0xc0,
RGBA32Sint = 0xc1,
RGBA32Uint = 0xc2,
RGBX32Float = 0xc3,
RGBX32Sint = 0xc4,
RGBX32Uint = 0xc5,
RGBA16Unorm = 0xc6,
RGBA16Snorm = 0xc7,
RGBA16Sint = 0xc8,
RGBA16Uint = 0xc9,
RGBA16Float = 0xca,
RG32Float = 0xcb,
RG32Sint = 0xcc,
RG32Uint = 0xcd,
RGBX16Float = 0xce,
BGRA8Unorm = 0xcf,
BGRA8Srgb = 0xd0,
RGB10A2Unorm = 0xd1,
RGB10A2Uint = 0xd2,
RGBA8Unorm = 0xd5,
RGBA8Srgb = 0xd6,
RGBA8Snorm = 0xd7,
RGBA8Sint = 0xd8,
RGBA8Uint = 0xd9,
RG16Unorm = 0xda,
RG16Snorm = 0xdb,
RG16Sint = 0xdc,
RG16Uint = 0xdd,
RG16Float = 0xde,
BGR10A2Unorm = 0xdf,
Rgba32Float = 0xc0,
Rgba32Sint = 0xc1,
Rgba32Uint = 0xc2,
Rgbx32Float = 0xc3,
Rgbx32Sint = 0xc4,
Rgbx32Uint = 0xc5,
Rgba16Unorm = 0xc6,
Rgba16Snorm = 0xc7,
Rgba16Sint = 0xc8,
Rgba16Uint = 0xc9,
Rgba16Float = 0xca,
Rg32Float = 0xcb,
Rg32Sint = 0xcc,
Rg32Uint = 0xcd,
Rgbx16Float = 0xce,
Bgra8Unorm = 0xcf,
Bgra8Srgb = 0xd0,
Rgb10A2Unorm = 0xd1,
Rgb10A2Uint = 0xd2,
Rgba8Unorm = 0xd5,
Rgba8Srgb = 0xd6,
Rgba8Snorm = 0xd7,
Rgba8Sint = 0xd8,
Rgba8Uint = 0xd9,
Rg16Unorm = 0xda,
Rg16Snorm = 0xdb,
Rg16Sint = 0xdc,
Rg16Uint = 0xdd,
Rg16Float = 0xde,
Bgr10A2Unorm = 0xdf,
R11G11B10Float = 0xe0,
R32Sint = 0xe3,
R32Uint = 0xe4,
R32Float = 0xe5,
BGRX8Unorm = 0xe6,
BGRX8Srgb = 0xe7,
Bgrx8Unorm = 0xe6,
Bgrx8Srgb = 0xe7,
B5G6R5Unorm = 0xe8,
BGR5A1Unorm = 0xe9,
RG8Unorm = 0xea,
RG8Snorm = 0xeb,
RG8Sint = 0xec,
RG8Uint = 0xed,
Bgr5A1Unorm = 0xe9,
Rg8Unorm = 0xea,
Rg8Snorm = 0xeb,
Rg8Sint = 0xec,
Rg8Uint = 0xed,
R16Unorm = 0xee,
R16Snorm = 0xef,
R16Sint = 0xf0,
@ -56,13 +56,13 @@
R8Sint = 0xf5,
R8Uint = 0xf6,
A8Unorm = 0xf7,
BGR5X1Unorm = 0xf8,
RGBX8Unorm = 0xf9,
RGBX8Srgb = 0xfa,
BGR5X1UnormUnknownFB = 0xfb,
BGR5X1UnormUnknownFC = 0xfc,
BGRX8UnormUnknownFD = 0xfd,
BGRX8UnormUnknownFE = 0xfe,
Bgr5x1Unorm = 0xf8,
Rgbx8Unorm = 0xf9,
Rgbx8Srgb = 0xfa,
Bgr5x1UnormUnknownFB = 0xfb,
Bgr5x1UnormUnknownFC = 0xfc,
Bgrx8UnormUnknownFD = 0xfd,
Bgrx8UnormUnknownFE = 0xfe,
Y32UintUnknownFF = 0xff
}
}

View file

@ -2,20 +2,20 @@ namespace Ryujinx.Graphics.Gal
{
public enum GalTextureFormat
{
RGBA32 = 0x1,
RGBA16 = 0x3,
RG32 = 0x4,
RGBA8 = 0x8,
RGB10A2 = 0x9,
RG16 = 0xc,
Rgba32 = 0x1,
Rgba16 = 0x3,
Rg32 = 0x4,
Rgba8 = 0x8,
Rgb10A2 = 0x9,
Rg16 = 0xc,
R32 = 0xf,
BptcSfloat = 0x10,
BptcUfloat = 0x11,
RGBA4 = 0x12,
RGB5A1 = 0x14,
RGB565 = 0x15,
Rgba4 = 0x12,
Rgb5A1 = 0x14,
Rgb565 = 0x15,
BptcUnorm = 0x17,
RG8 = 0x18,
Rg8 = 0x18,
R16 = 0x1b,
R8 = 0x1d,
R11G11B10F = 0x21,
@ -26,7 +26,7 @@ namespace Ryujinx.Graphics.Gal
BC5 = 0x28,
D24S8 = 0x29,
D32F = 0x2f,
D32FX24S8 = 0x30,
D32Fx24S8 = 0x30,
D16 = 0x3a,
Astc2D4x4 = 0x40,
Astc2D5x5 = 0x41,

View file

@ -12,22 +12,30 @@ namespace Ryujinx.Graphics.Gal
public GalColorF BorderColor { get; private set; }
public bool DepthCompare { get; private set; }
public DepthCompareFunc DepthCompareFunc { get; private set; }
public GalTextureSampler(
GalTextureWrap AddressU,
GalTextureWrap AddressV,
GalTextureWrap AddressP,
GalTextureFilter MinFilter,
GalTextureFilter MagFilter,
GalTextureMipFilter MipFilter,
GalColorF BorderColor)
GalTextureWrap addressU,
GalTextureWrap addressV,
GalTextureWrap addressP,
GalTextureFilter minFilter,
GalTextureFilter magFilter,
GalTextureMipFilter mipFilter,
GalColorF borderColor,
bool depthCompare,
DepthCompareFunc depthCompareFunc)
{
this.AddressU = AddressU;
this.AddressV = AddressV;
this.AddressP = AddressP;
this.MinFilter = MinFilter;
this.MagFilter = MagFilter;
this.MipFilter = MipFilter;
this.BorderColor = BorderColor;
AddressU = addressU;
AddressV = addressV;
AddressP = addressP;
MinFilter = minFilter;
MagFilter = magFilter;
MipFilter = mipFilter;
BorderColor = borderColor;
DepthCompare = depthCompare;
DepthCompareFunc = depthCompareFunc;
}
}
}

View file

@ -0,0 +1,15 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalTextureTarget
{
OneD = 0,
TwoD = 1,
ThreeD = 2,
CubeMap = 3,
OneDArray = 4,
TwoDArray = 5,
OneDBuffer = 6,
TwoDNoMipMap = 7,
CubeArray = 8,
}
}

View file

@ -6,8 +6,8 @@
Unorm = 2,
Sint = 3,
Uint = 4,
Snorm_Force_Fp16 = 5,
Unorm_Force_Fp16 = 6,
SnormForceFp16 = 5,
UnormForceFp16 = 6,
Float = 7
}
}

View file

@ -13,21 +13,21 @@ namespace Ryujinx.Graphics.Gal
public bool IsBgra { get; private set; }
public GalVertexAttrib(
int Index,
bool IsConst,
int Offset,
byte[] Data,
GalVertexAttribSize Size,
GalVertexAttribType Type,
bool IsBgra)
int index,
bool isConst,
int offset,
byte[] data,
GalVertexAttribSize size,
GalVertexAttribType type,
bool isBgra)
{
this.Index = Index;
this.IsConst = IsConst;
this.Data = Data;
this.Offset = Offset;
this.Size = Size;
this.Type = Type;
this.IsBgra = IsBgra;
Index = index;
IsConst = isConst;
Data = data;
Offset = offset;
Size = size;
Type = type;
IsBgra = isBgra;
}
}
}

View file

@ -7,11 +7,11 @@ namespace Ryujinx.Graphics.Gal
void LockCache();
void UnlockCache();
void Create(long Key, long Size);
void Create(long key, long size);
bool IsCached(long Key, long Size);
bool IsCached(long key, long size);
void SetData(long Key, long Size, IntPtr HostAddress);
void SetData(long Key, byte[] Data);
void SetData(long key, long size, IntPtr hostAddress);
void SetData(long key, byte[] data);
}
}

View file

@ -1,7 +1,7 @@
namespace Ryujinx.Graphics.Gal
{
public unsafe interface IGalMemory
public interface IGalMemory
{
int ReadInt32(long Position);
int ReadInt32(long position);
}
}

View file

@ -2,9 +2,10 @@
{
public interface IGalPipeline
{
void Bind(GalPipelineState State);
void Bind(GalPipelineState state);
void Unbind(GalPipelineState state);
void ResetDepthMask();
void ResetColorMask(int Index);
void ResetColorMask(int index);
}
}

View file

@ -8,29 +8,29 @@ namespace Ryujinx.Graphics.Gal
void UnlockCaches();
void ClearBuffers(
GalClearBufferFlags Flags,
int Attachment,
float Red,
float Green,
float Blue,
float Alpha,
float Depth,
int Stencil);
GalClearBufferFlags flags,
int attachment,
float red,
float green,
float blue,
float alpha,
float depth,
int stencil);
bool IsVboCached(long Key, long DataSize);
bool IsVboCached(long key, long dataSize);
bool IsIboCached(long Key, long DataSize);
bool IsIboCached(long key, long dataSize);
void CreateVbo(long Key, int DataSize, IntPtr HostAddress);
void CreateVbo(long Key, byte[] Data);
void CreateVbo(long key, int dataSize, IntPtr hostAddress);
void CreateVbo(long key, byte[] data);
void CreateIbo(long Key, int DataSize, IntPtr HostAddress);
void CreateIbo(long Key, int DataSize, byte[] Buffer);
void CreateIbo(long key, int dataSize, IntPtr hostAddress);
void CreateIbo(long key, int dataSize, byte[] buffer);
void SetIndexArray(int Size, GalIndexFormat Format);
void SetIndexArray(int size, GalIndexFormat format);
void DrawArrays(int First, int Count, GalPrimitiveType PrimType);
void DrawArrays(int first, int count, GalPrimitiveType primType);
void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType);
void DrawElements(long iboKey, int first, int vertexBase, GalPrimitiveType primType);
}
}

View file

@ -4,38 +4,42 @@ namespace Ryujinx.Graphics.Gal
{
void Bind();
void BindColor(long Key, int Attachment);
void BindColor(long key, int attachment);
void UnbindColor(int Attachment);
void UnbindColor(int attachment);
void BindZeta(long Key);
void BindZeta(long key);
void UnbindZeta();
void Present(long Key);
void Present(long key);
void SetMap(int[] Map);
void SetMap(int[] map);
void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom);
void SetTransform(bool flipX, bool flipY, int top, int left, int right, int bottom);
void SetWindowSize(int Width, int Height);
void SetWindowSize(int width, int height);
void SetViewport(int Attachment, int X, int Y, int Width, int Height);
void SetViewport(int attachment, int x, int y, int width, int height);
void Render();
void Copy(
long SrcKey,
long DstKey,
int SrcX0,
int SrcY0,
int SrcX1,
int SrcY1,
int DstX0,
int DstY0,
int DstX1,
int DstY1);
GalImage srcImage,
GalImage dstImage,
long srcKey,
long dstKey,
int srcLayer,
int dstLayer,
int srcX0,
int srcY0,
int srcX1,
int srcY1,
int dstX0,
int dstY0,
int dstX1,
int dstY1);
void Reinterpret(long Key, GalImage NewImage);
void Reinterpret(long key, GalImage newImage);
}
}

View file

@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Gal
{
public interface IGalRenderer
{
void QueueAction(Action ActionMthd);
void QueueAction(Action actionMthd);
void RunActions();

View file

@ -1,19 +1,20 @@
using Ryujinx.Graphics.Shader;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal
{
public interface IGalShader
{
void Create(IGalMemory Memory, long Key, GalShaderType Type);
void Create(IGalMemory memory, long key, GalShaderType type);
void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type);
void Create(IGalMemory memory, long vpAPos, long key, GalShaderType type);
IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key);
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key);
IEnumerable<CBufferDescriptor> GetConstBufferUsage(long key);
IEnumerable<TextureDescriptor> GetTextureUsage(long key);
void Bind(long Key);
void Bind(long key);
void Unbind(GalShaderType Type);
void Unbind(GalShaderType type);
void BindProgram();
}

View file

@ -5,14 +5,14 @@ namespace Ryujinx.Graphics.Gal
void LockCache();
void UnlockCache();
void Create(long Key, int Size, GalImage Image);
void Create(long key, int size, GalImage image);
void Create(long Key, byte[] Data, GalImage Image);
void Create(long key, byte[] data, GalImage image);
bool TryGetImage(long Key, out GalImage Image);
bool TryGetImage(long key, out GalImage image);
void Bind(long Key, int Index, GalImage Image);
void Bind(long key, int index, GalImage image);
void SetSampler(GalTextureSampler Sampler);
void SetSampler(GalImage image, GalTextureSampler sampler);
}
}

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Graphics.Gal.OpenGL
{
delegate void DeleteValue<T>(T Value);
delegate void DeleteValue<T>(T value);
}

View file

@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public int Width => Image.Width;
public int Height => Image.Height;
public int Depth => Image.Depth;
public GalImageFormat Format => Image.Format;
@ -17,10 +18,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public bool HasDepth => ImageUtils.HasDepth(Image.Format);
public bool HasStencil => ImageUtils.HasStencil(Image.Format);
public ImageHandler(int Handle, GalImage Image)
public ImageHandler(int handle, GalImage image)
{
this.Handle = Handle;
this.Image = Image;
Handle = handle;
Image = image;
}
}
}

View file

@ -1,191 +0,0 @@
using Ryujinx.Common;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLCachedResource<T>
{
public delegate void DeleteValue(T Value);
private const int MinTimeDelta = 5 * 60000;
private const int MaxRemovalsPerRun = 10;
private struct CacheBucket
{
public T Value { get; private set; }
public LinkedListNode<long> Node { get; private set; }
public long DataSize { get; private set; }
public long Timestamp { get; private set; }
public CacheBucket(T Value, long DataSize, LinkedListNode<long> Node)
{
this.Value = Value;
this.DataSize = DataSize;
this.Node = Node;
Timestamp = PerformanceCounter.ElapsedMilliseconds;
}
}
private Dictionary<long, CacheBucket> Cache;
private LinkedList<long> SortedCache;
private DeleteValue DeleteValueCallback;
private Queue<T> DeletePending;
private bool Locked;
private long MaxSize;
private long TotalSize;
public OGLCachedResource(DeleteValue DeleteValueCallback, long MaxSize)
{
this.MaxSize = MaxSize;
if (DeleteValueCallback == null)
{
throw new ArgumentNullException(nameof(DeleteValueCallback));
}
this.DeleteValueCallback = DeleteValueCallback;
Cache = new Dictionary<long, CacheBucket>();
SortedCache = new LinkedList<long>();
DeletePending = new Queue<T>();
}
public void Lock()
{
Locked = true;
}
public void Unlock()
{
Locked = false;
while (DeletePending.TryDequeue(out T Value))
{
DeleteValueCallback(Value);
}
ClearCacheIfNeeded();
}
public void AddOrUpdate(long Key, T Value, long Size)
{
if (!Locked)
{
ClearCacheIfNeeded();
}
LinkedListNode<long> Node = SortedCache.AddLast(Key);
CacheBucket NewBucket = new CacheBucket(Value, Size, Node);
if (Cache.TryGetValue(Key, out CacheBucket Bucket))
{
if (Locked)
{
DeletePending.Enqueue(Bucket.Value);
}
else
{
DeleteValueCallback(Bucket.Value);
}
SortedCache.Remove(Bucket.Node);
TotalSize -= Bucket.DataSize;
Cache[Key] = NewBucket;
}
else
{
Cache.Add(Key, NewBucket);
}
TotalSize += Size;
}
public bool TryGetValue(long Key, out T Value)
{
if (Cache.TryGetValue(Key, out CacheBucket Bucket))
{
Value = Bucket.Value;
SortedCache.Remove(Bucket.Node);
LinkedListNode<long> Node = SortedCache.AddLast(Key);
Cache[Key] = new CacheBucket(Value, Bucket.DataSize, Node);
return true;
}
Value = default(T);
return false;
}
public bool TryGetSize(long Key, out long Size)
{
if (Cache.TryGetValue(Key, out CacheBucket Bucket))
{
Size = Bucket.DataSize;
return true;
}
Size = 0;
return false;
}
private void ClearCacheIfNeeded()
{
long Timestamp = PerformanceCounter.ElapsedMilliseconds;
int Count = 0;
while (Count++ < MaxRemovalsPerRun)
{
LinkedListNode<long> Node = SortedCache.First;
if (Node == null)
{
break;
}
CacheBucket Bucket = Cache[Node.Value];
long TimeDelta = Timestamp - Bucket.Timestamp;
if (TimeDelta <= MinTimeDelta && !UnderMemoryPressure())
{
break;
}
SortedCache.Remove(Node);
Cache.Remove(Node.Value);
DeleteValueCallback(Bucket.Value);
TotalSize -= Bucket.DataSize;
}
}
private bool UnderMemoryPressure()
{
return TotalSize >= MaxSize;
}
}
}

View file

@ -1,74 +0,0 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLConstBuffer : IGalConstBuffer
{
private const long MaxConstBufferCacheSize = 64 * 1024 * 1024;
private OGLCachedResource<OGLStreamBuffer> Cache;
public OGLConstBuffer()
{
Cache = new OGLCachedResource<OGLStreamBuffer>(DeleteBuffer, MaxConstBufferCacheSize);
}
public void LockCache()
{
Cache.Lock();
}
public void UnlockCache()
{
Cache.Unlock();
}
public void Create(long Key, long Size)
{
OGLStreamBuffer Buffer = new OGLStreamBuffer(BufferTarget.UniformBuffer, Size);
Cache.AddOrUpdate(Key, Buffer, Size);
}
public bool IsCached(long Key, long Size)
{
return Cache.TryGetSize(Key, out long CachedSize) && CachedSize == Size;
}
public void SetData(long Key, long Size, IntPtr HostAddress)
{
if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
{
Buffer.SetData(Size, HostAddress);
}
}
public void SetData(long Key, byte[] Data)
{
if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
{
Buffer.SetData(Data);
}
}
public bool TryGetUbo(long Key, out int UboHandle)
{
if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
{
UboHandle = Buffer.Handle;
return true;
}
UboHandle = 0;
return false;
}
private static void DeleteBuffer(OGLStreamBuffer Buffer)
{
Buffer.Dispose();
}
}
}

View file

@ -1,31 +0,0 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OGLExtension
{
private static Lazy<bool> s_EnhancedLayouts = new Lazy<bool>(() => HasExtension("GL_ARB_enhanced_layouts"));
private static Lazy<bool> s_TextureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp"));
private static Lazy<bool> s_ViewportArray = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array"));
public static bool EnhancedLayouts => s_EnhancedLayouts.Value;
public static bool TextureMirrorClamp => s_TextureMirrorClamp.Value;
public static bool ViewportArray => s_ViewportArray.Value;
private static bool HasExtension(string Name)
{
int NumExtensions = GL.GetInteger(GetPName.NumExtensions);
for (int Extension = 0; Extension < NumExtensions; Extension++)
{
if (GL.GetString(StringNameIndexed.Extensions, Extension) == Name)
{
return true;
}
}
return false;
}
}
}

View file

@ -1,12 +0,0 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OGLLimit
{
private static Lazy<int> s_MaxUboSize = new Lazy<int>(() => GL.GetInteger(GetPName.MaxUniformBlockSize));
public static int MaxUboSize => s_MaxUboSize.Value;
}
}

View file

@ -1,207 +0,0 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLRasterizer : IGalRasterizer
{
private const long MaxVertexBufferCacheSize = 128 * 1024 * 1024;
private const long MaxIndexBufferCacheSize = 64 * 1024 * 1024;
private int[] VertexBuffers;
private OGLCachedResource<int> VboCache;
private OGLCachedResource<int> IboCache;
private struct IbInfo
{
public int Count;
public int ElemSizeLog2;
public DrawElementsType Type;
}
private IbInfo IndexBuffer;
public OGLRasterizer()
{
VertexBuffers = new int[32];
VboCache = new OGLCachedResource<int>(GL.DeleteBuffer, MaxVertexBufferCacheSize);
IboCache = new OGLCachedResource<int>(GL.DeleteBuffer, MaxIndexBufferCacheSize);
IndexBuffer = new IbInfo();
}
public void LockCaches()
{
VboCache.Lock();
IboCache.Lock();
}
public void UnlockCaches()
{
VboCache.Unlock();
IboCache.Unlock();
}
public void ClearBuffers(
GalClearBufferFlags Flags,
int Attachment,
float Red,
float Green,
float Blue,
float Alpha,
float Depth,
int Stencil)
{
GL.ColorMask(
Attachment,
Flags.HasFlag(GalClearBufferFlags.ColorRed),
Flags.HasFlag(GalClearBufferFlags.ColorGreen),
Flags.HasFlag(GalClearBufferFlags.ColorBlue),
Flags.HasFlag(GalClearBufferFlags.ColorAlpha));
GL.ClearBuffer(ClearBuffer.Color, Attachment, new float[] { Red, Green, Blue, Alpha });
GL.ColorMask(Attachment, true, true, true, true);
GL.DepthMask(true);
if (Flags.HasFlag(GalClearBufferFlags.Depth))
{
GL.ClearBuffer(ClearBuffer.Depth, 0, ref Depth);
}
if (Flags.HasFlag(GalClearBufferFlags.Stencil))
{
GL.ClearBuffer(ClearBuffer.Stencil, 0, ref Stencil);
}
}
public bool IsVboCached(long Key, long DataSize)
{
return VboCache.TryGetSize(Key, out long Size) && Size == DataSize;
}
public bool IsIboCached(long Key, long DataSize)
{
return IboCache.TryGetSize(Key, out long Size) && Size == DataSize;
}
public void CreateVbo(long Key, int DataSize, IntPtr HostAddress)
{
int Handle = GL.GenBuffer();
VboCache.AddOrUpdate(Key, Handle, DataSize);
IntPtr Length = new IntPtr(DataSize);
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
GL.BufferData(BufferTarget.ArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
}
public void CreateVbo(long Key, byte[] Data)
{
int Handle = GL.GenBuffer();
VboCache.AddOrUpdate(Key, Handle, Data.Length);
IntPtr Length = new IntPtr(Data.Length);
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
GL.BufferData(BufferTarget.ArrayBuffer, Length, Data, BufferUsageHint.StreamDraw);
}
public void CreateIbo(long Key, int DataSize, IntPtr HostAddress)
{
int Handle = GL.GenBuffer();
IboCache.AddOrUpdate(Key, Handle, (uint)DataSize);
IntPtr Length = new IntPtr(DataSize);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw);
}
public void CreateIbo(long Key, int DataSize, byte[] Buffer)
{
int Handle = GL.GenBuffer();
IboCache.AddOrUpdate(Key, Handle, DataSize);
IntPtr Length = new IntPtr(Buffer.Length);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
}
public void SetIndexArray(int Size, GalIndexFormat Format)
{
IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
IndexBuffer.Count = Size >> (int)Format;
IndexBuffer.ElemSizeLog2 = (int)Format;
}
public void DrawArrays(int First, int Count, GalPrimitiveType PrimType)
{
if (Count == 0)
{
return;
}
if (PrimType == GalPrimitiveType.Quads)
{
for (int Offset = 0; Offset < Count; Offset += 4)
{
GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4);
}
}
else if (PrimType == GalPrimitiveType.QuadStrip)
{
GL.DrawArrays(PrimitiveType.TriangleFan, First, 4);
for (int Offset = 2; Offset < Count; Offset += 2)
{
GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4);
}
}
else
{
GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count);
}
}
public void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType)
{
if (!IboCache.TryGetValue(IboKey, out int IboHandle))
{
return;
}
PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, IboHandle);
First <<= IndexBuffer.ElemSizeLog2;
if (VertexBase != 0)
{
IntPtr Indices = new IntPtr(First);
GL.DrawElementsBaseVertex(Mode, IndexBuffer.Count, IndexBuffer.Type, Indices, VertexBase);
}
else
{
GL.DrawElements(Mode, IndexBuffer.Count, IndexBuffer.Type, First);
}
}
public bool TryGetVbo(long VboKey, out int VboHandle)
{
return VboCache.TryGetValue(VboKey, out VboHandle);
}
}
}

View file

@ -1,527 +0,0 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Texture;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLRenderTarget : IGalRenderTarget
{
private const int NativeWidth = 1280;
private const int NativeHeight = 720;
private const int RenderTargetsCount = GalPipelineState.RenderTargetsCount;
private struct Rect
{
public int X { get; private set; }
public int Y { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
public Rect(int X, int Y, int Width, int Height)
{
this.X = X;
this.Y = Y;
this.Width = Width;
this.Height = Height;
}
}
private class FrameBufferAttachments
{
public int MapCount { get; set; }
public DrawBuffersEnum[] Map { get; private set; }
public long[] Colors { get; private set; }
public long Zeta { get; set; }
public FrameBufferAttachments()
{
Colors = new long[RenderTargetsCount];
Map = new DrawBuffersEnum[RenderTargetsCount];
}
public void Update(FrameBufferAttachments Source)
{
for (int Index = 0; Index < RenderTargetsCount; Index++)
{
Map[Index] = Source.Map[Index];
Colors[Index] = Source.Colors[Index];
}
MapCount = Source.MapCount;
Zeta = Source.Zeta;
}
}
private int[] ColorHandles;
private int ZetaHandle;
private OGLTexture Texture;
private ImageHandler ReadTex;
private Rect Window;
private float[] Viewports;
private bool FlipX;
private bool FlipY;
private int CropTop;
private int CropLeft;
private int CropRight;
private int CropBottom;
//This framebuffer is used to attach guest rendertargets,
//think of it as a dummy OpenGL VAO
private int DummyFrameBuffer;
//These framebuffers are used to blit images
private int SrcFb;
private int DstFb;
private FrameBufferAttachments Attachments;
private FrameBufferAttachments OldAttachments;
private int CopyPBO;
public bool FramebufferSrgb { get; set; }
public OGLRenderTarget(OGLTexture Texture)
{
Attachments = new FrameBufferAttachments();
OldAttachments = new FrameBufferAttachments();
ColorHandles = new int[RenderTargetsCount];
Viewports = new float[RenderTargetsCount * 4];
this.Texture = Texture;
Texture.TextureDeleted += TextureDeletionHandler;
}
private void TextureDeletionHandler(object Sender, int Handle)
{
//Texture was deleted, the handle is no longer valid, so
//reset all uses of this handle on a render target.
for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++)
{
if (ColorHandles[Attachment] == Handle)
{
ColorHandles[Attachment] = 0;
}
}
if (ZetaHandle == Handle)
{
ZetaHandle = 0;
}
}
public void Bind()
{
if (DummyFrameBuffer == 0)
{
DummyFrameBuffer = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer);
ImageHandler CachedImage;
for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++)
{
long Key = Attachments.Colors[Attachment];
int Handle = 0;
if (Key != 0 && Texture.TryGetImageHandler(Key, out CachedImage))
{
Handle = CachedImage.Handle;
}
if (Handle == ColorHandles[Attachment])
{
continue;
}
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.ColorAttachment0 + Attachment,
Handle,
0);
ColorHandles[Attachment] = Handle;
}
if (Attachments.Zeta != 0 && Texture.TryGetImageHandler(Attachments.Zeta, out CachedImage))
{
if (CachedImage.Handle != ZetaHandle)
{
if (CachedImage.HasDepth && CachedImage.HasStencil)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
CachedImage.Handle,
0);
}
else if (CachedImage.HasDepth)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthAttachment,
CachedImage.Handle,
0);
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.StencilAttachment,
0,
0);
}
else
{
throw new InvalidOperationException("Invalid image format \"" + CachedImage.Format + "\" used as Zeta!");
}
ZetaHandle = CachedImage.Handle;
}
}
else if (ZetaHandle != 0)
{
GL.FramebufferTexture(
FramebufferTarget.DrawFramebuffer,
FramebufferAttachment.DepthStencilAttachment,
0,
0);
ZetaHandle = 0;
}
if (OGLExtension.ViewportArray)
{
GL.ViewportArray(0, RenderTargetsCount, Viewports);
}
else
{
GL.Viewport(
(int)Viewports[0],
(int)Viewports[1],
(int)Viewports[2],
(int)Viewports[3]);
}
if (Attachments.MapCount > 1)
{
GL.DrawBuffers(Attachments.MapCount, Attachments.Map);
}
else if (Attachments.MapCount == 1)
{
GL.DrawBuffer((DrawBufferMode)Attachments.Map[0]);
}
else
{
GL.DrawBuffer(DrawBufferMode.None);
}
OldAttachments.Update(Attachments);
}
public void BindColor(long Key, int Attachment)
{
Attachments.Colors[Attachment] = Key;
}
public void UnbindColor(int Attachment)
{
Attachments.Colors[Attachment] = 0;
}
public void BindZeta(long Key)
{
Attachments.Zeta = Key;
}
public void UnbindZeta()
{
Attachments.Zeta = 0;
}
public void Present(long Key)
{
Texture.TryGetImageHandler(Key, out ReadTex);
}
public void SetMap(int[] Map)
{
if (Map != null)
{
Attachments.MapCount = Map.Length;
for (int Attachment = 0; Attachment < Attachments.MapCount; Attachment++)
{
Attachments.Map[Attachment] = DrawBuffersEnum.ColorAttachment0 + Map[Attachment];
}
}
else
{
Attachments.MapCount = 0;
}
}
public void SetTransform(bool FlipX, bool FlipY, int Top, int Left, int Right, int Bottom)
{
this.FlipX = FlipX;
this.FlipY = FlipY;
CropTop = Top;
CropLeft = Left;
CropRight = Right;
CropBottom = Bottom;
}
public void SetWindowSize(int Width, int Height)
{
Window = new Rect(0, 0, Width, Height);
}
public void SetViewport(int Attachment, int X, int Y, int Width, int Height)
{
int Offset = Attachment * 4;
Viewports[Offset + 0] = X;
Viewports[Offset + 1] = Y;
Viewports[Offset + 2] = Width;
Viewports[Offset + 3] = Height;
}
public void Render()
{
if (ReadTex == null)
{
return;
}
int SrcX0, SrcX1, SrcY0, SrcY1;
if (CropLeft == 0 && CropRight == 0)
{
SrcX0 = 0;
SrcX1 = ReadTex.Width;
}
else
{
SrcX0 = CropLeft;
SrcX1 = CropRight;
}
if (CropTop == 0 && CropBottom == 0)
{
SrcY0 = 0;
SrcY1 = ReadTex.Height;
}
else
{
SrcY0 = CropTop;
SrcY1 = CropBottom;
}
float RatioX = MathF.Min(1f, (Window.Height * (float)NativeWidth) / ((float)NativeHeight * Window.Width));
float RatioY = MathF.Min(1f, (Window.Width * (float)NativeHeight) / ((float)NativeWidth * Window.Height));
int DstWidth = (int)(Window.Width * RatioX);
int DstHeight = (int)(Window.Height * RatioY);
int DstPaddingX = (Window.Width - DstWidth) / 2;
int DstPaddingY = (Window.Height - DstHeight) / 2;
int DstX0 = FlipX ? Window.Width - DstPaddingX : DstPaddingX;
int DstX1 = FlipX ? DstPaddingX : Window.Width - DstPaddingX;
int DstY0 = FlipY ? DstPaddingY : Window.Height - DstPaddingY;
int DstY1 = FlipY ? Window.Height - DstPaddingY : DstPaddingY;
GL.Viewport(0, 0, Window.Width, Window.Height);
if (SrcFb == 0)
{
SrcFb = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0);
GL.ReadBuffer(ReadBufferMode.ColorAttachment0);
GL.Clear(ClearBufferMask.ColorBufferBit);
GL.Disable(EnableCap.FramebufferSrgb);
// Will be re-enabled if needed while binding, called before any game GL calls
GL.Disable(EnableCap.ScissorTest);
GL.BlitFramebuffer(
SrcX0,
SrcY0,
SrcX1,
SrcY1,
DstX0,
DstY0,
DstX1,
DstY1,
ClearBufferMask.ColorBufferBit,
BlitFramebufferFilter.Linear);
if (FramebufferSrgb)
{
GL.Enable(EnableCap.FramebufferSrgb);
}
}
public void Copy(
long SrcKey,
long DstKey,
int SrcX0,
int SrcY0,
int SrcX1,
int SrcY1,
int DstX0,
int DstY0,
int DstX1,
int DstY1)
{
if (Texture.TryGetImageHandler(SrcKey, out ImageHandler SrcTex) &&
Texture.TryGetImageHandler(DstKey, out ImageHandler DstTex))
{
if (SrcTex.HasColor != DstTex.HasColor ||
SrcTex.HasDepth != DstTex.HasDepth ||
SrcTex.HasStencil != DstTex.HasStencil)
{
throw new NotImplementedException();
}
if (SrcFb == 0)
{
SrcFb = GL.GenFramebuffer();
}
if (DstFb == 0)
{
DstFb = GL.GenFramebuffer();
}
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, SrcFb);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DstFb);
FramebufferAttachment Attachment = GetAttachment(SrcTex);
GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, Attachment, SrcTex.Handle, 0);
GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, Attachment, DstTex.Handle, 0);
BlitFramebufferFilter Filter = BlitFramebufferFilter.Nearest;
if (SrcTex.HasColor)
{
GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
Filter = BlitFramebufferFilter.Linear;
}
ClearBufferMask Mask = GetClearMask(SrcTex);
GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter);
}
}
public void Reinterpret(long Key, GalImage NewImage)
{
if (!Texture.TryGetImage(Key, out GalImage OldImage))
{
return;
}
if (NewImage.Format == OldImage.Format &&
NewImage.Width == OldImage.Width &&
NewImage.Height == OldImage.Height)
{
return;
}
if (CopyPBO == 0)
{
CopyPBO = GL.GenBuffer();
}
GL.BindBuffer(BufferTarget.PixelPackBuffer, CopyPBO);
//The buffer should be large enough to hold the largest texture.
int BufferSize = Math.Max(ImageUtils.GetSize(OldImage),
ImageUtils.GetSize(NewImage));
GL.BufferData(BufferTarget.PixelPackBuffer, BufferSize, IntPtr.Zero, BufferUsageHint.StreamCopy);
if (!Texture.TryGetImageHandler(Key, out ImageHandler CachedImage))
{
throw new InvalidOperationException();
}
(_, PixelFormat Format, PixelType Type) = OGLEnumConverter.GetImageFormat(CachedImage.Format);
GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle);
GL.GetTexImage(TextureTarget.Texture2D, 0, Format, Type, IntPtr.Zero);
GL.BindBuffer(BufferTarget.PixelPackBuffer, 0);
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, CopyPBO);
GL.PixelStore(PixelStoreParameter.UnpackRowLength, OldImage.Width);
Texture.Create(Key, ImageUtils.GetSize(NewImage), NewImage);
GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0);
GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0);
}
private static FramebufferAttachment GetAttachment(ImageHandler CachedImage)
{
if (CachedImage.HasColor)
{
return FramebufferAttachment.ColorAttachment0;
}
else if (CachedImage.HasDepth && CachedImage.HasStencil)
{
return FramebufferAttachment.DepthStencilAttachment;
}
else if (CachedImage.HasDepth)
{
return FramebufferAttachment.DepthAttachment;
}
else if (CachedImage.HasStencil)
{
return FramebufferAttachment.StencilAttachment;
}
else
{
throw new InvalidOperationException();
}
}
private static ClearBufferMask GetClearMask(ImageHandler CachedImage)
{
return (CachedImage.HasColor ? ClearBufferMask.ColorBufferBit : 0) |
(CachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) |
(CachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0);
}
}
}

View file

@ -1,58 +0,0 @@
using System;
using System.Collections.Concurrent;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OGLRenderer : IGalRenderer
{
public IGalConstBuffer Buffer { get; private set; }
public IGalRenderTarget RenderTarget { get; private set; }
public IGalRasterizer Rasterizer { get; private set; }
public IGalShader Shader { get; private set; }
public IGalPipeline Pipeline { get; private set; }
public IGalTexture Texture { get; private set; }
private ConcurrentQueue<Action> ActionsQueue;
public OGLRenderer()
{
Buffer = new OGLConstBuffer();
Texture = new OGLTexture();
RenderTarget = new OGLRenderTarget(Texture as OGLTexture);
Rasterizer = new OGLRasterizer();
Shader = new OGLShader(Buffer as OGLConstBuffer);
Pipeline = new OGLPipeline(
Buffer as OGLConstBuffer,
RenderTarget as OGLRenderTarget,
Rasterizer as OGLRasterizer,
Shader as OGLShader);
ActionsQueue = new ConcurrentQueue<Action>();
}
public void QueueAction(Action ActionMthd)
{
ActionsQueue.Enqueue(ActionMthd);
}
public void RunActions()
{
int Count = ActionsQueue.Count;
while (Count-- > 0 && ActionsQueue.TryDequeue(out Action RenderAction))
{
RenderAction();
}
}
}
}

View file

@ -1,298 +0,0 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Gal.Shader;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLShader : IGalShader
{
public const int ReservedCbufCount = 1;
private const int ExtraDataSize = 4;
public OGLShaderProgram Current;
private ConcurrentDictionary<long, OGLShaderStage> Stages;
private Dictionary<OGLShaderProgram, int> Programs;
public int CurrentProgramHandle { get; private set; }
private OGLConstBuffer Buffer;
private int ExtraUboHandle;
public OGLShader(OGLConstBuffer Buffer)
{
this.Buffer = Buffer;
Stages = new ConcurrentDictionary<long, OGLShaderStage>();
Programs = new Dictionary<OGLShaderProgram, int>();
}
public void Create(IGalMemory Memory, long Key, GalShaderType Type)
{
Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, Key, 0, false, Type));
}
public void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type)
{
Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, VpAPos, Key, true, Type));
}
private OGLShaderStage ShaderStageFactory(
IGalMemory Memory,
long Position,
long PositionB,
bool IsDualVp,
GalShaderType Type)
{
GlslProgram Program;
GlslDecompiler Decompiler = new GlslDecompiler(OGLLimit.MaxUboSize);
int ShaderDumpIndex = ShaderDumper.DumpIndex;
if (IsDualVp)
{
ShaderDumper.Dump(Memory, Position, Type, "a");
ShaderDumper.Dump(Memory, PositionB, Type, "b");
Program = Decompiler.Decompile(Memory, Position, PositionB, Type);
}
else
{
ShaderDumper.Dump(Memory, Position, Type);
Program = Decompiler.Decompile(Memory, Position, Type);
}
string Code = Program.Code;
if (ShaderDumper.IsDumpEnabled())
{
Code = "//Shader " + ShaderDumpIndex + Environment.NewLine + Code;
}
return new OGLShaderStage(Type, Code, Program.Uniforms, Program.Textures);
}
public IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key)
{
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
{
return Stage.ConstBufferUsage;
}
return Enumerable.Empty<ShaderDeclInfo>();
}
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
{
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
{
return Stage.TextureUsage;
}
return Enumerable.Empty<ShaderDeclInfo>();
}
public unsafe void SetExtraData(float FlipX, float FlipY, int Instance)
{
BindProgram();
EnsureExtraBlock();
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
float* Data = stackalloc float[ExtraDataSize];
Data[0] = FlipX;
Data[1] = FlipY;
Data[2] = BitConverter.Int32BitsToSingle(Instance);
//Invalidate buffer
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, ExtraDataSize * sizeof(float), (IntPtr)Data);
}
public void Bind(long Key)
{
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
{
Bind(Stage);
}
}
private void Bind(OGLShaderStage Stage)
{
if (Stage.Type == GalShaderType.Geometry)
{
//Enhanced layouts are required for Geometry shaders
//skip this stage if current driver has no ARB_enhanced_layouts
if (!OGLExtension.EnhancedLayouts)
{
return;
}
}
switch (Stage.Type)
{
case GalShaderType.Vertex: Current.Vertex = Stage; break;
case GalShaderType.TessControl: Current.TessControl = Stage; break;
case GalShaderType.TessEvaluation: Current.TessEvaluation = Stage; break;
case GalShaderType.Geometry: Current.Geometry = Stage; break;
case GalShaderType.Fragment: Current.Fragment = Stage; break;
}
}
public void Unbind(GalShaderType Type)
{
switch (Type)
{
case GalShaderType.Vertex: Current.Vertex = null; break;
case GalShaderType.TessControl: Current.TessControl = null; break;
case GalShaderType.TessEvaluation: Current.TessEvaluation = null; break;
case GalShaderType.Geometry: Current.Geometry = null; break;
case GalShaderType.Fragment: Current.Fragment = null; break;
}
}
public void BindProgram()
{
if (Current.Vertex == null ||
Current.Fragment == null)
{
return;
}
if (!Programs.TryGetValue(Current, out int Handle))
{
Handle = GL.CreateProgram();
AttachIfNotNull(Handle, Current.Vertex);
AttachIfNotNull(Handle, Current.TessControl);
AttachIfNotNull(Handle, Current.TessEvaluation);
AttachIfNotNull(Handle, Current.Geometry);
AttachIfNotNull(Handle, Current.Fragment);
GL.LinkProgram(Handle);
CheckProgramLink(Handle);
BindUniformBlocks(Handle);
BindTextureLocations(Handle);
Programs.Add(Current, Handle);
}
GL.UseProgram(Handle);
CurrentProgramHandle = Handle;
}
private void EnsureExtraBlock()
{
if (ExtraUboHandle == 0)
{
ExtraUboHandle = GL.GenBuffer();
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, ExtraUboHandle);
}
}
private void AttachIfNotNull(int ProgramHandle, OGLShaderStage Stage)
{
if (Stage != null)
{
Stage.Compile();
GL.AttachShader(ProgramHandle, Stage.Handle);
}
}
private void BindUniformBlocks(int ProgramHandle)
{
int ExtraBlockindex = GL.GetUniformBlockIndex(ProgramHandle, GlslDecl.ExtraUniformBlockName);
GL.UniformBlockBinding(ProgramHandle, ExtraBlockindex, 0);
int FreeBinding = ReservedCbufCount;
void BindUniformBlocksIfNotNull(OGLShaderStage Stage)
{
if (Stage != null)
{
foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage)
{
int BlockIndex = GL.GetUniformBlockIndex(ProgramHandle, DeclInfo.Name);
if (BlockIndex < 0)
{
//It is expected that its found, if it's not then driver might be in a malfunction
throw new InvalidOperationException();
}
GL.UniformBlockBinding(ProgramHandle, BlockIndex, FreeBinding);
FreeBinding++;
}
}
}
BindUniformBlocksIfNotNull(Current.Vertex);
BindUniformBlocksIfNotNull(Current.TessControl);
BindUniformBlocksIfNotNull(Current.TessEvaluation);
BindUniformBlocksIfNotNull(Current.Geometry);
BindUniformBlocksIfNotNull(Current.Fragment);
}
private void BindTextureLocations(int ProgramHandle)
{
int Index = 0;
void BindTexturesIfNotNull(OGLShaderStage Stage)
{
if (Stage != null)
{
foreach (ShaderDeclInfo Decl in Stage.TextureUsage)
{
int Location = GL.GetUniformLocation(ProgramHandle, Decl.Name);
GL.Uniform1(Location, Index);
Index++;
}
}
}
GL.UseProgram(ProgramHandle);
BindTexturesIfNotNull(Current.Vertex);
BindTexturesIfNotNull(Current.TessControl);
BindTexturesIfNotNull(Current.TessEvaluation);
BindTexturesIfNotNull(Current.Geometry);
BindTexturesIfNotNull(Current.Fragment);
}
private static void CheckProgramLink(int Handle)
{
int Status = 0;
GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out Status);
if (Status == 0)
{
throw new ShaderException(GL.GetProgramInfoLog(Handle));
}
}
}
}

View file

@ -1,86 +0,0 @@
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL
{
struct OGLShaderProgram
{
public OGLShaderStage Vertex;
public OGLShaderStage TessControl;
public OGLShaderStage TessEvaluation;
public OGLShaderStage Geometry;
public OGLShaderStage Fragment;
}
class OGLShaderStage : IDisposable
{
public int Handle { get; private set; }
public bool IsCompiled { get; private set; }
public GalShaderType Type { get; private set; }
public string Code { get; private set; }
public IEnumerable<ShaderDeclInfo> ConstBufferUsage { get; private set; }
public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
public OGLShaderStage(
GalShaderType Type,
string Code,
IEnumerable<ShaderDeclInfo> ConstBufferUsage,
IEnumerable<ShaderDeclInfo> TextureUsage)
{
this.Type = Type;
this.Code = Code;
this.ConstBufferUsage = ConstBufferUsage;
this.TextureUsage = TextureUsage;
}
public void Compile()
{
if (Handle == 0)
{
Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
CompileAndCheck(Handle, Code);
}
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing && Handle != 0)
{
GL.DeleteShader(Handle);
Handle = 0;
}
}
public static void CompileAndCheck(int Handle, string Code)
{
GL.ShaderSource(Handle, Code);
GL.CompileShader(Handle);
CheckCompilation(Handle);
}
private static void CheckCompilation(int Handle)
{
int Status = 0;
GL.GetShader(Handle, ShaderParameter.CompileStatus, out Status);
if (Status == 0)
{
throw new ShaderException(GL.GetShaderInfoLog(Handle));
}
}
}
}

View file

@ -1,207 +0,0 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.Texture;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLTexture : IGalTexture
{
private const long MaxTextureCacheSize = 768 * 1024 * 1024;
private OGLCachedResource<ImageHandler> TextureCache;
public EventHandler<int> TextureDeleted { get; set; }
public OGLTexture()
{
TextureCache = new OGLCachedResource<ImageHandler>(DeleteTexture, MaxTextureCacheSize);
}
public void LockCache()
{
TextureCache.Lock();
}
public void UnlockCache()
{
TextureCache.Unlock();
}
private void DeleteTexture(ImageHandler CachedImage)
{
TextureDeleted?.Invoke(this, CachedImage.Handle);
GL.DeleteTexture(CachedImage.Handle);
}
public void Create(long Key, int Size, GalImage Image)
{
int Handle = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, Handle);
const int Level = 0; //TODO: Support mipmap textures.
const int Border = 0;
TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Size);
if (ImageUtils.IsCompressed(Image.Format))
{
throw new InvalidOperationException("Surfaces with compressed formats are not supported!");
}
(PixelInternalFormat InternalFmt,
PixelFormat Format,
PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
GL.TexImage2D(
TextureTarget.Texture2D,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Format,
Type,
IntPtr.Zero);
}
public void Create(long Key, byte[] Data, GalImage Image)
{
int Handle = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, Handle);
const int Level = 0; //TODO: Support mipmap textures.
const int Border = 0;
TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length);
if (ImageUtils.IsCompressed(Image.Format) && !IsAstc(Image.Format))
{
InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format);
GL.CompressedTexImage2D(
TextureTarget.Texture2D,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Data.Length,
Data);
}
else
{
//TODO: Use KHR_texture_compression_astc_hdr when available
if (IsAstc(Image.Format))
{
int TextureBlockWidth = ImageUtils.GetBlockWidth(Image.Format);
int TextureBlockHeight = ImageUtils.GetBlockHeight(Image.Format);
Data = ASTCDecoder.DecodeToRGBA8888(
Data,
TextureBlockWidth,
TextureBlockHeight, 1,
Image.Width,
Image.Height, 1);
Image.Format = GalImageFormat.RGBA8 | (Image.Format & GalImageFormat.TypeMask);
}
(PixelInternalFormat InternalFmt,
PixelFormat Format,
PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format);
GL.TexImage2D(
TextureTarget.Texture2D,
Level,
InternalFmt,
Image.Width,
Image.Height,
Border,
Format,
Type,
Data);
}
}
private static bool IsAstc(GalImageFormat Format)
{
Format &= GalImageFormat.FormatMask;
return Format > GalImageFormat.Astc2DStart && Format < GalImageFormat.Astc2DEnd;
}
public bool TryGetImage(long Key, out GalImage Image)
{
if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))
{
Image = CachedImage.Image;
return true;
}
Image = default(GalImage);
return false;
}
public bool TryGetImageHandler(long Key, out ImageHandler CachedImage)
{
if (TextureCache.TryGetValue(Key, out CachedImage))
{
return true;
}
CachedImage = null;
return false;
}
public void Bind(long Key, int Index, GalImage Image)
{
if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage))
{
GL.ActiveTexture(TextureUnit.Texture0 + Index);
GL.BindTexture(TextureTarget.Texture2D, CachedImage.Handle);
int[] SwizzleRgba = new int[]
{
(int)OGLEnumConverter.GetTextureSwizzle(Image.XSource),
(int)OGLEnumConverter.GetTextureSwizzle(Image.YSource),
(int)OGLEnumConverter.GetTextureSwizzle(Image.ZSource),
(int)OGLEnumConverter.GetTextureSwizzle(Image.WSource)
};
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, SwizzleRgba);
}
}
public void SetSampler(GalTextureSampler Sampler)
{
int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
int MinFilter = (int)OGLEnumConverter.GetTextureMinFilter(Sampler.MinFilter, Sampler.MipFilter);
int MagFilter = (int)OGLEnumConverter.GetTextureMagFilter(Sampler.MagFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, WrapS);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, WrapT);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
float[] Color = new float[]
{
Sampler.BorderColor.Red,
Sampler.BorderColor.Green,
Sampler.BorderColor.Blue,
Sampler.BorderColor.Alpha
};
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBorderColor, Color);
}
}
}

View file

@ -0,0 +1,191 @@
using Ryujinx.Common;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OglCachedResource<T>
{
public delegate void DeleteValue(T value);
private const int MinTimeDelta = 5 * 60000;
private const int MaxRemovalsPerRun = 10;
private struct CacheBucket
{
public T Value { get; private set; }
public LinkedListNode<long> Node { get; private set; }
public long DataSize { get; private set; }
public long Timestamp { get; private set; }
public CacheBucket(T value, long dataSize, LinkedListNode<long> node)
{
Value = value;
DataSize = dataSize;
Node = node;
Timestamp = PerformanceCounter.ElapsedMilliseconds;
}
}
private Dictionary<long, CacheBucket> _cache;
private LinkedList<long> _sortedCache;
private DeleteValue _deleteValueCallback;
private Queue<T> _deletePending;
private bool _locked;
private long _maxSize;
private long _totalSize;
public OglCachedResource(DeleteValue deleteValueCallback, long maxSize)
{
_maxSize = maxSize;
if (deleteValueCallback == null)
{
throw new ArgumentNullException(nameof(deleteValueCallback));
}
_deleteValueCallback = deleteValueCallback;
_cache = new Dictionary<long, CacheBucket>();
_sortedCache = new LinkedList<long>();
_deletePending = new Queue<T>();
}
public void Lock()
{
_locked = true;
}
public void Unlock()
{
_locked = false;
while (_deletePending.TryDequeue(out T value))
{
_deleteValueCallback(value);
}
ClearCacheIfNeeded();
}
public void AddOrUpdate(long key, T value, long size)
{
if (!_locked)
{
ClearCacheIfNeeded();
}
LinkedListNode<long> node = _sortedCache.AddLast(key);
CacheBucket newBucket = new CacheBucket(value, size, node);
if (_cache.TryGetValue(key, out CacheBucket bucket))
{
if (_locked)
{
_deletePending.Enqueue(bucket.Value);
}
else
{
_deleteValueCallback(bucket.Value);
}
_sortedCache.Remove(bucket.Node);
_totalSize -= bucket.DataSize;
_cache[key] = newBucket;
}
else
{
_cache.Add(key, newBucket);
}
_totalSize += size;
}
public bool TryGetValue(long key, out T value)
{
if (_cache.TryGetValue(key, out CacheBucket bucket))
{
value = bucket.Value;
_sortedCache.Remove(bucket.Node);
LinkedListNode<long> node = _sortedCache.AddLast(key);
_cache[key] = new CacheBucket(value, bucket.DataSize, node);
return true;
}
value = default(T);
return false;
}
public bool TryGetSize(long key, out long size)
{
if (_cache.TryGetValue(key, out CacheBucket bucket))
{
size = bucket.DataSize;
return true;
}
size = 0;
return false;
}
private void ClearCacheIfNeeded()
{
long timestamp = PerformanceCounter.ElapsedMilliseconds;
int count = 0;
while (count++ < MaxRemovalsPerRun)
{
LinkedListNode<long> node = _sortedCache.First;
if (node == null)
{
break;
}
CacheBucket bucket = _cache[node.Value];
long timeDelta = timestamp - bucket.Timestamp;
if (timeDelta <= MinTimeDelta && !UnderMemoryPressure())
{
break;
}
_sortedCache.Remove(node);
_cache.Remove(node.Value);
_deleteValueCallback(bucket.Value);
_totalSize -= bucket.DataSize;
}
}
private bool UnderMemoryPressure()
{
return _totalSize >= _maxSize;
}
}
}

View file

@ -0,0 +1,74 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OglConstBuffer : IGalConstBuffer
{
private const long MaxConstBufferCacheSize = 64 * 1024 * 1024;
private OglCachedResource<OglStreamBuffer> _cache;
public OglConstBuffer()
{
_cache = new OglCachedResource<OglStreamBuffer>(DeleteBuffer, MaxConstBufferCacheSize);
}
public void LockCache()
{
_cache.Lock();
}
public void UnlockCache()
{
_cache.Unlock();
}
public void Create(long key, long size)
{
OglStreamBuffer buffer = new OglStreamBuffer(BufferTarget.UniformBuffer, size);
_cache.AddOrUpdate(key, buffer, size);
}
public bool IsCached(long key, long size)
{
return _cache.TryGetSize(key, out long cachedSize) && cachedSize == size;
}
public void SetData(long key, long size, IntPtr hostAddress)
{
if (_cache.TryGetValue(key, out OglStreamBuffer buffer))
{
buffer.SetData(size, hostAddress);
}
}
public void SetData(long key, byte[] data)
{
if (_cache.TryGetValue(key, out OglStreamBuffer buffer))
{
buffer.SetData(data);
}
}
public bool TryGetUbo(long key, out int uboHandle)
{
if (_cache.TryGetValue(key, out OglStreamBuffer buffer))
{
uboHandle = buffer.Handle;
return true;
}
uboHandle = 0;
return false;
}
private static void DeleteBuffer(OglStreamBuffer buffer)
{
buffer.Dispose();
}
}
}

View file

@ -3,34 +3,34 @@ using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OGLEnumConverter
static class OglEnumConverter
{
public static FrontFaceDirection GetFrontFace(GalFrontFace FrontFace)
public static FrontFaceDirection GetFrontFace(GalFrontFace frontFace)
{
switch (FrontFace)
switch (frontFace)
{
case GalFrontFace.CW: return FrontFaceDirection.Cw;
case GalFrontFace.CCW: return FrontFaceDirection.Ccw;
case GalFrontFace.Cw: return FrontFaceDirection.Cw;
case GalFrontFace.Ccw: return FrontFaceDirection.Ccw;
}
throw new ArgumentException(nameof(FrontFace) + " \"" + FrontFace + "\" is not valid!");
throw new ArgumentException(nameof(frontFace) + " \"" + frontFace + "\" is not valid!");
}
public static CullFaceMode GetCullFace(GalCullFace CullFace)
public static CullFaceMode GetCullFace(GalCullFace cullFace)
{
switch (CullFace)
switch (cullFace)
{
case GalCullFace.Front: return CullFaceMode.Front;
case GalCullFace.Back: return CullFaceMode.Back;
case GalCullFace.FrontAndBack: return CullFaceMode.FrontAndBack;
}
throw new ArgumentException(nameof(CullFace) + " \"" + CullFace + "\" is not valid!");
throw new ArgumentException(nameof(cullFace) + " \"" + cullFace + "\" is not valid!");
}
public static StencilOp GetStencilOp(GalStencilOp Op)
public static StencilOp GetStencilOp(GalStencilOp op)
{
switch (Op)
switch (op)
{
case GalStencilOp.Keep: return StencilOp.Keep;
case GalStencilOp.Zero: return StencilOp.Zero;
@ -42,28 +42,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalStencilOp.DecrWrap: return StencilOp.DecrWrap;
}
throw new ArgumentException(nameof(Op) + " \"" + Op + "\" is not valid!");
throw new ArgumentException(nameof(op) + " \"" + op + "\" is not valid!");
}
public static DepthFunction GetDepthFunc(GalComparisonOp Func)
public static DepthFunction GetDepthFunc(GalComparisonOp func)
{
return (DepthFunction)GetFunc(Func);
return (DepthFunction)GetFunc(func);
}
public static StencilFunction GetStencilFunc(GalComparisonOp Func)
public static StencilFunction GetStencilFunc(GalComparisonOp func)
{
return (StencilFunction)GetFunc(Func);
return (StencilFunction)GetFunc(func);
}
private static All GetFunc(GalComparisonOp Func)
private static All GetFunc(GalComparisonOp func)
{
if ((int)Func >= (int)All.Never &&
(int)Func <= (int)All.Always)
if ((int)func >= (int)All.Never &&
(int)func <= (int)All.Always)
{
return (All)Func;
return (All)func;
}
switch (Func)
switch (func)
{
case GalComparisonOp.Never: return All.Never;
case GalComparisonOp.Less: return All.Less;
@ -75,24 +75,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalComparisonOp.Always: return All.Always;
}
throw new ArgumentException(nameof(Func) + " \"" + Func + "\" is not valid!");
throw new ArgumentException(nameof(func) + " \"" + func + "\" is not valid!");
}
public static DrawElementsType GetDrawElementsType(GalIndexFormat Format)
public static DrawElementsType GetDrawElementsType(GalIndexFormat format)
{
switch (Format)
switch (format)
{
case GalIndexFormat.Byte: return DrawElementsType.UnsignedByte;
case GalIndexFormat.Int16: return DrawElementsType.UnsignedShort;
case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt;
}
throw new ArgumentException(nameof(Format) + " \"" + Format + "\" is not valid!");
throw new ArgumentException(nameof(format) + " \"" + format + "\" is not valid!");
}
public static PrimitiveType GetPrimitiveType(GalPrimitiveType Type)
public static PrimitiveType GetPrimitiveType(GalPrimitiveType type)
{
switch (Type)
switch (type)
{
case GalPrimitiveType.Points: return PrimitiveType.Points;
case GalPrimitiveType.Lines: return PrimitiveType.Lines;
@ -109,12 +109,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalPrimitiveType.Patches: return PrimitiveType.Patches;
}
throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!");
throw new ArgumentException(nameof(type) + " \"" + type + "\" is not valid!");
}
public static ShaderType GetShaderType(GalShaderType Type)
public static ShaderType GetShaderType(GalShaderType type)
{
switch (Type)
switch (type)
{
case GalShaderType.Vertex: return ShaderType.VertexShader;
case GalShaderType.TessControl: return ShaderType.TessControlShader;
@ -123,50 +123,50 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalShaderType.Fragment: return ShaderType.FragmentShader;
}
throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!");
throw new ArgumentException(nameof(type) + " \"" + type + "\" is not valid!");
}
public static (PixelInternalFormat, PixelFormat, PixelType) GetImageFormat(GalImageFormat Format)
public static (PixelInternalFormat, PixelFormat, PixelType) GetImageFormat(GalImageFormat format)
{
switch (Format)
switch (format)
{
case GalImageFormat.RGBA32 | GalImageFormat.Float: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float);
case GalImageFormat.RGBA32 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int);
case GalImageFormat.RGBA32 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt);
case GalImageFormat.RGBA16 | GalImageFormat.Float: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat);
case GalImageFormat.RGBA16 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short);
case GalImageFormat.RGBA16 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort);
case GalImageFormat.RGBA16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba16, PixelFormat.Rgba, PixelType.UnsignedShort);
case GalImageFormat.RG32 | GalImageFormat.Float: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float);
case GalImageFormat.RG32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int);
case GalImageFormat.RG32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt);
case GalImageFormat.RGBX8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.RGBA8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte);
case GalImageFormat.RGBA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.RGBA8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte);
case GalImageFormat.RGBA8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte);
case GalImageFormat.RGBA8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.BGRA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte);
case GalImageFormat.BGRA8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Bgra, PixelType.UnsignedByte);
case GalImageFormat.RGBA4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed);
case GalImageFormat.RGB10A2 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.RGB10A2 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.Rgba32 | GalImageFormat.Float: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float);
case GalImageFormat.Rgba32 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int);
case GalImageFormat.Rgba32 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt);
case GalImageFormat.Rgba16 | GalImageFormat.Float: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat);
case GalImageFormat.Rgba16 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short);
case GalImageFormat.Rgba16 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort);
case GalImageFormat.Rgba16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba16, PixelFormat.Rgba, PixelType.UnsignedShort);
case GalImageFormat.Rg32 | GalImageFormat.Float: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float);
case GalImageFormat.Rg32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int);
case GalImageFormat.Rg32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt);
case GalImageFormat.Rgbx8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.Rgba8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte);
case GalImageFormat.Rgba8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.Rgba8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte);
case GalImageFormat.Rgba8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte);
case GalImageFormat.Rgba8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.Bgra8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte);
case GalImageFormat.Bgra8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Bgra, PixelType.UnsignedByte);
case GalImageFormat.Rgba4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed);
case GalImageFormat.Rgb10A2 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.Rgb10A2 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed);
case GalImageFormat.R32 | GalImageFormat.Float: return (PixelInternalFormat.R32f, PixelFormat.Red, PixelType.Float);
case GalImageFormat.R32 | GalImageFormat.Sint: return (PixelInternalFormat.R32i, PixelFormat.Red, PixelType.Int);
case GalImageFormat.R32 | GalImageFormat.Uint: return (PixelInternalFormat.R32ui, PixelFormat.Red, PixelType.UnsignedInt);
case GalImageFormat.BGR5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort5551);
case GalImageFormat.RGB5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed);
case GalImageFormat.RGB565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed);
case GalImageFormat.BGR565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565);
case GalImageFormat.RG16 | GalImageFormat.Float: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat);
case GalImageFormat.RG16 | GalImageFormat.Sint: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short);
case GalImageFormat.RG16 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Short);
case GalImageFormat.RG16 | GalImageFormat.Uint: return (PixelInternalFormat.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort);
case GalImageFormat.RG16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort);
case GalImageFormat.RG8 | GalImageFormat.Sint: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte);
case GalImageFormat.RG8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte);
case GalImageFormat.RG8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte);
case GalImageFormat.RG8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte);
case GalImageFormat.Bgr5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort5551);
case GalImageFormat.Rgb5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed);
case GalImageFormat.Rgb565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed);
case GalImageFormat.Bgr565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565);
case GalImageFormat.Rg16 | GalImageFormat.Float: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat);
case GalImageFormat.Rg16 | GalImageFormat.Sint: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short);
case GalImageFormat.Rg16 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Short);
case GalImageFormat.Rg16 | GalImageFormat.Uint: return (PixelInternalFormat.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort);
case GalImageFormat.Rg16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort);
case GalImageFormat.Rg8 | GalImageFormat.Sint: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte);
case GalImageFormat.Rg8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte);
case GalImageFormat.Rg8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte);
case GalImageFormat.Rg8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte);
case GalImageFormat.R16 | GalImageFormat.Float: return (PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat);
case GalImageFormat.R16 | GalImageFormat.Sint: return (PixelInternalFormat.R16i, PixelFormat.RedInteger, PixelType.Short);
case GalImageFormat.R16 | GalImageFormat.Snorm: return (PixelInternalFormat.R16Snorm, PixelFormat.Red, PixelType.Short);
@ -186,12 +186,37 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalImageFormat.D32S8 | GalImageFormat.Float: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev);
}
throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}");
throw new NotImplementedException($"{format & GalImageFormat.FormatMask} {format & GalImageFormat.TypeMask}");
}
public static InternalFormat GetCompressedImageFormat(GalImageFormat Format)
public static All GetDepthCompareFunc(DepthCompareFunc depthCompareFunc)
{
switch (Format)
switch (depthCompareFunc)
{
case DepthCompareFunc.LEqual:
return All.Lequal;
case DepthCompareFunc.GEqual:
return All.Gequal;
case DepthCompareFunc.Less:
return All.Less;
case DepthCompareFunc.Greater:
return All.Greater;
case DepthCompareFunc.Equal:
return All.Equal;
case DepthCompareFunc.NotEqual:
return All.Notequal;
case DepthCompareFunc.Always:
return All.Always;
case DepthCompareFunc.Never:
return All.Never;
default:
throw new ArgumentException(nameof(depthCompareFunc) + " \"" + depthCompareFunc + "\" is not valid!");
}
}
public static InternalFormat GetCompressedImageFormat(GalImageFormat format)
{
switch (format)
{
case GalImageFormat.BptcSfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcSignedFloat;
case GalImageFormat.BptcUfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcUnsignedFloat;
@ -209,12 +234,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalImageFormat.BC5 | GalImageFormat.Unorm: return InternalFormat.CompressedRgRgtc2;
}
throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}");
throw new NotImplementedException($"{format & GalImageFormat.FormatMask} {format & GalImageFormat.TypeMask}");
}
public static All GetTextureSwizzle(GalTextureSource Source)
public static All GetTextureSwizzle(GalTextureSource source)
{
switch (Source)
switch (source)
{
case GalTextureSource.Zero: return All.Zero;
case GalTextureSource.Red: return All.Red;
@ -225,12 +250,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureSource.OneFloat: return All.One;
}
throw new ArgumentException(nameof(Source) + " \"" + Source + "\" is not valid!");
throw new ArgumentException(nameof(source) + " \"" + source + "\" is not valid!");
}
public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap)
public static TextureWrapMode GetTextureWrapMode(GalTextureWrap wrap)
{
switch (Wrap)
switch (wrap)
{
case GalTextureWrap.Repeat: return TextureWrapMode.Repeat;
case GalTextureWrap.MirroredRepeat: return TextureWrapMode.MirroredRepeat;
@ -239,9 +264,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalTextureWrap.Clamp: return TextureWrapMode.Clamp;
}
if (OGLExtension.TextureMirrorClamp)
if (OglExtension.TextureMirrorClamp)
{
switch (Wrap)
switch (wrap)
{
case GalTextureWrap.MirrorClampToEdge: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToEdgeExt;
case GalTextureWrap.MirrorClampToBorder: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToBorderExt;
@ -251,7 +276,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
else
{
//Fallback to non-mirrored clamps
switch (Wrap)
switch (wrap)
{
case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge;
case GalTextureWrap.MirrorClampToBorder: return TextureWrapMode.ClampToBorder;
@ -259,37 +284,37 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
throw new ArgumentException(nameof(Wrap) + " \"" + Wrap + "\" is not valid!");
throw new ArgumentException(nameof(wrap) + " \"" + wrap + "\" is not valid!");
}
public static TextureMinFilter GetTextureMinFilter(
GalTextureFilter MinFilter,
GalTextureMipFilter MipFilter)
GalTextureFilter minFilter,
GalTextureMipFilter mipFilter)
{
//TODO: Mip (needs mipmap support first).
switch (MinFilter)
switch (minFilter)
{
case GalTextureFilter.Nearest: return TextureMinFilter.Nearest;
case GalTextureFilter.Linear: return TextureMinFilter.Linear;
}
throw new ArgumentException(nameof(MinFilter) + " \"" + MinFilter + "\" is not valid!");
throw new ArgumentException(nameof(minFilter) + " \"" + minFilter + "\" is not valid!");
}
public static TextureMagFilter GetTextureMagFilter(GalTextureFilter Filter)
public static TextureMagFilter GetTextureMagFilter(GalTextureFilter filter)
{
switch (Filter)
switch (filter)
{
case GalTextureFilter.Nearest: return TextureMagFilter.Nearest;
case GalTextureFilter.Linear: return TextureMagFilter.Linear;
}
throw new ArgumentException(nameof(Filter) + " \"" + Filter + "\" is not valid!");
throw new ArgumentException(nameof(filter) + " \"" + filter + "\" is not valid!");
}
public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
public static BlendEquationMode GetBlendEquation(GalBlendEquation blendEquation)
{
switch (BlendEquation)
switch (blendEquation)
{
case GalBlendEquation.FuncAdd:
case GalBlendEquation.FuncAddGl:
@ -312,12 +337,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return BlendEquationMode.Max;
}
throw new ArgumentException(nameof(BlendEquation) + " \"" + BlendEquation + "\" is not valid!");
throw new ArgumentException(nameof(blendEquation) + " \"" + blendEquation + "\" is not valid!");
}
public static BlendingFactor GetBlendFactor(GalBlendFactor BlendFactor)
public static BlendingFactor GetBlendFactor(GalBlendFactor blendFactor)
{
switch (BlendFactor)
switch (blendFactor)
{
case GalBlendFactor.Zero:
case GalBlendFactor.ZeroGl:
@ -396,7 +421,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return BlendingFactor.ConstantColor;
}
throw new ArgumentException(nameof(BlendFactor) + " \"" + BlendFactor + "\" is not valid!");
throw new ArgumentException(nameof(blendFactor) + " \"" + blendFactor + "\" is not valid!");
}
}
}

View file

@ -0,0 +1,70 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Logging;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OglExtension
{
// Private lazy backing variables
private static Lazy<bool> _enhancedLayouts = new Lazy<bool>(() => HasExtension("GL_ARB_enhanced_layouts"));
private static Lazy<bool> _textureMirrorClamp = new Lazy<bool>(() => HasExtension("GL_EXT_texture_mirror_clamp"));
private static Lazy<bool> _viewportArray = new Lazy<bool>(() => HasExtension("GL_ARB_viewport_array"));
private static Lazy<bool> _nvidiaDriver = new Lazy<bool>(() => IsNvidiaDriver());
// Public accessors
public static bool EnhancedLayouts => _enhancedLayouts.Value;
public static bool TextureMirrorClamp => _textureMirrorClamp.Value;
public static bool ViewportArray => _viewportArray.Value;
public static bool NvidiaDriver => _nvidiaDriver.Value;
private static bool HasExtension(string name)
{
int numExtensions = GL.GetInteger(GetPName.NumExtensions);
for (int extension = 0; extension < numExtensions; extension++)
{
if (GL.GetString(StringNameIndexed.Extensions, extension) == name)
{
return true;
}
}
Logger.PrintInfo(LogClass.Gpu, $"OpenGL extension {name} unavailable. You may experience some performance degradation");
return false;
}
private static bool IsNvidiaDriver()
{
return GL.GetString(StringName.Vendor).Equals("NVIDIA Corporation");
}
public static class Required
{
// Public accessors
public static bool EnhancedLayouts => _enhancedLayoutsRequired.Value;
public static bool TextureMirrorClamp => _textureMirrorClampRequired.Value;
public static bool ViewportArray => _viewportArrayRequired.Value;
// Private lazy backing variables
private static Lazy<bool> _enhancedLayoutsRequired = new Lazy<bool>(() => HasExtensionRequired(OglExtension.EnhancedLayouts, "GL_ARB_enhanced_layouts"));
private static Lazy<bool> _textureMirrorClampRequired = new Lazy<bool>(() => HasExtensionRequired(OglExtension.TextureMirrorClamp, "GL_EXT_texture_mirror_clamp"));
private static Lazy<bool> _viewportArrayRequired = new Lazy<bool>(() => HasExtensionRequired(OglExtension.ViewportArray, "GL_ARB_viewport_array"));
private static bool HasExtensionRequired(bool value, string name)
{
if (value)
{
return true;
}
Logger.PrintWarning(LogClass.Gpu, $"Required OpenGL extension {name} unavailable. You may experience some rendering issues");
return false;
}
}
}
}

View file

@ -0,0 +1,12 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OglLimit
{
private static Lazy<int> _sMaxUboSize = new Lazy<int>(() => GL.GetInteger(GetPName.MaxUniformBlockSize));
public static int MaxUboSize => _sMaxUboSize.Value;
}
}

View file

@ -0,0 +1,207 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OglRasterizer : IGalRasterizer
{
private const long MaxVertexBufferCacheSize = 128 * 1024 * 1024;
private const long MaxIndexBufferCacheSize = 64 * 1024 * 1024;
private int[] _vertexBuffers;
private OglCachedResource<int> _vboCache;
private OglCachedResource<int> _iboCache;
private struct IbInfo
{
public int Count;
public int ElemSizeLog2;
public DrawElementsType Type;
}
private IbInfo _indexBuffer;
public OglRasterizer()
{
_vertexBuffers = new int[32];
_vboCache = new OglCachedResource<int>(GL.DeleteBuffer, MaxVertexBufferCacheSize);
_iboCache = new OglCachedResource<int>(GL.DeleteBuffer, MaxIndexBufferCacheSize);
_indexBuffer = new IbInfo();
}
public void LockCaches()
{
_vboCache.Lock();
_iboCache.Lock();
}
public void UnlockCaches()
{
_vboCache.Unlock();
_iboCache.Unlock();
}
public void ClearBuffers(
GalClearBufferFlags flags,
int attachment,
float red,
float green,
float blue,
float alpha,
float depth,
int stencil)
{
GL.ColorMask(
attachment,
flags.HasFlag(GalClearBufferFlags.ColorRed),
flags.HasFlag(GalClearBufferFlags.ColorGreen),
flags.HasFlag(GalClearBufferFlags.ColorBlue),
flags.HasFlag(GalClearBufferFlags.ColorAlpha));
GL.ClearBuffer(ClearBuffer.Color, attachment, new float[] { red, green, blue, alpha });
GL.ColorMask(attachment, true, true, true, true);
GL.DepthMask(true);
if (flags.HasFlag(GalClearBufferFlags.Depth))
{
GL.ClearBuffer(ClearBuffer.Depth, 0, ref depth);
}
if (flags.HasFlag(GalClearBufferFlags.Stencil))
{
GL.ClearBuffer(ClearBuffer.Stencil, 0, ref stencil);
}
}
public bool IsVboCached(long key, long dataSize)
{
return _vboCache.TryGetSize(key, out long size) && size == dataSize;
}
public bool IsIboCached(long key, long dataSize)
{
return _iboCache.TryGetSize(key, out long size) && size == dataSize;
}
public void CreateVbo(long key, int dataSize, IntPtr hostAddress)
{
int handle = GL.GenBuffer();
_vboCache.AddOrUpdate(key, handle, dataSize);
IntPtr length = new IntPtr(dataSize);
GL.BindBuffer(BufferTarget.ArrayBuffer, handle);
GL.BufferData(BufferTarget.ArrayBuffer, length, hostAddress, BufferUsageHint.StreamDraw);
}
public void CreateVbo(long key, byte[] data)
{
int handle = GL.GenBuffer();
_vboCache.AddOrUpdate(key, handle, data.Length);
IntPtr length = new IntPtr(data.Length);
GL.BindBuffer(BufferTarget.ArrayBuffer, handle);
GL.BufferData(BufferTarget.ArrayBuffer, length, data, BufferUsageHint.StreamDraw);
}
public void CreateIbo(long key, int dataSize, IntPtr hostAddress)
{
int handle = GL.GenBuffer();
_iboCache.AddOrUpdate(key, handle, (uint)dataSize);
IntPtr length = new IntPtr(dataSize);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, handle);
GL.BufferData(BufferTarget.ElementArrayBuffer, length, hostAddress, BufferUsageHint.StreamDraw);
}
public void CreateIbo(long key, int dataSize, byte[] buffer)
{
int handle = GL.GenBuffer();
_iboCache.AddOrUpdate(key, handle, dataSize);
IntPtr length = new IntPtr(buffer.Length);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, handle);
GL.BufferData(BufferTarget.ElementArrayBuffer, length, buffer, BufferUsageHint.StreamDraw);
}
public void SetIndexArray(int size, GalIndexFormat format)
{
_indexBuffer.Type = OglEnumConverter.GetDrawElementsType(format);
_indexBuffer.Count = size >> (int)format;
_indexBuffer.ElemSizeLog2 = (int)format;
}
public void DrawArrays(int first, int count, GalPrimitiveType primType)
{
if (count == 0)
{
return;
}
if (primType == GalPrimitiveType.Quads)
{
for (int offset = 0; offset < count; offset += 4)
{
GL.DrawArrays(PrimitiveType.TriangleFan, first + offset, 4);
}
}
else if (primType == GalPrimitiveType.QuadStrip)
{
GL.DrawArrays(PrimitiveType.TriangleFan, first, 4);
for (int offset = 2; offset < count; offset += 2)
{
GL.DrawArrays(PrimitiveType.TriangleFan, first + offset, 4);
}
}
else
{
GL.DrawArrays(OglEnumConverter.GetPrimitiveType(primType), first, count);
}
}
public void DrawElements(long iboKey, int first, int vertexBase, GalPrimitiveType primType)
{
if (!_iboCache.TryGetValue(iboKey, out int iboHandle))
{
return;
}
PrimitiveType mode = OglEnumConverter.GetPrimitiveType(primType);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, iboHandle);
first <<= _indexBuffer.ElemSizeLog2;
if (vertexBase != 0)
{
IntPtr indices = new IntPtr(first);
GL.DrawElementsBaseVertex(mode, _indexBuffer.Count, _indexBuffer.Type, indices, vertexBase);
}
else
{
GL.DrawElements(mode, _indexBuffer.Count, _indexBuffer.Type, first);
}
}
public bool TryGetVbo(long vboKey, out int vboHandle)
{
return _vboCache.TryGetValue(vboKey, out vboHandle);
}
}
}

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