Merge branch 'original_master' into profiling
This commit is contained in:
commit
e0fc1a7a5f
395 changed files with 25871 additions and 14559 deletions
|
@ -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>
|
||||
|
||||
|
|
|
@ -32,8 +32,6 @@ namespace ChocolArm64
|
|||
{
|
||||
translator.ExecuteSubroutine(this, entrypoint);
|
||||
|
||||
memory.RemoveMonitor(ThreadState.Core);
|
||||
|
||||
WorkFinished?.Invoke(this, EventArgs.Empty);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)) { }
|
||||
}
|
||||
}
|
|
@ -51,6 +51,8 @@ namespace ChocolArm64.Instructions
|
|||
|
||||
public static void Adds(ILEmitterCtx context)
|
||||
{
|
||||
context.TryOptMarkCondWithoutCmp();
|
||||
|
||||
EmitAluLoadOpers(context);
|
||||
|
||||
context.Emit(OpCodes.Add);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
151
ChocolArm64/Memory/CompareExchange128.cs
Normal file
151
ChocolArm64/Memory/CompareExchange128.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
114
ChocolArm64/Memory/MemoryManagement.cs
Normal file
114
ChocolArm64/Memory/MemoryManagement.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
70
ChocolArm64/Memory/MemoryManagementUnix.cs
Normal file
70
ChocolArm64/Memory/MemoryManagementUnix.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
155
ChocolArm64/Memory/MemoryManagementWindows.cs
Normal file
155
ChocolArm64/Memory/MemoryManagementWindows.cs
Normal 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
16
ChocolArm64/Memory/MemoryProtection.cs
Normal file
16
ChocolArm64/Memory/MemoryProtection.cs
Normal 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
|
||||
}
|
||||
}
|
10
ChocolArm64/Memory/MemoryProtectionException.cs
Normal file
10
ChocolArm64/Memory/MemoryProtectionException.cs
Normal 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}\".") { }
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
9
ChocolArm64/Translation/CallType.cs
Normal file
9
ChocolArm64/Translation/CallType.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace ChocolArm64.Translation
|
||||
{
|
||||
enum CallType
|
||||
{
|
||||
Call,
|
||||
VirtualCall,
|
||||
VirtualJump
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -16,6 +16,8 @@ namespace ChocolArm64.Translation
|
|||
|
||||
private ImmVal _value;
|
||||
|
||||
public long Value => _value.I8;
|
||||
|
||||
private enum ConstType
|
||||
{
|
||||
Int32,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace ChocolArm64.Translation
|
|||
{
|
||||
struct ILOpCodeLoadField : IILEmit
|
||||
{
|
||||
public FieldInfo Info { get; private set; }
|
||||
public FieldInfo Info { get; }
|
||||
|
||||
public ILOpCodeLoadField(FieldInfo info)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
namespace ChocolArm64.Translation
|
||||
{
|
||||
enum IoType
|
||||
enum VarType
|
||||
{
|
||||
Arg,
|
||||
Flag,
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx.Graphics/DepthCompareFunc.cs
Normal file
14
Ryujinx.Graphics/DepthCompareFunc.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
{
|
||||
public enum GalFrontFace
|
||||
{
|
||||
CW = 0x900,
|
||||
CCW = 0x901
|
||||
Cw = 0x900,
|
||||
Ccw = 0x901
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
15
Ryujinx.Graphics/Gal/GalTextureTarget.cs
Normal file
15
Ryujinx.Graphics/Gal/GalTextureTarget.cs
Normal 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,
|
||||
}
|
||||
}
|
|
@ -6,8 +6,8 @@
|
|||
Unorm = 2,
|
||||
Sint = 3,
|
||||
Uint = 4,
|
||||
Snorm_Force_Fp16 = 5,
|
||||
Unorm_Force_Fp16 = 6,
|
||||
SnormForceFp16 = 5,
|
||||
UnormForceFp16 = 6,
|
||||
Float = 7
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
namespace Ryujinx.Graphics.Gal
|
||||
{
|
||||
public unsafe interface IGalMemory
|
||||
public interface IGalMemory
|
||||
{
|
||||
int ReadInt32(long Position);
|
||||
int ReadInt32(long position);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
{
|
||||
public interface IGalRenderer
|
||||
{
|
||||
void QueueAction(Action ActionMthd);
|
||||
void QueueAction(Action actionMthd);
|
||||
|
||||
void RunActions();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.Graphics.Gal.OpenGL
|
||||
{
|
||||
delegate void DeleteValue<T>(T Value);
|
||||
delegate void DeleteValue<T>(T value);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
191
Ryujinx.Graphics/Gal/OpenGL/OglCachedResource.cs
Normal file
191
Ryujinx.Graphics/Gal/OpenGL/OglCachedResource.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
74
Ryujinx.Graphics/Gal/OpenGL/OglConstBuffer.cs
Normal file
74
Ryujinx.Graphics/Gal/OpenGL/OglConstBuffer.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
70
Ryujinx.Graphics/Gal/OpenGL/OglExtension.cs
Normal file
70
Ryujinx.Graphics/Gal/OpenGL/OglExtension.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics/Gal/OpenGL/OglLimit.cs
Normal file
12
Ryujinx.Graphics/Gal/OpenGL/OglLimit.cs
Normal 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;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
207
Ryujinx.Graphics/Gal/OpenGL/OglRasterizer.cs
Normal file
207
Ryujinx.Graphics/Gal/OpenGL/OglRasterizer.cs
Normal 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
Loading…
Add table
Reference in a new issue