diff --git a/ChocolArm64/ChocolArm64.csproj b/ChocolArm64/ChocolArm64.csproj index 5178e8eb40..ea98003f9b 100644 --- a/ChocolArm64/ChocolArm64.csproj +++ b/ChocolArm64/ChocolArm64.csproj @@ -27,6 +27,7 @@ + diff --git a/ChocolArm64/CpuThread.cs b/ChocolArm64/CpuThread.cs index 6cd34f8127..ad1fd6f3c1 100644 --- a/ChocolArm64/CpuThread.cs +++ b/ChocolArm64/CpuThread.cs @@ -32,8 +32,6 @@ namespace ChocolArm64 { translator.ExecuteSubroutine(this, entrypoint); - memory.RemoveMonitor(ThreadState.Core); - WorkFinished?.Invoke(this, EventArgs.Empty); }); } diff --git a/ChocolArm64/Decoders/OpCodeSimdCvt64.cs b/ChocolArm64/Decoders/OpCodeSimdCvt64.cs index eacd594099..3181a85a34 100644 --- a/ChocolArm64/Decoders/OpCodeSimdCvt64.cs +++ b/ChocolArm64/Decoders/OpCodeSimdCvt64.cs @@ -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 diff --git a/ChocolArm64/Events/InvalidAccessEventArgs.cs b/ChocolArm64/Events/InvalidAccessEventArgs.cs deleted file mode 100644 index 9c349755f0..0000000000 --- a/ChocolArm64/Events/InvalidAccessEventArgs.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Exceptions/VmmPageFaultException.cs b/ChocolArm64/Exceptions/VmmPageFaultException.cs deleted file mode 100644 index f33aafc013..0000000000 --- a/ChocolArm64/Exceptions/VmmPageFaultException.cs +++ /dev/null @@ -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)) { } - } -} \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitAlu.cs b/ChocolArm64/Instructions/InstEmitAlu.cs index d5d9cd6541..bd49124e49 100644 --- a/ChocolArm64/Instructions/InstEmitAlu.cs +++ b/ChocolArm64/Instructions/InstEmitAlu.cs @@ -51,6 +51,8 @@ namespace ChocolArm64.Instructions public static void Adds(ILEmitterCtx context) { + context.TryOptMarkCondWithoutCmp(); + EmitAluLoadOpers(context); context.Emit(OpCodes.Add); diff --git a/ChocolArm64/Instructions/InstEmitBfm.cs b/ChocolArm64/Instructions/InstEmitBfm.cs index d25af8be8a..4a03959940 100644 --- a/ChocolArm64/Instructions/InstEmitBfm.cs +++ b/ChocolArm64/Instructions/InstEmitBfm.cs @@ -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) diff --git a/ChocolArm64/Instructions/InstEmitFlow.cs b/ChocolArm64/Instructions/InstEmitFlow.cs index a842dca9d1..5eae89cc09 100644 --- a/ChocolArm64/Instructions/InstEmitFlow.cs +++ b/ChocolArm64/Instructions/InstEmitFlow.cs @@ -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); diff --git a/ChocolArm64/Instructions/InstEmitFlow32.cs b/ChocolArm64/Instructions/InstEmitFlow32.cs index 61f1d34c53..dea490c775 100644 --- a/ChocolArm64/Instructions/InstEmitFlow32.cs +++ b/ChocolArm64/Instructions/InstEmitFlow32.cs @@ -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. diff --git a/ChocolArm64/Instructions/InstEmitFlowHelper.cs b/ChocolArm64/Instructions/InstEmitFlowHelper.cs index e93ef42679..a6091a5711 100644 --- a/ChocolArm64/Instructions/InstEmitFlowHelper.cs +++ b/ChocolArm64/Instructions/InstEmitFlowHelper.cs @@ -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); diff --git a/ChocolArm64/Instructions/InstEmitMemory.cs b/ChocolArm64/Instructions/InstEmitMemory.cs index 96f782df64..ea779c8da4 100644 --- a/ChocolArm64/Instructions/InstEmitMemory.cs +++ b/ChocolArm64/Instructions/InstEmitMemory.cs @@ -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); diff --git a/ChocolArm64/Instructions/InstEmitMemory32.cs b/ChocolArm64/Instructions/InstEmitMemory32.cs index 4d6a57a472..1e1419e65e 100644 --- a/ChocolArm64/Instructions/InstEmitMemory32.cs +++ b/ChocolArm64/Instructions/InstEmitMemory32.cs @@ -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(); diff --git a/ChocolArm64/Instructions/InstEmitMemoryEx.cs b/ChocolArm64/Instructions/InstEmitMemoryEx.cs index 42daca63b7..920c695fff 100644 --- a/ChocolArm64/Instructions/InstEmitMemoryEx.cs +++ b/ChocolArm64/Instructions/InstEmitMemoryEx.cs @@ -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) diff --git a/ChocolArm64/Instructions/InstEmitMemoryHelper.cs b/ChocolArm64/Instructions/InstEmitMemoryHelper.cs index f953564c46..c225cdd8cc 100644 --- a/ChocolArm64/Instructions/InstEmitMemoryHelper.cs +++ b/ChocolArm64/Instructions/InstEmitMemoryHelper.cs @@ -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); + } } } \ No newline at end of file diff --git a/ChocolArm64/Instructions/InstEmitSimdArithmetic.cs b/ChocolArm64/Instructions/InstEmitSimdArithmetic.cs index acb9f7f093..d2d87beffe 100644 --- a/ChocolArm64/Instructions/InstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instructions/InstEmitSimdArithmetic.cs @@ -1,4 +1,5 @@ // https://github.com/intel/ARM_NEON_2_x86_SSE/blob/master/NEON_2_SSE.h +// https://www.agner.org/optimize/#vectorclass @ vectori128.h using ChocolArm64.Decoders; using ChocolArm64.State; @@ -184,8 +185,8 @@ namespace ChocolArm64.Instructions if (sizeF == 0) { - Type[] typesSsv = new Type[] { typeof(float) }; - Type[] typesSubAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesSsv = new Type[] { typeof(float) }; + Type[] typesSubAnt = new Type[] { typeof(Vector128), typeof(Vector128) }; context.EmitLdc_R4(-0f); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), typesSsv)); @@ -193,9 +194,8 @@ namespace ChocolArm64.Instructions context.EmitLdvec(op.Rn); context.EmitLdvec(op.Rm); - context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), typesSubAndNot)); - - context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AndNot), typesSubAndNot)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), typesSubAnt)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AndNot), typesSubAnt)); context.EmitStvec(op.Rd); @@ -203,20 +203,19 @@ namespace ChocolArm64.Instructions } else /* if (sizeF == 1) */ { - Type[] typesSsv = new Type[] { typeof(double) }; - Type[] typesSubAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesSsv = new Type[] { typeof(double) }; + Type[] typesSubAnt = new Type[] { typeof(Vector128), typeof(Vector128) }; context.EmitLdc_R8(-0d); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); - EmitLdvecWithCastToDouble(context, op.Rn); - EmitLdvecWithCastToDouble(context, op.Rm); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), typesSubAndNot)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), typesSubAnt)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesSubAnt)); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesSubAndNot)); - - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); EmitVectorZeroUpper(context, op.Rd); } @@ -242,8 +241,8 @@ namespace ChocolArm64.Instructions if (sizeF == 0) { - Type[] typesSav = new Type[] { typeof(float) }; - Type[] typesSubAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesSav = new Type[] { typeof(float) }; + Type[] typesSubAnt = new Type[] { typeof(Vector128), typeof(Vector128) }; context.EmitLdc_R4(-0f); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), typesSav)); @@ -251,9 +250,8 @@ namespace ChocolArm64.Instructions context.EmitLdvec(op.Rn); context.EmitLdvec(op.Rm); - context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), typesSubAndNot)); - - context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AndNot), typesSubAndNot)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), typesSubAnt)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AndNot), typesSubAnt)); context.EmitStvec(op.Rd); @@ -264,20 +262,19 @@ namespace ChocolArm64.Instructions } else /* if (sizeF == 1) */ { - Type[] typesSav = new Type[] { typeof(double) }; - Type[] typesSubAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesSav = new Type[] { typeof(double) }; + Type[] typesSubAnt = new Type[] { typeof(Vector128), typeof(Vector128) }; context.EmitLdc_R8(-0d); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); - EmitLdvecWithCastToDouble(context, op.Rn); - EmitLdvecWithCastToDouble(context, op.Rm); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSubAndNot)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSubAnt)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesSubAnt)); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesSubAndNot)); - - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } } else @@ -299,15 +296,15 @@ namespace ChocolArm64.Instructions if (op.Size == 0) { - Type[] typesSsv = new Type[] { typeof(float) }; - Type[] typesAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesSsv = new Type[] { typeof(float) }; + Type[] typesAnt = new Type[] { typeof(Vector128), typeof(Vector128) }; context.EmitLdc_R4(-0f); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), typesSsv)); context.EmitLdvec(op.Rn); - context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AndNot), typesAndNot)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AndNot), typesAnt)); context.EmitStvec(op.Rd); @@ -315,17 +312,17 @@ namespace ChocolArm64.Instructions } else /* if (op.Size == 1) */ { - Type[] typesSsv = new Type[] { typeof(double) }; - Type[] typesAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesSsv = new Type[] { typeof(double) }; + Type[] typesAnt = new Type[] { typeof(Vector128), typeof(Vector128) }; context.EmitLdc_R8(-0d); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); - EmitLdvecWithCastToDouble(context, op.Rn); + context.EmitLdvec(op.Rn); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndNot)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAnt)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); EmitVectorZeroUpper(context, op.Rd); } @@ -349,15 +346,15 @@ namespace ChocolArm64.Instructions if (sizeF == 0) { - Type[] typesSav = new Type[] { typeof(float) }; - Type[] typesAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesSav = new Type[] { typeof(float) }; + Type[] typesAnt = new Type[] { typeof(Vector128), typeof(Vector128) }; context.EmitLdc_R4(-0f); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), typesSav)); context.EmitLdvec(op.Rn); - context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AndNot), typesAndNot)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AndNot), typesAnt)); context.EmitStvec(op.Rd); @@ -368,17 +365,17 @@ namespace ChocolArm64.Instructions } else /* if (sizeF == 1) */ { - Type[] typesSav = new Type[] { typeof(double) }; - Type[] typesAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesSav = new Type[] { typeof(double) }; + Type[] typesAnt = new Type[] { typeof(Vector128), typeof(Vector128) }; context.EmitLdc_R8(-0d); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); - EmitLdvecWithCastToDouble(context, op.Rn); + context.EmitLdvec(op.Rn); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndNot)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAnt)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } } else @@ -433,7 +430,7 @@ namespace ChocolArm64.Instructions Type[] typesAddH = new Type[] { typeof(Vector128), typeof(Vector128) }; context.EmitLdvec(op.Rn); - context.Emit(OpCodes.Dup); + context.EmitLdvec(op.Rn); context.EmitCall(typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), typesAddH)); @@ -445,12 +442,12 @@ namespace ChocolArm64.Instructions { Type[] typesAddH = new Type[] { typeof(Vector128), typeof(Vector128) }; - EmitLdvecWithCastToDouble(context, op.Rn); - context.Emit(OpCodes.Dup); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rn); context.EmitCall(typeof(Sse3).GetMethod(nameof(Sse3.HorizontalAdd), typesAddH)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); EmitVectorZeroUpper(context, op.Rd); } @@ -536,14 +533,14 @@ namespace ChocolArm64.Instructions { Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; - EmitLdvecWithCastToDouble(context, op.Ra); - EmitLdvecWithCastToDouble(context, op.Rn); - EmitLdvecWithCastToDouble(context, op.Rm); + context.EmitLdvec(op.Ra); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulAdd)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AddScalar), typesMulAdd)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); EmitVectorZeroUpper(context, op.Rd); } @@ -718,14 +715,14 @@ namespace ChocolArm64.Instructions { Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; - EmitLdvecWithCastToDouble(context, op.Rd); - EmitLdvecWithCastToDouble(context, op.Rn); - EmitLdvecWithCastToDouble(context, op.Rm); + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulAdd)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesMulAdd)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } } else @@ -751,18 +748,16 @@ namespace ChocolArm64.Instructions Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; context.EmitLdvec(op.Rd); - context.EmitLdvec(op.Rn); context.EmitLdvec(op.Rm); - context.Emit(OpCodes.Dup); + context.EmitLdvec(op.Rm); context.EmitLdc_I4(op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Shuffle), typesSfl)); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulAdd)); - - context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Add), typesMulAdd)); + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Add), typesMulAdd)); context.EmitStvec(op.Rd); @@ -776,21 +771,19 @@ namespace ChocolArm64.Instructions Type[] typesSfl = new Type[] { typeof(Vector128), typeof(Vector128), typeof(byte) }; Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; - EmitLdvecWithCastToDouble(context, op.Rd); + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); - EmitLdvecWithCastToDouble(context, op.Rn); - - EmitLdvecWithCastToDouble(context, op.Rm); - context.Emit(OpCodes.Dup); + context.EmitLdvec(op.Rm); + context.EmitLdvec(op.Rm); context.EmitLdc_I4(op.Index | op.Index << 1); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Shuffle), typesSfl)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulAdd)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesMulAdd)); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesMulAdd)); - - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } } else @@ -841,14 +834,14 @@ namespace ChocolArm64.Instructions { Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - EmitLdvecWithCastToDouble(context, op.Rd); - EmitLdvecWithCastToDouble(context, op.Rn); - EmitLdvecWithCastToDouble(context, op.Rm); + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } } else @@ -874,17 +867,15 @@ namespace ChocolArm64.Instructions Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; context.EmitLdvec(op.Rd); - context.EmitLdvec(op.Rn); context.EmitLdvec(op.Rm); - context.Emit(OpCodes.Dup); + context.EmitLdvec(op.Rm); context.EmitLdc_I4(op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Shuffle), typesSfl)); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulSub)); - context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), typesMulSub)); context.EmitStvec(op.Rd); @@ -899,21 +890,19 @@ namespace ChocolArm64.Instructions Type[] typesSfl = new Type[] { typeof(Vector128), typeof(Vector128), typeof(byte) }; Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - EmitLdvecWithCastToDouble(context, op.Rd); + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); - EmitLdvecWithCastToDouble(context, op.Rn); - - EmitLdvecWithCastToDouble(context, op.Rm); - context.Emit(OpCodes.Dup); + context.EmitLdvec(op.Rm); + context.EmitLdvec(op.Rm); context.EmitLdc_I4(op.Index | op.Index << 1); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Shuffle), typesSfl)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } } else @@ -950,14 +939,14 @@ namespace ChocolArm64.Instructions { Type[] typesMulSub = new Type[] { typeof(Vector128), typeof(Vector128) }; - EmitLdvecWithCastToDouble(context, op.Ra); - EmitLdvecWithCastToDouble(context, op.Rn); - EmitLdvecWithCastToDouble(context, op.Rm); + context.EmitLdvec(op.Ra); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), typesMulSub)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); EmitVectorZeroUpper(context, op.Rd); } @@ -1022,7 +1011,7 @@ namespace ChocolArm64.Instructions context.EmitLdvec(op.Rn); context.EmitLdvec(op.Rm); - context.Emit(OpCodes.Dup); + context.EmitLdvec(op.Rm); context.EmitLdc_I4(op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6); context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Shuffle), typesSfl)); @@ -1041,17 +1030,17 @@ namespace ChocolArm64.Instructions Type[] typesSfl = new Type[] { typeof(Vector128), typeof(Vector128), typeof(byte) }; Type[] typesMul = new Type[] { typeof(Vector128), typeof(Vector128) }; - EmitLdvecWithCastToDouble(context, op.Rn); + context.EmitLdvec(op.Rn); - EmitLdvecWithCastToDouble(context, op.Rm); - context.Emit(OpCodes.Dup); + context.EmitLdvec(op.Rm); + context.EmitLdvec(op.Rm); context.EmitLdc_I4(op.Index | op.Index << 1); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Shuffle), typesSfl)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMul)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } } else @@ -1125,11 +1114,11 @@ namespace ChocolArm64.Instructions context.EmitLdc_R8(-0d); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); - EmitLdvecWithCastToDouble(context, op.Rn); + context.EmitLdvec(op.Rn); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXor)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); EmitVectorZeroUpper(context, op.Rd); } @@ -1175,11 +1164,11 @@ namespace ChocolArm64.Instructions context.EmitLdc_R8(-0d); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); - EmitLdvecWithCastToDouble(context, op.Rn); + context.EmitLdvec(op.Rn); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesXor)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } } else @@ -1242,8 +1231,7 @@ namespace ChocolArm64.Instructions int sizeF = op.Size & 1; - if (Optimizations.FastFP && Optimizations.UseSse - && sizeF == 0) + if (Optimizations.FastFP && Optimizations.UseSse && sizeF == 0) { EmitScalarSseOrSse2OpF(context, nameof(Sse.ReciprocalScalar)); } @@ -1262,8 +1250,7 @@ namespace ChocolArm64.Instructions int sizeF = op.Size & 1; - if (Optimizations.FastFP && Optimizations.UseSse - && sizeF == 0) + if (Optimizations.FastFP && Optimizations.UseSse && sizeF == 0) { EmitVectorSseOrSse2OpF(context, nameof(Sse.Reciprocal)); } @@ -1310,13 +1297,13 @@ namespace ChocolArm64.Instructions context.EmitLdc_R8(2d); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); - EmitLdvecWithCastToDouble(context, op.Rn); - EmitLdvecWithCastToDouble(context, op.Rm); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), typesMulSub)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); EmitVectorZeroUpper(context, op.Rd); } @@ -1367,13 +1354,13 @@ namespace ChocolArm64.Instructions context.EmitLdc_R8(2d); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); - EmitLdvecWithCastToDouble(context, op.Rn); - EmitLdvecWithCastToDouble(context, op.Rm); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } } else @@ -1579,8 +1566,7 @@ namespace ChocolArm64.Instructions int sizeF = op.Size & 1; - if (Optimizations.FastFP && Optimizations.UseSse - && sizeF == 0) + if (Optimizations.FastFP && Optimizations.UseSse && sizeF == 0) { EmitScalarSseOrSse2OpF(context, nameof(Sse.ReciprocalSqrtScalar)); } @@ -1599,8 +1585,7 @@ namespace ChocolArm64.Instructions int sizeF = op.Size & 1; - if (Optimizations.FastFP && Optimizations.UseSse - && sizeF == 0) + if (Optimizations.FastFP && Optimizations.UseSse && sizeF == 0) { EmitVectorSseOrSse2OpF(context, nameof(Sse.ReciprocalSqrt)); } @@ -1654,14 +1639,14 @@ namespace ChocolArm64.Instructions context.EmitLdc_R8(3d); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), typesSsv)); - EmitLdvecWithCastToDouble(context, op.Rn); - EmitLdvecWithCastToDouble(context, op.Rm); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), typesMulSub)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), typesMulSub)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); EmitVectorZeroUpper(context, op.Rd); } @@ -1719,14 +1704,14 @@ namespace ChocolArm64.Instructions context.EmitLdc_R8(3d); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); - EmitLdvecWithCastToDouble(context, op.Rn); - EmitLdvecWithCastToDouble(context, op.Rm); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), typesMulSub)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } } else @@ -1800,11 +1785,18 @@ namespace ChocolArm64.Instructions public static void Mla_V(ILEmitterCtx context) { - EmitVectorTernaryOpZx(context, () => + if (Optimizations.UseSse41) { - context.Emit(OpCodes.Mul); - context.Emit(OpCodes.Add); - }); + EmitSse41Mul_AddSub(context, nameof(Sse2.Add)); + } + else + { + EmitVectorTernaryOpZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Add); + }); + } } public static void Mla_Ve(ILEmitterCtx context) @@ -1818,11 +1810,18 @@ namespace ChocolArm64.Instructions public static void Mls_V(ILEmitterCtx context) { - EmitVectorTernaryOpZx(context, () => + if (Optimizations.UseSse41) { - context.Emit(OpCodes.Mul); - context.Emit(OpCodes.Sub); - }); + EmitSse41Mul_AddSub(context, nameof(Sse2.Subtract)); + } + else + { + EmitVectorTernaryOpZx(context, () => + { + context.Emit(OpCodes.Mul); + context.Emit(OpCodes.Sub); + }); + } } public static void Mls_Ve(ILEmitterCtx context) @@ -1836,7 +1835,14 @@ namespace ChocolArm64.Instructions public static void Mul_V(ILEmitterCtx context) { - EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Mul)); + if (Optimizations.UseSse41) + { + EmitSse41Mul_AddSub(context); + } + else + { + EmitVectorBinaryOpZx(context, () => context.Emit(OpCodes.Mul)); + } } public static void Mul_Ve(ILEmitterCtx context) @@ -1857,18 +1863,12 @@ namespace ChocolArm64.Instructions Type[] typesSub = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; - string[] namesSzv = new string[] { nameof(VectorHelper.VectorSByteZero), - nameof(VectorHelper.VectorInt16Zero), - nameof(VectorHelper.VectorInt32Zero), - nameof(VectorHelper.VectorInt64Zero) }; - - VectorHelper.EmitCall(context, namesSzv[op.Size]); - - EmitLdvecWithSignedCast(context, op.Rn, op.Size); + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + context.EmitLdvec(op.Rn); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSub)); - EmitStvecWithSignedCast(context, op.Rd, op.Size); + context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { @@ -1915,20 +1915,125 @@ namespace ChocolArm64.Instructions public static void Sabd_V(ILEmitterCtx context) { - EmitVectorBinaryOpSx(context, () => + if (Optimizations.UseSse2) { - context.Emit(OpCodes.Sub); - EmitAbs(context); - }); + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesCmpSub = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + Type[] typesAndOr = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareGreaterThan), typesCmpSub)); + + context.EmitStvectmp(); // Cmp mask + context.EmitLdvectmp(); // Cmp mask + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesCmpSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesAndOr)); + + context.EmitLdvectmp(); // Cmp mask + + context.EmitLdvec(op.Rm); + context.EmitLdvec(op.Rn); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesCmpSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndOr)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), typesAndOr)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } } public static void Sabdl_V(ILEmitterCtx context) { - EmitVectorWidenRnRmBinaryOpSx(context, () => + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse41 && op.Size < 2) { - context.Emit(OpCodes.Sub); - EmitAbs(context); - }); + Type[] typesCmpSub = new Type[] { VectorIntTypesPerSizeLog2[op.Size + 1], + VectorIntTypesPerSizeLog2[op.Size + 1] }; + Type[] typesSrl = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAndOr = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesCvt = new Type[] { VectorIntTypesPerSizeLog2[op.Size] }; + + string nameCvt = op.Size == 0 + ? nameof(Sse41.ConvertToVector128Int16) + : nameof(Sse41.ConvertToVector128Int32); + + context.EmitLdvec(op.Rn); + + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitLdvec(op.Rm); + + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitStvectmp2(); // Long Rm + context.EmitStvectmp(); // Long Rn + + context.EmitLdvectmp(); // Long Rn + context.EmitLdvectmp2(); // Long Rm + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareGreaterThan), typesCmpSub)); + + context.EmitStvectmp3(); // Cmp mask + context.EmitLdvectmp3(); // Cmp mask + + context.EmitLdvectmp(); // Long Rn + context.EmitLdvectmp2(); // Long Rm + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesCmpSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesAndOr)); + + context.EmitLdvectmp3(); // Cmp mask + + context.EmitLdvectmp2(); // Long Rm + context.EmitLdvectmp(); // Long Rn + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesCmpSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndOr)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), typesAndOr)); + + context.EmitStvec(op.Rd); + } + else + { + EmitVectorWidenRnRmBinaryOpSx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } } public static void Sadalp_V(ILEmitterCtx context) @@ -1951,25 +2056,29 @@ 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), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); - EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rm); - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); - EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -1997,20 +2106,20 @@ namespace ChocolArm64.Instructions nameof(Sse41.ConvertToVector128Int32), nameof(Sse41.ConvertToVector128Int64) }; - int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); - EmitLdvecWithSignedCast(context, op.Rn, op.Size + 1); - - EmitLdvecWithSignedCast(context, op.Rm, op.Size); - - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); - EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -2027,29 +2136,22 @@ namespace ChocolArm64.Instructions Type[] typesSra = new Type[] { VectorIntTypesPerSizeLog2[op.Size], typeof(byte) }; Type[] typesAndXorAdd = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; - EmitLdvecWithSignedCast(context, op.Rn, op.Size); - - context.Emit(OpCodes.Dup); - context.EmitStvectmp(); - - EmitLdvecWithSignedCast(context, op.Rm, op.Size); - - context.Emit(OpCodes.Dup); - context.EmitStvectmp2(); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesAndXorAdd)); - context.EmitLdvectmp(); - context.EmitLdvectmp2(); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesAndXorAdd)); - context.EmitLdc_I4(1); + context.Emit(OpCodes.Ldc_I4_1); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), typesSra)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAndXorAdd)); - EmitStvecWithSignedCast(context, op.Rd, op.Size); + context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { @@ -2083,23 +2185,21 @@ namespace ChocolArm64.Instructions context.EmitStvectmp(); - EmitLdvecWithSignedCast(context, op.Rn, op.Size); + context.EmitLdvec(op.Rn); context.EmitLdvectmp(); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAddSub)); context.Emit(OpCodes.Dup); - EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rm); context.EmitLdvectmp(); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAddSub)); - - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvg)); - + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAddSub)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvg)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesAddSub)); - EmitStvecWithSignedCast(context, op.Rd, op.Size); + context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { @@ -2128,12 +2228,12 @@ namespace ChocolArm64.Instructions Type typeSse = op.Size == 1 ? typeof(Sse2) : typeof(Sse41); - EmitLdvecWithSignedCast(context, op.Rn, op.Size); - EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeSse.GetMethod(nameof(Sse2.Max), typesMax)); - EmitStvecWithSignedCast(context, op.Rd, op.Size); + context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { @@ -2169,12 +2269,12 @@ namespace ChocolArm64.Instructions Type typeSse = op.Size == 1 ? typeof(Sse2) : typeof(Sse41); - EmitLdvecWithSignedCast(context, op.Rn, op.Size); - EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeSse.GetMethod(nameof(Sse2.Min), typesMin)); - EmitStvecWithSignedCast(context, op.Rd, op.Size); + context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { @@ -2217,21 +2317,24 @@ namespace ChocolArm64.Instructions ? nameof(Sse41.ConvertToVector128Int16) : nameof(Sse41.ConvertToVector128Int32); - int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); - EmitLdvecWithSignedCast(context, op.Rd, op.Size + 1); - - EmitLdvecWithSignedCast(context, op.Rn, op.Size); - - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); - EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rm); - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); @@ -2239,7 +2342,7 @@ namespace ChocolArm64.Instructions context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesMulAdd)); - EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -2277,21 +2380,24 @@ namespace ChocolArm64.Instructions ? nameof(Sse41.ConvertToVector128Int16) : nameof(Sse41.ConvertToVector128Int32); - int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); - EmitLdvecWithSignedCast(context, op.Rd, op.Size + 1); - - EmitLdvecWithSignedCast(context, op.Rn, op.Size); - - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); - EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rm); - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); @@ -2299,7 +2405,7 @@ namespace ChocolArm64.Instructions context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); - EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -2423,23 +2529,22 @@ namespace ChocolArm64.Instructions context.EmitLdc_I4(op.Size == 0 ? sbyte.MinValue : short.MinValue); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); - context.Emit(OpCodes.Dup); context.EmitStvectmp(); + context.EmitLdvectmp(); - EmitLdvecWithSignedCast(context, op.Rn, op.Size); + context.EmitLdvec(op.Rn); context.EmitLdvectmp(); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSubAdd)); - EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rm); context.EmitLdvectmp(); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSubAdd)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvg)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesSubAdd)); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvg)); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesSubAdd)); - - EmitStvecWithSignedCast(context, op.Rd, op.Size); + context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { @@ -2476,25 +2581,29 @@ 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), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); - EmitLdvecWithSignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rm); - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSub)); - EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -2517,20 +2626,20 @@ namespace ChocolArm64.Instructions nameof(Sse41.ConvertToVector128Int32), nameof(Sse41.ConvertToVector128Int64) }; - int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); - EmitLdvecWithSignedCast(context, op.Rn, op.Size + 1); - - EmitLdvecWithSignedCast(context, op.Rm, op.Size); - - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSub)); - EmitStvecWithSignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -2594,20 +2703,152 @@ namespace ChocolArm64.Instructions public static void Uabd_V(ILEmitterCtx context) { - EmitVectorBinaryOpZx(context, () => + if (Optimizations.UseSse41) { - context.Emit(OpCodes.Sub); - EmitAbs(context); - }); + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + Type[] typesMax = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesCmpSub = new Type[] { VectorIntTypesPerSizeLog2 [op.Size], VectorIntTypesPerSizeLog2 [op.Size] }; + Type[] typesAndOr = new Type[] { typeof(Vector128), typeof(Vector128) }; + 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), typesCmpSub)); + + context.EmitLdc_I8(-1L); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndOr)); + + context.EmitStvectmp(); // Cmp mask + context.EmitLdvectmp(); // Cmp mask + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesCmpSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesAndOr)); + + context.EmitLdvectmp(); // Cmp mask + + context.EmitLdvec(op.Rm); + context.EmitLdvec(op.Rn); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesCmpSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndOr)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), typesAndOr)); + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else + { + EmitVectorBinaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } } public static void Uabdl_V(ILEmitterCtx context) { - EmitVectorWidenRnRmBinaryOpZx(context, () => + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (Optimizations.UseSse41 && op.Size < 2) { - context.Emit(OpCodes.Sub); - EmitAbs(context); - }); + Type[] typesMax = new Type[] { VectorUIntTypesPerSizeLog2[op.Size + 1], + VectorUIntTypesPerSizeLog2[op.Size + 1] }; + Type[] typesCmpSub = new Type[] { VectorIntTypesPerSizeLog2 [op.Size + 1], + VectorIntTypesPerSizeLog2 [op.Size + 1] }; + Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; + Type[] typesAndOr = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesCvt = new Type[] { VectorUIntTypesPerSizeLog2[op.Size] }; + Type[] typesSav = new Type[] { typeof(long) }; + + string nameCvt = op.Size == 0 + ? nameof(Sse41.ConvertToVector128Int16) + : nameof(Sse41.ConvertToVector128Int32); + + context.EmitLdvec(op.Rn); + + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitLdvec(op.Rm); + + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } + + context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); + + context.EmitStvectmp2(); // Long Rm + context.EmitStvectmp(); // Long Rn + + context.EmitLdvectmp2(); // Long Rm + context.EmitLdvectmp(); // Long Rn + + context.EmitCall(typeof(Sse41).GetMethod(nameof(Sse41.Max), typesMax)); + + context.EmitLdvectmp2(); // Long Rm + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareEqual), typesCmpSub)); + + context.EmitLdc_I8(-1L); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndOr)); + + context.EmitStvectmp3(); // Cmp mask + context.EmitLdvectmp3(); // Cmp mask + + context.EmitLdvectmp(); // Long Rn + context.EmitLdvectmp2(); // Long Rm + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesCmpSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesAndOr)); + + context.EmitLdvectmp3(); // Cmp mask + + context.EmitLdvectmp2(); // Long Rm + context.EmitLdvectmp(); // Long Rn + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesCmpSub)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), typesAndOr)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), typesAndOr)); + + context.EmitStvec(op.Rd); + } + else + { + EmitVectorWidenRnRmBinaryOpZx(context, () => + { + context.Emit(OpCodes.Sub); + EmitAbs(context); + }); + } } public static void Uadalp_V(ILEmitterCtx context) @@ -2630,25 +2871,29 @@ 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), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rm); - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); - EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -2695,20 +2940,20 @@ namespace ChocolArm64.Instructions nameof(Sse41.ConvertToVector128Int32), nameof(Sse41.ConvertToVector128Int64) }; - int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); - EmitLdvecWithUnsignedCast(context, op.Rn, op.Size + 1); - - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); - - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); - EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -2725,29 +2970,22 @@ namespace ChocolArm64.Instructions Type[] typesSrl = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], typeof(byte) }; Type[] typesAndXorAdd = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; - EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); - - context.Emit(OpCodes.Dup); - context.EmitStvectmp(); - - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); - - context.Emit(OpCodes.Dup); - context.EmitStvectmp2(); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), typesAndXorAdd)); - context.EmitLdvectmp(); - context.EmitLdvectmp2(); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), typesAndXorAdd)); - context.EmitLdc_I4(1); + context.Emit(OpCodes.Ldc_I4_1); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrl)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAndXorAdd)); - EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { @@ -2774,16 +3012,14 @@ namespace ChocolArm64.Instructions { Type[] typesAvgSub = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; - EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); - context.Emit(OpCodes.Dup); - - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); - - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvgSub)); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvgSub)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesAvgSub)); - EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { @@ -2812,12 +3048,12 @@ namespace ChocolArm64.Instructions Type typeSse = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); - EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeSse.GetMethod(nameof(Sse2.Max), typesMax)); - EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { @@ -2853,12 +3089,12 @@ namespace ChocolArm64.Instructions Type typeSse = op.Size == 0 ? typeof(Sse2) : typeof(Sse41); - EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeSse.GetMethod(nameof(Sse2.Min), typesMin)); - EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { @@ -2901,21 +3137,24 @@ namespace ChocolArm64.Instructions ? nameof(Sse41.ConvertToVector128Int16) : nameof(Sse41.ConvertToVector128Int32); - int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); - EmitLdvecWithUnsignedCast(context, op.Rd, op.Size + 1); - - EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); - - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rm); - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); @@ -2923,7 +3162,7 @@ namespace ChocolArm64.Instructions context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesMulAdd)); - EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -2961,21 +3200,24 @@ namespace ChocolArm64.Instructions ? nameof(Sse41.ConvertToVector128Int16) : nameof(Sse41.ConvertToVector128Int32); - int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + context.EmitLdvec(op.Rd); + context.EmitLdvec(op.Rn); - EmitLdvecWithUnsignedCast(context, op.Rd, op.Size + 1); - - EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); - - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rm); - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(nameCvt, typesCvt)); @@ -2983,7 +3225,7 @@ namespace ChocolArm64.Instructions context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesMulSub)); - EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -3052,12 +3294,12 @@ namespace ChocolArm64.Instructions { Type[] typesAvg = new Type[] { VectorUIntTypesPerSizeLog2[op.Size], VectorUIntTypesPerSizeLog2[op.Size] }; - EmitLdvecWithUnsignedCast(context, op.Rn, op.Size); - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Average), typesAvg)); - EmitStvecWithUnsignedCast(context, op.Rd, op.Size); + context.EmitStvec(op.Rd); if (op.RegisterSize == RegisterSize.Simd64) { @@ -3104,25 +3346,29 @@ 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), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); + context.EmitLdvec(op.Rm); - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSub)); - EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -3145,20 +3391,20 @@ namespace ChocolArm64.Instructions nameof(Sse41.ConvertToVector128Int32), nameof(Sse41.ConvertToVector128Int64) }; - int numBytes = op.RegisterSize == RegisterSize.Simd128 ? 8 : 0; + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); - EmitLdvecWithUnsignedCast(context, op.Rn, op.Size + 1); - - EmitLdvecWithUnsignedCast(context, op.Rm, op.Size); - - context.EmitLdc_I4(numBytes); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), typesSrl)); + } context.EmitCall(typeof(Sse41).GetMethod(namesCvt[op.Size], typesCvt)); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), typesSub)); - EmitStvecWithUnsignedCast(context, op.Rd, op.Size + 1); + context.EmitStvec(op.Rd); } else { @@ -3295,5 +3541,77 @@ namespace ChocolArm64.Instructions EmitVectorZeroUpper(context, op.Rd); } } + + private static void EmitSse41Mul_AddSub(ILEmitterCtx context, string nameAddSub = null) + { + OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; + + if (nameAddSub != null) + { + context.EmitLdvec(op.Rd); + } + + if (op.Size == 0) + { + Type[] typesBle = new Type[] { typeof(Vector128), typeof(Vector128), typeof(Vector128) }; + Type[] typesMul = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesShs = new Type[] { typeof(Vector128), typeof(byte) }; + Type[] typesSav = new Type[] { typeof(int) }; + + context.EmitLdvec(op.Rn); + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitLdvec(op.Rm); + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesShs)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyLow), typesMul)); + + context.Emit(OpCodes.Ldc_I4_8); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesShs)); + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyLow), typesMul)); + + context.EmitLdc_I4(0x00FF00FF); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse41).GetMethod(nameof(Sse41.BlendVariable), typesBle)); + } + else if (op.Size == 1) + { + Type[] typesMul = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyLow), typesMul)); + } + else /* if (op.Size == 2) */ + { + Type[] typesMul = new Type[] { typeof(Vector128), typeof(Vector128) }; + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rm); + + context.EmitCall(typeof(Sse41).GetMethod(nameof(Sse41.MultiplyLow), typesMul)); + } + + if (nameAddSub != null) + { + Type[] typesAddSub = new Type[] { VectorIntTypesPerSizeLog2[op.Size], VectorIntTypesPerSizeLog2[op.Size] }; + + context.EmitCall(typeof(Sse2).GetMethod(nameAddSub, typesAddSub)); + } + + context.EmitStvec(op.Rd); + + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } } } diff --git a/ChocolArm64/Instructions/InstEmitSimdCmp.cs b/ChocolArm64/Instructions/InstEmitSimdCmp.cs index fdf3951e64..d54edb7eda 100644 --- a/ChocolArm64/Instructions/InstEmitSimdCmp.cs +++ b/ChocolArm64/Instructions/InstEmitSimdCmp.cs @@ -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), typeof(Vector128) }; + 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), typeof(Vector128) }; + 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), typeof(Vector128) }; + 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) { diff --git a/ChocolArm64/Instructions/InstEmitSimdCvt.cs b/ChocolArm64/Instructions/InstEmitSimdCvt.cs index 2eac3194d6..c5f16f86cb 100644 --- a/ChocolArm64/Instructions/InstEmitSimdCvt.cs +++ b/ChocolArm64/Instructions/InstEmitSimdCvt.cs @@ -21,28 +21,26 @@ namespace ChocolArm64.Instructions if (op.Size == 1 && op.Opc == 0) { //Double -> Single. + Type[] typesCvt = new Type[] { typeof(Vector128), typeof(Vector128) }; + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + context.EmitLdvec(op.Rn); - EmitLdvecWithCastToDouble(context, op.Rn); - - Type[] types = new Type[] { typeof(Vector128), typeof(Vector128) }; - - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Single), types)); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Single), typesCvt)); context.EmitStvec(op.Rd); } else if (op.Size == 0 && op.Opc == 1) { //Single -> Double. - VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleZero)); + Type[] typesCvt = new Type[] { typeof(Vector128), typeof(Vector128) }; + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); context.EmitLdvec(op.Rn); - Type[] types = new Type[] { typeof(Vector128), typeof(Vector128) }; + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Double), typesCvt)); - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Double), types)); - - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } else { @@ -80,18 +78,18 @@ namespace ChocolArm64.Instructions { Type[] typesCvt = new Type[] { typeof(Vector128) }; - string nameMov = op.RegisterSize == RegisterSize.Simd128 - ? nameof(Sse.MoveHighToLow) - : nameof(Sse.MoveLowToHigh); - context.EmitLdvec(op.Rn); - context.Emit(OpCodes.Dup); - context.EmitCall(typeof(Sse).GetMethod(nameMov)); + if (op.RegisterSize == RegisterSize.Simd128) + { + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveHighToLow))); + } context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Double), typesCvt)); - EmitStvecWithCastFromDouble(context, op.Rd); + context.EmitStvec(op.Rd); } else { @@ -154,7 +152,7 @@ namespace ChocolArm64.Instructions context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MoveLowToHigh))); - EmitLdvecWithCastToDouble(context, op.Rn); + context.EmitLdvec(op.Rn); context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Single), typesCvt)); context.Emit(OpCodes.Dup); @@ -209,22 +207,50 @@ namespace ChocolArm64.Instructions public static void Fcvtns_S(ILEmitterCtx context) { - EmitFcvtn(context, signed: true, scalar: true); + if (Optimizations.UseSse41) + { + EmitSse41Fcvt_Signed(context, RoundMode.ToNearest, scalar: true); + } + else + { + EmitFcvtn(context, signed: true, scalar: true); + } } public static void Fcvtns_V(ILEmitterCtx context) { - EmitFcvtn(context, signed: true, scalar: false); + if (Optimizations.UseSse41) + { + EmitSse41Fcvt_Signed(context, RoundMode.ToNearest, scalar: false); + } + else + { + EmitFcvtn(context, signed: true, scalar: false); + } } public static void Fcvtnu_S(ILEmitterCtx context) { - EmitFcvtn(context, signed: false, scalar: true); + if (Optimizations.UseSse41) + { + EmitSse41Fcvt_Unsigned(context, RoundMode.ToNearest, scalar: true); + } + else + { + EmitFcvtn(context, signed: false, scalar: true); + } } public static void Fcvtnu_V(ILEmitterCtx context) { - EmitFcvtn(context, signed: false, scalar: false); + if (Optimizations.UseSse41) + { + EmitSse41Fcvt_Unsigned(context, RoundMode.ToNearest, scalar: false); + } + else + { + EmitFcvtn(context, signed: false, scalar: false); + } } public static void Fcvtps_Gp(ILEmitterCtx context) @@ -244,17 +270,43 @@ namespace ChocolArm64.Instructions public static void Fcvtzs_Gp_Fixed(ILEmitterCtx context) { - EmitFcvtzs_Gp_Fix(context); + EmitFcvtzs_Gp_Fixed(context); } public static void Fcvtzs_S(ILEmitterCtx context) { - EmitScalarFcvtzs(context); + if (Optimizations.UseSse41) + { + EmitSse41Fcvt_Signed(context, RoundMode.TowardsZero, scalar: true); + } + else + { + EmitFcvtz(context, signed: true, scalar: true); + } } public static void Fcvtzs_V(ILEmitterCtx context) { - EmitVectorFcvtzs(context); + if (Optimizations.UseSse41) + { + EmitSse41Fcvt_Signed(context, RoundMode.TowardsZero, scalar: false); + } + else + { + EmitFcvtz(context, signed: true, scalar: false); + } + } + + public static void Fcvtzs_V_Fixed(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + EmitSse41Fcvt_Signed(context, RoundMode.TowardsZero, scalar: false); + } + else + { + EmitFcvtz(context, signed: true, scalar: false); + } } public static void Fcvtzu_Gp(ILEmitterCtx context) @@ -264,17 +316,43 @@ namespace ChocolArm64.Instructions public static void Fcvtzu_Gp_Fixed(ILEmitterCtx context) { - EmitFcvtzu_Gp_Fix(context); + EmitFcvtzu_Gp_Fixed(context); } public static void Fcvtzu_S(ILEmitterCtx context) { - EmitScalarFcvtzu(context); + if (Optimizations.UseSse41) + { + EmitSse41Fcvt_Unsigned(context, RoundMode.TowardsZero, scalar: true); + } + else + { + EmitFcvtz(context, signed: false, scalar: true); + } } public static void Fcvtzu_V(ILEmitterCtx context) { - EmitVectorFcvtzu(context); + if (Optimizations.UseSse41) + { + EmitSse41Fcvt_Unsigned(context, RoundMode.TowardsZero, scalar: false); + } + else + { + EmitFcvtz(context, signed: false, scalar: false); + } + } + + public static void Fcvtzu_V_Fixed(ILEmitterCtx context) + { + if (Optimizations.UseSse41) + { + EmitSse41Fcvt_Unsigned(context, RoundMode.TowardsZero, scalar: false); + } + else + { + EmitFcvtz(context, signed: false, scalar: false); + } } public static void Scvtf_Gp(ILEmitterCtx context) @@ -285,7 +363,7 @@ namespace ChocolArm64.Instructions if (context.CurrOp.RegisterSize == RegisterSize.Int32) { - context.Emit(OpCodes.Conv_U4); + context.Emit(OpCodes.Conv_I4); } EmitFloatCast(context, op.Size); @@ -293,15 +371,42 @@ namespace ChocolArm64.Instructions EmitScalarSetF(context, op.Rd, op.Size); } + public static void Scvtf_Gp_Fixed(ILEmitterCtx context) + { + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; + + context.EmitLdintzr(op.Rn); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_I4); + } + + EmitFloatCast(context, op.Size); + + EmitI2fFBitsMul(context, op.Size, op.FBits); + + EmitScalarSetF(context, op.Rd, op.Size); + } + public static void Scvtf_S(ILEmitterCtx context) { OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; - EmitVectorExtractSx(context, op.Rn, 0, op.Size + 2); + int sizeF = op.Size & 1; - EmitFloatCast(context, op.Size); + if (Optimizations.UseSse2 && sizeF == 0) + { + EmitSse2cvtF_Signed(context, scalar: true); + } + else + { + EmitVectorExtractSx(context, op.Rn, 0, sizeF + 2); - EmitScalarSetF(context, op.Rd, op.Size); + EmitFloatCast(context, sizeF); + + EmitScalarSetF(context, op.Rd, sizeF); + } } public static void Scvtf_V(ILEmitterCtx context) @@ -312,18 +417,24 @@ namespace ChocolArm64.Instructions if (Optimizations.UseSse2 && sizeF == 0) { - Type[] typesCvt = new Type[] { typeof(Vector128) }; + EmitSse2cvtF_Signed(context, scalar: false); + } + else + { + EmitVectorCvtf(context, signed: true); + } + } - EmitLdvecWithSignedCast(context, op.Rn, 2); + public static void Scvtf_V_Fixed(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; - context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Single), typesCvt)); + // sizeF == ((OpCodeSimdShImm64)op).Size - 2 + int sizeF = op.Size & 1; - context.EmitStvec(op.Rd); - - if (op.RegisterSize == RegisterSize.Simd64) - { - EmitVectorZeroUpper(context, op.Rd); - } + if (Optimizations.UseSse2 && sizeF == 0) + { + EmitSse2cvtF_Signed(context, scalar: false); } else { @@ -349,47 +460,78 @@ namespace ChocolArm64.Instructions EmitScalarSetF(context, op.Rd, op.Size); } - public static void Ucvtf_S(ILEmitterCtx context) + public static void Ucvtf_Gp_Fixed(ILEmitterCtx context) { - OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; - EmitVectorExtractZx(context, op.Rn, 0, op.Size + 2); + context.EmitLdintzr(op.Rn); + + if (context.CurrOp.RegisterSize == RegisterSize.Int32) + { + context.Emit(OpCodes.Conv_U4); + } context.Emit(OpCodes.Conv_R_Un); EmitFloatCast(context, op.Size); + EmitI2fFBitsMul(context, op.Size, op.FBits); + EmitScalarSetF(context, op.Rd, op.Size); } + public static void Ucvtf_S(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + + if (Optimizations.UseSse2 && sizeF == 0) + { + EmitSse2cvtF_Unsigned(context, scalar: true); + } + else + { + EmitVectorExtractZx(context, op.Rn, 0, sizeF + 2); + + context.Emit(OpCodes.Conv_R_Un); + + EmitFloatCast(context, sizeF); + + EmitScalarSetF(context, op.Rd, sizeF); + } + } + public static void Ucvtf_V(ILEmitterCtx context) { - EmitVectorCvtf(context, signed: false); - } + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; - private static int GetFBits(ILEmitterCtx context) - { - if (context.CurrOp is OpCodeSimdShImm64 op) - { - return GetImmShr(op); - } + int sizeF = op.Size & 1; - return 0; - } - - private static void EmitFloatCast(ILEmitterCtx context, int size) - { - if (size == 0) + if (Optimizations.UseSse2 && sizeF == 0) { - context.Emit(OpCodes.Conv_R4); - } - else if (size == 1) - { - context.Emit(OpCodes.Conv_R8); + EmitSse2cvtF_Unsigned(context, scalar: false); } else { - throw new ArgumentOutOfRangeException(nameof(size)); + EmitVectorCvtf(context, signed: false); + } + } + + public static void Ucvtf_V_Fixed(ILEmitterCtx context) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + // sizeF == ((OpCodeSimdShImm64)op).Size - 2 + int sizeF = op.Size & 1; + + if (Optimizations.UseSse2 && sizeF == 0) + { + EmitSse2cvtF_Unsigned(context, scalar: false); + } + else + { + EmitVectorCvtf(context, signed: false); } } @@ -403,11 +545,6 @@ namespace ChocolArm64.Instructions int bytes = op.GetBitsCount() >> 3; int elems = !scalar ? bytes >> sizeI : 1; - if (scalar && (sizeF == 0)) - { - EmitVectorZeroLowerTmp(context); - } - for (int index = 0; index < elems; index++) { EmitVectorExtractF(context, op.Rn, index, sizeF); @@ -429,13 +566,62 @@ namespace ChocolArm64.Instructions : nameof(VectorHelper.SatF64ToU64)); } - EmitVectorInsertTmp(context, index, sizeI); + if (scalar) + { + EmitVectorZeroAll(context, op.Rd); + } + + EmitVectorInsert(context, op.Rd, index, sizeI); } - context.EmitLdvectmp(); - context.EmitStvec(op.Rd); + if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } - if ((op.RegisterSize == RegisterSize.Simd64) || scalar) + private static void EmitFcvtz(ILEmitterCtx context, bool signed, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + int sizeF = op.Size & 1; + int sizeI = sizeF + 2; + + int fBits = GetFBits(context); + + int bytes = op.GetBitsCount() >> 3; + int elems = !scalar ? bytes >> sizeI : 1; + + for (int index = 0; index < elems; index++) + { + EmitVectorExtractF(context, op.Rn, index, sizeF); + + EmitF2iFBitsMul(context, sizeF, fBits); + + if (sizeF == 0) + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF32ToS32) + : nameof(VectorHelper.SatF32ToU32)); + + context.Emit(OpCodes.Conv_U8); + } + else /* if (sizeF == 1) */ + { + VectorHelper.EmitCall(context, signed + ? nameof(VectorHelper.SatF64ToS64) + : nameof(VectorHelper.SatF64ToU64)); + } + + if (scalar) + { + EmitVectorZeroAll(context, op.Rd); + } + + EmitVectorInsert(context, op.Rd, index, sizeI); + } + + if (op.RegisterSize == RegisterSize.Simd64) { EmitVectorZeroUpper(context, op.Rd); } @@ -476,17 +662,17 @@ namespace ChocolArm64.Instructions context.EmitStintzr(op.Rd); } - private static void EmitFcvtzs_Gp_Fix(ILEmitterCtx context) + private static void EmitFcvtzs_Gp_Fixed(ILEmitterCtx context) { - EmitFcvtz__Gp_Fix(context, true); + EmitFcvtz__Gp_Fixed(context, true); } - private static void EmitFcvtzu_Gp_Fix(ILEmitterCtx context) + private static void EmitFcvtzu_Gp_Fixed(ILEmitterCtx context) { - EmitFcvtz__Gp_Fix(context, false); + EmitFcvtz__Gp_Fixed(context, false); } - private static void EmitFcvtz__Gp_Fix(ILEmitterCtx context, bool signed) + private static void EmitFcvtz__Gp_Fixed(ILEmitterCtx context, bool signed) { OpCodeSimdCvt64 op = (OpCodeSimdCvt64)context.CurrOp; @@ -530,9 +716,7 @@ namespace ChocolArm64.Instructions context.Emit(OpCodes.Conv_R_Un); } - context.Emit(sizeF == 0 - ? OpCodes.Conv_R4 - : OpCodes.Conv_R8); + EmitFloatCast(context, sizeF); EmitI2fFBitsMul(context, sizeF, fBits); @@ -545,102 +729,29 @@ namespace ChocolArm64.Instructions } } - private static void EmitScalarFcvtzs(ILEmitterCtx context) + private static int GetFBits(ILEmitterCtx context) { - EmitScalarFcvtz(context, true); - } - - private static void EmitScalarFcvtzu(ILEmitterCtx context) - { - EmitScalarFcvtz(context, false); - } - - private static void EmitScalarFcvtz(ILEmitterCtx context, bool signed) - { - OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; - - int sizeF = op.Size & 1; - int sizeI = sizeF + 2; - - int fBits = GetFBits(context); - - EmitVectorExtractF(context, op.Rn, 0, sizeF); - - EmitF2iFBitsMul(context, sizeF, fBits); - - if (sizeF == 0) + if (context.CurrOp is OpCodeSimdShImm64 op) { - VectorHelper.EmitCall(context, signed - ? nameof(VectorHelper.SatF32ToS32) - : nameof(VectorHelper.SatF32ToU32)); - } - else /* if (sizeF == 1) */ - { - VectorHelper.EmitCall(context, signed - ? nameof(VectorHelper.SatF64ToS64) - : nameof(VectorHelper.SatF64ToU64)); + return GetImmShr(op); } - if (sizeF == 0) + return 0; + } + + private static void EmitFloatCast(ILEmitterCtx context, int size) + { + if (size == 0) { - context.Emit(OpCodes.Conv_U8); + context.Emit(OpCodes.Conv_R4); } - - EmitScalarSet(context, op.Rd, sizeI); - } - - private static void EmitVectorFcvtzs(ILEmitterCtx context) - { - EmitVectorFcvtz(context, true); - } - - private static void EmitVectorFcvtzu(ILEmitterCtx context) - { - EmitVectorFcvtz(context, false); - } - - private static void EmitVectorFcvtz(ILEmitterCtx context, bool signed) - { - OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; - - int sizeF = op.Size & 1; - int sizeI = sizeF + 2; - - int fBits = GetFBits(context); - - int bytes = op.GetBitsCount() >> 3; - int elems = bytes >> sizeI; - - for (int index = 0; index < elems; index++) + else if (size == 1) { - EmitVectorExtractF(context, op.Rn, index, sizeF); - - EmitF2iFBitsMul(context, sizeF, fBits); - - if (sizeF == 0) - { - VectorHelper.EmitCall(context, signed - ? nameof(VectorHelper.SatF32ToS32) - : nameof(VectorHelper.SatF32ToU32)); - } - else /* if (sizeF == 1) */ - { - VectorHelper.EmitCall(context, signed - ? nameof(VectorHelper.SatF64ToS64) - : nameof(VectorHelper.SatF64ToU64)); - } - - if (sizeF == 0) - { - context.Emit(OpCodes.Conv_U8); - } - - EmitVectorInsert(context, op.Rd, index, sizeI); + context.Emit(OpCodes.Conv_R8); } - - if (op.RegisterSize == RegisterSize.Simd64) + else { - EmitVectorZeroUpper(context, op.Rd); + throw new ArgumentOutOfRangeException(nameof(size)); } } @@ -751,5 +862,467 @@ namespace ChocolArm64.Instructions context.Emit(OpCodes.Mul); } } + + private static void EmitSse41Fcvt_Signed(ILEmitterCtx context, RoundMode roundMode, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + // sizeF == ((OpCodeSimdShImm64)op).Size - 2 + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] types = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesRndCvt = new Type[] { typeof(Vector128) }; + Type[] typesSav = new Type[] { typeof(int) }; + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareOrdered), types)); + + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.And), types)); + + if (op is OpCodeSimdShImm64 fixedOp) + { + int fBits = GetImmShr(fixedOp); + + // BitConverter.Int32BitsToSingle(fpScaled) == MathF.Pow(2f, fBits) + int fpScaled = 0x3F800000 + fBits * 0x800000; + + context.EmitLdc_I4(fpScaled); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), types)); + } + + context.EmitCall(typeof(Sse41).GetMethod(GetSse41NameRnd(roundMode), typesRndCvt)); + + context.EmitStvectmp(); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Int32), typesRndCvt)); + + context.EmitLdvectmp(); + + context.EmitLdc_I4(0x4F000000); // 2.14748365E9f (2147483648) + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareGreaterThanOrEqual), types)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Xor), types)); + + context.EmitStvec(op.Rd); + + if (scalar) + { + EmitVectorZero32_128(context, op.Rd); + } + else if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (sizeF == 1) */ + { + Type[] types = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesRndCvt = new Type[] { typeof(Vector128) }; + Type[] typesSv = new Type[] { typeof(long), typeof(long) }; + Type[] typesSav = new Type[] { typeof(long) }; + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareOrdered), types)); + + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), types)); + + if (op is OpCodeSimdShImm64 fixedOp) + { + int fBits = GetImmShr(fixedOp); + + // BitConverter.Int64BitsToDouble(fpScaled) == Math.Pow(2d, fBits) + long fpScaled = 0x3FF0000000000000L + fBits * 0x10000000000000L; + + context.EmitLdc_I8(fpScaled); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), types)); + } + + context.EmitCall(typeof(Sse41).GetMethod(GetSse41NameRnd(roundMode), typesRndCvt)); + + context.EmitStvectmp(); + + if (!scalar) + { + context.EmitLdvectmp(); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackHigh), types)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToInt64), typesRndCvt)); + } + else + { + context.EmitLdc_I8(0L); + } + + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToInt64), typesRndCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSv)); + + context.EmitLdvectmp(); + + context.EmitLdc_I8(0x43E0000000000000L); // 9.2233720368547760E18d (9223372036854775808) + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareGreaterThanOrEqual), types)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), types)); + + context.EmitStvec(op.Rd); + + if (scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } + + private static void EmitSse41Fcvt_Unsigned(ILEmitterCtx context, RoundMode roundMode, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + // sizeF == ((OpCodeSimdShImm64)op).Size - 2 + int sizeF = op.Size & 1; + + if (sizeF == 0) + { + Type[] types = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesRndCvt = new Type[] { typeof(Vector128) }; + Type[] typesSav = new Type[] { typeof(int) }; + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareOrdered), types)); + + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.And), types)); + + if (op is OpCodeSimdShImm64 fixedOp) + { + int fBits = GetImmShr(fixedOp); + + // BitConverter.Int32BitsToSingle(fpScaled) == MathF.Pow(2f, fBits) + int fpScaled = 0x3F800000 + fBits * 0x800000; + + context.EmitLdc_I4(fpScaled); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), types)); + } + + context.EmitCall(typeof(Sse41).GetMethod(GetSse41NameRnd(roundMode), typesRndCvt)); + + context.Emit(OpCodes.Dup); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareGreaterThan), types)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.And), types)); + + context.EmitStvectmp(); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Int32), typesRndCvt)); + + context.EmitLdvectmp(); + + context.EmitLdc_I4(0x4F000000); // 2.14748365E9f (2147483648) + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitStvectmp2(); + context.EmitLdvectmp2(); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), types)); + + context.Emit(OpCodes.Dup); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareGreaterThan), types)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.And), types)); + + context.EmitStvectmp(); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Int32), typesRndCvt)); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareGreaterThanOrEqual), types)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Xor), types)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + context.EmitStvec(op.Rd); + + if (scalar) + { + EmitVectorZero32_128(context, op.Rd); + } + else if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + else /* if (sizeF == 1) */ + { + Type[] types = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesRndCvt = new Type[] { typeof(Vector128) }; + Type[] typesSv = new Type[] { typeof(long), typeof(long) }; + Type[] typesSav = new Type[] { typeof(long) }; + + context.EmitLdvec(op.Rn); + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareOrdered), types)); + + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), types)); + + if (op is OpCodeSimdShImm64 fixedOp) + { + int fBits = GetImmShr(fixedOp); + + // BitConverter.Int64BitsToDouble(fpScaled) == Math.Pow(2d, fBits) + long fpScaled = 0x3FF0000000000000L + fBits * 0x10000000000000L; + + context.EmitLdc_I8(fpScaled); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), types)); + } + + context.EmitCall(typeof(Sse41).GetMethod(GetSse41NameRnd(roundMode), typesRndCvt)); + + context.Emit(OpCodes.Dup); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareGreaterThan), types)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), types)); + + context.EmitStvectmp(); + + if (!scalar) + { + context.EmitLdvectmp(); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackHigh), types)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToInt64), typesRndCvt)); + } + else + { + context.EmitLdc_I8(0L); + } + + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToInt64), typesRndCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSv)); + + context.EmitLdvectmp(); + + context.EmitLdc_I8(0x43E0000000000000L); // 9.2233720368547760E18d (9223372036854775808) + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitStvectmp2(); + context.EmitLdvectmp2(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), types)); + + context.Emit(OpCodes.Dup); + + VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareGreaterThan), types)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), types)); + + context.EmitStvectmp(); + + if (!scalar) + { + context.EmitLdvectmp(); + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.UnpackHigh), types)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToInt64), typesRndCvt)); + } + else + { + context.EmitLdc_I8(0L); + } + + context.EmitLdvectmp(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToInt64), typesRndCvt)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetVector128), typesSv)); + + context.EmitLdvectmp(); + context.EmitLdvectmp2(); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareGreaterThanOrEqual), types)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), types)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), typesAdd)); + + context.EmitStvec(op.Rd); + + if (scalar) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + } + + private static void EmitSse2cvtF_Signed(ILEmitterCtx context, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + Type[] typesMul = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesCvt = new Type[] { typeof(Vector128) }; + Type[] typesSav = new Type[] { typeof(int) }; + + context.EmitLdvec(op.Rn); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Single), typesCvt)); + + if (op is OpCodeSimdShImm64 fixedOp) + { + int fBits = GetImmShr(fixedOp); + + // BitConverter.Int32BitsToSingle(fpScaled) == 1f / MathF.Pow(2f, fBits) + int fpScaled = 0x3F800000 - fBits * 0x800000; + + context.EmitLdc_I4(fpScaled); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMul)); + } + + context.EmitStvec(op.Rd); + + if (scalar) + { + EmitVectorZero32_128(context, op.Rd); + } + else if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static void EmitSse2cvtF_Unsigned(ILEmitterCtx context, bool scalar) + { + OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp; + + Type[] typesMulAdd = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesSrlSll = new Type[] { typeof(Vector128), typeof(byte) }; + Type[] typesCvt = new Type[] { typeof(Vector128) }; + Type[] typesSav = new Type[] { typeof(int) }; + + context.EmitLdvec(op.Rn); + + context.EmitLdc_I4(16); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrlSll)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Single), typesCvt)); + + context.EmitLdc_I4(0x47800000); // 65536.0f (1 << 16) + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulAdd)); + + context.EmitLdvec(op.Rn); + + context.EmitLdc_I4(16); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), typesSrlSll)); + + context.EmitLdc_I4(16); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), typesSrlSll)); + + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertToVector128Single), typesCvt)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Add), typesMulAdd)); + + if (op is OpCodeSimdShImm64 fixedOp) + { + int fBits = GetImmShr(fixedOp); + + // BitConverter.Int32BitsToSingle(fpScaled) == 1f / MathF.Pow(2f, fBits) + int fpScaled = 0x3F800000 - fBits * 0x800000; + + context.EmitLdc_I4(fpScaled); + context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), typesSav)); + + context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), typesMulAdd)); + } + + context.EmitStvec(op.Rd); + + if (scalar) + { + EmitVectorZero32_128(context, op.Rd); + } + else if (op.RegisterSize == RegisterSize.Simd64) + { + EmitVectorZeroUpper(context, op.Rd); + } + } + + private static string GetSse41NameRnd(RoundMode roundMode) + { + switch (roundMode) + { + case RoundMode.ToNearest: + return nameof(Sse41.RoundToNearestInteger); // even + + case RoundMode.TowardsMinusInfinity: + return nameof(Sse41.RoundToNegativeInfinity); + + case RoundMode.TowardsPlusInfinity: + return nameof(Sse41.RoundToPositiveInfinity); + + case RoundMode.TowardsZero: + return nameof(Sse41.RoundToZero); + + default: throw new ArgumentException(nameof(roundMode)); + } + } } } diff --git a/ChocolArm64/Instructions/InstEmitSimdHelper.cs b/ChocolArm64/Instructions/InstEmitSimdHelper.cs index 5a44e1a148..6799a3a388 100644 --- a/ChocolArm64/Instructions/InstEmitSimdHelper.cs +++ b/ChocolArm64/Instructions/InstEmitSimdHelper.cs @@ -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), typeof(Vector128) }; 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), typeof(Vector128) }; - 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) }; - 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)); diff --git a/ChocolArm64/Instructions/InstEmitSimdLogical.cs b/ChocolArm64/Instructions/InstEmitSimdLogical.cs index 3473fc5d98..a5a9227410 100644 --- a/ChocolArm64/Instructions/InstEmitSimdLogical.cs +++ b/ChocolArm64/Instructions/InstEmitSimdLogical.cs @@ -30,14 +30,14 @@ namespace ChocolArm64.Instructions { OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp; - Type[] typesAndNot = new Type[] { typeof(Vector128), typeof(Vector128) }; + Type[] typesAnt = new Type[] { typeof(Vector128), typeof(Vector128) }; - 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), typeof(Vector128) }; + Type[] typesXorAnd = new Type[] { typeof(Vector128), typeof(Vector128) }; - 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), typeof(Vector128) }; - 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), typeof(Vector128) }; + Type[] typesSav = new Type[] { typeof(long) }; + Type[] typesAnt = new Type[] { typeof(Vector128), typeof(Vector128) }; - 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), typeof(Vector128) }; + Type[] typesSav = new Type[] { typeof(long) }; + Type[] typesAntOr = new Type[] { typeof(Vector128), typeof(Vector128) }; - 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), typeof(Vector128) }; - 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), typeof(Vector128) }; - 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), typeof(Vector128) }; - 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) { diff --git a/ChocolArm64/Instructions/InstEmitSimdMemory.cs b/ChocolArm64/Instructions/InstEmitSimdMemory.cs index 9b84eb8681..18ec1d33ea 100644 --- a/ChocolArm64/Instructions/InstEmitSimdMemory.cs +++ b/ChocolArm64/Instructions/InstEmitSimdMemory.cs @@ -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); diff --git a/ChocolArm64/Instructions/InstEmitSimdMove.cs b/ChocolArm64/Instructions/InstEmitSimdMove.cs index 2844dfdf4c..131ddec610 100644 --- a/ChocolArm64/Instructions/InstEmitSimdMove.cs +++ b/ChocolArm64/Instructions/InstEmitSimdMove.cs @@ -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), typeof(byte) }; Type[] typesOr = new Type[] { typeof(Vector128), typeof(Vector128) }; - 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), typeof(Vector128) }; + Type[] typesOr = new Type[] { typeof(Vector128), typeof(Vector128) }; + 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 { diff --git a/ChocolArm64/Instructions/InstEmitSimdShift.cs b/ChocolArm64/Instructions/InstEmitSimdShift.cs index 843052110f..6865948ae0 100644 --- a/ChocolArm64/Instructions/InstEmitSimdShift.cs +++ b/ChocolArm64/Instructions/InstEmitSimdShift.cs @@ -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), typeof(Vector128) }; + 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), typeof(Vector128) }; + 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); diff --git a/ChocolArm64/Instructions/InstEmitSystem.cs b/ChocolArm64/Instructions/InstEmitSystem.cs index 0e61d5bded..5687768a88 100644 --- a/ChocolArm64/Instructions/InstEmitSystem.cs +++ b/ChocolArm64/Instructions/InstEmitSystem.cs @@ -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); diff --git a/ChocolArm64/Instructions/VectorHelper.cs b/ChocolArm64/Instructions/VectorHelper.cs index f02c131e68..d1dfaced41 100644 --- a/ChocolArm64/Instructions/VectorHelper.cs +++ b/ChocolArm64/Instructions/VectorHelper.cs @@ -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 VectorSByteZero() - { - if (Sse2.IsSupported) - { - return Sse2.SetZeroVector128(); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt16Zero() - { - if (Sse2.IsSupported) - { - return Sse2.SetZeroVector128(); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt32Zero() - { - if (Sse2.IsSupported) - { - return Sse2.SetZeroVector128(); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt64Zero() - { - if (Sse2.IsSupported) - { - return Sse2.SetZeroVector128(); - } - - throw new PlatformNotSupportedException(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 VectorSingleZero() { @@ -554,214 +510,5 @@ namespace ChocolArm64.Instructions throw new PlatformNotSupportedException(); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorDoubleZero() - { - if (Sse2.IsSupported) - { - return Sse2.SetZeroVector128(); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToSByte(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToInt16(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToInt32(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToInt64(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToByte(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToUInt16(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToUInt32(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToUInt64(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSingleToDouble(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorSByteToSingle(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt16ToSingle(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt32ToSingle(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorInt64ToSingle(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorByteToSingle(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorUInt16ToSingle(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorUInt32ToSingle(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorUInt64ToSingle(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 VectorDoubleToSingle(Vector128 vector) - { - if (Sse.IsSupported) - { - return Sse.StaticCast(vector); - } - - throw new PlatformNotSupportedException(); - } } } diff --git a/ChocolArm64/Memory/CompareExchange128.cs b/ChocolArm64/Memory/CompareExchange128.cs new file mode 100644 index 0000000000..1618ff0fbc --- /dev/null +++ b/ChocolArm64/Memory/CompareExchange128.cs @@ -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(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(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; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Memory/MemoryManagement.cs b/ChocolArm64/Memory/MemoryManagement.cs new file mode 100644 index 0000000000..fa4bc4fac2 --- /dev/null +++ b/ChocolArm64/Memory/MemoryManagement.cs @@ -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; + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Memory/MemoryManagementUnix.cs b/ChocolArm64/Memory/MemoryManagementUnix.cs new file mode 100644 index 0000000000..9fe1aef094 --- /dev/null +++ b/ChocolArm64/Memory/MemoryManagementUnix.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Memory/MemoryManagementWindows.cs b/ChocolArm64/Memory/MemoryManagementWindows.cs new file mode 100644 index 0000000000..6cee134279 --- /dev/null +++ b/ChocolArm64/Memory/MemoryManagementWindows.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Memory/MemoryManager.cs b/ChocolArm64/Memory/MemoryManager.cs index 1f21256807..ce102e096c 100644 --- a/ChocolArm64/Memory/MemoryManager.cs +++ b/ChocolArm64/Memory/MemoryManager.cs @@ -1,178 +1,540 @@ -using ChocolArm64.Events; -using ChocolArm64.Exceptions; using ChocolArm64.Instructions; -using ChocolArm64.State; using System; -using System.Collections.Concurrent; -using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; using System.Threading; +using static ChocolArm64.Memory.CompareExchange128; +using static ChocolArm64.Memory.MemoryManagement; + namespace ChocolArm64.Memory { public unsafe class MemoryManager : IMemory, IDisposable { - private const int PtLvl0Bits = 13; - private const int PtLvl1Bits = 14; - public const int PageBits = 12; + public const int PageBits = 12; + public const int PageSize = 1 << PageBits; + public const int PageMask = PageSize - 1; - private const int PtLvl0Size = 1 << PtLvl0Bits; - private const int PtLvl1Size = 1 << PtLvl1Bits; - public const int PageSize = 1 << PageBits; + private const long PteFlagNotModified = 1; - private const int PtLvl0Mask = PtLvl0Size - 1; - private const int PtLvl1Mask = PtLvl1Size - 1; - public const int PageMask = PageSize - 1; - - private const int PtLvl0Bit = PageBits + PtLvl1Bits; - private const int PtLvl1Bit = PageBits; - - private const long ErgMask = (4 << CpuThreadState.ErgSizeLog2) - 1; - - private class ArmMonitor - { - public long Position; - public bool ExState; - - public bool HasExclusiveAccess(long position) - { - return Position == position && ExState; - } - } - - private Dictionary _monitors; - - private ConcurrentDictionary _observedPages; + internal const long PteFlagsMask = 7; public IntPtr Ram { get; private set; } private byte* _ramPtr; - private byte*** _pageTable; + private IntPtr _pageTable; - public event EventHandler InvalidAccess; + internal IntPtr PageTable => _pageTable; - public event EventHandler ObservedAccess; + internal int PtLevelBits { get; } + internal int PtLevelSize { get; } + internal int PtLevelMask { get; } - public MemoryManager(IntPtr ram) + public bool HasWriteWatchSupport => MemoryManagement.HasWriteWatchSupport; + + public int AddressSpaceBits { get; } + public long AddressSpaceSize { get; } + + public MemoryManager( + IntPtr ram, + int addressSpaceBits = 48, + bool useFlatPageTable = false) { - _monitors = new Dictionary(); - - _observedPages = new ConcurrentDictionary(); - Ram = ram; _ramPtr = (byte*)ram; - _pageTable = (byte***)Marshal.AllocHGlobal(PtLvl0Size * IntPtr.Size); + AddressSpaceBits = addressSpaceBits; + AddressSpaceSize = 1L << addressSpaceBits; - for (int l0 = 0; l0 < PtLvl0Size; l0++) + //When flat page table is requested, we use a single + //array for the mappings of the entire address space. + //This has better performance, but also high memory usage. + //The multi level page table uses 9 bits per level, so + //the memory usage is lower, but the performance is also + //lower, since each address translation requires multiple reads. + if (useFlatPageTable) { - _pageTable[l0] = null; + PtLevelBits = addressSpaceBits - PageBits; } + else + { + PtLevelBits = 9; + } + + PtLevelSize = 1 << PtLevelBits; + PtLevelMask = PtLevelSize - 1; + + _pageTable = Allocate((ulong)(PtLevelSize * IntPtr.Size)); } - public void RemoveMonitor(int core) + public void Map(long va, long pa, long size) { - lock (_monitors) - { - ClearExclusive(core); - - _monitors.Remove(core); - } + SetPtEntries(va, _ramPtr + pa, size); } - public void SetExclusive(int core, long position) + public void Unmap(long position, long size) { - position &= ~ErgMask; + SetPtEntries(position, null, size); + } - lock (_monitors) + public bool IsMapped(long position) + { + return Translate(position) != IntPtr.Zero; + } + + public long GetPhysicalAddress(long virtualAddress) + { + byte* ptr = (byte*)Translate(virtualAddress); + + return (long)(ptr - _ramPtr); + } + + private IntPtr Translate(long position) + { + if (!IsValidPosition(position)) { - foreach (ArmMonitor mon in _monitors.Values) + return IntPtr.Zero; + } + + byte* ptr = GetPtEntry(position); + + ulong ptrUlong = (ulong)ptr; + + if ((ptrUlong & PteFlagsMask) != 0) + { + ptrUlong &= ~(ulong)PteFlagsMask; + + ptr = (byte*)ptrUlong; + } + + return new IntPtr(ptr + (position & PageMask)); + } + + private IntPtr TranslateWrite(long position) + { + if (!IsValidPosition(position)) + { + return IntPtr.Zero; + } + + byte* ptr = GetPtEntry(position); + + ulong ptrUlong = (ulong)ptr; + + if ((ptrUlong & PteFlagsMask) != 0) + { + if ((ptrUlong & PteFlagNotModified) != 0) { - if (mon.Position == position && mon.ExState) + ClearPtEntryFlag(position, PteFlagNotModified); + } + + ptrUlong &= ~(ulong)PteFlagsMask; + + ptr = (byte*)ptrUlong; + } + + return new IntPtr(ptr + (position & PageMask)); + } + + private byte* GetPtEntry(long position) + { + return *(byte**)GetPtPtr(position); + } + + private void SetPtEntries(long va, byte* ptr, long size) + { + long endPosition = (va + size + PageMask) & ~PageMask; + + while ((ulong)va < (ulong)endPosition) + { + SetPtEntry(va, ptr); + + va += PageSize; + + if (ptr != null) + { + ptr += PageSize; + } + } + } + + private void SetPtEntry(long position, byte* ptr) + { + *(byte**)GetPtPtr(position) = ptr; + } + + private void SetPtEntryFlag(long position, long flag) + { + ModifyPtEntryFlag(position, flag, setFlag: true); + } + + private void ClearPtEntryFlag(long position, long flag) + { + ModifyPtEntryFlag(position, flag, setFlag: false); + } + + private void ModifyPtEntryFlag(long position, long flag, bool setFlag) + { + IntPtr* pt = (IntPtr*)_pageTable; + + while (true) + { + IntPtr* ptPtr = GetPtPtr(position); + + IntPtr old = *ptPtr; + + long modified = old.ToInt64(); + + if (setFlag) + { + modified |= flag; + } + else + { + modified &= ~flag; + } + + IntPtr origValue = Interlocked.CompareExchange(ref *ptPtr, new IntPtr(modified), old); + + if (origValue == old) + { + break; + } + } + } + + private IntPtr* GetPtPtr(long position) + { + if (!IsValidPosition(position)) + { + throw new ArgumentOutOfRangeException(nameof(position)); + } + + IntPtr nextPtr = _pageTable; + + IntPtr* ptePtr = null; + + int bit = PageBits; + + while (true) + { + long index = (position >> bit) & PtLevelMask; + + ptePtr = &((IntPtr*)nextPtr)[index]; + + bit += PtLevelBits; + + if (bit >= AddressSpaceBits) + { + break; + } + + nextPtr = *ptePtr; + + if (nextPtr == IntPtr.Zero) + { + //Entry does not yet exist, allocate a new one. + IntPtr newPtr = Allocate((ulong)(PtLevelSize * IntPtr.Size)); + + //Try to swap the current pointer (should be zero), with the allocated one. + nextPtr = Interlocked.Exchange(ref *ptePtr, newPtr); + + //If the old pointer is not null, then another thread already has set it. + if (nextPtr != IntPtr.Zero) { - mon.ExState = false; + Free(newPtr); + } + else + { + nextPtr = newPtr; } } + } - if (!_monitors.TryGetValue(core, out ArmMonitor threadMon)) + return ptePtr; + } + + public bool IsRegionModified(long position, long size) + { + if (!HasWriteWatchSupport) + { + return IsRegionModifiedFallback(position, size); + } + + IntPtr address = Translate(position); + + IntPtr baseAddr = address; + IntPtr expectedAddr = address; + + long pendingPages = 0; + + long pages = size / PageSize; + + bool modified = false; + + bool IsAnyPageModified() + { + IntPtr pendingSize = new IntPtr(pendingPages * PageSize); + + IntPtr[] addresses = new IntPtr[pendingPages]; + + bool result = GetModifiedPages(baseAddr, pendingSize, addresses, out ulong count); + + if (result) { - threadMon = new ArmMonitor(); - - _monitors.Add(core, threadMon); + return count != 0; } - - threadMon.Position = position; - threadMon.ExState = true; - } - } - - public bool TestExclusive(int core, long position) - { - //Note: Any call to this method also should be followed by a - //call to ClearExclusiveForStore if this method returns true. - position &= ~ErgMask; - - Monitor.Enter(_monitors); - - if (!_monitors.TryGetValue(core, out ArmMonitor threadMon)) - { - Monitor.Exit(_monitors); - - return false; - } - - bool exState = threadMon.HasExclusiveAccess(position); - - if (!exState) - { - Monitor.Exit(_monitors); - } - - return exState; - } - - public void ClearExclusiveForStore(int core) - { - if (_monitors.TryGetValue(core, out ArmMonitor threadMon)) - { - threadMon.ExState = false; - } - - Monitor.Exit(_monitors); - } - - public void ClearExclusive(int core) - { - lock (_monitors) - { - if (_monitors.TryGetValue(core, out ArmMonitor threadMon)) + else { - threadMon.ExState = false; + return true; } } + + while (pages-- > 0) + { + if (address != expectedAddr) + { + modified |= IsAnyPageModified(); + + baseAddr = address; + + pendingPages = 0; + } + + expectedAddr = address + PageSize; + + pendingPages++; + + if (pages == 0) + { + break; + } + + position += PageSize; + + address = Translate(position); + } + + if (pendingPages != 0) + { + modified |= IsAnyPageModified(); + } + + return modified; } - public void WriteInt32ToSharedAddr(long position, int value) + private unsafe bool IsRegionModifiedFallback(long position, long size) { - long maskedPosition = position & ~ErgMask; + long endAddr = (position + size + PageMask) & ~PageMask; - lock (_monitors) + bool modified = false; + + while ((ulong)position < (ulong)endAddr) { - foreach (ArmMonitor mon in _monitors.Values) + if (IsValidPosition(position)) { - if (mon.Position == maskedPosition && mon.ExState) + byte* ptr = ((byte**)_pageTable)[position >> PageBits]; + + ulong ptrUlong = (ulong)ptr; + + if ((ptrUlong & PteFlagNotModified) == 0) { - mon.ExState = false; + modified = true; + + SetPtEntryFlag(position, PteFlagNotModified); } } + else + { + modified = true; + } - WriteInt32(position, value); + position += PageSize; } + + return modified; + } + + public bool TryGetHostAddress(long position, long size, out IntPtr ptr) + { + if (IsContiguous(position, size)) + { + ptr = (IntPtr)Translate(position); + + return true; + } + + ptr = IntPtr.Zero; + + return false; + } + + private bool IsContiguous(long position, long size) + { + long endPos = position + size; + + position &= ~PageMask; + + long expectedPa = GetPhysicalAddress(position); + + while ((ulong)position < (ulong)endPos) + { + long pa = GetPhysicalAddress(position); + + if (pa != expectedPa) + { + return false; + } + + position += PageSize; + expectedPa += PageSize; + } + + return true; + } + + public bool IsValidPosition(long position) + { + return (ulong)position < (ulong)AddressSpaceSize; + } + + internal bool AtomicCompareExchange2xInt32( + long position, + int expectedLow, + int expectedHigh, + int desiredLow, + int desiredHigh) + { + long expected = (uint)expectedLow; + long desired = (uint)desiredLow; + + expected |= (long)expectedHigh << 32; + desired |= (long)desiredHigh << 32; + + return AtomicCompareExchangeInt64(position, expected, desired); + } + + internal bool AtomicCompareExchangeInt128( + long position, + ulong expectedLow, + ulong expectedHigh, + ulong desiredLow, + ulong desiredHigh) + { + if ((position & 0xf) != 0) + { + AbortWithAlignmentFault(position); + } + + IntPtr ptr = TranslateWrite(position); + + return InterlockedCompareExchange128(ptr, expectedLow, expectedHigh, desiredLow, desiredHigh); + } + + internal Vector128 AtomicReadInt128(long position) + { + if ((position & 0xf) != 0) + { + AbortWithAlignmentFault(position); + } + + IntPtr ptr = Translate(position); + + InterlockedRead128(ptr, out ulong low, out ulong high); + + Vector128 vector = default(Vector128); + + vector = VectorHelper.VectorInsertInt(low, vector, 0, 3); + vector = VectorHelper.VectorInsertInt(high, vector, 1, 3); + + return vector; + } + + public bool AtomicCompareExchangeByte(long position, byte expected, byte desired) + { + int* ptr = (int*)Translate(position); + + int currentValue = *ptr; + + int expected32 = (currentValue & ~byte.MaxValue) | expected; + int desired32 = (currentValue & ~byte.MaxValue) | desired; + + return Interlocked.CompareExchange(ref *ptr, desired32, expected32) == expected32; + } + + public bool AtomicCompareExchangeInt16(long position, short expected, short desired) + { + if ((position & 1) != 0) + { + AbortWithAlignmentFault(position); + } + + int* ptr = (int*)Translate(position); + + int currentValue = *ptr; + + int expected32 = (currentValue & ~ushort.MaxValue) | (ushort)expected; + int desired32 = (currentValue & ~ushort.MaxValue) | (ushort)desired; + + return Interlocked.CompareExchange(ref *ptr, desired32, expected32) == expected32; + } + + public bool AtomicCompareExchangeInt32(long position, int expected, int desired) + { + if ((position & 3) != 0) + { + AbortWithAlignmentFault(position); + } + + int* ptr = (int*)TranslateWrite(position); + + return Interlocked.CompareExchange(ref *ptr, desired, expected) == expected; + } + + public bool AtomicCompareExchangeInt64(long position, long expected, long desired) + { + if ((position & 7) != 0) + { + AbortWithAlignmentFault(position); + } + + long* ptr = (long*)TranslateWrite(position); + + return Interlocked.CompareExchange(ref *ptr, desired, expected) == expected; + } + + public int AtomicIncrementInt32(long position) + { + if ((position & 3) != 0) + { + AbortWithAlignmentFault(position); + } + + int* ptr = (int*)TranslateWrite(position); + + return Interlocked.Increment(ref *ptr); + } + + public int AtomicDecrementInt32(long position) + { + if ((position & 3) != 0) + { + AbortWithAlignmentFault(position); + } + + int* ptr = (int*)TranslateWrite(position); + + return Interlocked.Decrement(ref *ptr); + } + + private void AbortWithAlignmentFault(long position) + { + //TODO: Abort mode and exception support on the CPU. + throw new InvalidOperationException($"Tried to compare exchange a misaligned address 0x{position:X16}."); } public sbyte ReadSByte(long position) @@ -353,7 +715,7 @@ namespace ChocolArm64.Memory int copySize = (int)(pageLimit - position); - Marshal.Copy((IntPtr)Translate(position), data, offset, copySize); + Marshal.Copy(Translate(position), data, offset, copySize); position += copySize; offset += copySize; @@ -390,7 +752,7 @@ namespace ChocolArm64.Memory int copySize = (int)(pageLimit - position); - Marshal.Copy((IntPtr)Translate(position), data, offset, copySize); + Marshal.Copy(Translate(position), data, offset, copySize); position += copySize; offset += copySize; @@ -553,7 +915,7 @@ namespace ChocolArm64.Memory int copySize = (int)(pageLimit - position); - Marshal.Copy(data, offset, (IntPtr)TranslateWrite(position), copySize); + Marshal.Copy(data, offset, TranslateWrite(position), copySize); position += copySize; offset += copySize; @@ -583,7 +945,7 @@ namespace ChocolArm64.Memory int copySize = (int)(pageLimit - position); - Marshal.Copy(data, offset, (IntPtr)TranslateWrite(position), copySize); + Marshal.Copy(data, offset, Translate(position), copySize); position += copySize; offset += copySize; @@ -596,8 +958,8 @@ namespace ChocolArm64.Memory if (IsContiguous(src, size) && IsContiguous(dst, size)) { - byte* srcPtr = Translate(src); - byte* dstPtr = TranslateWrite(dst); + byte* srcPtr = (byte*)Translate(src); + byte* dstPtr = (byte*)Translate(dst); Buffer.MemoryCopy(srcPtr, dstPtr, size, size); } @@ -607,266 +969,6 @@ namespace ChocolArm64.Memory } } - public void Map(long va, long pa, long size) - { - SetPtEntries(va, _ramPtr + pa, size); - } - - public void Unmap(long position, long size) - { - SetPtEntries(position, null, size); - - StopObservingRegion(position, size); - } - - public bool IsMapped(long position) - { - if (!(IsValidPosition(position))) - { - return false; - } - - long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; - long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; - - if (_pageTable[l0] == null) - { - return false; - } - - return _pageTable[l0][l1] != null || _observedPages.ContainsKey(position >> PageBits); - } - - public long GetPhysicalAddress(long virtualAddress) - { - byte* ptr = Translate(virtualAddress); - - return (long)(ptr - _ramPtr); - } - - internal byte* Translate(long position) - { - long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; - long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; - - long old = position; - - byte** lvl1 = _pageTable[l0]; - - if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0) - { - goto Unmapped; - } - - if (lvl1 == null) - { - goto Unmapped; - } - - position &= PageMask; - - byte* ptr = lvl1[l1]; - - if (ptr == null) - { - goto Unmapped; - } - - return ptr + position; - -Unmapped: - return HandleNullPte(old); - } - - private byte* HandleNullPte(long position) - { - long key = position >> PageBits; - - if (_observedPages.TryGetValue(key, out IntPtr ptr)) - { - return (byte*)ptr + (position & PageMask); - } - - InvalidAccess?.Invoke(this, new MemoryAccessEventArgs(position)); - - throw new VmmPageFaultException(position); - } - - internal byte* TranslateWrite(long position) - { - long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; - long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; - - long old = position; - - byte** lvl1 = _pageTable[l0]; - - if ((position >> (PtLvl0Bit + PtLvl0Bits)) != 0) - { - goto Unmapped; - } - - if (lvl1 == null) - { - goto Unmapped; - } - - position &= PageMask; - - byte* ptr = lvl1[l1]; - - if (ptr == null) - { - goto Unmapped; - } - - return ptr + position; - -Unmapped: - return HandleNullPteWrite(old); - } - - private byte* HandleNullPteWrite(long position) - { - long key = position >> PageBits; - - MemoryAccessEventArgs e = new MemoryAccessEventArgs(position); - - if (_observedPages.TryGetValue(key, out IntPtr ptr)) - { - SetPtEntry(position, (byte*)ptr); - - ObservedAccess?.Invoke(this, e); - - return (byte*)ptr + (position & PageMask); - } - - InvalidAccess?.Invoke(this, e); - - throw new VmmPageFaultException(position); - } - - private void SetPtEntries(long va, byte* ptr, long size) - { - long endPosition = (va + size + PageMask) & ~PageMask; - - while ((ulong)va < (ulong)endPosition) - { - SetPtEntry(va, ptr); - - va += PageSize; - - if (ptr != null) - { - ptr += PageSize; - } - } - } - - private void SetPtEntry(long position, byte* ptr) - { - if (!IsValidPosition(position)) - { - throw new ArgumentOutOfRangeException(nameof(position)); - } - - long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; - long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; - - if (_pageTable[l0] == null) - { - byte** lvl1 = (byte**)Marshal.AllocHGlobal(PtLvl1Size * IntPtr.Size); - - for (int zl1 = 0; zl1 < PtLvl1Size; zl1++) - { - lvl1[zl1] = null; - } - - Thread.MemoryBarrier(); - - _pageTable[l0] = lvl1; - } - - _pageTable[l0][l1] = ptr; - } - - public void StartObservingRegion(long position, long size) - { - long endPosition = (position + size + PageMask) & ~PageMask; - - position &= ~PageMask; - - while ((ulong)position < (ulong)endPosition) - { - _observedPages[position >> PageBits] = (IntPtr)Translate(position); - - SetPtEntry(position, null); - - position += PageSize; - } - } - - public void StopObservingRegion(long position, long size) - { - long endPosition = (position + size + PageMask) & ~PageMask; - - while (position < endPosition) - { - lock (_observedPages) - { - if (_observedPages.TryRemove(position >> PageBits, out IntPtr ptr)) - { - SetPtEntry(position, (byte*)ptr); - } - } - - position += PageSize; - } - } - - public bool TryGetHostAddress(long position, long size, out IntPtr ptr) - { - if (IsContiguous(position, size)) - { - ptr = (IntPtr)Translate(position); - - return true; - } - - ptr = IntPtr.Zero; - - return false; - } - - private bool IsContiguous(long position, long size) - { - long endPos = position + size; - - position &= ~PageMask; - - long expectedPa = GetPhysicalAddress(position); - - while ((ulong)position < (ulong)endPos) - { - long pa = GetPhysicalAddress(position); - - if (pa != expectedPa) - { - return false; - } - - position += PageSize; - expectedPa += PageSize; - } - - return true; - } - - public bool IsValidPosition(long position) - { - return position >> (PtLvl0Bits + PtLvl1Bits + PageBits) == 0; - } - public void Dispose() { Dispose(true); @@ -874,24 +976,36 @@ Unmapped: protected virtual void Dispose(bool disposing) { - if (_pageTable == null) + IntPtr ptr = Interlocked.Exchange(ref _pageTable, IntPtr.Zero); + + if (ptr != IntPtr.Zero) { + FreePageTableEntry(ptr, PageBits); + } + } + + private void FreePageTableEntry(IntPtr ptr, int levelBitEnd) + { + levelBitEnd += PtLevelBits; + + if (levelBitEnd >= AddressSpaceBits) + { + Free(ptr); + return; } - for (int l0 = 0; l0 < PtLvl0Size; l0++) + for (int index = 0; index < PtLevelSize; index++) { - if (_pageTable[l0] != null) - { - Marshal.FreeHGlobal((IntPtr)_pageTable[l0]); - } + IntPtr ptePtr = ((IntPtr*)ptr)[index]; - _pageTable[l0] = null; + if (ptePtr != IntPtr.Zero) + { + FreePageTableEntry(ptePtr, levelBitEnd); + } } - Marshal.FreeHGlobal((IntPtr)_pageTable); - - _pageTable = null; + Free(ptr); } } } \ No newline at end of file diff --git a/ChocolArm64/Memory/MemoryProtection.cs b/ChocolArm64/Memory/MemoryProtection.cs new file mode 100644 index 0000000000..d0874bfc0f --- /dev/null +++ b/ChocolArm64/Memory/MemoryProtection.cs @@ -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 + } +} \ No newline at end of file diff --git a/ChocolArm64/Memory/MemoryProtectionException.cs b/ChocolArm64/Memory/MemoryProtectionException.cs new file mode 100644 index 0000000000..3d2cebad33 --- /dev/null +++ b/ChocolArm64/Memory/MemoryProtectionException.cs @@ -0,0 +1,10 @@ +using System; + +namespace ChocolArm64.Memory +{ + class MemoryProtectionException : Exception + { + public MemoryProtectionException(MemoryProtection protection) : + base($"Failed to set memory protection to \"{protection}\".") { } + } +} \ No newline at end of file diff --git a/ChocolArm64/OpCodeTable.cs b/ChocolArm64/OpCodeTable.cs index 3a8d3948d8..fb8b19cd19 100644 --- a/ChocolArm64/OpCodeTable.cs +++ b/ChocolArm64/OpCodeTable.cs @@ -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)); diff --git a/ChocolArm64/Optimizations.cs b/ChocolArm64/Optimizations.cs index 8fa6f4626c..cbb8131f5c 100644 --- a/ChocolArm64/Optimizations.cs +++ b/ChocolArm64/Optimizations.cs @@ -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; +} \ No newline at end of file diff --git a/ChocolArm64/State/CpuThreadState.cs b/ChocolArm64/State/CpuThreadState.cs index abec60bb2e..caf73deb1f 100644 --- a/ChocolArm64/State/CpuThreadState.cs +++ b/ChocolArm64/State/CpuThreadState.cs @@ -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) { diff --git a/ChocolArm64/Translation/CallType.cs b/ChocolArm64/Translation/CallType.cs new file mode 100644 index 0000000000..937ede768a --- /dev/null +++ b/ChocolArm64/Translation/CallType.cs @@ -0,0 +1,9 @@ +namespace ChocolArm64.Translation +{ + enum CallType + { + Call, + VirtualCall, + VirtualJump + } +} \ No newline at end of file diff --git a/ChocolArm64/Translation/ILBlock.cs b/ChocolArm64/Translation/ILBlock.cs index 136579012b..12773705a1 100644 --- a/ChocolArm64/Translation/ILBlock.cs +++ b/ChocolArm64/Translation/ILBlock.cs @@ -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) diff --git a/ChocolArm64/Translation/ILEmitterCtx.cs b/ChocolArm64/Translation/ILEmitterCtx.cs index ef63e60cd3..8804521c57 100644 --- a/ChocolArm64/Translation/ILEmitterCtx.cs +++ b/ChocolArm64/Translation/ILEmitterCtx.cs @@ -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 _visitedBlocks; private Queue _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 _branchOps = new Dictionary() @@ -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) diff --git a/ChocolArm64/Translation/ILLabel.cs b/ChocolArm64/Translation/ILLabel.cs index f423a4256c..17a31783df 100644 --- a/ChocolArm64/Translation/ILLabel.cs +++ b/ChocolArm64/Translation/ILLabel.cs @@ -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; } } } \ No newline at end of file diff --git a/ChocolArm64/Translation/ILMethodBuilder.cs b/ChocolArm64/Translation/ILMethodBuilder.cs index 892f831be3..98b5052043 100644 --- a/ChocolArm64/Translation/ILMethodBuilder.cs +++ b/ChocolArm64/Translation/ILMethodBuilder.cs @@ -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(); _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; } } } \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCode.cs b/ChocolArm64/Translation/ILOpCode.cs index 4021603c01..486452820d 100644 --- a/ChocolArm64/Translation/ILOpCode.cs +++ b/ChocolArm64/Translation/ILOpCode.cs @@ -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); } } } \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCodeBranch.cs b/ChocolArm64/Translation/ILOpCodeBranch.cs index 22b80b5d52..9d4e40fa9d 100644 --- a/ChocolArm64/Translation/ILOpCodeBranch.cs +++ b/ChocolArm64/Translation/ILOpCodeBranch.cs @@ -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)); } } } \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCodeCall.cs b/ChocolArm64/Translation/ILOpCodeCall.cs index c046aeeb75..dc20417a9a 100644 --- a/ChocolArm64/Translation/ILOpCodeCall.cs +++ b/ChocolArm64/Translation/ILOpCodeCall.cs @@ -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) { diff --git a/ChocolArm64/Translation/ILOpCodeConst.cs b/ChocolArm64/Translation/ILOpCodeConst.cs index 2aaf8676ee..cd3b58ff04 100644 --- a/ChocolArm64/Translation/ILOpCodeConst.cs +++ b/ChocolArm64/Translation/ILOpCodeConst.cs @@ -16,6 +16,8 @@ namespace ChocolArm64.Translation private ImmVal _value; + public long Value => _value.I8; + private enum ConstType { Int32, diff --git a/ChocolArm64/Translation/ILOpCodeLoad.cs b/ChocolArm64/Translation/ILOpCodeLoad.cs index c31e06bbd9..0d11eeaa4b 100644 --- a/ChocolArm64/Translation/ILOpCodeLoad.cs +++ b/ChocolArm64/Translation/ILOpCodeLoad.cs @@ -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; } } diff --git a/ChocolArm64/Translation/ILOpCodeLoadField.cs b/ChocolArm64/Translation/ILOpCodeLoadField.cs index abcd37c348..f0507ac226 100644 --- a/ChocolArm64/Translation/ILOpCodeLoadField.cs +++ b/ChocolArm64/Translation/ILOpCodeLoadField.cs @@ -5,7 +5,7 @@ namespace ChocolArm64.Translation { struct ILOpCodeLoadField : IILEmit { - public FieldInfo Info { get; private set; } + public FieldInfo Info { get; } public ILOpCodeLoadField(FieldInfo info) { diff --git a/ChocolArm64/Translation/ILOpCodeLoadState.cs b/ChocolArm64/Translation/ILOpCodeLoadState.cs index ddab611019..c23dc94329 100644 --- a/ChocolArm64/Translation/ILOpCodeLoadState.cs +++ b/ChocolArm64/Translation/ILOpCodeLoadState.cs @@ -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); diff --git a/ChocolArm64/Translation/ILOpCodeLog.cs b/ChocolArm64/Translation/ILOpCodeLog.cs index ebb042b596..53846f927e 100644 --- a/ChocolArm64/Translation/ILOpCodeLog.cs +++ b/ChocolArm64/Translation/ILOpCodeLog.cs @@ -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); } } } \ No newline at end of file diff --git a/ChocolArm64/Translation/ILOpCodeStore.cs b/ChocolArm64/Translation/ILOpCodeStore.cs index 17a6259c6f..7ac78e9ae4 100644 --- a/ChocolArm64/Translation/ILOpCodeStore.cs +++ b/ChocolArm64/Translation/ILOpCodeStore.cs @@ -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; } } diff --git a/ChocolArm64/Translation/ILOpCodeStoreState.cs b/ChocolArm64/Translation/ILOpCodeStoreState.cs index 458e9eda43..a587dbfe84 100644 --- a/ChocolArm64/Translation/ILOpCodeStoreState.cs +++ b/ChocolArm64/Translation/ILOpCodeStoreState.cs @@ -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); diff --git a/ChocolArm64/Translation/LocalAlloc.cs b/ChocolArm64/Translation/RegisterUsage.cs similarity index 56% rename from ChocolArm64/Translation/LocalAlloc.cs rename to ChocolArm64/Translation/RegisterUsage.cs index 763be6190d..2e6829d512 100644 --- a/ChocolArm64/Translation/LocalAlloc.cs +++ b/ChocolArm64/Translation/RegisterUsage.cs @@ -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 _allInputs; @@ -18,31 +23,30 @@ namespace ChocolArm64.Translation _cmnOutputs = new Dictionary(); } - 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 _intPaths; private Dictionary _vecPaths; - private struct BlockIo + private struct BlockIo : IEquatable { - 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(); _vecPaths = new Dictionary(); - - 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 visited = new HashSet(); @@ -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 values) + private long GetInputsImpl(ILBlock entry, IEnumerable 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 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; + } } } \ No newline at end of file diff --git a/ChocolArm64/Translation/TranslatedSub.cs b/ChocolArm64/Translation/TranslatedSub.cs index 65d7035107..8b599b7a93 100644 --- a/ChocolArm64/Translation/TranslatedSub.cs +++ b/ChocolArm64/Translation/TranslatedSub.cs @@ -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; + } } } \ No newline at end of file diff --git a/ChocolArm64/Translation/Translator.cs b/ChocolArm64/Translation/Translator.cs index 7f7df6e5b2..bda0bca09f 100644 --- a/ChocolArm64/Translation/Translator.cs +++ b/ChocolArm64/Translation/Translator.cs @@ -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) diff --git a/ChocolArm64/Translation/TranslatorQueue.cs b/ChocolArm64/Translation/TranslatorQueue.cs index 89d665bfbd..0f1d847470 100644 --- a/ChocolArm64/Translation/TranslatorQueue.cs +++ b/ChocolArm64/Translation/TranslatorQueue.cs @@ -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[] _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 queue = _translationQueue[(int)item.Tier]; + TranslatorQueueItem item = new TranslatorQueueItem(position, mode, tier, isComplete); - if (queue.Count >= MaxQueueSize) - { - queue.TryPop(out _); - } + ConcurrentStack queue = _translationQueue[(int)tier]; queue.Push(item); diff --git a/ChocolArm64/Translation/TranslatorQueueItem.cs b/ChocolArm64/Translation/TranslatorQueueItem.cs index 0988414a50..dde2706d98 100644 --- a/ChocolArm64/Translation/TranslatorQueueItem.cs +++ b/ChocolArm64/Translation/TranslatorQueueItem.cs @@ -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; } } } \ No newline at end of file diff --git a/ChocolArm64/Translation/IoType.cs b/ChocolArm64/Translation/VarType.cs similarity index 85% rename from ChocolArm64/Translation/IoType.cs rename to ChocolArm64/Translation/VarType.cs index c7710e0c67..d671575e98 100644 --- a/ChocolArm64/Translation/IoType.cs +++ b/ChocolArm64/Translation/VarType.cs @@ -1,6 +1,6 @@ namespace ChocolArm64.Translation { - enum IoType + enum VarType { Arg, Flag, diff --git a/Ryujinx.Common/Utilities/BitUtils.cs b/Ryujinx.Common/Utilities/BitUtils.cs index 135b397d3d..5f70f742a0 100644 --- a/Ryujinx.Common/Utilities/BitUtils.cs +++ b/Ryujinx.Common/Utilities/BitUtils.cs @@ -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); } } -} \ No newline at end of file +} diff --git a/Ryujinx.Graphics/CdmaProcessor.cs b/Ryujinx.Graphics/CdmaProcessor.cs index 4ebf200751..4ff12fbf50 100644 --- a/Ryujinx.Graphics/CdmaProcessor.cs +++ b/Ryujinx.Graphics/CdmaProcessor.cs @@ -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 Commands = new List(); + List commands = new List(); - 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; diff --git a/Ryujinx.Graphics/ChClassId.cs b/Ryujinx.Graphics/ChClassId.cs index 7b74c6fb73..115f0b89c3 100644 --- a/Ryujinx.Graphics/ChClassId.cs +++ b/Ryujinx.Graphics/ChClassId.cs @@ -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, diff --git a/Ryujinx.Graphics/ChCommandEntry.cs b/Ryujinx.Graphics/ChCommandEntry.cs index 9001fab137..b01b77eda5 100644 --- a/Ryujinx.Graphics/ChCommandEntry.cs +++ b/Ryujinx.Graphics/ChCommandEntry.cs @@ -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; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/DepthCompareFunc.cs b/Ryujinx.Graphics/DepthCompareFunc.cs new file mode 100644 index 0000000000..24c8854a4b --- /dev/null +++ b/Ryujinx.Graphics/DepthCompareFunc.cs @@ -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 + } +} diff --git a/Ryujinx.Graphics/DmaPusher.cs b/Ryujinx.Graphics/DmaPusher.cs index 608d8a1d1a..c6825abaf6 100644 --- a/Ryujinx.Graphics/DmaPusher.cs +++ b/Ryujinx.Graphics/DmaPusher.cs @@ -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)); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/EmbeddedResource.cs b/Ryujinx.Graphics/Gal/EmbeddedResource.cs index 45b77da71c..ba6624991e 100644 --- a/Ryujinx.Graphics/Gal/EmbeddedResource.cs +++ b/Ryujinx.Graphics/Gal/EmbeddedResource.cs @@ -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(); } } } diff --git a/Ryujinx.Graphics/Gal/GalColorF.cs b/Ryujinx.Graphics/Gal/GalColorF.cs index 7cfb171dcd..e915870c2c 100644 --- a/Ryujinx.Graphics/Gal/GalColorF.cs +++ b/Ryujinx.Graphics/Gal/GalColorF.cs @@ -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; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalFrontFace.cs b/Ryujinx.Graphics/Gal/GalFrontFace.cs index 17ad11267b..6cc4a80242 100644 --- a/Ryujinx.Graphics/Gal/GalFrontFace.cs +++ b/Ryujinx.Graphics/Gal/GalFrontFace.cs @@ -2,7 +2,7 @@ { public enum GalFrontFace { - CW = 0x900, - CCW = 0x901 + Cw = 0x900, + Ccw = 0x901 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalImage.cs b/Ryujinx.Graphics/Gal/GalImage.cs index 92f43cc9d4..1345704d29 100644 --- a/Ryujinx.Graphics/Gal/GalImage.cs +++ b/Ryujinx.Graphics/Gal/GalImage.cs @@ -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; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalImageFormat.cs b/Ryujinx.Graphics/Gal/GalImageFormat.cs index 70998d1113..8dcde18201 100644 --- a/Ryujinx.Graphics/Gal/GalImageFormat.cs +++ b/Ryujinx.Graphics/Gal/GalImageFormat.cs @@ -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, diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs index 8deb68b48f..c044a55fc3 100644 --- a/Ryujinx.Graphics/Gal/GalPipelineState.cs +++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs @@ -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]; diff --git a/Ryujinx.Graphics/Gal/GalSurfaceFormat.cs b/Ryujinx.Graphics/Gal/GalSurfaceFormat.cs index 08bd622b3b..9cebcc272b 100644 --- a/Ryujinx.Graphics/Gal/GalSurfaceFormat.cs +++ b/Ryujinx.Graphics/Gal/GalSurfaceFormat.cs @@ -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 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs index 51ce577970..ed27180a27 100644 --- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs +++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs @@ -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, diff --git a/Ryujinx.Graphics/Gal/GalTextureSampler.cs b/Ryujinx.Graphics/Gal/GalTextureSampler.cs index b9e5c7658d..2e57a130c6 100644 --- a/Ryujinx.Graphics/Gal/GalTextureSampler.cs +++ b/Ryujinx.Graphics/Gal/GalTextureSampler.cs @@ -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; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureTarget.cs b/Ryujinx.Graphics/Gal/GalTextureTarget.cs new file mode 100644 index 0000000000..bcc0c49a51 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalTextureTarget.cs @@ -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, + } +} diff --git a/Ryujinx.Graphics/Gal/GalTextureType.cs b/Ryujinx.Graphics/Gal/GalTextureType.cs index f7dd16d15a..b02b8b37b4 100644 --- a/Ryujinx.Graphics/Gal/GalTextureType.cs +++ b/Ryujinx.Graphics/Gal/GalTextureType.cs @@ -6,8 +6,8 @@ Unorm = 2, Sint = 3, Uint = 4, - Snorm_Force_Fp16 = 5, - Unorm_Force_Fp16 = 6, + SnormForceFp16 = 5, + UnormForceFp16 = 6, Float = 7 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs index 31b648df38..feca5aea33 100644 --- a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs +++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs @@ -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; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalConstBuffer.cs b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs index 0cdcc2371f..8c4e6d0328 100644 --- a/Ryujinx.Graphics/Gal/IGalConstBuffer.cs +++ b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs @@ -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); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalMemory.cs b/Ryujinx.Graphics/Gal/IGalMemory.cs index e6762b50cc..78eb7154b0 100644 --- a/Ryujinx.Graphics/Gal/IGalMemory.cs +++ b/Ryujinx.Graphics/Gal/IGalMemory.cs @@ -1,7 +1,7 @@ namespace Ryujinx.Graphics.Gal { - public unsafe interface IGalMemory + public interface IGalMemory { - int ReadInt32(long Position); + int ReadInt32(long position); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalPipeline.cs b/Ryujinx.Graphics/Gal/IGalPipeline.cs index cba4e7dcc6..1ecb260288 100644 --- a/Ryujinx.Graphics/Gal/IGalPipeline.cs +++ b/Ryujinx.Graphics/Gal/IGalPipeline.cs @@ -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); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs index 04f7aae50a..33bdeaad8c 100644 --- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs +++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs @@ -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); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs index f941ccd584..c281fe06c3 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs @@ -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); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index 41e95a8789..1acc4d03b1 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Gal { public interface IGalRenderer { - void QueueAction(Action ActionMthd); + void QueueAction(Action actionMthd); void RunActions(); diff --git a/Ryujinx.Graphics/Gal/IGalShader.cs b/Ryujinx.Graphics/Gal/IGalShader.cs index 4b951fa611..6a9abe75bc 100644 --- a/Ryujinx.Graphics/Gal/IGalShader.cs +++ b/Ryujinx.Graphics/Gal/IGalShader.cs @@ -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 GetConstBufferUsage(long Key); - IEnumerable GetTextureUsage(long Key); + IEnumerable GetConstBufferUsage(long key); + IEnumerable GetTextureUsage(long key); - void Bind(long Key); + void Bind(long key); - void Unbind(GalShaderType Type); + void Unbind(GalShaderType type); void BindProgram(); } diff --git a/Ryujinx.Graphics/Gal/IGalTexture.cs b/Ryujinx.Graphics/Gal/IGalTexture.cs index aeecdf1ac5..23ce054ae9 100644 --- a/Ryujinx.Graphics/Gal/IGalTexture.cs +++ b/Ryujinx.Graphics/Gal/IGalTexture.cs @@ -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); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs b/Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs index acd8d72f66..63b626f18a 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs @@ -1,4 +1,4 @@ namespace Ryujinx.Graphics.Gal.OpenGL { - delegate void DeleteValue(T Value); + delegate void DeleteValue(T value); } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs b/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs index 8db0b8a8c9..d7f6f00443 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs @@ -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; } } } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs deleted file mode 100644 index 6e17872ba0..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLCachedResource.cs +++ /dev/null @@ -1,191 +0,0 @@ -using Ryujinx.Common; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - class OGLCachedResource - { - 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 Node { get; private set; } - - public long DataSize { get; private set; } - - public long Timestamp { get; private set; } - - public CacheBucket(T Value, long DataSize, LinkedListNode Node) - { - this.Value = Value; - this.DataSize = DataSize; - this.Node = Node; - - Timestamp = PerformanceCounter.ElapsedMilliseconds; - } - } - - private Dictionary Cache; - - private LinkedList SortedCache; - - private DeleteValue DeleteValueCallback; - - private Queue 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(); - - SortedCache = new LinkedList(); - - DeletePending = new Queue(); - } - - 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 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 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 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; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs deleted file mode 100644 index a12681c7ce..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs +++ /dev/null @@ -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 Cache; - - public OGLConstBuffer() - { - Cache = new OGLCachedResource(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(); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs deleted file mode 100644 index 11daeb593c..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs +++ /dev/null @@ -1,31 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using System; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - static class OGLExtension - { - private static Lazy s_EnhancedLayouts = new Lazy(() => HasExtension("GL_ARB_enhanced_layouts")); - private static Lazy s_TextureMirrorClamp = new Lazy(() => HasExtension("GL_EXT_texture_mirror_clamp")); - private static Lazy s_ViewportArray = new Lazy(() => 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; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLLimit.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLLimit.cs deleted file mode 100644 index 6c385bc4a2..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLLimit.cs +++ /dev/null @@ -1,12 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using System; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - static class OGLLimit - { - private static Lazy s_MaxUboSize = new Lazy(() => GL.GetInteger(GetPName.MaxUniformBlockSize)); - - public static int MaxUboSize => s_MaxUboSize.Value; - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs deleted file mode 100644 index c4015d020c..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ /dev/null @@ -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 VboCache; - private OGLCachedResource 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(GL.DeleteBuffer, MaxVertexBufferCacheSize); - IboCache = new OGLCachedResource(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); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs deleted file mode 100644 index 0d7bb3cd0a..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs deleted file mode 100644 index 14fb901809..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs +++ /dev/null @@ -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 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(); - } - - public void QueueAction(Action ActionMthd) - { - ActionsQueue.Enqueue(ActionMthd); - } - - public void RunActions() - { - int Count = ActionsQueue.Count; - - while (Count-- > 0 && ActionsQueue.TryDequeue(out Action RenderAction)) - { - RenderAction(); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs deleted file mode 100644 index 10a9120df2..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ /dev/null @@ -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 Stages; - - private Dictionary Programs; - - public int CurrentProgramHandle { get; private set; } - - private OGLConstBuffer Buffer; - - private int ExtraUboHandle; - - public OGLShader(OGLConstBuffer Buffer) - { - this.Buffer = Buffer; - - Stages = new ConcurrentDictionary(); - - Programs = new Dictionary(); - } - - 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 GetConstBufferUsage(long Key) - { - if (Stages.TryGetValue(Key, out OGLShaderStage Stage)) - { - return Stage.ConstBufferUsage; - } - - return Enumerable.Empty(); - } - - public IEnumerable GetTextureUsage(long Key) - { - if (Stages.TryGetValue(Key, out OGLShaderStage Stage)) - { - return Stage.TextureUsage; - } - - return Enumerable.Empty(); - } - - 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)); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs deleted file mode 100644 index c87b0d4053..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs +++ /dev/null @@ -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 ConstBufferUsage { get; private set; } - public IEnumerable TextureUsage { get; private set; } - - public OGLShaderStage( - GalShaderType Type, - string Code, - IEnumerable ConstBufferUsage, - IEnumerable 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)); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs deleted file mode 100644 index ef984b1ed3..0000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ /dev/null @@ -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 TextureCache; - - public EventHandler TextureDeleted { get; set; } - - public OGLTexture() - { - TextureCache = new OGLCachedResource(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); - } - } -} diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglCachedResource.cs b/Ryujinx.Graphics/Gal/OpenGL/OglCachedResource.cs new file mode 100644 index 0000000000..91f0a7e169 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OglCachedResource.cs @@ -0,0 +1,191 @@ +using Ryujinx.Common; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + class OglCachedResource + { + 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 Node { get; private set; } + + public long DataSize { get; private set; } + + public long Timestamp { get; private set; } + + public CacheBucket(T value, long dataSize, LinkedListNode node) + { + Value = value; + DataSize = dataSize; + Node = node; + + Timestamp = PerformanceCounter.ElapsedMilliseconds; + } + } + + private Dictionary _cache; + + private LinkedList _sortedCache; + + private DeleteValue _deleteValueCallback; + + private Queue _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(); + + _sortedCache = new LinkedList(); + + _deletePending = new Queue(); + } + + 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 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 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 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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglConstBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OglConstBuffer.cs new file mode 100644 index 0000000000..e076be336d --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OglConstBuffer.cs @@ -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 _cache; + + public OglConstBuffer() + { + _cache = new OglCachedResource(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(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OglEnumConverter.cs similarity index 75% rename from Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs rename to Ryujinx.Graphics/Gal/OpenGL/OglEnumConverter.cs index f2afe7b556..a3f9957f3e 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OglEnumConverter.cs @@ -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!"); } } } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglExtension.cs b/Ryujinx.Graphics/Gal/OpenGL/OglExtension.cs new file mode 100644 index 0000000000..8a1a0510f7 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OglExtension.cs @@ -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 _enhancedLayouts = new Lazy(() => HasExtension("GL_ARB_enhanced_layouts")); + private static Lazy _textureMirrorClamp = new Lazy(() => HasExtension("GL_EXT_texture_mirror_clamp")); + private static Lazy _viewportArray = new Lazy(() => HasExtension("GL_ARB_viewport_array")); + + private static Lazy _nvidiaDriver = new Lazy(() => 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 _enhancedLayoutsRequired = new Lazy(() => HasExtensionRequired(OglExtension.EnhancedLayouts, "GL_ARB_enhanced_layouts")); + private static Lazy _textureMirrorClampRequired = new Lazy(() => HasExtensionRequired(OglExtension.TextureMirrorClamp, "GL_EXT_texture_mirror_clamp")); + private static Lazy _viewportArrayRequired = new Lazy(() => 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; + } + } + } +} diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglLimit.cs b/Ryujinx.Graphics/Gal/OpenGL/OglLimit.cs new file mode 100644 index 0000000000..2a227a374a --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OglLimit.cs @@ -0,0 +1,12 @@ +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + static class OglLimit + { + private static Lazy _sMaxUboSize = new Lazy(() => GL.GetInteger(GetPName.MaxUniformBlockSize)); + + public static int MaxUboSize => _sMaxUboSize.Value; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OglPipeline.cs similarity index 52% rename from Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs rename to Ryujinx.Graphics/Gal/OpenGL/OglPipeline.cs index 6a928603ae..64768e285b 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OglPipeline.cs @@ -1,12 +1,13 @@ using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.Shader; using System; using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL { - class OGLPipeline : IGalPipeline + class OglPipeline : IGalPipeline { - private static Dictionary AttribElements = + private static Dictionary _attribElements = new Dictionary() { { GalVertexAttribSize._32_32_32_32, 4 }, @@ -25,7 +26,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GalVertexAttribSize._11_11_10, 3 } }; - private static Dictionary FloatAttribTypes = + private static Dictionary _floatAttribTypes = new Dictionary() { { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Float }, @@ -38,7 +39,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GalVertexAttribSize._16, VertexAttribPointerType.HalfFloat } }; - private static Dictionary SignedAttribTypes = + private static Dictionary _signedAttribTypes = new Dictionary() { { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int }, @@ -56,7 +57,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int2101010Rev } }; - private static Dictionary UnsignedAttribTypes = + private static Dictionary _unsignedAttribTypes = new Dictionary() { { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.UnsignedInt }, @@ -75,30 +76,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GalVertexAttribSize._11_11_10, VertexAttribPointerType.UnsignedInt10F11F11FRev } }; - private GalPipelineState Old; + private GalPipelineState _old; - private OGLConstBuffer Buffer; - private OGLRenderTarget RenderTarget; - private OGLRasterizer Rasterizer; - private OGLShader Shader; + private OglConstBuffer _buffer; + private OglRenderTarget _renderTarget; + private OglRasterizer _rasterizer; + private OglShader _shader; - private int VaoHandle; + private int _vaoHandle; - public OGLPipeline( - OGLConstBuffer Buffer, - OGLRenderTarget RenderTarget, - OGLRasterizer Rasterizer, - OGLShader Shader) + public OglPipeline( + OglConstBuffer buffer, + OglRenderTarget renderTarget, + OglRasterizer rasterizer, + OglShader shader) { - this.Buffer = Buffer; - this.RenderTarget = RenderTarget; - this.Rasterizer = Rasterizer; - this.Shader = Shader; + _buffer = buffer; + _renderTarget = renderTarget; + _rasterizer = rasterizer; + _shader = shader; //These values match OpenGL's defaults - Old = new GalPipelineState + _old = new GalPipelineState { - FrontFace = GalFrontFace.CCW, + FrontFace = GalFrontFace.Ccw, CullFaceEnabled = false, CullFace = GalCullFace.Back, @@ -133,11 +134,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL PrimitiveRestartIndex = 0 }; - for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) { - Old.Blends[Index] = BlendState.Default; + _old.Blends[index] = BlendState.Default; - Old.ColorMasks[Index] = ColorMaskState.Default; + _old.ColorMasks[index] = ColorMaskState.Default; } } @@ -147,122 +148,122 @@ namespace Ryujinx.Graphics.Gal.OpenGL BindVertexLayout(New); - if (New.FramebufferSrgb != Old.FramebufferSrgb) + if (New.FramebufferSrgb != _old.FramebufferSrgb) { Enable(EnableCap.FramebufferSrgb, New.FramebufferSrgb); - RenderTarget.FramebufferSrgb = New.FramebufferSrgb; + _renderTarget.FramebufferSrgb = New.FramebufferSrgb; } - if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY || New.Instance != Old.Instance) + if (New.FlipX != _old.FlipX || New.FlipY != _old.FlipY || New.Instance != _old.Instance) { - Shader.SetExtraData(New.FlipX, New.FlipY, New.Instance); + _shader.SetExtraData(New.FlipX, New.FlipY, New.Instance); } - if (New.FrontFace != Old.FrontFace) + if (New.FrontFace != _old.FrontFace) { - GL.FrontFace(OGLEnumConverter.GetFrontFace(New.FrontFace)); + GL.FrontFace(OglEnumConverter.GetFrontFace(New.FrontFace)); } - if (New.CullFaceEnabled != Old.CullFaceEnabled) + if (New.CullFaceEnabled != _old.CullFaceEnabled) { Enable(EnableCap.CullFace, New.CullFaceEnabled); } if (New.CullFaceEnabled) { - if (New.CullFace != Old.CullFace) + if (New.CullFace != _old.CullFace) { - GL.CullFace(OGLEnumConverter.GetCullFace(New.CullFace)); + GL.CullFace(OglEnumConverter.GetCullFace(New.CullFace)); } } - if (New.DepthTestEnabled != Old.DepthTestEnabled) + if (New.DepthTestEnabled != _old.DepthTestEnabled) { Enable(EnableCap.DepthTest, New.DepthTestEnabled); } - if (New.DepthWriteEnabled != Old.DepthWriteEnabled) + if (New.DepthWriteEnabled != _old.DepthWriteEnabled) { GL.DepthMask(New.DepthWriteEnabled); } if (New.DepthTestEnabled) { - if (New.DepthFunc != Old.DepthFunc) + if (New.DepthFunc != _old.DepthFunc) { - GL.DepthFunc(OGLEnumConverter.GetDepthFunc(New.DepthFunc)); + GL.DepthFunc(OglEnumConverter.GetDepthFunc(New.DepthFunc)); } } - if (New.DepthRangeNear != Old.DepthRangeNear || - New.DepthRangeFar != Old.DepthRangeFar) + if (New.DepthRangeNear != _old.DepthRangeNear || + New.DepthRangeFar != _old.DepthRangeFar) { GL.DepthRange(New.DepthRangeNear, New.DepthRangeFar); } - if (New.StencilTestEnabled != Old.StencilTestEnabled) + if (New.StencilTestEnabled != _old.StencilTestEnabled) { Enable(EnableCap.StencilTest, New.StencilTestEnabled); } - if (New.StencilTwoSideEnabled != Old.StencilTwoSideEnabled) + if (New.StencilTwoSideEnabled != _old.StencilTwoSideEnabled) { Enable((EnableCap)All.StencilTestTwoSideExt, New.StencilTwoSideEnabled); } if (New.StencilTestEnabled) { - if (New.StencilBackFuncFunc != Old.StencilBackFuncFunc || - New.StencilBackFuncRef != Old.StencilBackFuncRef || - New.StencilBackFuncMask != Old.StencilBackFuncMask) + if (New.StencilBackFuncFunc != _old.StencilBackFuncFunc || + New.StencilBackFuncRef != _old.StencilBackFuncRef || + New.StencilBackFuncMask != _old.StencilBackFuncMask) { GL.StencilFuncSeparate( StencilFace.Back, - OGLEnumConverter.GetStencilFunc(New.StencilBackFuncFunc), + OglEnumConverter.GetStencilFunc(New.StencilBackFuncFunc), New.StencilBackFuncRef, New.StencilBackFuncMask); } - if (New.StencilBackOpFail != Old.StencilBackOpFail || - New.StencilBackOpZFail != Old.StencilBackOpZFail || - New.StencilBackOpZPass != Old.StencilBackOpZPass) + if (New.StencilBackOpFail != _old.StencilBackOpFail || + New.StencilBackOpZFail != _old.StencilBackOpZFail || + New.StencilBackOpZPass != _old.StencilBackOpZPass) { GL.StencilOpSeparate( StencilFace.Back, - OGLEnumConverter.GetStencilOp(New.StencilBackOpFail), - OGLEnumConverter.GetStencilOp(New.StencilBackOpZFail), - OGLEnumConverter.GetStencilOp(New.StencilBackOpZPass)); + OglEnumConverter.GetStencilOp(New.StencilBackOpFail), + OglEnumConverter.GetStencilOp(New.StencilBackOpZFail), + OglEnumConverter.GetStencilOp(New.StencilBackOpZPass)); } - if (New.StencilBackMask != Old.StencilBackMask) + if (New.StencilBackMask != _old.StencilBackMask) { GL.StencilMaskSeparate(StencilFace.Back, New.StencilBackMask); } - if (New.StencilFrontFuncFunc != Old.StencilFrontFuncFunc || - New.StencilFrontFuncRef != Old.StencilFrontFuncRef || - New.StencilFrontFuncMask != Old.StencilFrontFuncMask) + if (New.StencilFrontFuncFunc != _old.StencilFrontFuncFunc || + New.StencilFrontFuncRef != _old.StencilFrontFuncRef || + New.StencilFrontFuncMask != _old.StencilFrontFuncMask) { GL.StencilFuncSeparate( StencilFace.Front, - OGLEnumConverter.GetStencilFunc(New.StencilFrontFuncFunc), + OglEnumConverter.GetStencilFunc(New.StencilFrontFuncFunc), New.StencilFrontFuncRef, New.StencilFrontFuncMask); } - if (New.StencilFrontOpFail != Old.StencilFrontOpFail || - New.StencilFrontOpZFail != Old.StencilFrontOpZFail || - New.StencilFrontOpZPass != Old.StencilFrontOpZPass) + if (New.StencilFrontOpFail != _old.StencilFrontOpFail || + New.StencilFrontOpZFail != _old.StencilFrontOpZFail || + New.StencilFrontOpZPass != _old.StencilFrontOpZPass) { GL.StencilOpSeparate( StencilFace.Front, - OGLEnumConverter.GetStencilOp(New.StencilFrontOpFail), - OGLEnumConverter.GetStencilOp(New.StencilFrontOpZFail), - OGLEnumConverter.GetStencilOp(New.StencilFrontOpZPass)); + OglEnumConverter.GetStencilOp(New.StencilFrontOpFail), + OglEnumConverter.GetStencilOp(New.StencilFrontOpZFail), + OglEnumConverter.GetStencilOp(New.StencilFrontOpZPass)); } - if (New.StencilFrontMask != Old.StencilFrontMask) + if (New.StencilFrontMask != _old.StencilFrontMask) { GL.StencilMaskSeparate(StencilFace.Front, New.StencilFrontMask); } @@ -270,47 +271,52 @@ namespace Ryujinx.Graphics.Gal.OpenGL // Scissor Test - bool forceUpdate; - - for (int Index = 0; Index < New.ScissorTestCount; Index++) + // All scissor test are disabled before drawing final framebuffer to screen so we don't need to handle disabling + // Skip if there are no scissor tests to enable + if (New.ScissorTestCount != 0) { - forceUpdate = false; + int scissorsApplied = 0; + bool applyToAll = false; - if (New.ScissorTestEnabled[Index]) + for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) { - // If there is only 1 scissor test, geometry shaders are disabled so the scissor test applies to all viewports - if (New.ScissorTestCount == 1) + if (New.ScissorTestEnabled[index]) { - GL.Enable(EnableCap.ScissorTest); - } - else - { - GL.Enable(IndexedEnableCap.ScissorTest, Index); - } - forceUpdate = true; - } - else - { - GL.Disable(IndexedEnableCap.ScissorTest, Index); - } + // If viewport arrays are unavailable apply first scissor test to all or + // there is only 1 scissor test and it's the first, the scissor test applies to all viewports + if (!OglExtension.Required.ViewportArray || (index == 0 && New.ScissorTestCount == 1)) + { + GL.Enable(EnableCap.ScissorTest); + applyToAll = true; + } + else + { + GL.Enable(IndexedEnableCap.ScissorTest, index); + } - if (New.ScissorTestEnabled[Index] && - (New.ScissorTestX[Index] != Old.ScissorTestX[Index] || - New.ScissorTestY[Index] != Old.ScissorTestY[Index] || - New.ScissorTestWidth[Index] != Old.ScissorTestWidth[Index] || - New.ScissorTestHeight[Index] != Old.ScissorTestHeight[Index] || - forceUpdate)) // Force update intentionally last to reduce if comparisons - { - // If there is only 1 scissor test geometry shaders are disables so the scissor test applies to all viewports - if (New.ScissorTestCount == 1) - { - GL.Scissor(New.ScissorTestX[Index], New.ScissorTestY[Index], - New.ScissorTestWidth[Index], New.ScissorTestHeight[Index]); - } - else - { - GL.ScissorIndexed(Index, New.ScissorTestX[Index], New.ScissorTestY[Index], - New.ScissorTestWidth[Index], New.ScissorTestHeight[Index]); + if (New.ScissorTestEnabled[index] != _old.ScissorTestEnabled[index] || + New.ScissorTestX[index] != _old.ScissorTestX[index] || + New.ScissorTestY[index] != _old.ScissorTestY[index] || + New.ScissorTestWidth[index] != _old.ScissorTestWidth[index] || + New.ScissorTestHeight[index] != _old.ScissorTestHeight[index]) + { + if (applyToAll) + { + GL.Scissor(New.ScissorTestX[index], New.ScissorTestY[index], + New.ScissorTestWidth[index], New.ScissorTestHeight[index]); + } + else + { + GL.ScissorIndexed(index, New.ScissorTestX[index], New.ScissorTestY[index], + New.ScissorTestWidth[index], New.ScissorTestHeight[index]); + } + } + + // If all scissor tests have been applied, or viewport arrays are unavailable we can skip remaining iterations + if (!OglExtension.Required.ViewportArray || ++scissorsApplied == New.ScissorTestCount) + { + break; + } } } } @@ -318,26 +324,26 @@ namespace Ryujinx.Graphics.Gal.OpenGL if (New.BlendIndependent) { - for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) { - SetBlendState(Index, New.Blends[Index], Old.Blends[Index]); + SetBlendState(index, New.Blends[index], _old.Blends[index]); } } else { - if (New.BlendIndependent != Old.BlendIndependent) + if (New.BlendIndependent != _old.BlendIndependent) { SetAllBlendState(New.Blends[0]); } else { - SetBlendState(New.Blends[0], Old.Blends[0]); + SetBlendState(New.Blends[0], _old.Blends[0]); } } if (New.ColorMaskCommon) { - if (New.ColorMaskCommon != Old.ColorMaskCommon || !New.ColorMasks[0].Equals(Old.ColorMasks[0])) + if (New.ColorMaskCommon != _old.ColorMaskCommon || !New.ColorMasks[0].Equals(_old.ColorMasks[0])) { GL.ColorMask( New.ColorMasks[0].Red, @@ -348,34 +354,42 @@ namespace Ryujinx.Graphics.Gal.OpenGL } else { - for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) { - if (!New.ColorMasks[Index].Equals(Old.ColorMasks[Index])) + if (!New.ColorMasks[index].Equals(_old.ColorMasks[index])) { GL.ColorMask( - Index, - New.ColorMasks[Index].Red, - New.ColorMasks[Index].Green, - New.ColorMasks[Index].Blue, - New.ColorMasks[Index].Alpha); + index, + New.ColorMasks[index].Red, + New.ColorMasks[index].Green, + New.ColorMasks[index].Blue, + New.ColorMasks[index].Alpha); } } } - if (New.PrimitiveRestartEnabled != Old.PrimitiveRestartEnabled) + if (New.PrimitiveRestartEnabled != _old.PrimitiveRestartEnabled) { Enable(EnableCap.PrimitiveRestart, New.PrimitiveRestartEnabled); } if (New.PrimitiveRestartEnabled) { - if (New.PrimitiveRestartIndex != Old.PrimitiveRestartIndex) + if (New.PrimitiveRestartIndex != _old.PrimitiveRestartIndex) { GL.PrimitiveRestartIndex(New.PrimitiveRestartIndex); } } - Old = New; + _old = New; + } + + public void Unbind(GalPipelineState state) + { + if (state.ScissorTestCount > 0) + { + GL.Disable(EnableCap.ScissorTest); + } } private void SetAllBlendState(BlendState New) @@ -387,29 +401,29 @@ namespace Ryujinx.Graphics.Gal.OpenGL if (New.SeparateAlpha) { GL.BlendEquationSeparate( - OGLEnumConverter.GetBlendEquation(New.EquationRgb), - OGLEnumConverter.GetBlendEquation(New.EquationAlpha)); + OglEnumConverter.GetBlendEquation(New.EquationRgb), + OglEnumConverter.GetBlendEquation(New.EquationAlpha)); GL.BlendFuncSeparate( - (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb), - (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha)); + (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb), + (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcAlpha), + (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstAlpha)); } else { - GL.BlendEquation(OGLEnumConverter.GetBlendEquation(New.EquationRgb)); + GL.BlendEquation(OglEnumConverter.GetBlendEquation(New.EquationRgb)); GL.BlendFunc( - OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), - OGLEnumConverter.GetBlendFactor(New.FuncDstRgb)); + OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), + OglEnumConverter.GetBlendFactor(New.FuncDstRgb)); } } } - private void SetBlendState(BlendState New, BlendState Old) + private void SetBlendState(BlendState New, BlendState old) { - if (New.Enabled != Old.Enabled) + if (New.Enabled != old.Enabled) { Enable(EnableCap.Blend, New.Enabled); } @@ -418,91 +432,91 @@ namespace Ryujinx.Graphics.Gal.OpenGL { if (New.SeparateAlpha) { - if (New.EquationRgb != Old.EquationRgb || - New.EquationAlpha != Old.EquationAlpha) + if (New.EquationRgb != old.EquationRgb || + New.EquationAlpha != old.EquationAlpha) { GL.BlendEquationSeparate( - OGLEnumConverter.GetBlendEquation(New.EquationRgb), - OGLEnumConverter.GetBlendEquation(New.EquationAlpha)); + OglEnumConverter.GetBlendEquation(New.EquationRgb), + OglEnumConverter.GetBlendEquation(New.EquationAlpha)); } - if (New.FuncSrcRgb != Old.FuncSrcRgb || - New.FuncDstRgb != Old.FuncDstRgb || - New.FuncSrcAlpha != Old.FuncSrcAlpha || - New.FuncDstAlpha != Old.FuncDstAlpha) + if (New.FuncSrcRgb != old.FuncSrcRgb || + New.FuncDstRgb != old.FuncDstRgb || + New.FuncSrcAlpha != old.FuncSrcAlpha || + New.FuncDstAlpha != old.FuncDstAlpha) { GL.BlendFuncSeparate( - (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb), - (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha)); + (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb), + (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcAlpha), + (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstAlpha)); } } else { - if (New.EquationRgb != Old.EquationRgb) + if (New.EquationRgb != old.EquationRgb) { - GL.BlendEquation(OGLEnumConverter.GetBlendEquation(New.EquationRgb)); + GL.BlendEquation(OglEnumConverter.GetBlendEquation(New.EquationRgb)); } - if (New.FuncSrcRgb != Old.FuncSrcRgb || - New.FuncDstRgb != Old.FuncDstRgb) + if (New.FuncSrcRgb != old.FuncSrcRgb || + New.FuncDstRgb != old.FuncDstRgb) { GL.BlendFunc( - OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), - OGLEnumConverter.GetBlendFactor(New.FuncDstRgb)); + OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), + OglEnumConverter.GetBlendFactor(New.FuncDstRgb)); } } } } - private void SetBlendState(int Index, BlendState New, BlendState Old) + private void SetBlendState(int index, BlendState New, BlendState old) { - if (New.Enabled != Old.Enabled) + if (New.Enabled != old.Enabled) { - Enable(IndexedEnableCap.Blend, Index, New.Enabled); + Enable(IndexedEnableCap.Blend, index, New.Enabled); } if (New.Enabled) { if (New.SeparateAlpha) { - if (New.EquationRgb != Old.EquationRgb || - New.EquationAlpha != Old.EquationAlpha) + if (New.EquationRgb != old.EquationRgb || + New.EquationAlpha != old.EquationAlpha) { GL.BlendEquationSeparate( - Index, - OGLEnumConverter.GetBlendEquation(New.EquationRgb), - OGLEnumConverter.GetBlendEquation(New.EquationAlpha)); + index, + OglEnumConverter.GetBlendEquation(New.EquationRgb), + OglEnumConverter.GetBlendEquation(New.EquationAlpha)); } - if (New.FuncSrcRgb != Old.FuncSrcRgb || - New.FuncDstRgb != Old.FuncDstRgb || - New.FuncSrcAlpha != Old.FuncSrcAlpha || - New.FuncDstAlpha != Old.FuncDstAlpha) + if (New.FuncSrcRgb != old.FuncSrcRgb || + New.FuncDstRgb != old.FuncDstRgb || + New.FuncSrcAlpha != old.FuncSrcAlpha || + New.FuncDstAlpha != old.FuncDstAlpha) { GL.BlendFuncSeparate( - Index, - (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb), - (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha)); + index, + (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb), + (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcAlpha), + (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstAlpha)); } } else { - if (New.EquationRgb != Old.EquationRgb) + if (New.EquationRgb != old.EquationRgb) { - GL.BlendEquation(Index, OGLEnumConverter.GetBlendEquation(New.EquationRgb)); + GL.BlendEquation(index, OglEnumConverter.GetBlendEquation(New.EquationRgb)); } - if (New.FuncSrcRgb != Old.FuncSrcRgb || - New.FuncDstRgb != Old.FuncDstRgb) + if (New.FuncSrcRgb != old.FuncSrcRgb || + New.FuncDstRgb != old.FuncDstRgb) { GL.BlendFunc( - Index, - (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb)); + index, + (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb)); } } } @@ -510,310 +524,310 @@ namespace Ryujinx.Graphics.Gal.OpenGL private void BindConstBuffers(GalPipelineState New) { - int FreeBinding = OGLShader.ReservedCbufCount; + int freeBinding = OglShader.ReservedCbufCount; - void BindIfNotNull(OGLShaderStage Stage) + void BindIfNotNull(OglShaderStage stage) { - if (Stage != null) + if (stage != null) { - foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage) + foreach (CBufferDescriptor desc in stage.ConstBufferUsage) { - long Key = New.ConstBufferKeys[(int)Stage.Type][DeclInfo.Cbuf]; + long key = New.ConstBufferKeys[(int)stage.Type][desc.Slot]; - if (Key != 0 && Buffer.TryGetUbo(Key, out int UboHandle)) + if (key != 0 && _buffer.TryGetUbo(key, out int uboHandle)) { - GL.BindBufferBase(BufferRangeTarget.UniformBuffer, FreeBinding, UboHandle); + GL.BindBufferBase(BufferRangeTarget.UniformBuffer, freeBinding, uboHandle); } - FreeBinding++; + freeBinding++; } } } - BindIfNotNull(Shader.Current.Vertex); - BindIfNotNull(Shader.Current.TessControl); - BindIfNotNull(Shader.Current.TessEvaluation); - BindIfNotNull(Shader.Current.Geometry); - BindIfNotNull(Shader.Current.Fragment); + BindIfNotNull(_shader.Current.Vertex); + BindIfNotNull(_shader.Current.TessControl); + BindIfNotNull(_shader.Current.TessEvaluation); + BindIfNotNull(_shader.Current.Geometry); + BindIfNotNull(_shader.Current.Fragment); } private void BindVertexLayout(GalPipelineState New) { - foreach (GalVertexBinding Binding in New.VertexBindings) + foreach (GalVertexBinding binding in New.VertexBindings) { - if (!Binding.Enabled || !Rasterizer.TryGetVbo(Binding.VboKey, out int VboHandle)) + if (!binding.Enabled || !_rasterizer.TryGetVbo(binding.VboKey, out int vboHandle)) { continue; } - if (VaoHandle == 0) + if (_vaoHandle == 0) { - VaoHandle = GL.GenVertexArray(); + _vaoHandle = GL.GenVertexArray(); //Vertex arrays shouldn't be used anywhere else in OpenGL's backend //if you want to use it, move this line out of the if - GL.BindVertexArray(VaoHandle); + GL.BindVertexArray(_vaoHandle); } - foreach (GalVertexAttrib Attrib in Binding.Attribs) + foreach (GalVertexAttrib attrib in binding.Attribs) { //Skip uninitialized attributes. - if (Attrib.Size == 0) + if (attrib.Size == 0) { continue; } - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); + GL.BindBuffer(BufferTarget.ArrayBuffer, vboHandle); - bool Unsigned = - Attrib.Type == GalVertexAttribType.Unorm || - Attrib.Type == GalVertexAttribType.Uint || - Attrib.Type == GalVertexAttribType.Uscaled; + bool unsigned = + attrib.Type == GalVertexAttribType.Unorm || + attrib.Type == GalVertexAttribType.Uint || + attrib.Type == GalVertexAttribType.Uscaled; - bool Normalize = - Attrib.Type == GalVertexAttribType.Snorm || - Attrib.Type == GalVertexAttribType.Unorm; + bool normalize = + attrib.Type == GalVertexAttribType.Snorm || + attrib.Type == GalVertexAttribType.Unorm; - VertexAttribPointerType Type = 0; + VertexAttribPointerType type = 0; - if (Attrib.Type == GalVertexAttribType.Float) + if (attrib.Type == GalVertexAttribType.Float) { - Type = GetType(FloatAttribTypes, Attrib); + type = GetType(_floatAttribTypes, attrib); } else { - if (Unsigned) + if (unsigned) { - Type = GetType(UnsignedAttribTypes, Attrib); + type = GetType(_unsignedAttribTypes, attrib); } else { - Type = GetType(SignedAttribTypes, Attrib); + type = GetType(_signedAttribTypes, attrib); } } - if (!AttribElements.TryGetValue(Attrib.Size, out int Size)) + if (!_attribElements.TryGetValue(attrib.Size, out int size)) { - throw new InvalidOperationException("Invalid attribute size \"" + Attrib.Size + "\"!"); + throw new InvalidOperationException("Invalid attribute size \"" + attrib.Size + "\"!"); } - int Offset = Attrib.Offset; + int offset = attrib.Offset; - if (Binding.Stride != 0) + if (binding.Stride != 0) { - GL.EnableVertexAttribArray(Attrib.Index); + GL.EnableVertexAttribArray(attrib.Index); - if (Attrib.Type == GalVertexAttribType.Sint || - Attrib.Type == GalVertexAttribType.Uint) + if (attrib.Type == GalVertexAttribType.Sint || + attrib.Type == GalVertexAttribType.Uint) { - IntPtr Pointer = new IntPtr(Offset); + IntPtr pointer = new IntPtr(offset); - VertexAttribIntegerType IType = (VertexAttribIntegerType)Type; + VertexAttribIntegerType iType = (VertexAttribIntegerType)type; - GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer); + GL.VertexAttribIPointer(attrib.Index, size, iType, binding.Stride, pointer); } else { - GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset); + GL.VertexAttribPointer(attrib.Index, size, type, normalize, binding.Stride, offset); } } else { - GL.DisableVertexAttribArray(Attrib.Index); + GL.DisableVertexAttribArray(attrib.Index); - SetConstAttrib(Attrib); + SetConstAttrib(attrib); } - if (Binding.Instanced && Binding.Divisor != 0) + if (binding.Instanced && binding.Divisor != 0) { - GL.VertexAttribDivisor(Attrib.Index, 1); + GL.VertexAttribDivisor(attrib.Index, 1); } else { - GL.VertexAttribDivisor(Attrib.Index, 0); + GL.VertexAttribDivisor(attrib.Index, 0); } } } } - private static VertexAttribPointerType GetType(Dictionary Dict, GalVertexAttrib Attrib) + private static VertexAttribPointerType GetType(Dictionary dict, GalVertexAttrib attrib) { - if (!Dict.TryGetValue(Attrib.Size, out VertexAttribPointerType Type)) + if (!dict.TryGetValue(attrib.Size, out VertexAttribPointerType type)) { - ThrowUnsupportedAttrib(Attrib); + ThrowUnsupportedAttrib(attrib); } - return Type; + return type; } - private unsafe static void SetConstAttrib(GalVertexAttrib Attrib) + private static unsafe void SetConstAttrib(GalVertexAttrib attrib) { - if (Attrib.Size == GalVertexAttribSize._10_10_10_2 || - Attrib.Size == GalVertexAttribSize._11_11_10) + if (attrib.Size == GalVertexAttribSize._10_10_10_2 || + attrib.Size == GalVertexAttribSize._11_11_10) { - ThrowUnsupportedAttrib(Attrib); + ThrowUnsupportedAttrib(attrib); } - fixed (byte* Ptr = Attrib.Data) + fixed (byte* ptr = attrib.Data) { - if (Attrib.Type == GalVertexAttribType.Unorm) + if (attrib.Type == GalVertexAttribType.Unorm) { - switch (Attrib.Size) + switch (attrib.Size) { case GalVertexAttribSize._8: case GalVertexAttribSize._8_8: case GalVertexAttribSize._8_8_8: case GalVertexAttribSize._8_8_8_8: - GL.VertexAttrib4N((uint)Attrib.Index, Ptr); + GL.VertexAttrib4N((uint)attrib.Index, ptr); break; case GalVertexAttribSize._16: case GalVertexAttribSize._16_16: case GalVertexAttribSize._16_16_16: case GalVertexAttribSize._16_16_16_16: - GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Ptr); + GL.VertexAttrib4N((uint)attrib.Index, (ushort*)ptr); break; case GalVertexAttribSize._32: case GalVertexAttribSize._32_32: case GalVertexAttribSize._32_32_32: case GalVertexAttribSize._32_32_32_32: - GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Ptr); + GL.VertexAttrib4N((uint)attrib.Index, (uint*)ptr); break; } } - else if (Attrib.Type == GalVertexAttribType.Snorm) + else if (attrib.Type == GalVertexAttribType.Snorm) { - switch (Attrib.Size) + switch (attrib.Size) { case GalVertexAttribSize._8: case GalVertexAttribSize._8_8: case GalVertexAttribSize._8_8_8: case GalVertexAttribSize._8_8_8_8: - GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Ptr); + GL.VertexAttrib4N((uint)attrib.Index, (sbyte*)ptr); break; case GalVertexAttribSize._16: case GalVertexAttribSize._16_16: case GalVertexAttribSize._16_16_16: case GalVertexAttribSize._16_16_16_16: - GL.VertexAttrib4N((uint)Attrib.Index, (short*)Ptr); + GL.VertexAttrib4N((uint)attrib.Index, (short*)ptr); break; case GalVertexAttribSize._32: case GalVertexAttribSize._32_32: case GalVertexAttribSize._32_32_32: case GalVertexAttribSize._32_32_32_32: - GL.VertexAttrib4N((uint)Attrib.Index, (int*)Ptr); + GL.VertexAttrib4N((uint)attrib.Index, (int*)ptr); break; } } - else if (Attrib.Type == GalVertexAttribType.Uint) + else if (attrib.Type == GalVertexAttribType.Uint) { - switch (Attrib.Size) + switch (attrib.Size) { case GalVertexAttribSize._8: case GalVertexAttribSize._8_8: case GalVertexAttribSize._8_8_8: case GalVertexAttribSize._8_8_8_8: - GL.VertexAttribI4((uint)Attrib.Index, Ptr); + GL.VertexAttribI4((uint)attrib.Index, ptr); break; case GalVertexAttribSize._16: case GalVertexAttribSize._16_16: case GalVertexAttribSize._16_16_16: case GalVertexAttribSize._16_16_16_16: - GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Ptr); + GL.VertexAttribI4((uint)attrib.Index, (ushort*)ptr); break; case GalVertexAttribSize._32: case GalVertexAttribSize._32_32: case GalVertexAttribSize._32_32_32: case GalVertexAttribSize._32_32_32_32: - GL.VertexAttribI4((uint)Attrib.Index, (uint*)Ptr); + GL.VertexAttribI4((uint)attrib.Index, (uint*)ptr); break; } } - else if (Attrib.Type == GalVertexAttribType.Sint) + else if (attrib.Type == GalVertexAttribType.Sint) { - switch (Attrib.Size) + switch (attrib.Size) { case GalVertexAttribSize._8: case GalVertexAttribSize._8_8: case GalVertexAttribSize._8_8_8: case GalVertexAttribSize._8_8_8_8: - GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Ptr); + GL.VertexAttribI4((uint)attrib.Index, (sbyte*)ptr); break; case GalVertexAttribSize._16: case GalVertexAttribSize._16_16: case GalVertexAttribSize._16_16_16: case GalVertexAttribSize._16_16_16_16: - GL.VertexAttribI4((uint)Attrib.Index, (short*)Ptr); + GL.VertexAttribI4((uint)attrib.Index, (short*)ptr); break; case GalVertexAttribSize._32: case GalVertexAttribSize._32_32: case GalVertexAttribSize._32_32_32: case GalVertexAttribSize._32_32_32_32: - GL.VertexAttribI4((uint)Attrib.Index, (int*)Ptr); + GL.VertexAttribI4((uint)attrib.Index, (int*)ptr); break; } } - else if (Attrib.Type == GalVertexAttribType.Float) + else if (attrib.Type == GalVertexAttribType.Float) { - switch (Attrib.Size) + switch (attrib.Size) { case GalVertexAttribSize._32: case GalVertexAttribSize._32_32: case GalVertexAttribSize._32_32_32: case GalVertexAttribSize._32_32_32_32: - GL.VertexAttrib4(Attrib.Index, (float*)Ptr); + GL.VertexAttrib4(attrib.Index, (float*)ptr); break; - default: ThrowUnsupportedAttrib(Attrib); break; + default: ThrowUnsupportedAttrib(attrib); break; } } } } - private static void ThrowUnsupportedAttrib(GalVertexAttrib Attrib) + private static void ThrowUnsupportedAttrib(GalVertexAttrib attrib) { - throw new NotImplementedException("Unsupported size \"" + Attrib.Size + "\" on type \"" + Attrib.Type + "\"!"); + throw new NotImplementedException("Unsupported size \"" + attrib.Size + "\" on type \"" + attrib.Type + "\"!"); } - private void Enable(EnableCap Cap, bool Enabled) + private void Enable(EnableCap cap, bool enabled) { - if (Enabled) + if (enabled) { - GL.Enable(Cap); + GL.Enable(cap); } else { - GL.Disable(Cap); + GL.Disable(cap); } } - private void Enable(IndexedEnableCap Cap, int Index, bool Enabled) + private void Enable(IndexedEnableCap cap, int index, bool enabled) { - if (Enabled) + if (enabled) { - GL.Enable(Cap, Index); + GL.Enable(cap, index); } else { - GL.Disable(Cap, Index); + GL.Disable(cap, index); } } public void ResetDepthMask() { - Old.DepthWriteEnabled = true; + _old.DepthWriteEnabled = true; } - public void ResetColorMask(int Index) + public void ResetColorMask(int index) { - Old.ColorMasks[Index] = ColorMaskState.Default; + _old.ColorMasks[index] = ColorMaskState.Default; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OglRasterizer.cs new file mode 100644 index 0000000000..c19911c57a --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OglRasterizer.cs @@ -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 _vboCache; + private OglCachedResource _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(GL.DeleteBuffer, MaxVertexBufferCacheSize); + _iboCache = new OglCachedResource(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); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OglRenderTarget.cs new file mode 100644 index 0000000000..d36bac1bcf --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OglRenderTarget.cs @@ -0,0 +1,549 @@ +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) + { + X = x; + Y = y; + Width = width; + 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]; + + _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) + { + _flipX = flipX; + _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); + + GL.BlitFramebuffer( + srcX0, + srcY0, + srcX1, + srcY1, + dstX0, + dstY0, + dstX1, + dstY1, + ClearBufferMask.ColorBufferBit, + BlitFramebufferFilter.Linear); + + if (FramebufferSrgb) + { + GL.Enable(EnableCap.FramebufferSrgb); + } + } + + public void Copy( + 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) + { + 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); + + if (ImageUtils.IsArray(srcImage.TextureTarget) && srcLayer > 0) + { + GL.FramebufferTextureLayer(FramebufferTarget.ReadFramebuffer, attachment, srcTex.Handle, 0, srcLayer); + } + else + { + GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, attachment, srcTex.Handle, 0); + } + + if (ImageUtils.IsArray(dstImage.TextureTarget) && dstLayer > 0) + { + GL.FramebufferTextureLayer(FramebufferTarget.DrawFramebuffer, attachment, dstTex.Handle, 0, dstLayer); + } + else + { + 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 && + newImage.Depth == oldImage.Depth && + newImage.LayerCount == oldImage.LayerCount && + newImage.TextureTarget == oldImage.TextureTarget) + { + 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); + + TextureTarget target = ImageUtils.GetTextureTarget(newImage.TextureTarget); + + GL.BindTexture(target, cachedImage.Handle); + + GL.GetTexImage(target, 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); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OglRenderer.cs new file mode 100644 index 0000000000..1ff8c7ad5d --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OglRenderer.cs @@ -0,0 +1,58 @@ +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 _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(); + } + + public void QueueAction(Action actionMthd) + { + _actionsQueue.Enqueue(actionMthd); + } + + public void RunActions() + { + int count = _actionsQueue.Count; + + while (count-- > 0 && _actionsQueue.TryDequeue(out Action renderAction)) + { + renderAction(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OglShader.cs new file mode 100644 index 0000000000..8f4072c53e --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OglShader.cs @@ -0,0 +1,291 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; +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 _stages; + + private Dictionary _programs; + + public int CurrentProgramHandle { get; private set; } + + private OglConstBuffer _buffer; + + private int _extraUboHandle; + + public OglShader(OglConstBuffer buffer) + { + _buffer = buffer; + + _stages = new ConcurrentDictionary(); + + _programs = new Dictionary(); + } + + 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) + { + ShaderConfig config = new ShaderConfig(type, OglLimit.MaxUboSize); + + ShaderProgram program; + + if (isDualVp) + { + ShaderDumper.Dump(memory, position, type, "a"); + ShaderDumper.Dump(memory, positionB, type, "b"); + + program = Translator.Translate(memory, (ulong)position, (ulong)positionB, config); + } + else + { + ShaderDumper.Dump(memory, position, type); + + program = Translator.Translate(memory, (ulong)position, config); + } + + string code = program.Code; + + if (ShaderDumper.IsDumpEnabled()) + { + int shaderDumpIndex = ShaderDumper.DumpIndex; + + code = "//Shader " + shaderDumpIndex + Environment.NewLine + code; + } + + return new OglShaderStage(type, code, program.Info.CBuffers, program.Info.Textures); + } + + public IEnumerable GetConstBufferUsage(long key) + { + if (_stages.TryGetValue(key, out OglShaderStage stage)) + { + return stage.ConstBufferUsage; + } + + return Enumerable.Empty(); + } + + public IEnumerable GetTextureUsage(long key) + { + if (_stages.TryGetValue(key, out OglShaderStage stage)) + { + return stage.TextureUsage; + } + + return Enumerable.Empty(); + } + + 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) + { + 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, "Extra"); + + GL.UniformBlockBinding(programHandle, extraBlockindex, 0); + + int freeBinding = ReservedCbufCount; + + void BindUniformBlocksIfNotNull(OglShaderStage stage) + { + if (stage != null) + { + foreach (CBufferDescriptor desc in stage.ConstBufferUsage) + { + int blockIndex = GL.GetUniformBlockIndex(programHandle, desc.Name); + + if (blockIndex < 0) + { + //This may be fine, the compiler may optimize away unused uniform buffers, + //and in this case the above call would return -1 as the buffer has been + //optimized away. + continue; + } + + 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 (TextureDescriptor desc in stage.TextureUsage) + { + int location = GL.GetUniformLocation(programHandle, desc.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)); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglShaderProgram.cs b/Ryujinx.Graphics/Gal/OpenGL/OglShaderProgram.cs new file mode 100644 index 0000000000..86126bca4d --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OglShaderProgram.cs @@ -0,0 +1,87 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.Shader; +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 ConstBufferUsage { get; private set; } + public IEnumerable TextureUsage { get; private set; } + + public OglShaderStage( + GalShaderType type, + string code, + IEnumerable constBufferUsage, + IEnumerable textureUsage) + { + Type = type; + Code = code; + ConstBufferUsage = constBufferUsage; + 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)); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OglStreamBuffer.cs similarity index 51% rename from Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs rename to Ryujinx.Graphics/Gal/OpenGL/OglStreamBuffer.cs index 411d33aab7..58b3ace5bb 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OglStreamBuffer.cs @@ -3,7 +3,7 @@ using System; namespace Ryujinx.Graphics.Gal.OpenGL { - class OGLStreamBuffer : IDisposable + class OglStreamBuffer : IDisposable { public int Handle { get; protected set; } @@ -11,30 +11,30 @@ namespace Ryujinx.Graphics.Gal.OpenGL protected BufferTarget Target { get; private set; } - public OGLStreamBuffer(BufferTarget Target, long Size) + public OglStreamBuffer(BufferTarget target, long size) { - this.Target = Target; - this.Size = Size; + Target = target; + Size = size; Handle = GL.GenBuffer(); - GL.BindBuffer(Target, Handle); + GL.BindBuffer(target, Handle); - GL.BufferData(Target, (IntPtr)Size, IntPtr.Zero, BufferUsageHint.StreamDraw); + GL.BufferData(target, (IntPtr)size, IntPtr.Zero, BufferUsageHint.StreamDraw); } - public void SetData(long Size, IntPtr HostAddress) + public void SetData(long size, IntPtr hostAddress) { GL.BindBuffer(Target, Handle); - GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Size, HostAddress); + GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)size, hostAddress); } - public void SetData(byte[] Data) + public void SetData(byte[] data) { GL.BindBuffer(Target, Handle); - GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Data.Length, Data); + GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)data.Length, data); } public void Dispose() @@ -42,9 +42,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL Dispose(true); } - protected virtual void Dispose(bool Disposing) + protected virtual void Dispose(bool disposing) { - if (Disposing && Handle != 0) + if (disposing && Handle != 0) { GL.DeleteBuffer(Handle); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OglTexture.cs new file mode 100644 index 0000000000..b6bf36c58e --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OglTexture.cs @@ -0,0 +1,408 @@ +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 _textureCache; + + public EventHandler TextureDeleted { get; set; } + + public OglTexture() + { + _textureCache = new OglCachedResource(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(); + + TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); + + GL.BindTexture(target, 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); + + switch (target) + { + case TextureTarget.Texture1D: + GL.TexImage1D( + target, + level, + internalFmt, + image.Width, + border, + format, + type, + IntPtr.Zero); + break; + + case TextureTarget.Texture2D: + GL.TexImage2D( + target, + level, + internalFmt, + image.Width, + image.Height, + border, + format, + type, + IntPtr.Zero); + break; + case TextureTarget.Texture3D: + GL.TexImage3D( + target, + level, + internalFmt, + image.Width, + image.Height, + image.Depth, + border, + format, + type, + IntPtr.Zero); + break; + // Cube map arrays are just 2D texture arrays with 6 entries + // per cube map so we can handle them in the same way + case TextureTarget.TextureCubeMapArray: + case TextureTarget.Texture2DArray: + GL.TexImage3D( + target, + level, + internalFmt, + image.Width, + image.Height, + image.LayerCount, + border, + format, + type, + IntPtr.Zero); + break; + default: + throw new NotImplementedException($"Unsupported texture target type: {target}"); + } + } + + public void Create(long key, byte[] data, GalImage image) + { + int handle = GL.GenTexture(); + + TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); + + GL.BindTexture(target, 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); + + switch (target) + { + case TextureTarget.Texture1D: + GL.CompressedTexImage1D( + target, + level, + internalFmt, + image.Width, + border, + data.Length, + data); + break; + case TextureTarget.Texture2D: + GL.CompressedTexImage2D( + target, + level, + internalFmt, + image.Width, + image.Height, + border, + data.Length, + data); + break; + case TextureTarget.Texture3D: + GL.CompressedTexImage3D( + target, + level, + internalFmt, + image.Width, + image.Height, + image.Depth, + border, + data.Length, + data); + break; + // Cube map arrays are just 2D texture arrays with 6 entries + // per cube map so we can handle them in the same way + case TextureTarget.TextureCubeMapArray: + case TextureTarget.Texture2DArray: + GL.CompressedTexImage3D( + target, + level, + internalFmt, + image.Width, + image.Height, + image.LayerCount, + border, + data.Length, + data); + break; + case TextureTarget.TextureCubeMap: + Span array = new Span(data); + + int faceSize = ImageUtils.GetSize(image) / 6; + + for (int Face = 0; Face < 6; Face++) + { + GL.CompressedTexImage2D( + TextureTarget.TextureCubeMapPositiveX + Face, + level, + internalFmt, + image.Width, + image.Height, + border, + faceSize, + array.Slice(Face * faceSize, faceSize).ToArray()); + } + break; + default: + throw new NotImplementedException($"Unsupported texture target type: {target}"); + } + } + 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); + int textureBlockDepth = ImageUtils.GetBlockDepth(image.Format); + + data = AstcDecoder.DecodeToRgba8888( + data, + textureBlockWidth, + textureBlockHeight, + textureBlockDepth, + image.Width, + image.Height, + image.Depth); + + image.Format = GalImageFormat.Rgba8 | (image.Format & GalImageFormat.TypeMask); + } + + (PixelInternalFormat internalFmt, + PixelFormat format, + PixelType type) = OglEnumConverter.GetImageFormat(image.Format); + + + switch (target) + { + case TextureTarget.Texture1D: + GL.TexImage1D( + target, + level, + internalFmt, + image.Width, + border, + format, + type, + data); + break; + case TextureTarget.Texture2D: + GL.TexImage2D( + target, + level, + internalFmt, + image.Width, + image.Height, + border, + format, + type, + data); + break; + case TextureTarget.Texture3D: + GL.TexImage3D( + target, + level, + internalFmt, + image.Width, + image.Height, + image.Depth, + border, + format, + type, + data); + break; + // Cube map arrays are just 2D texture arrays with 6 entries + // per cube map so we can handle them in the same way + case TextureTarget.TextureCubeMapArray: + case TextureTarget.Texture2DArray: + GL.TexImage3D( + target, + level, + internalFmt, + image.Width, + image.Height, + image.LayerCount, + border, + format, + type, + data); + break; + case TextureTarget.TextureCubeMap: + Span array = new Span(data); + + int faceSize = ImageUtils.GetSize(image) / 6; + + for (int face = 0; face < 6; face++) + { + GL.TexImage2D( + TextureTarget.TextureCubeMapPositiveX + face, + level, + internalFmt, + image.Width, + image.Height, + border, + format, + type, + array.Slice(face * faceSize, faceSize).ToArray()); + } + break; + default: + throw new NotImplementedException($"Unsupported texture target type: {target}"); + } + } + } + + 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); + + TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); + + GL.BindTexture(target, 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(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); + } + } + + public void SetSampler(GalImage image, GalTextureSampler sampler) + { + int wrapS = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressU); + int wrapT = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressV); + int wrapR = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressP); + + int minFilter = (int)OglEnumConverter.GetTextureMinFilter(sampler.MinFilter, sampler.MipFilter); + int magFilter = (int)OglEnumConverter.GetTextureMagFilter(sampler.MagFilter); + + TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); + + GL.TexParameter(target, TextureParameterName.TextureWrapS, wrapS); + GL.TexParameter(target, TextureParameterName.TextureWrapT, wrapT); + GL.TexParameter(target, TextureParameterName.TextureWrapR, wrapR); + + GL.TexParameter(target, TextureParameterName.TextureMinFilter, minFilter); + GL.TexParameter(target, TextureParameterName.TextureMagFilter, magFilter); + + float[] color = new float[] + { + sampler.BorderColor.Red, + sampler.BorderColor.Green, + sampler.BorderColor.Blue, + sampler.BorderColor.Alpha + }; + + GL.TexParameter(target, TextureParameterName.TextureBorderColor, color); + + if (sampler.DepthCompare) + { + GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.CompareRToTexture); + GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)OglEnumConverter.GetDepthCompareFunc(sampler.DepthCompareFunc)); + } + else + { + GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.None); + GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)All.Never); + } + } + } +} diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs deleted file mode 100644 index 43923da742..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ /dev/null @@ -1,399 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gal.Shader -{ - class GlslDecl - { - public const int LayerAttr = 0x064; - public const int PointSizeAttr = 0x06c; - public const int PointCoordAttrX = 0x2e0; - public const int PointCoordAttrY = 0x2e4; - public const int TessCoordAttrX = 0x2f0; - public const int TessCoordAttrY = 0x2f4; - public const int TessCoordAttrZ = 0x2f8; - public const int InstanceIdAttr = 0x2f8; - public const int VertexIdAttr = 0x2fc; - public const int FaceAttr = 0x3fc; - - public const int GlPositionVec4Index = 7; - - public const int PositionOutAttrLocation = 15; - - private const int AttrStartIndex = 8; - private const int TexStartIndex = 8; - - public const string PositionOutAttrName = "position"; - - private const string TextureName = "tex"; - private const string UniformName = "c"; - - private const string AttrName = "attr"; - private const string InAttrName = "in_" + AttrName; - private const string OutAttrName = "out_" + AttrName; - - private const string GprName = "gpr"; - private const string PredName = "pred"; - - public const string FragmentOutputName = "FragColor"; - - public const string ExtraUniformBlockName = "Extra"; - public const string FlipUniformName = "flip"; - public const string InstanceUniformName = "instance"; - - public const string BasicBlockName = "bb"; - public const string BasicBlockAName = BasicBlockName + "_a"; - public const string BasicBlockBName = BasicBlockName + "_b"; - - public const int SsyStackSize = 16; - public const string SsyStackName = "ssy_stack"; - public const string SsyCursorName = "ssy_cursor"; - - private string[] StagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" }; - - private string StagePrefix; - - private Dictionary m_CbTextures; - - private Dictionary m_Textures; - private Dictionary m_Uniforms; - - private Dictionary m_Attributes; - private Dictionary m_InAttributes; - private Dictionary m_OutAttributes; - - private Dictionary m_Gprs; - private Dictionary m_GprsHalf; - private Dictionary m_Preds; - - public IReadOnlyDictionary CbTextures => m_CbTextures; - - public IReadOnlyDictionary Textures => m_Textures; - public IReadOnlyDictionary Uniforms => m_Uniforms; - - public IReadOnlyDictionary Attributes => m_Attributes; - public IReadOnlyDictionary InAttributes => m_InAttributes; - public IReadOnlyDictionary OutAttributes => m_OutAttributes; - - public IReadOnlyDictionary Gprs => m_Gprs; - public IReadOnlyDictionary GprsHalf => m_GprsHalf; - public IReadOnlyDictionary Preds => m_Preds; - - public GalShaderType ShaderType { get; private set; } - - private GlslDecl(GalShaderType ShaderType) - { - this.ShaderType = ShaderType; - - m_CbTextures = new Dictionary(); - - m_Textures = new Dictionary(); - m_Uniforms = new Dictionary(); - - m_Attributes = new Dictionary(); - m_InAttributes = new Dictionary(); - m_OutAttributes = new Dictionary(); - - m_Gprs = new Dictionary(); - m_GprsHalf = new Dictionary(); - m_Preds = new Dictionary(); - } - - public GlslDecl(ShaderIrBlock[] Blocks, GalShaderType ShaderType, ShaderHeader Header) : this(ShaderType) - { - StagePrefix = StagePrefixes[(int)ShaderType] + "_"; - - if (ShaderType == GalShaderType.Fragment) - { - int Index = 0; - - for (int Attachment = 0; Attachment < 8; Attachment++) - { - for (int Component = 0; Component < 4; Component++) - { - if (Header.OmapTargets[Attachment].ComponentEnabled(Component)) - { - m_Gprs.TryAdd(Index, new ShaderDeclInfo(GetGprName(Index), Index)); - - Index++; - } - } - } - - if (Header.OmapDepth) - { - Index = Header.DepthRegister; - - m_Gprs.TryAdd(Index, new ShaderDeclInfo(GetGprName(Index), Index)); - } - } - - foreach (ShaderIrBlock Block in Blocks) - { - ShaderIrNode[] Nodes = Block.GetNodes(); - - foreach (ShaderIrNode Node in Nodes) - { - Traverse(Nodes, null, Node); - } - } - } - - public static GlslDecl Merge(GlslDecl VpA, GlslDecl VpB) - { - GlslDecl Combined = new GlslDecl(GalShaderType.Vertex); - - Merge(Combined.m_Textures, VpA.m_Textures, VpB.m_Textures); - Merge(Combined.m_Uniforms, VpA.m_Uniforms, VpB.m_Uniforms); - - Merge(Combined.m_Attributes, VpA.m_Attributes, VpB.m_Attributes); - Merge(Combined.m_OutAttributes, VpA.m_OutAttributes, VpB.m_OutAttributes); - - Merge(Combined.m_Gprs, VpA.m_Gprs, VpB.m_Gprs); - Merge(Combined.m_GprsHalf, VpA.m_GprsHalf, VpB.m_GprsHalf); - Merge(Combined.m_Preds, VpA.m_Preds, VpB.m_Preds); - - //Merge input attributes. - foreach (KeyValuePair KV in VpA.m_InAttributes) - { - Combined.m_InAttributes.TryAdd(KV.Key, KV.Value); - } - - foreach (KeyValuePair KV in VpB.m_InAttributes) - { - //If Vertex Program A already writes to this attribute, - //then we don't need to add it as an input attribute since - //Vertex Program A will already have written to it anyway, - //and there's no guarantee that there is an input attribute - //for this slot. - if (!VpA.m_OutAttributes.ContainsKey(KV.Key)) - { - Combined.m_InAttributes.TryAdd(KV.Key, KV.Value); - } - } - - return Combined; - } - - public static string GetGprName(int Index) - { - return GprName + Index; - } - - private static void Merge( - Dictionary C, - Dictionary A, - Dictionary B) - { - foreach (KeyValuePair KV in A) - { - C.TryAdd(KV.Key, KV.Value); - } - - foreach (KeyValuePair KV in B) - { - C.TryAdd(KV.Key, KV.Value); - } - } - - private void Traverse(ShaderIrNode[] Nodes, ShaderIrNode Parent, ShaderIrNode Node) - { - switch (Node) - { - case ShaderIrAsg Asg: - { - Traverse(Nodes, Asg, Asg.Dst); - Traverse(Nodes, Asg, Asg.Src); - - break; - } - - case ShaderIrCond Cond: - { - Traverse(Nodes, Cond, Cond.Pred); - Traverse(Nodes, Cond, Cond.Child); - - break; - } - - case ShaderIrOp Op: - { - Traverse(Nodes, Op, Op.OperandA); - Traverse(Nodes, Op, Op.OperandB); - Traverse(Nodes, Op, Op.OperandC); - - if (Op.Inst == ShaderIrInst.Texq || - Op.Inst == ShaderIrInst.Texs || - Op.Inst == ShaderIrInst.Txlf) - { - int Handle = ((ShaderIrOperImm)Op.OperandC).Value; - - int Index = Handle - TexStartIndex; - - string Name = StagePrefix + TextureName + Index; - - m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle)); - } - else if (Op.Inst == ShaderIrInst.Texb) - { - ShaderIrNode HandleSrc = null; - - int Index = Array.IndexOf(Nodes, Parent) - 1; - - for (; Index >= 0; Index--) - { - ShaderIrNode Curr = Nodes[Index]; - - if (Curr is ShaderIrAsg Asg && Asg.Dst is ShaderIrOperGpr Gpr) - { - if (Gpr.Index == ((ShaderIrOperGpr)Op.OperandC).Index) - { - HandleSrc = Asg.Src; - - break; - } - } - } - - if (HandleSrc != null && HandleSrc is ShaderIrOperCbuf Cbuf) - { - string Name = StagePrefix + TextureName + "_cb" + Cbuf.Index + "_" + Cbuf.Pos; - - m_CbTextures.Add(Op, new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index)); - } - else - { - throw new NotImplementedException("Shader TEX.B instruction is not fully supported!"); - } - } - break; - } - - case ShaderIrOperCbuf Cbuf: - { - if (!m_Uniforms.ContainsKey(Cbuf.Index)) - { - string Name = StagePrefix + UniformName + Cbuf.Index; - - ShaderDeclInfo DeclInfo = new ShaderDeclInfo(Name, Cbuf.Pos, true, Cbuf.Index); - - m_Uniforms.Add(Cbuf.Index, DeclInfo); - } - break; - } - - case ShaderIrOperAbuf Abuf: - { - //This is a built-in variable. - if (Abuf.Offs == LayerAttr || - Abuf.Offs == PointSizeAttr || - Abuf.Offs == PointCoordAttrX || - Abuf.Offs == PointCoordAttrY || - Abuf.Offs == VertexIdAttr || - Abuf.Offs == InstanceIdAttr || - Abuf.Offs == FaceAttr) - { - break; - } - - int Index = Abuf.Offs >> 4; - int Elem = (Abuf.Offs >> 2) & 3; - - int GlslIndex = Index - AttrStartIndex; - - if (GlslIndex < 0) - { - return; - } - - ShaderDeclInfo DeclInfo; - - if (Parent is ShaderIrAsg Asg && Asg.Dst == Node) - { - if (!m_OutAttributes.TryGetValue(Index, out DeclInfo)) - { - DeclInfo = new ShaderDeclInfo(OutAttrName + GlslIndex, GlslIndex); - - m_OutAttributes.Add(Index, DeclInfo); - } - } - else - { - if (!m_InAttributes.TryGetValue(Index, out DeclInfo)) - { - DeclInfo = new ShaderDeclInfo(InAttrName + GlslIndex, GlslIndex); - - m_InAttributes.Add(Index, DeclInfo); - } - } - - DeclInfo.Enlarge(Elem + 1); - - if (!m_Attributes.ContainsKey(Index)) - { - DeclInfo = new ShaderDeclInfo(AttrName + GlslIndex, GlslIndex, false, 0, 4); - - m_Attributes.Add(Index, DeclInfo); - } - - Traverse(Nodes, Abuf, Abuf.Vertex); - - break; - } - - case ShaderIrOperGpr Gpr: - { - if (!Gpr.IsConst) - { - string Name = GetGprName(Gpr.Index); - - if (Gpr.RegisterSize == ShaderRegisterSize.Single) - { - m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index)); - } - else if (Gpr.RegisterSize == ShaderRegisterSize.Half) - { - Name += "_h" + Gpr.HalfPart; - - m_GprsHalf.TryAdd((Gpr.Index << 1) | Gpr.HalfPart, new ShaderDeclInfo(Name, Gpr.Index)); - } - else /* if (Gpr.RegisterSize == ShaderRegisterSize.Double) */ - { - throw new NotImplementedException("Double types are not supported."); - } - } - break; - } - - case ShaderIrOperPred Pred: - { - if (!Pred.IsConst && !HasName(m_Preds, Pred.Index)) - { - string Name = PredName + Pred.Index; - - m_Preds.TryAdd(Pred.Index, new ShaderDeclInfo(Name, Pred.Index)); - } - break; - } - } - } - - private bool HasName(Dictionary Decls, int Index) - { - //This is used to check if the dictionary already contains - //a entry for a vector at a given index position. - //Used to enable turning gprs into vectors. - int VecIndex = Index & ~3; - - if (Decls.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo)) - { - if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size) - { - return true; - } - } - - return Decls.ContainsKey(Index); - } - } -} diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs deleted file mode 100644 index 854c827ee0..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ /dev/null @@ -1,1411 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; - -namespace Ryujinx.Graphics.Gal.Shader -{ - public class GlslDecompiler - { - private delegate string GetInstExpr(ShaderIrOp Op); - - private Dictionary InstsExpr; - - private enum OperType - { - Bool, - F32, - I32 - } - - private const string IdentationStr = " "; - - private const int MaxVertexInput = 3; - - private GlslDecl Decl; - - private ShaderHeader Header, HeaderB; - - private ShaderIrBlock[] Blocks, BlocksB; - - private StringBuilder SB; - - public int MaxUboSize { get; } - - public GlslDecompiler(int MaxUboSize) - { - InstsExpr = new Dictionary() - { - { ShaderIrInst.Abs, GetAbsExpr }, - { ShaderIrInst.Add, GetAddExpr }, - { ShaderIrInst.And, GetAndExpr }, - { ShaderIrInst.Asr, GetAsrExpr }, - { ShaderIrInst.Band, GetBandExpr }, - { ShaderIrInst.Bnot, GetBnotExpr }, - { ShaderIrInst.Bor, GetBorExpr }, - { ShaderIrInst.Bxor, GetBxorExpr }, - { ShaderIrInst.Ceil, GetCeilExpr }, - { ShaderIrInst.Ceq, GetCeqExpr }, - { ShaderIrInst.Cge, GetCgeExpr }, - { ShaderIrInst.Cgt, GetCgtExpr }, - { ShaderIrInst.Clamps, GetClampsExpr }, - { ShaderIrInst.Clampu, GetClampuExpr }, - { ShaderIrInst.Cle, GetCleExpr }, - { ShaderIrInst.Clt, GetCltExpr }, - { ShaderIrInst.Cne, GetCneExpr }, - { ShaderIrInst.Cut, GetCutExpr }, - { ShaderIrInst.Exit, GetExitExpr }, - { ShaderIrInst.Fabs, GetAbsExpr }, - { ShaderIrInst.Fadd, GetAddExpr }, - { ShaderIrInst.Fceq, GetCeqExpr }, - { ShaderIrInst.Fcequ, GetCequExpr }, - { ShaderIrInst.Fcge, GetCgeExpr }, - { ShaderIrInst.Fcgeu, GetCgeuExpr }, - { ShaderIrInst.Fcgt, GetCgtExpr }, - { ShaderIrInst.Fcgtu, GetCgtuExpr }, - { ShaderIrInst.Fclamp, GetFclampExpr }, - { ShaderIrInst.Fcle, GetCleExpr }, - { ShaderIrInst.Fcleu, GetCleuExpr }, - { ShaderIrInst.Fclt, GetCltExpr }, - { ShaderIrInst.Fcltu, GetCltuExpr }, - { ShaderIrInst.Fcnan, GetCnanExpr }, - { ShaderIrInst.Fcne, GetCneExpr }, - { ShaderIrInst.Fcneu, GetCneuExpr }, - { ShaderIrInst.Fcnum, GetCnumExpr }, - { ShaderIrInst.Fcos, GetFcosExpr }, - { ShaderIrInst.Fex2, GetFex2Expr }, - { ShaderIrInst.Ffma, GetFfmaExpr }, - { ShaderIrInst.Flg2, GetFlg2Expr }, - { ShaderIrInst.Floor, GetFloorExpr }, - { ShaderIrInst.Fmax, GetMaxExpr }, - { ShaderIrInst.Fmin, GetMinExpr }, - { ShaderIrInst.Fmul, GetMulExpr }, - { ShaderIrInst.Fneg, GetNegExpr }, - { ShaderIrInst.Frcp, GetFrcpExpr }, - { ShaderIrInst.Frsq, GetFrsqExpr }, - { ShaderIrInst.Fsin, GetFsinExpr }, - { ShaderIrInst.Fsqrt, GetFsqrtExpr }, - { ShaderIrInst.Ftos, GetFtosExpr }, - { ShaderIrInst.Ftou, GetFtouExpr }, - { ShaderIrInst.Ipa, GetIpaExpr }, - { ShaderIrInst.Kil, GetKilExpr }, - { ShaderIrInst.Lsl, GetLslExpr }, - { ShaderIrInst.Lsr, GetLsrExpr }, - { ShaderIrInst.Max, GetMaxExpr }, - { ShaderIrInst.Min, GetMinExpr }, - { ShaderIrInst.Mul, GetMulExpr }, - { ShaderIrInst.Neg, GetNegExpr }, - { ShaderIrInst.Not, GetNotExpr }, - { ShaderIrInst.Or, GetOrExpr }, - { ShaderIrInst.Stof, GetStofExpr }, - { ShaderIrInst.Sub, GetSubExpr }, - { ShaderIrInst.Texb, GetTexbExpr }, - { ShaderIrInst.Texq, GetTexqExpr }, - { ShaderIrInst.Texs, GetTexsExpr }, - { ShaderIrInst.Trunc, GetTruncExpr }, - { ShaderIrInst.Txlf, GetTxlfExpr }, - { ShaderIrInst.Utof, GetUtofExpr }, - { ShaderIrInst.Xor, GetXorExpr } - }; - - this.MaxUboSize = MaxUboSize / 16; - } - - public GlslProgram Decompile( - IGalMemory Memory, - long VpAPosition, - long VpBPosition, - GalShaderType ShaderType) - { - Header = new ShaderHeader(Memory, VpAPosition); - HeaderB = new ShaderHeader(Memory, VpBPosition); - - Blocks = ShaderDecoder.Decode(Memory, VpAPosition); - BlocksB = ShaderDecoder.Decode(Memory, VpBPosition); - - GlslDecl DeclVpA = new GlslDecl(Blocks, ShaderType, Header); - GlslDecl DeclVpB = new GlslDecl(BlocksB, ShaderType, HeaderB); - - Decl = GlslDecl.Merge(DeclVpA, DeclVpB); - - return Decompile(); - } - - public GlslProgram Decompile(IGalMemory Memory, long Position, GalShaderType ShaderType) - { - Header = new ShaderHeader(Memory, Position); - HeaderB = null; - - Blocks = ShaderDecoder.Decode(Memory, Position); - BlocksB = null; - - Decl = new GlslDecl(Blocks, ShaderType, Header); - - return Decompile(); - } - - private GlslProgram Decompile() - { - SB = new StringBuilder(); - - SB.AppendLine("#version 410 core"); - - PrintDeclHeader(); - PrintDeclTextures(); - PrintDeclUniforms(); - PrintDeclAttributes(); - PrintDeclInAttributes(); - PrintDeclOutAttributes(); - PrintDeclGprs(); - PrintDeclPreds(); - PrintDeclSsy(); - - if (BlocksB != null) - { - PrintBlockScope(Blocks, GlslDecl.BasicBlockAName); - - SB.AppendLine(); - - PrintBlockScope(BlocksB, GlslDecl.BasicBlockBName); - } - else - { - PrintBlockScope(Blocks, GlslDecl.BasicBlockName); - } - - SB.AppendLine(); - - PrintMain(); - - string GlslCode = SB.ToString(); - - List TextureInfo = new List(); - - TextureInfo.AddRange(Decl.Textures.Values); - TextureInfo.AddRange(IterateCbTextures()); - - return new GlslProgram(GlslCode, TextureInfo, Decl.Uniforms.Values); - } - - private void PrintDeclHeader() - { - if (Decl.ShaderType == GalShaderType.Geometry) - { - int MaxVertices = Header.MaxOutputVertexCount; - - string OutputTopology; - - switch (Header.OutputTopology) - { - case ShaderHeader.PointList: OutputTopology = "points"; break; - case ShaderHeader.LineStrip: OutputTopology = "line_strip"; break; - case ShaderHeader.TriangleStrip: OutputTopology = "triangle_strip"; break; - - default: throw new InvalidOperationException(); - } - - SB.AppendLine("#extension GL_ARB_enhanced_layouts : require"); - - SB.AppendLine(); - - SB.AppendLine("// Stubbed. Maxwell geometry shaders don't inform input geometry type"); - - SB.AppendLine("layout(triangles) in;" + Environment.NewLine); - - SB.AppendLine($"layout({OutputTopology}, max_vertices = {MaxVertices}) out;"); - - SB.AppendLine(); - } - } - - private void PrintDeclTextures() - { - foreach (ShaderDeclInfo DeclInfo in IterateCbTextures()) - { - SB.AppendLine("uniform sampler2D " + DeclInfo.Name + ";"); - } - - PrintDecls(Decl.Textures, "uniform sampler2D"); - } - - private IEnumerable IterateCbTextures() - { - HashSet Names = new HashSet(); - - foreach (ShaderDeclInfo DeclInfo in Decl.CbTextures.Values.OrderBy(DeclKeySelector)) - { - if (Names.Add(DeclInfo.Name)) - { - yield return DeclInfo; - } - } - } - - private void PrintDeclUniforms() - { - if (Decl.ShaderType == GalShaderType.Vertex) - { - //Memory layout here is [flip_x, flip_y, instance, unused] - //It's using 4 bytes, not 8 - - SB.AppendLine("layout (std140) uniform " + GlslDecl.ExtraUniformBlockName + " {"); - - SB.AppendLine(IdentationStr + "vec2 " + GlslDecl.FlipUniformName + ";"); - - SB.AppendLine(IdentationStr + "int " + GlslDecl.InstanceUniformName + ";"); - - SB.AppendLine("};"); - SB.AppendLine(); - } - - foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector)) - { - SB.AppendLine($"layout (std140) uniform {DeclInfo.Name} {{"); - - SB.AppendLine($"{IdentationStr}vec4 {DeclInfo.Name}_data[{MaxUboSize}];"); - - SB.AppendLine("};"); - } - - if (Decl.Uniforms.Count > 0) - { - SB.AppendLine(); - } - } - - private void PrintDeclAttributes() - { - string GeometryArray = (Decl.ShaderType == GalShaderType.Geometry) ? "[" + MaxVertexInput + "]" : ""; - - PrintDecls(Decl.Attributes, Suffix: GeometryArray); - } - - private void PrintDeclInAttributes() - { - if (Decl.ShaderType == GalShaderType.Fragment) - { - SB.AppendLine("layout (location = " + GlslDecl.PositionOutAttrLocation + ") in vec4 " + GlslDecl.PositionOutAttrName + ";"); - } - - if (Decl.ShaderType == GalShaderType.Geometry) - { - if (Decl.InAttributes.Count > 0) - { - SB.AppendLine("in Vertex {"); - - foreach (ShaderDeclInfo DeclInfo in Decl.InAttributes.Values.OrderBy(DeclKeySelector)) - { - if (DeclInfo.Index >= 0) - { - SB.AppendLine(IdentationStr + "layout (location = " + DeclInfo.Index + ") vec4 " + DeclInfo.Name + "; "); - } - } - - SB.AppendLine("} block_in[];" + Environment.NewLine); - } - } - else - { - PrintDeclAttributes(Decl.InAttributes.Values, "in"); - } - } - - private void PrintDeclOutAttributes() - { - if (Decl.ShaderType == GalShaderType.Fragment) - { - int Count = 0; - - for (int Attachment = 0; Attachment < 8; Attachment++) - { - if (Header.OmapTargets[Attachment].Enabled) - { - SB.AppendLine("layout (location = " + Attachment + ") out vec4 " + GlslDecl.FragmentOutputName + Attachment + ";"); - - Count++; - } - } - - if (Count > 0) - { - SB.AppendLine(); - } - } - else - { - SB.AppendLine("layout (location = " + GlslDecl.PositionOutAttrLocation + ") out vec4 " + GlslDecl.PositionOutAttrName + ";"); - SB.AppendLine(); - } - - PrintDeclAttributes(Decl.OutAttributes.Values, "out"); - } - - private void PrintDeclAttributes(IEnumerable Decls, string InOut) - { - int Count = 0; - - foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector)) - { - if (DeclInfo.Index >= 0) - { - SB.AppendLine("layout (location = " + DeclInfo.Index + ") " + InOut + " vec4 " + DeclInfo.Name + ";"); - - Count++; - } - } - - if (Count > 0) - { - SB.AppendLine(); - } - } - - private void PrintDeclGprs() - { - PrintDecls(Decl.Gprs); - PrintDecls(Decl.GprsHalf); - } - - private void PrintDeclPreds() - { - PrintDecls(Decl.Preds, "bool"); - } - - private void PrintDeclSsy() - { - SB.AppendLine("uint " + GlslDecl.SsyCursorName + " = 0;"); - - SB.AppendLine("uint " + GlslDecl.SsyStackName + "[" + GlslDecl.SsyStackSize + "];" + Environment.NewLine); - } - - private void PrintDecls(IReadOnlyDictionary Dict, string CustomType = null, string Suffix = "") - { - foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector)) - { - string Name; - - if (CustomType != null) - { - Name = CustomType + " " + DeclInfo.Name + Suffix + ";"; - } - else if (DeclInfo.Name.Contains(GlslDecl.FragmentOutputName)) - { - Name = "layout (location = " + DeclInfo.Index / 4 + ") out vec4 " + DeclInfo.Name + Suffix + ";"; - } - else - { - Name = GetDecl(DeclInfo) + Suffix + ";"; - } - - SB.AppendLine(Name); - } - - if (Dict.Count > 0) - { - SB.AppendLine(); - } - } - - private int DeclKeySelector(ShaderDeclInfo DeclInfo) - { - return DeclInfo.Cbuf << 24 | DeclInfo.Index; - } - - private string GetDecl(ShaderDeclInfo DeclInfo) - { - if (DeclInfo.Size == 4) - { - return "vec4 " + DeclInfo.Name; - } - else - { - return "float " + DeclInfo.Name; - } - } - - private void PrintMain() - { - SB.AppendLine("void main() {"); - - foreach (KeyValuePair KV in Decl.InAttributes) - { - if (!Decl.Attributes.TryGetValue(KV.Key, out ShaderDeclInfo Attr)) - { - continue; - } - - ShaderDeclInfo DeclInfo = KV.Value; - - if (Decl.ShaderType == GalShaderType.Geometry) - { - for (int Vertex = 0; Vertex < MaxVertexInput; Vertex++) - { - string Dst = Attr.Name + "[" + Vertex + "]"; - - string Src = "block_in[" + Vertex + "]." + DeclInfo.Name; - - SB.AppendLine(IdentationStr + Dst + " = " + Src + ";"); - } - } - else - { - SB.AppendLine(IdentationStr + Attr.Name + " = " + DeclInfo.Name + ";"); - } - } - - SB.AppendLine(IdentationStr + "uint pc;"); - - if (BlocksB != null) - { - PrintProgram(Blocks, GlslDecl.BasicBlockAName); - PrintProgram(BlocksB, GlslDecl.BasicBlockBName); - } - else - { - PrintProgram(Blocks, GlslDecl.BasicBlockName); - } - - if (Decl.ShaderType != GalShaderType.Geometry) - { - PrintAttrToOutput(); - } - - if (Decl.ShaderType == GalShaderType.Fragment) - { - if (Header.OmapDepth) - { - SB.AppendLine(IdentationStr + "gl_FragDepth = " + GlslDecl.GetGprName(Header.DepthRegister) + ";"); - } - - int GprIndex = 0; - - for (int Attachment = 0; Attachment < 8; Attachment++) - { - string Output = GlslDecl.FragmentOutputName + Attachment; - - OmapTarget Target = Header.OmapTargets[Attachment]; - - for (int Component = 0; Component < 4; Component++) - { - if (Target.ComponentEnabled(Component)) - { - SB.AppendLine(IdentationStr + Output + "[" + Component + "] = " + GlslDecl.GetGprName(GprIndex) + ";"); - - GprIndex++; - } - } - } - } - - SB.AppendLine("}"); - } - - private void PrintProgram(ShaderIrBlock[] Blocks, string Name) - { - const string Ident1 = IdentationStr; - const string Ident2 = Ident1 + IdentationStr; - const string Ident3 = Ident2 + IdentationStr; - const string Ident4 = Ident3 + IdentationStr; - - SB.AppendLine(Ident1 + "pc = " + GetBlockPosition(Blocks[0]) + ";"); - SB.AppendLine(Ident1 + "do {"); - SB.AppendLine(Ident2 + "switch (pc) {"); - - foreach (ShaderIrBlock Block in Blocks) - { - string FunctionName = Block.Position.ToString("x8"); - - SB.AppendLine(Ident3 + "case 0x" + FunctionName + ": pc = " + Name + "_" + FunctionName + "(); break;"); - } - - SB.AppendLine(Ident3 + "default:"); - SB.AppendLine(Ident4 + "pc = 0;"); - SB.AppendLine(Ident4 + "break;"); - - SB.AppendLine(Ident2 + "}"); - SB.AppendLine(Ident1 + "} while (pc != 0);"); - } - - private void PrintAttrToOutput(string Identation = IdentationStr) - { - foreach (KeyValuePair KV in Decl.OutAttributes) - { - if (!Decl.Attributes.TryGetValue(KV.Key, out ShaderDeclInfo Attr)) - { - continue; - } - - ShaderDeclInfo DeclInfo = KV.Value; - - string Name = Attr.Name; - - if (Decl.ShaderType == GalShaderType.Geometry) - { - Name += "[0]"; - } - - SB.AppendLine(Identation + DeclInfo.Name + " = " + Name + ";"); - } - - if (Decl.ShaderType == GalShaderType.Vertex) - { - SB.AppendLine(Identation + "gl_Position.xy *= " + GlslDecl.FlipUniformName + ";"); - } - - if (Decl.ShaderType != GalShaderType.Fragment) - { - SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;"); - SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + ".w = 1;"); - } - } - - private void PrintBlockScope(ShaderIrBlock[] Blocks, string Name) - { - foreach (ShaderIrBlock Block in Blocks) - { - SB.AppendLine("uint " + Name + "_" + Block.Position.ToString("x8") + "() {"); - - PrintNodes(Block, Block.GetNodes()); - - SB.AppendLine("}" + Environment.NewLine); - } - } - - private void PrintNodes(ShaderIrBlock Block, ShaderIrNode[] Nodes) - { - foreach (ShaderIrNode Node in Nodes) - { - PrintNode(Block, Node, IdentationStr); - } - - if (Nodes.Length == 0) - { - SB.AppendLine(IdentationStr + "return 0u;"); - - return; - } - - ShaderIrNode Last = Nodes[Nodes.Length - 1]; - - bool UnconditionalFlowChange = false; - - if (Last is ShaderIrOp Op) - { - switch (Op.Inst) - { - case ShaderIrInst.Bra: - case ShaderIrInst.Exit: - case ShaderIrInst.Sync: - UnconditionalFlowChange = true; - break; - } - } - - if (!UnconditionalFlowChange) - { - if (Block.Next != null) - { - SB.AppendLine(IdentationStr + "return " + GetBlockPosition(Block.Next) + ";"); - } - else - { - SB.AppendLine(IdentationStr + "return 0u;"); - } - } - } - - private void PrintNode(ShaderIrBlock Block, ShaderIrNode Node, string Identation) - { - if (Node is ShaderIrCond Cond) - { - string IfExpr = GetSrcExpr(Cond.Pred, true); - - if (Cond.Not) - { - IfExpr = "!(" + IfExpr + ")"; - } - - SB.AppendLine(Identation + "if (" + IfExpr + ") {"); - - PrintNode(Block, Cond.Child, Identation + IdentationStr); - - SB.AppendLine(Identation + "}"); - } - else if (Node is ShaderIrAsg Asg) - { - if (IsValidOutOper(Asg.Dst)) - { - string Expr = GetSrcExpr(Asg.Src, true); - - Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr); - - SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";"); - } - } - else if (Node is ShaderIrOp Op) - { - switch (Op.Inst) - { - case ShaderIrInst.Bra: - { - SB.AppendLine(Identation + "return " + GetBlockPosition(Block.Branch) + ";"); - - break; - } - - case ShaderIrInst.Emit: - { - PrintAttrToOutput(Identation); - - SB.AppendLine(Identation + "EmitVertex();"); - - break; - } - - case ShaderIrInst.Ssy: - { - string StackIndex = GlslDecl.SsyStackName + "[" + GlslDecl.SsyCursorName + "]"; - - int TargetPosition = (Op.OperandA as ShaderIrOperImm).Value; - - string Target = "0x" + TargetPosition.ToString("x8") + "u"; - - SB.AppendLine(Identation + StackIndex + " = " + Target + ";"); - - SB.AppendLine(Identation + GlslDecl.SsyCursorName + "++;"); - - break; - } - - case ShaderIrInst.Sync: - { - SB.AppendLine(Identation + GlslDecl.SsyCursorName + "--;"); - - string Target = GlslDecl.SsyStackName + "[" + GlslDecl.SsyCursorName + "]"; - - SB.AppendLine(Identation + "return " + Target + ";"); - - break; - } - - default: - SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";"); - - break; - } - } - else if (Node is ShaderIrCmnt Cmnt) - { - SB.AppendLine(Identation + "// " + Cmnt.Comment); - } - else - { - throw new InvalidOperationException(); - } - } - - private bool IsValidOutOper(ShaderIrNode Node) - { - if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst) - { - return false; - } - else if (Node is ShaderIrOperPred Pred && Pred.IsConst) - { - return false; - } - - return true; - } - - private string GetDstOperName(ShaderIrNode Node) - { - if (Node is ShaderIrOperAbuf Abuf) - { - return GetOutAbufName(Abuf); - } - else if (Node is ShaderIrOperGpr Gpr) - { - return GetName(Gpr); - } - else if (Node is ShaderIrOperPred Pred) - { - return GetName(Pred); - } - - throw new ArgumentException(nameof(Node)); - } - - private string GetSrcExpr(ShaderIrNode Node, bool Entry = false) - { - switch (Node) - { - case ShaderIrOperAbuf Abuf: return GetName (Abuf); - case ShaderIrOperCbuf Cbuf: return GetName (Cbuf); - case ShaderIrOperGpr Gpr: return GetName (Gpr); - case ShaderIrOperImm Imm: return GetValue(Imm); - case ShaderIrOperImmf Immf: return GetValue(Immf); - case ShaderIrOperPred Pred: return GetName (Pred); - - case ShaderIrOp Op: - string Expr; - - if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr)) - { - Expr = GetExpr(Op); - } - else - { - throw new NotImplementedException(Op.Inst.ToString()); - } - - if (!Entry && NeedsParentheses(Op)) - { - Expr = "(" + Expr + ")"; - } - - return Expr; - - default: throw new ArgumentException(nameof(Node)); - } - } - - private static bool NeedsParentheses(ShaderIrOp Op) - { - switch (Op.Inst) - { - case ShaderIrInst.Ipa: - case ShaderIrInst.Texq: - case ShaderIrInst.Texs: - case ShaderIrInst.Txlf: - return false; - } - - return true; - } - - private string GetName(ShaderIrOperCbuf Cbuf) - { - if (!Decl.Uniforms.TryGetValue(Cbuf.Index, out ShaderDeclInfo DeclInfo)) - { - throw new InvalidOperationException(); - } - - if (Cbuf.Offs != null) - { - string Offset = "floatBitsToInt(" + GetSrcExpr(Cbuf.Offs) + ")"; - - string Index = "(" + Cbuf.Pos * 4 + " + " + Offset + ")"; - - return $"{DeclInfo.Name}_data[{Index} / 16][({Index} / 4) % 4]"; - } - else - { - return $"{DeclInfo.Name}_data[{Cbuf.Pos / 4}][{Cbuf.Pos % 4}]"; - } - } - - private string GetOutAbufName(ShaderIrOperAbuf Abuf) - { - if (Decl.ShaderType == GalShaderType.Geometry) - { - switch (Abuf.Offs) - { - case GlslDecl.LayerAttr: return "gl_Layer"; - } - } - - return GetAttrTempName(Abuf); - } - - private string GetName(ShaderIrOperAbuf Abuf) - { - //Handle special scalar read-only attributes here. - if (Decl.ShaderType == GalShaderType.Vertex) - { - switch (Abuf.Offs) - { - case GlslDecl.VertexIdAttr: return "gl_VertexID"; - case GlslDecl.InstanceIdAttr: return GlslDecl.InstanceUniformName; - } - } - else if (Decl.ShaderType == GalShaderType.TessEvaluation) - { - switch (Abuf.Offs) - { - case GlslDecl.TessCoordAttrX: return "gl_TessCoord.x"; - case GlslDecl.TessCoordAttrY: return "gl_TessCoord.y"; - case GlslDecl.TessCoordAttrZ: return "gl_TessCoord.z"; - } - } - else if (Decl.ShaderType == GalShaderType.Fragment) - { - switch (Abuf.Offs) - { - case GlslDecl.PointCoordAttrX: return "gl_PointCoord.x"; - case GlslDecl.PointCoordAttrY: return "gl_PointCoord.y"; - case GlslDecl.FaceAttr: return "(gl_FrontFacing ? -1 : 0)"; - } - } - - return GetAttrTempName(Abuf); - } - - private string GetAttrTempName(ShaderIrOperAbuf Abuf) - { - int Index = Abuf.Offs >> 4; - int Elem = (Abuf.Offs >> 2) & 3; - - string Swizzle = "." + GetAttrSwizzle(Elem); - - if (!Decl.Attributes.TryGetValue(Index, out ShaderDeclInfo DeclInfo)) - { - //Handle special vec4 attributes here - //(for example, index 7 is always gl_Position). - if (Index == GlslDecl.GlPositionVec4Index) - { - string Name = - Decl.ShaderType != GalShaderType.Vertex && - Decl.ShaderType != GalShaderType.Geometry ? GlslDecl.PositionOutAttrName : "gl_Position"; - - return Name + Swizzle; - } - else if (Abuf.Offs == GlslDecl.PointSizeAttr) - { - return "gl_PointSize"; - } - } - - if (DeclInfo.Index >= 32) - { - throw new InvalidOperationException($"Shader attribute offset {Abuf.Offs} is invalid."); - } - - if (Decl.ShaderType == GalShaderType.Geometry) - { - string Vertex = "floatBitsToInt(" + GetSrcExpr(Abuf.Vertex) + ")"; - - return DeclInfo.Name + "[" + Vertex + "]" + Swizzle; - } - else - { - return DeclInfo.Name + Swizzle; - } - } - - private string GetName(ShaderIrOperGpr Gpr) - { - if (Gpr.IsConst) - { - return "0"; - } - - if (Gpr.RegisterSize == ShaderRegisterSize.Single) - { - return GetNameWithSwizzle(Decl.Gprs, Gpr.Index); - } - else if (Gpr.RegisterSize == ShaderRegisterSize.Half) - { - return GetNameWithSwizzle(Decl.GprsHalf, (Gpr.Index << 1) | Gpr.HalfPart); - } - else /* if (Gpr.RegisterSize == ShaderRegisterSize.Double) */ - { - throw new NotImplementedException("Double types are not supported."); - } - } - - private string GetValue(ShaderIrOperImm Imm) - { - //Only use hex is the value is too big and would likely be hard to read as int. - if (Imm.Value > 0xfff || - Imm.Value < -0xfff) - { - return "0x" + Imm.Value.ToString("x8", CultureInfo.InvariantCulture); - } - else - { - return GetIntConst(Imm.Value); - } - } - - private string GetValue(ShaderIrOperImmf Immf) - { - return GetFloatConst(Immf.Value); - } - - private string GetName(ShaderIrOperPred Pred) - { - return Pred.IsConst ? "true" : GetNameWithSwizzle(Decl.Preds, Pred.Index); - } - - private string GetNameWithSwizzle(IReadOnlyDictionary Dict, int Index) - { - int VecIndex = Index & ~3; - - if (Dict.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo)) - { - if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size) - { - return DeclInfo.Name + "." + GetAttrSwizzle(Index & 3); - } - } - - if (!Dict.TryGetValue(Index, out DeclInfo)) - { - throw new InvalidOperationException(); - } - - return DeclInfo.Name; - } - - private string GetAttrSwizzle(int Elem) - { - return "xyzw".Substring(Elem, 1); - } - - private string GetAbsExpr(ShaderIrOp Op) => GetUnaryCall(Op, "abs"); - - private string GetAddExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "+"); - - private string GetAndExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&"); - - private string GetAsrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">>"); - - private string GetBandExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&&"); - - private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!"); - - private string GetBorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "||"); - - private string GetBxorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "^^"); - - private string GetCeilExpr(ShaderIrOp Op) => GetUnaryCall(Op, "ceil"); - - private string GetClampsExpr(ShaderIrOp Op) - { - return "clamp(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ", " + - GetOperExpr(Op, Op.OperandC) + ")"; - } - - private string GetClampuExpr(ShaderIrOp Op) - { - return "int(clamp(uint(" + GetOperExpr(Op, Op.OperandA) + "), " + - "uint(" + GetOperExpr(Op, Op.OperandB) + "), " + - "uint(" + GetOperExpr(Op, Op.OperandC) + ")))"; - } - - private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "=="); - - private string GetCequExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "=="); - - private string GetCgeExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">="); - - private string GetCgeuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, ">="); - - private string GetCgtExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">"); - - private string GetCgtuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, ">"); - - private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<="); - - private string GetCleuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "<="); - - private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<"); - - private string GetCltuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "<"); - - private string GetCnanExpr(ShaderIrOp Op) => GetUnaryCall(Op, "isnan"); - - private string GetCneExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "!="); - - private string GetCutExpr(ShaderIrOp Op) => "EndPrimitive()"; - - private string GetCneuExpr(ShaderIrOp Op) => GetBinaryExprWithNaN(Op, "!="); - - private string GetCnumExpr(ShaderIrOp Op) => GetUnaryCall(Op, "!isnan"); - - private string GetExitExpr(ShaderIrOp Op) => "return 0u"; - - private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos"); - - private string GetFex2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "exp2"); - - private string GetFfmaExpr(ShaderIrOp Op) => GetTernaryExpr(Op, "*", "+"); - - private string GetFclampExpr(ShaderIrOp Op) => GetTernaryCall(Op, "clamp"); - - private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2"); - - private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor"); - - private string GetFrcpExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "1 / "); - - private string GetFrsqExpr(ShaderIrOp Op) => GetUnaryCall(Op, "inversesqrt"); - - private string GetFsinExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sin"); - - private string GetFsqrtExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sqrt"); - - private string GetFtosExpr(ShaderIrOp Op) - { - return "int(" + GetOperExpr(Op, Op.OperandA) + ")"; - } - - private string GetFtouExpr(ShaderIrOp Op) - { - return "int(uint(" + GetOperExpr(Op, Op.OperandA) + "))"; - } - - private string GetIpaExpr(ShaderIrOp Op) - { - ShaderIrMetaIpa Meta = (ShaderIrMetaIpa)Op.MetaData; - - ShaderIrOperAbuf Abuf = (ShaderIrOperAbuf)Op.OperandA; - - if (Meta.Mode == ShaderIpaMode.Pass) - { - int Index = Abuf.Offs >> 4; - int Elem = (Abuf.Offs >> 2) & 3; - - if (Decl.ShaderType == GalShaderType.Fragment && Index == GlslDecl.GlPositionVec4Index) - { - switch (Elem) - { - case 0: return "gl_FragCoord.x"; - case 1: return "gl_FragCoord.y"; - case 2: return "gl_FragCoord.z"; - case 3: return "1"; - } - } - } - - return GetSrcExpr(Op.OperandA); - } - - private string GetKilExpr(ShaderIrOp Op) => "discard"; - - private string GetLslExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<<"); - private string GetLsrExpr(ShaderIrOp Op) - { - return "int(uint(" + GetOperExpr(Op, Op.OperandA) + ") >> " + - GetOperExpr(Op, Op.OperandB) + ")"; - } - - private string GetMaxExpr(ShaderIrOp Op) => GetBinaryCall(Op, "max"); - private string GetMinExpr(ShaderIrOp Op) => GetBinaryCall(Op, "min"); - - private string GetMulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*"); - - private string GetNegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-"); - - private string GetNotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "~"); - - private string GetOrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "|"); - - private string GetStofExpr(ShaderIrOp Op) - { - return "float(" + GetOperExpr(Op, Op.OperandA) + ")"; - } - - private string GetSubExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "-"); - - private string GetTexbExpr(ShaderIrOp Op) - { - ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; - - if (!Decl.CbTextures.TryGetValue(Op, out ShaderDeclInfo DeclInfo)) - { - throw new InvalidOperationException(); - } - - string Coords = GetTexSamplerCoords(Op); - - string Ch = "rgba".Substring(Meta.Elem, 1); - - return "texture(" + DeclInfo.Name + ", " + Coords + ")." + Ch; - } - - private string GetTexqExpr(ShaderIrOp Op) - { - ShaderIrMetaTexq Meta = (ShaderIrMetaTexq)Op.MetaData; - - string Ch = "xyzw".Substring(Meta.Elem, 1); - - if (Meta.Info == ShaderTexqInfo.Dimension) - { - string Sampler = GetTexSamplerName(Op); - - string Lod = GetOperExpr(Op, Op.OperandA); //??? - - return "textureSize(" + Sampler + ", " + Lod + ")." + Ch; - } - else - { - throw new NotImplementedException(Meta.Info.ToString()); - } - } - - private string GetTexsExpr(ShaderIrOp Op) - { - ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; - - string Sampler = GetTexSamplerName(Op); - - string Coords = GetTexSamplerCoords(Op); - - string Ch = "rgba".Substring(Meta.Elem, 1); - - return "texture(" + Sampler + ", " + Coords + ")." + Ch; - } - - private string GetTxlfExpr(ShaderIrOp Op) - { - ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; - - string Sampler = GetTexSamplerName(Op); - - string Coords = GetITexSamplerCoords(Op); - - string Ch = "rgba".Substring(Meta.Elem, 1); - - return "texelFetch(" + Sampler + ", " + Coords + ", 0)." + Ch; - } - - private string GetTruncExpr(ShaderIrOp Op) => GetUnaryCall(Op, "trunc"); - - private string GetUtofExpr(ShaderIrOp Op) - { - return "float(uint(" + GetOperExpr(Op, Op.OperandA) + "))"; - } - - private string GetXorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "^"); - - private string GetUnaryCall(ShaderIrOp Op, string FuncName) - { - return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ")"; - } - - private string GetBinaryCall(ShaderIrOp Op, string FuncName) - { - return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ")"; - } - - private string GetTernaryCall(ShaderIrOp Op, string FuncName) - { - return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ", " + - GetOperExpr(Op, Op.OperandC) + ")"; - } - - private string GetUnaryExpr(ShaderIrOp Op, string Opr) - { - return Opr + GetOperExpr(Op, Op.OperandA); - } - - private string GetBinaryExpr(ShaderIrOp Op, string Opr) - { - return GetOperExpr(Op, Op.OperandA) + " " + Opr + " " + - GetOperExpr(Op, Op.OperandB); - } - - private string GetBinaryExprWithNaN(ShaderIrOp Op, string Opr) - { - string A = GetOperExpr(Op, Op.OperandA); - string B = GetOperExpr(Op, Op.OperandB); - - string NaNCheck = - " || isnan(" + A + ")" + - " || isnan(" + B + ")"; - - return A + " " + Opr + " " + B + NaNCheck; - } - - private string GetTernaryExpr(ShaderIrOp Op, string Opr1, string Opr2) - { - return GetOperExpr(Op, Op.OperandA) + " " + Opr1 + " " + - GetOperExpr(Op, Op.OperandB) + " " + Opr2 + " " + - GetOperExpr(Op, Op.OperandC); - } - - private string GetTexSamplerName(ShaderIrOp Op) - { - ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC; - - int Handle = ((ShaderIrOperImm)Op.OperandC).Value; - - if (!Decl.Textures.TryGetValue(Handle, out ShaderDeclInfo DeclInfo)) - { - throw new InvalidOperationException(); - } - - return DeclInfo.Name; - } - - private string GetTexSamplerCoords(ShaderIrOp Op) - { - return "vec2(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ")"; - } - - private string GetITexSamplerCoords(ShaderIrOp Op) - { - return "ivec2(" + GetOperExpr(Op, Op.OperandA) + ", " + - GetOperExpr(Op, Op.OperandB) + ")"; - } - - private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper) - { - return GetExprWithCast(Op, Oper, GetSrcExpr(Oper)); - } - - private static string GetExprWithCast(ShaderIrNode Dst, ShaderIrNode Src, string Expr) - { - //Note: The "DstType" (of the cast) is the type that the operation - //uses on the source operands, while the "SrcType" is the destination - //type of the operand result (if it is a operation) or just the type - //of the variable for registers/uniforms/attributes. - OperType DstType = GetSrcNodeType(Dst); - OperType SrcType = GetDstNodeType(Src); - - if (DstType != SrcType) - { - //Check for invalid casts - //(like bool to int/float and others). - if (SrcType != OperType.F32 && - SrcType != OperType.I32) - { - throw new InvalidOperationException(); - } - - switch (Src) - { - case ShaderIrOperGpr Gpr: - { - //When the Gpr is ZR, just return the 0 value directly, - //since the float encoding for 0 is 0. - if (Gpr.IsConst) - { - return "0"; - } - break; - } - - case ShaderIrOperImm Imm: - { - //For integer immediates being used as float, - //it's better (for readability) to just return the float value. - if (DstType == OperType.F32) - { - float Value = BitConverter.Int32BitsToSingle(Imm.Value); - - if (!float.IsNaN(Value) && !float.IsInfinity(Value)) - { - return GetFloatConst(Value); - } - } - break; - } - } - - switch (DstType) - { - case OperType.F32: Expr = "intBitsToFloat(" + Expr + ")"; break; - case OperType.I32: Expr = "floatBitsToInt(" + Expr + ")"; break; - } - } - - return Expr; - } - - private static string GetIntConst(int Value) - { - string Expr = Value.ToString(CultureInfo.InvariantCulture); - - return Value < 0 ? "(" + Expr + ")" : Expr; - } - - private static string GetFloatConst(float Value) - { - string Expr = Value.ToString(CultureInfo.InvariantCulture); - - return Value < 0 ? "(" + Expr + ")" : Expr; - } - - private static OperType GetDstNodeType(ShaderIrNode Node) - { - //Special case instructions with the result type different - //from the input types (like integer <-> float conversion) here. - if (Node is ShaderIrOp Op) - { - switch (Op.Inst) - { - case ShaderIrInst.Stof: - case ShaderIrInst.Txlf: - case ShaderIrInst.Utof: - return OperType.F32; - - case ShaderIrInst.Ftos: - case ShaderIrInst.Ftou: - return OperType.I32; - } - } - - return GetSrcNodeType(Node); - } - - private static OperType GetSrcNodeType(ShaderIrNode Node) - { - switch (Node) - { - case ShaderIrOperAbuf Abuf: - return Abuf.Offs == GlslDecl.LayerAttr || - Abuf.Offs == GlslDecl.InstanceIdAttr || - Abuf.Offs == GlslDecl.VertexIdAttr || - Abuf.Offs == GlslDecl.FaceAttr - ? OperType.I32 - : OperType.F32; - - case ShaderIrOperCbuf Cbuf: return OperType.F32; - case ShaderIrOperGpr Gpr: return OperType.F32; - case ShaderIrOperImm Imm: return OperType.I32; - case ShaderIrOperImmf Immf: return OperType.F32; - case ShaderIrOperPred Pred: return OperType.Bool; - - case ShaderIrOp Op: - if (Op.Inst > ShaderIrInst.B_Start && - Op.Inst < ShaderIrInst.B_End) - { - return OperType.Bool; - } - else if (Op.Inst > ShaderIrInst.F_Start && - Op.Inst < ShaderIrInst.F_End) - { - return OperType.F32; - } - else if (Op.Inst > ShaderIrInst.I_Start && - Op.Inst < ShaderIrInst.I_End) - { - return OperType.I32; - } - break; - } - - throw new ArgumentException(nameof(Node)); - } - - private static string GetBlockPosition(ShaderIrBlock Block) - { - if (Block != null) - { - return "0x" + Block.Position.ToString("x8") + "u"; - } - else - { - return "0u"; - } - } - } -} diff --git a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs deleted file mode 100644 index a7af05aef9..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gal.Shader -{ - public struct GlslProgram - { - public string Code { get; private set; } - - public IEnumerable Textures { get; private set; } - public IEnumerable Uniforms { get; private set; } - - public GlslProgram( - string Code, - IEnumerable Textures, - IEnumerable Uniforms) - { - this.Code = Code; - this.Textures = Textures; - this.Uniforms = Uniforms; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs deleted file mode 100644 index 469092a23c..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ /dev/null @@ -1,1299 +0,0 @@ -using System; - -using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; - -namespace Ryujinx.Graphics.Gal.Shader -{ - static partial class ShaderDecode - { - private enum HalfOutputType - { - PackedFp16, - Fp32, - MergeH0, - MergeH1 - } - - public static void Bfe_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitBfe(Block, OpCode, ShaderOper.CR); - } - - public static void Bfe_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitBfe(Block, OpCode, ShaderOper.Imm); - } - - public static void Bfe_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitBfe(Block, OpCode, ShaderOper.RR); - } - - public static void Fadd_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFadd(Block, OpCode, ShaderOper.CR); - } - - public static void Fadd_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFadd(Block, OpCode, ShaderOper.Immf); - } - - public static void Fadd_I32(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrNode OperA = OpCode.Gpr8(); - ShaderIrNode OperB = OpCode.Immf32_20(); - - bool NegB = OpCode.Read(53); - bool AbsA = OpCode.Read(54); - bool NegA = OpCode.Read(56); - bool AbsB = OpCode.Read(57); - - OperA = GetAluFabsFneg(OperA, AbsA, NegA); - OperB = GetAluFabsFneg(OperB, AbsB, NegB); - - ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Fadd, OperA, OperB); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - - public static void Fadd_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFadd(Block, OpCode, ShaderOper.RR); - } - - public static void Ffma_CR(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFfma(Block, OpCode, ShaderOper.CR); - } - - public static void Ffma_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFfma(Block, OpCode, ShaderOper.Immf); - } - - public static void Ffma_RC(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFfma(Block, OpCode, ShaderOper.RC); - } - - public static void Ffma_RR(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFfma(Block, OpCode, ShaderOper.RR); - } - - public static void Fmnmx_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFmnmx(Block, OpCode, ShaderOper.CR); - } - - public static void Fmnmx_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFmnmx(Block, OpCode, ShaderOper.Immf); - } - - public static void Fmnmx_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFmnmx(Block, OpCode, ShaderOper.RR); - } - - public static void Fmul_I32(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrNode OperA = OpCode.Gpr8(); - ShaderIrNode OperB = OpCode.Immf32_20(); - - ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Fmul, OperA, OperB); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - - public static void Fmul_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFmul(Block, OpCode, ShaderOper.CR); - } - - public static void Fmul_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFmul(Block, OpCode, ShaderOper.Immf); - } - - public static void Fmul_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFmul(Block, OpCode, ShaderOper.RR); - } - - public static void Fset_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFset(Block, OpCode, ShaderOper.CR); - } - - public static void Fset_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFset(Block, OpCode, ShaderOper.Immf); - } - - public static void Fset_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFset(Block, OpCode, ShaderOper.RR); - } - - public static void Fsetp_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFsetp(Block, OpCode, ShaderOper.CR); - } - - public static void Fsetp_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFsetp(Block, OpCode, ShaderOper.Immf); - } - - public static void Fsetp_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitFsetp(Block, OpCode, ShaderOper.RR); - } - - public static void Hadd2_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitBinaryHalfOp(Block, OpCode, ShaderIrInst.Fadd); - } - - public static void Hmul2_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitBinaryHalfOp(Block, OpCode, ShaderIrInst.Fmul); - } - - public static void Iadd_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIadd(Block, OpCode, ShaderOper.CR); - } - - public static void Iadd_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIadd(Block, OpCode, ShaderOper.Imm); - } - - public static void Iadd_I32(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrNode OperA = OpCode.Gpr8(); - ShaderIrNode OperB = OpCode.Imm32_20(); - - bool NegA = OpCode.Read(56); - - OperA = GetAluIneg(OperA, NegA); - - ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Add, OperA, OperB); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - - public static void Iadd_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIadd(Block, OpCode, ShaderOper.RR); - } - - public static void Iadd3_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIadd3(Block, OpCode, ShaderOper.CR); - } - - public static void Iadd3_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIadd3(Block, OpCode, ShaderOper.Imm); - } - - public static void Iadd3_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIadd3(Block, OpCode, ShaderOper.RR); - } - - public static void Imnmx_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitImnmx(Block, OpCode, ShaderOper.CR); - } - - public static void Imnmx_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitImnmx(Block, OpCode, ShaderOper.Imm); - } - - public static void Imnmx_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitImnmx(Block, OpCode, ShaderOper.RR); - } - - public static void Ipa(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrNode OperA = OpCode.Abuf28(); - ShaderIrNode OperB = OpCode.Gpr20(); - - ShaderIpaMode Mode = (ShaderIpaMode)(OpCode.Read(54, 3)); - - ShaderIrMetaIpa Meta = new ShaderIrMetaIpa(Mode); - - ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ipa, OperA, OperB, null, Meta); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - - public static void Iscadd_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIscadd(Block, OpCode, ShaderOper.CR); - } - - public static void Iscadd_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIscadd(Block, OpCode, ShaderOper.Imm); - } - - public static void Iscadd_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIscadd(Block, OpCode, ShaderOper.RR); - } - - public static void Iset_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIset(Block, OpCode, ShaderOper.CR); - } - - public static void Iset_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIset(Block, OpCode, ShaderOper.Imm); - } - - public static void Iset_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIset(Block, OpCode, ShaderOper.RR); - } - - public static void Isetp_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIsetp(Block, OpCode, ShaderOper.CR); - } - - public static void Isetp_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIsetp(Block, OpCode, ShaderOper.Imm); - } - - public static void Isetp_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitIsetp(Block, OpCode, ShaderOper.RR); - } - - public static void Lop_I32(ShaderIrBlock Block, long OpCode, int Position) - { - int SubOp = OpCode.Read(53, 3); - - bool InvA = OpCode.Read(55); - bool InvB = OpCode.Read(56); - - ShaderIrInst Inst = 0; - - switch (SubOp) - { - case 0: Inst = ShaderIrInst.And; break; - case 1: Inst = ShaderIrInst.Or; break; - case 2: Inst = ShaderIrInst.Xor; break; - } - - ShaderIrNode OperB = GetAluNot(OpCode.Imm32_20(), InvB); - - //SubOp == 3 is pass, used by the not instruction - //which just moves the inverted register value. - if (SubOp < 3) - { - ShaderIrNode OperA = GetAluNot(OpCode.Gpr8(), InvA); - - ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - else - { - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OperB))); - } - } - - public static void Lop_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitLop(Block, OpCode, ShaderOper.CR); - } - - public static void Lop_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitLop(Block, OpCode, ShaderOper.Imm); - } - - public static void Lop_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitLop(Block, OpCode, ShaderOper.RR); - } - - public static void Mufu(ShaderIrBlock Block, long OpCode, int Position) - { - int SubOp = OpCode.Read(20, 0xf); - - bool AbsA = OpCode.Read(46); - bool NegA = OpCode.Read(48); - - ShaderIrInst Inst = 0; - - switch (SubOp) - { - case 0: Inst = ShaderIrInst.Fcos; break; - case 1: Inst = ShaderIrInst.Fsin; break; - case 2: Inst = ShaderIrInst.Fex2; break; - case 3: Inst = ShaderIrInst.Flg2; break; - case 4: Inst = ShaderIrInst.Frcp; break; - case 5: Inst = ShaderIrInst.Frsq; break; - case 8: Inst = ShaderIrInst.Fsqrt; break; - - default: throw new NotImplementedException(SubOp.ToString()); - } - - ShaderIrNode OperA = OpCode.Gpr8(); - - ShaderIrOp Op = new ShaderIrOp(Inst, GetAluFabsFneg(OperA, AbsA, NegA)); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - - public static void Psetp(ShaderIrBlock Block, long OpCode, int Position) - { - bool NegA = OpCode.Read(15); - bool NegB = OpCode.Read(32); - bool NegP = OpCode.Read(42); - - ShaderIrInst LopInst = OpCode.BLop24(); - - ShaderIrNode OperA = OpCode.Pred12(); - ShaderIrNode OperB = OpCode.Pred29(); - - if (NegA) - { - OperA = new ShaderIrOp(ShaderIrInst.Bnot, OperA); - } - - if (NegB) - { - OperB = new ShaderIrOp(ShaderIrInst.Bnot, OperB); - } - - ShaderIrOp Op = new ShaderIrOp(LopInst, OperA, OperB); - - ShaderIrOperPred P0Node = OpCode.Pred3(); - ShaderIrOperPred P1Node = OpCode.Pred0(); - ShaderIrOperPred P2Node = OpCode.Pred39(); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op))); - - LopInst = OpCode.BLop45(); - - if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst) - { - return; - } - - ShaderIrNode P2NNode = P2Node; - - if (NegP) - { - P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode); - } - - Op = new ShaderIrOp(ShaderIrInst.Bnot, P0Node); - - Op = new ShaderIrOp(LopInst, Op, P2NNode); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P1Node, Op))); - - Op = new ShaderIrOp(LopInst, P0Node, P2NNode); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op))); - } - - public static void Rro_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitRro(Block, OpCode, ShaderOper.CR); - } - - public static void Rro_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitRro(Block, OpCode, ShaderOper.Immf); - } - - public static void Rro_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitRro(Block, OpCode, ShaderOper.RR); - } - - public static void Shl_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitAluBinary(Block, OpCode, ShaderOper.CR, ShaderIrInst.Lsl); - } - - public static void Shl_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitAluBinary(Block, OpCode, ShaderOper.Imm, ShaderIrInst.Lsl); - } - - public static void Shl_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitAluBinary(Block, OpCode, ShaderOper.RR, ShaderIrInst.Lsl); - } - - public static void Shr_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitAluBinary(Block, OpCode, ShaderOper.CR, GetShrInst(OpCode)); - } - - public static void Shr_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitAluBinary(Block, OpCode, ShaderOper.Imm, GetShrInst(OpCode)); - } - - public static void Shr_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitAluBinary(Block, OpCode, ShaderOper.RR, GetShrInst(OpCode)); - } - - private static ShaderIrInst GetShrInst(long OpCode) - { - bool Signed = OpCode.Read(48); - - return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr; - } - - public static void Vmad(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrNode OperA = OpCode.Gpr8(); - - ShaderIrNode OperB; - - if (OpCode.Read(50)) - { - OperB = OpCode.Gpr20(); - } - else - { - OperB = OpCode.Imm19_20(); - } - - ShaderIrOperGpr OperC = OpCode.Gpr39(); - - ShaderIrNode Tmp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperB); - - ShaderIrNode Final = new ShaderIrOp(ShaderIrInst.Add, Tmp, OperC); - - int Shr = OpCode.Read(51, 3); - - if (Shr != 0) - { - int Shift = (Shr == 2) ? 15 : 7; - - Final = new ShaderIrOp(ShaderIrInst.Lsr, Final, new ShaderIrOperImm(Shift)); - } - - Block.AddNode(new ShaderIrCmnt("Stubbed. Instruction is reduced to a * b + c")); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Final))); - } - - public static void Xmad_CR(ShaderIrBlock Block, long OpCode, int Position) - { - EmitXmad(Block, OpCode, ShaderOper.CR); - } - - public static void Xmad_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitXmad(Block, OpCode, ShaderOper.Imm); - } - - public static void Xmad_RC(ShaderIrBlock Block, long OpCode, int Position) - { - EmitXmad(Block, OpCode, ShaderOper.RC); - } - - public static void Xmad_RR(ShaderIrBlock Block, long OpCode, int Position) - { - EmitXmad(Block, OpCode, ShaderOper.RR); - } - - private static void EmitAluBinary( - ShaderIrBlock Block, - long OpCode, - ShaderOper Oper, - ShaderIrInst Inst) - { - ShaderIrNode OperA = OpCode.Gpr8(), OperB; - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - - private static void EmitBfe(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - //TODO: Handle the case where position + length - //is greater than the word size, in this case the sign bit - //needs to be replicated to fill the remaining space. - bool NegB = OpCode.Read(48); - bool NegA = OpCode.Read(49); - - ShaderIrNode OperA = OpCode.Gpr8(), OperB; - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - ShaderIrNode Op; - - bool Signed = OpCode.Read(48); //? - - if (OperB is ShaderIrOperImm PosLen) - { - int Position = (PosLen.Value >> 0) & 0xff; - int Length = (PosLen.Value >> 8) & 0xff; - - int LSh = 32 - (Position + Length); - - ShaderIrInst RightShift = Signed - ? ShaderIrInst.Asr - : ShaderIrInst.Lsr; - - Op = new ShaderIrOp(ShaderIrInst.Lsl, OperA, new ShaderIrOperImm(LSh)); - Op = new ShaderIrOp(RightShift, Op, new ShaderIrOperImm(LSh + Position)); - } - else - { - ShaderIrOperImm Shift = new ShaderIrOperImm(8); - ShaderIrOperImm Mask = new ShaderIrOperImm(0xff); - - ShaderIrNode OpPos, OpLen; - - OpPos = new ShaderIrOp(ShaderIrInst.And, OperB, Mask); - OpLen = new ShaderIrOp(ShaderIrInst.Lsr, OperB, Shift); - OpLen = new ShaderIrOp(ShaderIrInst.And, OpLen, Mask); - - Op = new ShaderIrOp(ShaderIrInst.Lsr, OperA, OpPos); - - Op = ExtendTo32(Op, Signed, OpLen); - } - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - - private static void EmitFadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - bool NegB = OpCode.Read(45); - bool AbsA = OpCode.Read(46); - bool NegA = OpCode.Read(48); - bool AbsB = OpCode.Read(49); - bool Sat = OpCode.Read(50); - - ShaderIrNode OperA = OpCode.Gpr8(), OperB; - - OperA = GetAluFabsFneg(OperA, AbsA, NegA); - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - OperB = GetAluFabsFneg(OperB, AbsB, NegB); - - ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fadd, OperA, OperB); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat)))); - } - - private static void EmitFmul(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - bool NegB = OpCode.Read(48); - bool Sat = OpCode.Read(50); - - ShaderIrNode OperA = OpCode.Gpr8(), OperB; - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - OperB = GetAluFneg(OperB, NegB); - - ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fmul, OperA, OperB); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat)))); - } - - private static void EmitFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - bool NegB = OpCode.Read(48); - bool NegC = OpCode.Read(49); - bool Sat = OpCode.Read(50); - - ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC; - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break; - case ShaderOper.RC: OperB = OpCode.Gpr39(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - OperB = GetAluFneg(OperB, NegB); - - if (Oper == ShaderOper.RC) - { - OperC = GetAluFneg(OpCode.Cbuf34(), NegC); - } - else - { - OperC = GetAluFneg(OpCode.Gpr39(), NegC); - } - - ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ffma, OperA, OperB, OperC); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat)))); - } - - private static void EmitIadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - ShaderIrNode OperA = OpCode.Gpr8(); - ShaderIrNode OperB; - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - bool NegA = OpCode.Read(49); - bool NegB = OpCode.Read(48); - - OperA = GetAluIneg(OperA, NegA); - OperB = GetAluIneg(OperB, NegB); - - ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Add, OperA, OperB); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - - private static void EmitIadd3(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - int Mode = OpCode.Read(37, 3); - - bool Neg1 = OpCode.Read(51); - bool Neg2 = OpCode.Read(50); - bool Neg3 = OpCode.Read(49); - - int Height1 = OpCode.Read(35, 3); - int Height2 = OpCode.Read(33, 3); - int Height3 = OpCode.Read(31, 3); - - ShaderIrNode OperB; - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - ShaderIrNode ApplyHeight(ShaderIrNode Src, int Height) - { - if (Oper != ShaderOper.RR) - { - return Src; - } - - switch (Height) - { - case 0: return Src; - case 1: return new ShaderIrOp(ShaderIrInst.And, Src, new ShaderIrOperImm(0xffff)); - case 2: return new ShaderIrOp(ShaderIrInst.Lsr, Src, new ShaderIrOperImm(16)); - - default: throw new InvalidOperationException(); - } - } - - ShaderIrNode Src1 = GetAluIneg(ApplyHeight(OpCode.Gpr8(), Height1), Neg1); - ShaderIrNode Src2 = GetAluIneg(ApplyHeight(OperB, Height2), Neg2); - ShaderIrNode Src3 = GetAluIneg(ApplyHeight(OpCode.Gpr39(), Height3), Neg3); - - ShaderIrOp Sum = new ShaderIrOp(ShaderIrInst.Add, Src1, Src2); - - if (Oper == ShaderOper.RR) - { - switch (Mode) - { - case 1: Sum = new ShaderIrOp(ShaderIrInst.Lsr, Sum, new ShaderIrOperImm(16)); break; - case 2: Sum = new ShaderIrOp(ShaderIrInst.Lsl, Sum, new ShaderIrOperImm(16)); break; - } - } - - //Note: Here there should be a "+ 1" when carry flag is set - //but since carry is mostly ignored by other instructions, it's excluded for now - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), new ShaderIrOp(ShaderIrInst.Add, Sum, Src3)))); - } - - private static void EmitIscadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - bool NegB = OpCode.Read(48); - bool NegA = OpCode.Read(49); - - ShaderIrNode OperA = OpCode.Gpr8(), OperB; - - ShaderIrOperImm Scale = OpCode.Imm5_39(); - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - OperA = GetAluIneg(OperA, NegA); - OperB = GetAluIneg(OperB, NegB); - - ShaderIrOp ScaleOp = new ShaderIrOp(ShaderIrInst.Lsl, OperA, Scale); - ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, OperB, ScaleOp); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), AddOp))); - } - - private static void EmitFmnmx(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - EmitMnmx(Block, OpCode, true, Oper); - } - - private static void EmitImnmx(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - EmitMnmx(Block, OpCode, false, Oper); - } - - private static void EmitMnmx(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) - { - bool NegB = OpCode.Read(45); - bool AbsA = OpCode.Read(46); - bool NegA = OpCode.Read(48); - bool AbsB = OpCode.Read(49); - - ShaderIrNode OperA = OpCode.Gpr8(), OperB; - - if (IsFloat) - { - OperA = GetAluFabsFneg(OperA, AbsA, NegA); - } - else - { - OperA = GetAluIabsIneg(OperA, AbsA, NegA); - } - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; - case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - if (IsFloat) - { - OperB = GetAluFabsFneg(OperB, AbsB, NegB); - } - else - { - OperB = GetAluIabsIneg(OperB, AbsB, NegB); - } - - ShaderIrOperPred Pred = OpCode.Pred39(); - - ShaderIrOp Op; - - ShaderIrInst MaxInst = IsFloat ? ShaderIrInst.Fmax : ShaderIrInst.Max; - ShaderIrInst MinInst = IsFloat ? ShaderIrInst.Fmin : ShaderIrInst.Min; - - if (Pred.IsConst) - { - bool IsMax = OpCode.Read(42); - - Op = new ShaderIrOp(IsMax - ? MaxInst - : MinInst, OperA, OperB); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - else - { - ShaderIrNode PredN = OpCode.Pred39N(); - - ShaderIrOp OpMax = new ShaderIrOp(MaxInst, OperA, OperB); - ShaderIrOp OpMin = new ShaderIrOp(MinInst, OperA, OperB); - - ShaderIrAsg AsgMax = new ShaderIrAsg(OpCode.Gpr0(), OpMax); - ShaderIrAsg AsgMin = new ShaderIrAsg(OpCode.Gpr0(), OpMin); - - Block.AddNode(OpCode.PredNode(new ShaderIrCond(PredN, AsgMax, Not: true))); - Block.AddNode(OpCode.PredNode(new ShaderIrCond(PredN, AsgMin, Not: false))); - } - } - - private static void EmitRro(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - //Note: this is a range reduction instruction and is supposed to - //be used with Mufu, here it just moves the value and ignores the operation. - bool NegA = OpCode.Read(45); - bool AbsA = OpCode.Read(49); - - ShaderIrNode OperA; - - switch (Oper) - { - case ShaderOper.CR: OperA = OpCode.Cbuf34(); break; - case ShaderOper.Immf: OperA = OpCode.Immf19_20(); break; - case ShaderOper.RR: OperA = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - OperA = GetAluFabsFneg(OperA, AbsA, NegA); - - Block.AddNode(new ShaderIrCmnt("Stubbed.")); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OperA))); - } - - private static void EmitFset(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - EmitSet(Block, OpCode, true, Oper); - } - - private static void EmitIset(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - EmitSet(Block, OpCode, false, Oper); - } - - private static void EmitSet(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) - { - bool NegA = OpCode.Read(43); - bool AbsB = OpCode.Read(44); - bool NegB = OpCode.Read(53); - bool AbsA = OpCode.Read(54); - - bool BoolFloat = OpCode.Read(IsFloat ? 52 : 44); - - ShaderIrNode OperA = OpCode.Gpr8(), OperB; - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; - case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - ShaderIrInst CmpInst; - - if (IsFloat) - { - OperA = GetAluFabsFneg(OperA, AbsA, NegA); - OperB = GetAluFabsFneg(OperB, AbsB, NegB); - - CmpInst = OpCode.CmpF(); - } - else - { - CmpInst = OpCode.Cmp(); - } - - ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB); - - ShaderIrInst LopInst = OpCode.BLop45(); - - ShaderIrOperPred PNode = OpCode.Pred39(); - - ShaderIrNode Imm0, Imm1; - - if (BoolFloat) - { - Imm0 = new ShaderIrOperImmf(0); - Imm1 = new ShaderIrOperImmf(1); - } - else - { - Imm0 = new ShaderIrOperImm(0); - Imm1 = new ShaderIrOperImm(-1); - } - - ShaderIrNode Asg0 = new ShaderIrAsg(OpCode.Gpr0(), Imm0); - ShaderIrNode Asg1 = new ShaderIrAsg(OpCode.Gpr0(), Imm1); - - if (LopInst != ShaderIrInst.Band || !PNode.IsConst) - { - ShaderIrOp Op2 = new ShaderIrOp(LopInst, Op, PNode); - - Asg0 = new ShaderIrCond(Op2, Asg0, Not: true); - Asg1 = new ShaderIrCond(Op2, Asg1, Not: false); - } - else - { - Asg0 = new ShaderIrCond(Op, Asg0, Not: true); - Asg1 = new ShaderIrCond(Op, Asg1, Not: false); - } - - Block.AddNode(OpCode.PredNode(Asg0)); - Block.AddNode(OpCode.PredNode(Asg1)); - } - - private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - EmitSetp(Block, OpCode, true, Oper); - } - - private static void EmitIsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - EmitSetp(Block, OpCode, false, Oper); - } - - private static void EmitSetp(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) - { - bool AbsA = OpCode.Read(7); - bool NegP = OpCode.Read(42); - bool NegA = OpCode.Read(43); - bool AbsB = OpCode.Read(44); - - ShaderIrNode OperA = OpCode.Gpr8(), OperB; - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; - case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - ShaderIrInst CmpInst; - - if (IsFloat) - { - OperA = GetAluFabsFneg(OperA, AbsA, NegA); - OperB = GetAluFabs (OperB, AbsB); - - CmpInst = OpCode.CmpF(); - } - else - { - CmpInst = OpCode.Cmp(); - } - - ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB); - - ShaderIrOperPred P0Node = OpCode.Pred3(); - ShaderIrOperPred P1Node = OpCode.Pred0(); - ShaderIrOperPred P2Node = OpCode.Pred39(); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op))); - - ShaderIrInst LopInst = OpCode.BLop45(); - - if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst) - { - return; - } - - ShaderIrNode P2NNode = P2Node; - - if (NegP) - { - P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode); - } - - Op = new ShaderIrOp(ShaderIrInst.Bnot, P0Node); - - Op = new ShaderIrOp(LopInst, Op, P2NNode); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P1Node, Op))); - - Op = new ShaderIrOp(LopInst, P0Node, P2NNode); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op))); - } - - private static void EmitBinaryHalfOp(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) - { - bool AbsB = OpCode.Read(30); - bool NegB = OpCode.Read(31); - bool Sat = OpCode.Read(32); - bool AbsA = OpCode.Read(44); - - ShaderIrOperGpr[] VecA = OpCode.GprHalfVec8(); - ShaderIrOperGpr[] VecB = OpCode.GprHalfVec20(); - - HalfOutputType OutputType = (HalfOutputType)OpCode.Read(49, 3); - - int Elems = OutputType == HalfOutputType.PackedFp16 ? 2 : 1; - int First = OutputType == HalfOutputType.MergeH1 ? 1 : 0; - - for (int Index = First; Index < Elems; Index++) - { - ShaderIrNode OperA = GetAluFabs (VecA[Index], AbsA); - ShaderIrNode OperB = GetAluFabsFneg(VecB[Index], AbsB, NegB); - - ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB); - - ShaderIrOperGpr Dst = GetHalfDst(OpCode, OutputType, Index); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, GetAluFsat(Op, Sat)))); - } - } - - private static ShaderIrOperGpr GetHalfDst(long OpCode, HalfOutputType OutputType, int Index) - { - switch (OutputType) - { - case HalfOutputType.PackedFp16: return OpCode.GprHalf0(Index); - case HalfOutputType.Fp32: return OpCode.Gpr0(); - case HalfOutputType.MergeH0: return OpCode.GprHalf0(0); - case HalfOutputType.MergeH1: return OpCode.GprHalf0(1); - } - - throw new ArgumentException(nameof(OutputType)); - } - - private static void EmitLop(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - int SubOp = OpCode.Read(41, 3); - - bool InvA = OpCode.Read(39); - bool InvB = OpCode.Read(40); - - ShaderIrInst Inst = 0; - - switch (SubOp) - { - case 0: Inst = ShaderIrInst.And; break; - case 1: Inst = ShaderIrInst.Or; break; - case 2: Inst = ShaderIrInst.Xor; break; - } - - ShaderIrNode OperA = GetAluNot(OpCode.Gpr8(), InvA); - ShaderIrNode OperB; - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - OperB = GetAluNot(OperB, InvB); - - ShaderIrNode Op; - - if (SubOp < 3) - { - Op = new ShaderIrOp(Inst, OperA, OperB); - } - else - { - Op = OperB; - } - - ShaderIrNode Compare = new ShaderIrOp(ShaderIrInst.Cne, Op, new ShaderIrOperImm(0)); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Pred48(), Compare))); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - - private enum XmadMode - { - Cfull = 0, - Clo = 1, - Chi = 2, - Csfu = 3, - Cbcc = 4 - } - - private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - bool SignedA = OpCode.Read(48); - bool SignedB = OpCode.Read(49); - bool HighB = OpCode.Read(52); - bool HighA = OpCode.Read(53); - - int Mode = OpCode.Read(50, 7); - - ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC; - - switch (Oper) - { - case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; - case ShaderOper.Imm: OperB = OpCode.ImmU16_20(); break; - case ShaderOper.RC: OperB = OpCode.Gpr39(); break; - case ShaderOper.RR: OperB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - ShaderIrNode OperB2 = OperB; - - if (Oper == ShaderOper.Imm) - { - int Imm = ((ShaderIrOperImm)OperB2).Value; - - if (!HighB) - { - Imm <<= 16; - } - - if (SignedB) - { - Imm >>= 16; - } - else - { - Imm = (int)((uint)Imm >> 16); - } - - OperB2 = new ShaderIrOperImm(Imm); - } - - ShaderIrOperImm Imm16 = new ShaderIrOperImm(16); - - //If we are working with the lower 16-bits of the A/B operands, - //we need to shift the lower 16-bits to the top 16-bits. Later, - //they will be right shifted. For U16 types, this will be a logical - //right shift, and for S16 types, a arithmetic right shift. - if (!HighA) - { - OperA = new ShaderIrOp(ShaderIrInst.Lsl, OperA, Imm16); - } - - if (!HighB && Oper != ShaderOper.Imm) - { - OperB2 = new ShaderIrOp(ShaderIrInst.Lsl, OperB2, Imm16); - } - - ShaderIrInst ShiftA = SignedA ? ShaderIrInst.Asr : ShaderIrInst.Lsr; - ShaderIrInst ShiftB = SignedB ? ShaderIrInst.Asr : ShaderIrInst.Lsr; - - OperA = new ShaderIrOp(ShiftA, OperA, Imm16); - - if (Oper != ShaderOper.Imm) - { - OperB2 = new ShaderIrOp(ShiftB, OperB2, Imm16); - } - - bool ProductShiftLeft = false; - bool Merge = false; - - if (Oper == ShaderOper.RC) - { - OperC = OpCode.Cbuf34(); - } - else - { - OperC = OpCode.Gpr39(); - - ProductShiftLeft = OpCode.Read(36); - Merge = OpCode.Read(37); - } - - ShaderIrOp MulOp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperB2); - - if (ProductShiftLeft) - { - MulOp = new ShaderIrOp(ShaderIrInst.Lsl, MulOp, Imm16); - } - - switch ((XmadMode)Mode) - { - case XmadMode.Clo: OperC = ExtendTo32(OperC, Signed: false, Size: 16); break; - - case XmadMode.Chi: OperC = new ShaderIrOp(ShaderIrInst.Lsr, OperC, Imm16); break; - - case XmadMode.Cbcc: - { - ShaderIrOp OperBLsh16 = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16); - - OperC = new ShaderIrOp(ShaderIrInst.Add, OperC, OperBLsh16); - - break; - } - - case XmadMode.Csfu: - { - ShaderIrOperImm Imm31 = new ShaderIrOperImm(31); - - ShaderIrOp SignAdjustA = new ShaderIrOp(ShaderIrInst.Lsr, OperA, Imm31); - ShaderIrOp SignAdjustB = new ShaderIrOp(ShaderIrInst.Lsr, OperB2, Imm31); - - SignAdjustA = new ShaderIrOp(ShaderIrInst.Lsl, SignAdjustA, Imm16); - SignAdjustB = new ShaderIrOp(ShaderIrInst.Lsl, SignAdjustB, Imm16); - - ShaderIrOp SignAdjust = new ShaderIrOp(ShaderIrInst.Add, SignAdjustA, SignAdjustB); - - OperC = new ShaderIrOp(ShaderIrInst.Sub, OperC, SignAdjust); - - break; - } - } - - ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, MulOp, OperC); - - if (Merge) - { - ShaderIrOperImm Imm16Mask = new ShaderIrOperImm(0xffff); - - AddOp = new ShaderIrOp(ShaderIrInst.And, AddOp, Imm16Mask); - OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16); - AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB); - } - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), AddOp))); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs deleted file mode 100644 index bc2539bd7e..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gal.Shader -{ - static partial class ShaderDecode - { - public static void Bra(ShaderIrBlock Block, long OpCode, int Position) - { - if ((OpCode & 0x20) != 0) - { - //This reads the target offset from the constant buffer. - //Almost impossible to support with GLSL. - throw new NotImplementedException(); - } - - ShaderIrOperImm Imm = new ShaderIrOperImm(Position + OpCode.Branch()); - - Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Bra, Imm))); - } - - public static void Exit(ShaderIrBlock Block, long OpCode, int Position) - { - int CCode = (int)OpCode & 0x1f; - - //TODO: Figure out what the other condition codes mean... - if (CCode == 0xf) - { - Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Exit))); - } - } - - public static void Kil(ShaderIrBlock Block, long OpCode, int Position) - { - Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Kil))); - } - - public static void Ssy(ShaderIrBlock Block, long OpCode, int Position) - { - if ((OpCode & 0x20) != 0) - { - //This reads the target offset from the constant buffer. - //Almost impossible to support with GLSL. - throw new NotImplementedException(); - } - - ShaderIrOperImm Imm = new ShaderIrOperImm(Position + OpCode.Branch()); - - Block.AddNode(new ShaderIrOp(ShaderIrInst.Ssy, Imm)); - } - - public static void Sync(ShaderIrBlock Block, long OpCode, int Position) - { - //TODO: Implement Sync condition codes - Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Sync))); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFunc.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFunc.cs deleted file mode 100644 index 73248aa0c7..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFunc.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode, int Position); -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs deleted file mode 100644 index d07bcd9171..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs +++ /dev/null @@ -1,78 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - static class ShaderDecodeHelper - { - private static readonly ShaderIrOperImmf ImmfZero = new ShaderIrOperImmf(0); - private static readonly ShaderIrOperImmf ImmfOne = new ShaderIrOperImmf(1); - - public static ShaderIrNode GetAluFabsFneg(ShaderIrNode Node, bool Abs, bool Neg) - { - return GetAluFneg(GetAluFabs(Node, Abs), Neg); - } - - public static ShaderIrNode GetAluFabs(ShaderIrNode Node, bool Abs) - { - return Abs ? new ShaderIrOp(ShaderIrInst.Fabs, Node) : Node; - } - - public static ShaderIrNode GetAluFneg(ShaderIrNode Node, bool Neg) - { - return Neg ? new ShaderIrOp(ShaderIrInst.Fneg, Node) : Node; - } - - public static ShaderIrNode GetAluFsat(ShaderIrNode Node, bool Sat) - { - return Sat ? new ShaderIrOp(ShaderIrInst.Fclamp, Node, ImmfZero, ImmfOne) : Node; - } - - public static ShaderIrNode GetAluIabsIneg(ShaderIrNode Node, bool Abs, bool Neg) - { - return GetAluIneg(GetAluIabs(Node, Abs), Neg); - } - - public static ShaderIrNode GetAluIabs(ShaderIrNode Node, bool Abs) - { - return Abs ? new ShaderIrOp(ShaderIrInst.Abs, Node) : Node; - } - - public static ShaderIrNode GetAluIneg(ShaderIrNode Node, bool Neg) - { - return Neg ? new ShaderIrOp(ShaderIrInst.Neg, Node) : Node; - } - - public static ShaderIrNode GetAluNot(ShaderIrNode Node, bool Not) - { - return Not ? new ShaderIrOp(ShaderIrInst.Not, Node) : Node; - } - - public static ShaderIrNode ExtendTo32(ShaderIrNode Node, bool Signed, int Size) - { - int Shift = 32 - Size; - - ShaderIrInst RightShift = Signed - ? ShaderIrInst.Asr - : ShaderIrInst.Lsr; - - Node = new ShaderIrOp(ShaderIrInst.Lsl, Node, new ShaderIrOperImm(Shift)); - Node = new ShaderIrOp(RightShift, Node, new ShaderIrOperImm(Shift)); - - return Node; - } - - public static ShaderIrNode ExtendTo32(ShaderIrNode Node, bool Signed, ShaderIrNode Size) - { - ShaderIrOperImm WordSize = new ShaderIrOperImm(32); - - ShaderIrOp Shift = new ShaderIrOp(ShaderIrInst.Sub, WordSize, Size); - - ShaderIrInst RightShift = Signed - ? ShaderIrInst.Asr - : ShaderIrInst.Lsr; - - Node = new ShaderIrOp(ShaderIrInst.Lsl, Node, Shift); - Node = new ShaderIrOp(RightShift, Node, Shift); - - return Node; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs deleted file mode 100644 index adcc47b955..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs +++ /dev/null @@ -1,311 +0,0 @@ -using System; - -using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; - -namespace Ryujinx.Graphics.Gal.Shader -{ - static partial class ShaderDecode - { - private const int ____ = 0x0; - private const int R___ = 0x1; - private const int _G__ = 0x2; - private const int RG__ = 0x3; - private const int __B_ = 0x4; - private const int RGB_ = 0x7; - private const int ___A = 0x8; - private const int R__A = 0x9; - private const int _G_A = 0xa; - private const int RG_A = 0xb; - private const int __BA = 0xc; - private const int R_BA = 0xd; - private const int _GBA = 0xe; - private const int RGBA = 0xf; - - private static int[,] MaskLut = new int[,] - { - { ____, ____, ____, ____, ____, ____, ____, ____ }, - { R___, _G__, __B_, ___A, RG__, R__A, _G_A, __BA }, - { R___, _G__, __B_, ___A, RG__, ____, ____, ____ }, - { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ } - }; - - public static void Ld_A(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrNode[] Opers = OpCode.Abuf20(); - - //Used by GS - ShaderIrOperGpr Vertex = OpCode.Gpr39(); - - int Index = 0; - - foreach (ShaderIrNode OperA in Opers) - { - ShaderIrOperGpr OperD = OpCode.Gpr0(); - - OperD.Index += Index++; - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperD, OperA))); - } - } - - public static void Ld_C(ShaderIrBlock Block, long OpCode, int Position) - { - int CbufPos = OpCode.Read(22, 0x3fff); - int CbufIndex = OpCode.Read(36, 0x1f); - int Type = OpCode.Read(48, 7); - - if (Type > 5) - { - throw new InvalidOperationException(); - } - - ShaderIrOperGpr Temp = ShaderIrOperGpr.MakeTemporary(); - - Block.AddNode(new ShaderIrAsg(Temp, OpCode.Gpr8())); - - int Count = Type == 5 ? 2 : 1; - - for (int Index = 0; Index < Count; Index++) - { - ShaderIrOperCbuf OperA = new ShaderIrOperCbuf(CbufIndex, CbufPos, Temp); - - ShaderIrOperGpr OperD = OpCode.Gpr0(); - - OperA.Pos += Index; - OperD.Index += Index; - - if (!OperD.IsValidRegister) - { - break; - } - - ShaderIrNode Node = OperA; - - if (Type < 4) - { - //This is a 8 or 16 bits type. - bool Signed = (Type & 1) != 0; - - int Size = 8 << (Type >> 1); - - Node = ExtendTo32(Node, Signed, Size); - } - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperD, Node))); - } - } - - public static void St_A(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrNode[] Opers = OpCode.Abuf20(); - - int Index = 0; - - foreach (ShaderIrNode OperA in Opers) - { - ShaderIrOperGpr OperD = OpCode.Gpr0(); - - OperD.Index += Index++; - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperA, OperD))); - } - } - - public static void Texq(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrNode OperD = OpCode.Gpr0(); - ShaderIrNode OperA = OpCode.Gpr8(); - - ShaderTexqInfo Info = (ShaderTexqInfo)(OpCode.Read(22, 0x1f)); - - ShaderIrMetaTexq Meta0 = new ShaderIrMetaTexq(Info, 0); - ShaderIrMetaTexq Meta1 = new ShaderIrMetaTexq(Info, 1); - - ShaderIrNode OperC = OpCode.Imm13_36(); - - ShaderIrOp Op0 = new ShaderIrOp(ShaderIrInst.Texq, OperA, null, OperC, Meta0); - ShaderIrOp Op1 = new ShaderIrOp(ShaderIrInst.Texq, OperA, null, OperC, Meta1); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperD, Op0))); - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OperA, Op1))); //Is this right? - } - - public static void Tex(ShaderIrBlock Block, long OpCode, int Position) - { - EmitTex(Block, OpCode, GprHandle: false); - } - - public static void Tex_B(ShaderIrBlock Block, long OpCode, int Position) - { - EmitTex(Block, OpCode, GprHandle: true); - } - - private static void EmitTex(ShaderIrBlock Block, long OpCode, bool GprHandle) - { - //TODO: Support other formats. - ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[2]; - - for (int Index = 0; Index < Coords.Length; Index++) - { - ShaderIrOperGpr CoordReg = OpCode.Gpr8(); - - CoordReg.Index += Index; - - if (!CoordReg.IsValidRegister) - { - CoordReg.Index = ShaderIrOperGpr.ZRIndex; - } - - Coords[Index] = ShaderIrOperGpr.MakeTemporary(Index); - - Block.AddNode(new ShaderIrAsg(Coords[Index], CoordReg)); - } - - int ChMask = OpCode.Read(31, 0xf); - - ShaderIrNode OperC = GprHandle - ? (ShaderIrNode)OpCode.Gpr20() - : (ShaderIrNode)OpCode.Imm13_36(); - - ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs; - - int RegInc = 0; - - for (int Ch = 0; Ch < 4; Ch++) - { - if (!IsChannelUsed(ChMask, Ch)) - { - continue; - } - - ShaderIrOperGpr Dst = OpCode.Gpr0(); - - Dst.Index += RegInc++; - - if (!Dst.IsValidRegister || Dst.IsConst) - { - continue; - } - - ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); - - ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); - } - } - - public static void Texs(ShaderIrBlock Block, long OpCode, int Position) - { - EmitTexs(Block, OpCode, ShaderIrInst.Texs); - } - - public static void Tlds(ShaderIrBlock Block, long OpCode, int Position) - { - EmitTexs(Block, OpCode, ShaderIrInst.Txlf); - } - - private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) - { - //TODO: Support other formats. - int LutIndex; - - LutIndex = !OpCode.Gpr0().IsConst ? 1 : 0; - LutIndex |= !OpCode.Gpr28().IsConst ? 2 : 0; - - if (LutIndex == 0) - { - //Both destination registers are RZ, do nothing. - return; - } - - bool Fp16 = !OpCode.Read(59); - - int DstIncrement = 0; - - ShaderIrOperGpr GetDst() - { - ShaderIrOperGpr Dst; - - if (Fp16) - { - //FP16 mode, two components are packed on the two - //halfs of a 32-bits register, as two half-float values. - int HalfPart = DstIncrement & 1; - - switch (LutIndex) - { - case 1: Dst = OpCode.GprHalf0(HalfPart); break; - case 2: Dst = OpCode.GprHalf28(HalfPart); break; - case 3: Dst = (DstIncrement >> 1) != 0 - ? OpCode.GprHalf28(HalfPart) - : OpCode.GprHalf0(HalfPart); break; - - default: throw new InvalidOperationException(); - } - } - else - { - //32-bits mode, each component uses one register. - //Two components uses two consecutive registers. - switch (LutIndex) - { - case 1: Dst = OpCode.Gpr0(); break; - case 2: Dst = OpCode.Gpr28(); break; - case 3: Dst = (DstIncrement >> 1) != 0 - ? OpCode.Gpr28() - : OpCode.Gpr0(); break; - - default: throw new InvalidOperationException(); - } - - Dst.Index += DstIncrement & 1; - } - - DstIncrement++; - - return Dst; - } - - int ChMask = MaskLut[LutIndex, OpCode.Read(50, 7)]; - - if (ChMask == 0) - { - //All channels are disabled, do nothing. - return; - } - - ShaderIrNode OperC = OpCode.Imm13_36(); - - ShaderIrOperGpr Coord0 = ShaderIrOperGpr.MakeTemporary(0); - ShaderIrOperGpr Coord1 = ShaderIrOperGpr.MakeTemporary(1); - - Block.AddNode(new ShaderIrAsg(Coord0, OpCode.Gpr8())); - Block.AddNode(new ShaderIrAsg(Coord1, OpCode.Gpr20())); - - for (int Ch = 0; Ch < 4; Ch++) - { - if (!IsChannelUsed(ChMask, Ch)) - { - continue; - } - - ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); - - ShaderIrOp Op = new ShaderIrOp(Inst, Coord0, Coord1, OperC, Meta); - - ShaderIrOperGpr Dst = GetDst(); - - if (Dst.IsValidRegister && !Dst.IsConst) - { - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); - } - } - } - - private static bool IsChannelUsed(int ChMask, int Ch) - { - return (ChMask & (1 << Ch)) != 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs deleted file mode 100644 index cd602db7c1..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs +++ /dev/null @@ -1,431 +0,0 @@ -using System; - -using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper; - -namespace Ryujinx.Graphics.Gal.Shader -{ - static partial class ShaderDecode - { - private enum IntType - { - U8 = 0, - U16 = 1, - U32 = 2, - U64 = 3, - S8 = 4, - S16 = 5, - S32 = 6, - S64 = 7 - } - - private enum FloatType - { - F16 = 1, - F32 = 2, - F64 = 3 - } - - public static void F2f_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitF2f(Block, OpCode, ShaderOper.CR); - } - - public static void F2f_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitF2f(Block, OpCode, ShaderOper.Immf); - } - - public static void F2f_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitF2f(Block, OpCode, ShaderOper.RR); - } - - public static void F2i_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitF2i(Block, OpCode, ShaderOper.CR); - } - - public static void F2i_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitF2i(Block, OpCode, ShaderOper.Immf); - } - - public static void F2i_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitF2i(Block, OpCode, ShaderOper.RR); - } - - public static void I2f_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitI2f(Block, OpCode, ShaderOper.CR); - } - - public static void I2f_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitI2f(Block, OpCode, ShaderOper.Imm); - } - - public static void I2f_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitI2f(Block, OpCode, ShaderOper.RR); - } - - public static void I2i_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitI2i(Block, OpCode, ShaderOper.CR); - } - - public static void I2i_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitI2i(Block, OpCode, ShaderOper.Imm); - } - - public static void I2i_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitI2i(Block, OpCode, ShaderOper.RR); - } - - public static void Isberd(ShaderIrBlock Block, long OpCode, int Position) - { - //This instruction seems to be used to translate from an address to a vertex index in a GS - //Stub it as such - - Block.AddNode(new ShaderIrCmnt("Stubbed.")); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OpCode.Gpr8()))); - } - - public static void Mov_C(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrOperCbuf Cbuf = OpCode.Cbuf34(); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Cbuf))); - } - - public static void Mov_I(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrOperImm Imm = OpCode.Imm19_20(); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Imm))); - } - - public static void Mov_I32(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrOperImm Imm = OpCode.Imm32_20(); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Imm))); - } - - public static void Mov_R(ShaderIrBlock Block, long OpCode, int Position) - { - ShaderIrOperGpr Gpr = OpCode.Gpr20(); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Gpr))); - } - - public static void Sel_C(ShaderIrBlock Block, long OpCode, int Position) - { - EmitSel(Block, OpCode, ShaderOper.CR); - } - - public static void Sel_I(ShaderIrBlock Block, long OpCode, int Position) - { - EmitSel(Block, OpCode, ShaderOper.Imm); - } - - public static void Sel_R(ShaderIrBlock Block, long OpCode, int Position) - { - EmitSel(Block, OpCode, ShaderOper.RR); - } - - public static void Mov_S(ShaderIrBlock Block, long OpCode, int Position) - { - Block.AddNode(new ShaderIrCmnt("Stubbed.")); - - //Zero is used as a special number to get a valid "0 * 0 + VertexIndex" in a GS - ShaderIrNode Source = new ShaderIrOperImm(0); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Source))); - } - - private static void EmitF2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - bool NegA = OpCode.Read(45); - bool AbsA = OpCode.Read(49); - - ShaderIrNode OperA; - - switch (Oper) - { - case ShaderOper.CR: OperA = OpCode.Cbuf34(); break; - case ShaderOper.Immf: OperA = OpCode.Immf19_20(); break; - case ShaderOper.RR: OperA = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - OperA = GetAluFabsFneg(OperA, AbsA, NegA); - - ShaderIrInst RoundInst = GetRoundInst(OpCode); - - if (RoundInst != ShaderIrInst.Invalid) - { - OperA = new ShaderIrOp(RoundInst, OperA); - } - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OperA))); - } - - private static void EmitF2i(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - IntType Type = GetIntType(OpCode); - - if (Type == IntType.U64 || - Type == IntType.S64) - { - //TODO: 64-bits support. - //Note: GLSL doesn't support 64-bits integers. - throw new NotImplementedException(); - } - - bool NegA = OpCode.Read(45); - bool AbsA = OpCode.Read(49); - - ShaderIrNode OperA; - - switch (Oper) - { - case ShaderOper.CR: OperA = OpCode.Cbuf34(); break; - case ShaderOper.Immf: OperA = OpCode.Immf19_20(); break; - case ShaderOper.RR: OperA = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - OperA = GetAluFabsFneg(OperA, AbsA, NegA); - - ShaderIrInst RoundInst = GetRoundInst(OpCode); - - if (RoundInst != ShaderIrInst.Invalid) - { - OperA = new ShaderIrOp(RoundInst, OperA); - } - - bool Signed = Type >= IntType.S8; - - int Size = 8 << ((int)Type & 3); - - if (Size < 32) - { - uint Mask = uint.MaxValue >> (32 - Size); - - float CMin = 0; - float CMax = Mask; - - if (Signed) - { - uint HalfMask = Mask >> 1; - - CMin -= HalfMask + 1; - CMax = HalfMask; - } - - ShaderIrOperImmf IMin = new ShaderIrOperImmf(CMin); - ShaderIrOperImmf IMax = new ShaderIrOperImmf(CMax); - - OperA = new ShaderIrOp(ShaderIrInst.Fclamp, OperA, IMin, IMax); - } - - ShaderIrInst Inst = Signed - ? ShaderIrInst.Ftos - : ShaderIrInst.Ftou; - - ShaderIrNode Op = new ShaderIrOp(Inst, OperA); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - - private static void EmitI2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - IntType Type = GetIntType(OpCode); - - if (Type == IntType.U64 || - Type == IntType.S64) - { - //TODO: 64-bits support. - //Note: GLSL doesn't support 64-bits integers. - throw new NotImplementedException(); - } - - int Sel = OpCode.Read(41, 3); - - bool NegA = OpCode.Read(45); - bool AbsA = OpCode.Read(49); - - ShaderIrNode OperA; - - switch (Oper) - { - case ShaderOper.CR: OperA = OpCode.Cbuf34(); break; - case ShaderOper.Imm: OperA = OpCode.Imm19_20(); break; - case ShaderOper.RR: OperA = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - OperA = GetAluIabsIneg(OperA, AbsA, NegA); - - bool Signed = Type >= IntType.S8; - - int Shift = Sel * 8; - - int Size = 8 << ((int)Type & 3); - - if (Shift != 0) - { - OperA = new ShaderIrOp(ShaderIrInst.Asr, OperA, new ShaderIrOperImm(Shift)); - } - - if (Size < 32) - { - OperA = ExtendTo32(OperA, Signed, Size); - } - - ShaderIrInst Inst = Signed - ? ShaderIrInst.Stof - : ShaderIrInst.Utof; - - ShaderIrNode Op = new ShaderIrOp(Inst, OperA); - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); - } - - private static void EmitI2i(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - IntType Type = GetIntType(OpCode); - - if (Type == IntType.U64 || - Type == IntType.S64) - { - //TODO: 64-bits support. - //Note: GLSL doesn't support 64-bits integers. - throw new NotImplementedException(); - } - - int Sel = OpCode.Read(41, 3); - - bool NegA = OpCode.Read(45); - bool AbsA = OpCode.Read(49); - bool SatA = OpCode.Read(50); - - ShaderIrNode OperA; - - switch (Oper) - { - case ShaderOper.CR: OperA = OpCode.Cbuf34(); break; - case ShaderOper.Immf: OperA = OpCode.Immf19_20(); break; - case ShaderOper.RR: OperA = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - OperA = GetAluIabsIneg(OperA, AbsA, NegA); - - bool Signed = Type >= IntType.S8; - - int Shift = Sel * 8; - - int Size = 8 << ((int)Type & 3); - - if (Shift != 0) - { - OperA = new ShaderIrOp(ShaderIrInst.Asr, OperA, new ShaderIrOperImm(Shift)); - } - - if (Size < 32) - { - uint Mask = uint.MaxValue >> (32 - Size); - - if (SatA) - { - uint CMin = 0; - uint CMax = Mask; - - if (Signed) - { - uint HalfMask = Mask >> 1; - - CMin -= HalfMask + 1; - CMax = HalfMask; - } - - ShaderIrOperImm IMin = new ShaderIrOperImm((int)CMin); - ShaderIrOperImm IMax = new ShaderIrOperImm((int)CMax); - - OperA = new ShaderIrOp(Signed - ? ShaderIrInst.Clamps - : ShaderIrInst.Clampu, OperA, IMin, IMax); - } - else - { - OperA = ExtendTo32(OperA, Signed, Size); - } - } - - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), OperA))); - } - - private static void EmitSel(ShaderIrBlock Block, long OpCode, ShaderOper Oper) - { - ShaderIrOperGpr Dst = OpCode.Gpr0(); - ShaderIrNode Pred = OpCode.Pred39N(); - - ShaderIrNode ResultA = OpCode.Gpr8(); - ShaderIrNode ResultB; - - switch (Oper) - { - case ShaderOper.CR: ResultB = OpCode.Cbuf34(); break; - case ShaderOper.Imm: ResultB = OpCode.Imm19_20(); break; - case ShaderOper.RR: ResultB = OpCode.Gpr20(); break; - - default: throw new ArgumentException(nameof(Oper)); - } - - Block.AddNode(OpCode.PredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultA), false))); - - Block.AddNode(OpCode.PredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultB), true))); - } - - private static IntType GetIntType(long OpCode) - { - bool Signed = OpCode.Read(13); - - IntType Type = (IntType)(OpCode.Read(10, 3)); - - if (Signed) - { - Type += (int)IntType.S8; - } - - return Type; - } - - private static FloatType GetFloatType(long OpCode) - { - return (FloatType)(OpCode.Read(8, 3)); - } - - private static ShaderIrInst GetRoundInst(long OpCode) - { - switch (OpCode.Read(39, 3)) - { - case 1: return ShaderIrInst.Floor; - case 2: return ShaderIrInst.Ceil; - case 3: return ShaderIrInst.Trunc; - } - - return ShaderIrInst.Invalid; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs deleted file mode 100644 index f0f92148e6..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeOpCode.cs +++ /dev/null @@ -1,308 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gal.Shader -{ - static partial class ShaderDecode - { - private static int Read(this long OpCode, int Position, int Mask) - { - return (int)(OpCode >> Position) & Mask; - } - - private static bool Read(this long OpCode, int Position) - { - return ((OpCode >> Position) & 1) != 0; - } - - private static int Branch(this long OpCode) - { - return ((int)(OpCode >> 20) << 8) >> 8; - } - - private static ShaderIrOperAbuf[] Abuf20(this long OpCode) - { - int Abuf = OpCode.Read(20, 0x3ff); - int Size = OpCode.Read(47, 3); - - ShaderIrOperGpr Vertex = OpCode.Gpr39(); - - ShaderIrOperAbuf[] Opers = new ShaderIrOperAbuf[Size + 1]; - - for (int Index = 0; Index <= Size; Index++) - { - Opers[Index] = new ShaderIrOperAbuf(Abuf + Index * 4, Vertex); - } - - return Opers; - } - - private static ShaderIrOperAbuf Abuf28(this long OpCode) - { - int Abuf = OpCode.Read(28, 0x3ff); - - return new ShaderIrOperAbuf(Abuf, OpCode.Gpr39()); - } - - private static ShaderIrOperCbuf Cbuf34(this long OpCode) - { - return new ShaderIrOperCbuf( - OpCode.Read(34, 0x1f), - OpCode.Read(20, 0x3fff)); - } - - private static ShaderIrOperGpr Gpr8(this long OpCode) - { - return new ShaderIrOperGpr(OpCode.Read(8, 0xff)); - } - - private static ShaderIrOperGpr Gpr20(this long OpCode) - { - return new ShaderIrOperGpr(OpCode.Read(20, 0xff)); - } - - private static ShaderIrOperGpr Gpr39(this long OpCode) - { - return new ShaderIrOperGpr(OpCode.Read(39, 0xff)); - } - - private static ShaderIrOperGpr Gpr0(this long OpCode) - { - return new ShaderIrOperGpr(OpCode.Read(0, 0xff)); - } - - private static ShaderIrOperGpr Gpr28(this long OpCode) - { - return new ShaderIrOperGpr(OpCode.Read(28, 0xff)); - } - - private static ShaderIrOperGpr[] GprHalfVec8(this long OpCode) - { - return GetGprHalfVec2(OpCode.Read(8, 0xff), OpCode.Read(47, 3)); - } - - private static ShaderIrOperGpr[] GprHalfVec20(this long OpCode) - { - return GetGprHalfVec2(OpCode.Read(20, 0xff), OpCode.Read(28, 3)); - } - - private static ShaderIrOperGpr[] GetGprHalfVec2(int Gpr, int Mask) - { - if (Mask == 1) - { - //This value is used for FP32, the whole 32-bits register - //is used as each element on the vector. - return new ShaderIrOperGpr[] - { - new ShaderIrOperGpr(Gpr), - new ShaderIrOperGpr(Gpr) - }; - } - - ShaderIrOperGpr Low = new ShaderIrOperGpr(Gpr, 0); - ShaderIrOperGpr High = new ShaderIrOperGpr(Gpr, 1); - - return new ShaderIrOperGpr[] - { - (Mask & 1) != 0 ? High : Low, - (Mask & 2) != 0 ? High : Low - }; - } - - private static ShaderIrOperGpr GprHalf0(this long OpCode, int HalfPart) - { - return new ShaderIrOperGpr(OpCode.Read(0, 0xff), HalfPart); - } - - private static ShaderIrOperGpr GprHalf28(this long OpCode, int HalfPart) - { - return new ShaderIrOperGpr(OpCode.Read(28, 0xff), HalfPart); - } - - private static ShaderIrOperImm Imm5_39(this long OpCode) - { - return new ShaderIrOperImm(OpCode.Read(39, 0x1f)); - } - - private static ShaderIrOperImm Imm13_36(this long OpCode) - { - return new ShaderIrOperImm(OpCode.Read(36, 0x1fff)); - } - - private static ShaderIrOperImm Imm32_20(this long OpCode) - { - return new ShaderIrOperImm((int)(OpCode >> 20)); - } - - private static ShaderIrOperImmf Immf32_20(this long OpCode) - { - return new ShaderIrOperImmf(BitConverter.Int32BitsToSingle((int)(OpCode >> 20))); - } - - private static ShaderIrOperImm ImmU16_20(this long OpCode) - { - return new ShaderIrOperImm(OpCode.Read(20, 0xffff)); - } - - private static ShaderIrOperImm Imm19_20(this long OpCode) - { - int Value = OpCode.Read(20, 0x7ffff); - - bool Neg = OpCode.Read(56); - - if (Neg) - { - Value = -Value; - } - - return new ShaderIrOperImm(Value); - } - - private static ShaderIrOperImmf Immf19_20(this long OpCode) - { - uint Imm = (uint)(OpCode >> 20) & 0x7ffff; - - bool Neg = OpCode.Read(56); - - Imm <<= 12; - - if (Neg) - { - Imm |= 0x80000000; - } - - float Value = BitConverter.Int32BitsToSingle((int)Imm); - - return new ShaderIrOperImmf(Value); - } - - private static ShaderIrOperPred Pred0(this long OpCode) - { - return new ShaderIrOperPred(OpCode.Read(0, 7)); - } - - private static ShaderIrOperPred Pred3(this long OpCode) - { - return new ShaderIrOperPred(OpCode.Read(3, 7)); - } - - private static ShaderIrOperPred Pred12(this long OpCode) - { - return new ShaderIrOperPred(OpCode.Read(12, 7)); - } - - private static ShaderIrOperPred Pred29(this long OpCode) - { - return new ShaderIrOperPred(OpCode.Read(29, 7)); - } - - private static ShaderIrNode Pred39N(this long OpCode) - { - ShaderIrNode Node = OpCode.Pred39(); - - if (OpCode.Read(42)) - { - Node = new ShaderIrOp(ShaderIrInst.Bnot, Node); - } - - return Node; - } - - private static ShaderIrOperPred Pred39(this long OpCode) - { - return new ShaderIrOperPred(OpCode.Read(39, 7)); - } - - private static ShaderIrOperPred Pred48(this long OpCode) - { - return new ShaderIrOperPred(OpCode.Read(48, 7)); - } - - private static ShaderIrInst Cmp(this long OpCode) - { - switch (OpCode.Read(49, 7)) - { - case 1: return ShaderIrInst.Clt; - case 2: return ShaderIrInst.Ceq; - case 3: return ShaderIrInst.Cle; - case 4: return ShaderIrInst.Cgt; - case 5: return ShaderIrInst.Cne; - case 6: return ShaderIrInst.Cge; - } - - throw new ArgumentException(nameof(OpCode)); - } - - private static ShaderIrInst CmpF(this long OpCode) - { - switch (OpCode.Read(48, 0xf)) - { - case 0x1: return ShaderIrInst.Fclt; - case 0x2: return ShaderIrInst.Fceq; - case 0x3: return ShaderIrInst.Fcle; - case 0x4: return ShaderIrInst.Fcgt; - case 0x5: return ShaderIrInst.Fcne; - case 0x6: return ShaderIrInst.Fcge; - case 0x7: return ShaderIrInst.Fcnum; - case 0x8: return ShaderIrInst.Fcnan; - case 0x9: return ShaderIrInst.Fcltu; - case 0xa: return ShaderIrInst.Fcequ; - case 0xb: return ShaderIrInst.Fcleu; - case 0xc: return ShaderIrInst.Fcgtu; - case 0xd: return ShaderIrInst.Fcneu; - case 0xe: return ShaderIrInst.Fcgeu; - } - - throw new ArgumentException(nameof(OpCode)); - } - - private static ShaderIrInst BLop45(this long OpCode) - { - switch (OpCode.Read(45, 3)) - { - case 0: return ShaderIrInst.Band; - case 1: return ShaderIrInst.Bor; - case 2: return ShaderIrInst.Bxor; - } - - throw new ArgumentException(nameof(OpCode)); - } - - private static ShaderIrInst BLop24(this long OpCode) - { - switch (OpCode.Read(24, 3)) - { - case 0: return ShaderIrInst.Band; - case 1: return ShaderIrInst.Bor; - case 2: return ShaderIrInst.Bxor; - } - - throw new ArgumentException(nameof(OpCode)); - } - - private static ShaderIrNode PredNode(this long OpCode, ShaderIrNode Node) - { - ShaderIrOperPred Pred = OpCode.PredNode(); - - if (Pred.Index != ShaderIrOperPred.UnusedIndex) - { - bool Inv = OpCode.Read(19); - - Node = new ShaderIrCond(Pred, Node, Inv); - } - - return Node; - } - - private static ShaderIrOperPred PredNode(this long OpCode) - { - int Pred = OpCode.Read(16, 0xf); - - if (Pred != 0xf) - { - Pred &= 7; - } - - return new ShaderIrOperPred(Pred); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs deleted file mode 100644 index 35abdb7641..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeSpecial.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - static partial class ShaderDecode - { - public static void Out_R(ShaderIrBlock Block, long OpCode, int Position) - { - //TODO: Those registers have to be used for something - ShaderIrOperGpr Gpr0 = OpCode.Gpr0(); - ShaderIrOperGpr Gpr8 = OpCode.Gpr8(); - ShaderIrOperGpr Gpr20 = OpCode.Gpr20(); - - int Type = OpCode.Read(39, 3); - - if ((Type & 1) != 0) - { - Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Emit))); - } - - if ((Type & 2) != 0) - { - Block.AddNode(OpCode.PredNode(new ShaderIrOp(ShaderIrInst.Cut))); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs deleted file mode 100644 index f8c07f31ab..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs +++ /dev/null @@ -1,218 +0,0 @@ -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gal.Shader -{ - static class ShaderDecoder - { - private const long HeaderSize = 0x50; - - private const bool AddDbgComments = true; - - public static ShaderIrBlock[] Decode(IGalMemory Memory, long Start) - { - Dictionary Visited = new Dictionary(); - Dictionary VisitedEnd = new Dictionary(); - - Queue Blocks = new Queue(); - - long Beginning = Start + HeaderSize; - - ShaderIrBlock Enqueue(int Position, ShaderIrBlock Source = null) - { - if (!Visited.TryGetValue(Position, out ShaderIrBlock Output)) - { - Output = new ShaderIrBlock(Position); - - Blocks.Enqueue(Output); - - Visited.Add(Position, Output); - } - - if (Source != null) - { - Output.Sources.Add(Source); - } - - return Output; - } - - ShaderIrBlock Entry = Enqueue(0); - - while (Blocks.Count > 0) - { - ShaderIrBlock Current = Blocks.Dequeue(); - - FillBlock(Memory, Current, Beginning); - - //Set child blocks. "Branch" is the block the branch instruction - //points to (when taken), "Next" is the block at the next address, - //executed when the branch is not taken. For Unconditional Branches - //or end of shader, Next is null. - if (Current.Nodes.Count > 0) - { - ShaderIrNode LastNode = Current.GetLastNode(); - - ShaderIrOp InnerOp = GetInnermostOp(LastNode); - - if (InnerOp?.Inst == ShaderIrInst.Bra) - { - int Target = ((ShaderIrOperImm)InnerOp.OperandA).Value; - - Current.Branch = Enqueue(Target, Current); - } - - foreach (ShaderIrNode Node in Current.Nodes) - { - InnerOp = GetInnermostOp(Node); - - if (InnerOp is ShaderIrOp CurrOp && CurrOp.Inst == ShaderIrInst.Ssy) - { - int Target = ((ShaderIrOperImm)CurrOp.OperandA).Value; - - Enqueue(Target, Current); - } - } - - if (NodeHasNext(LastNode)) - { - Current.Next = Enqueue(Current.EndPosition); - } - } - - //If we have on the graph two blocks with the same end position, - //then we need to split the bigger block and have two small blocks, - //the end position of the bigger "Current" block should then be == to - //the position of the "Smaller" block. - while (VisitedEnd.TryGetValue(Current.EndPosition, out ShaderIrBlock Smaller)) - { - if (Current.Position > Smaller.Position) - { - ShaderIrBlock Temp = Smaller; - - Smaller = Current; - Current = Temp; - } - - Current.EndPosition = Smaller.Position; - Current.Next = Smaller; - Current.Branch = null; - - Current.Nodes.RemoveRange( - Current.Nodes.Count - Smaller.Nodes.Count, - Smaller.Nodes.Count); - - VisitedEnd[Smaller.EndPosition] = Smaller; - } - - VisitedEnd.Add(Current.EndPosition, Current); - } - - //Make and sort Graph blocks array by position. - ShaderIrBlock[] Graph = new ShaderIrBlock[Visited.Count]; - - while (Visited.Count > 0) - { - uint FirstPos = uint.MaxValue; - - foreach (ShaderIrBlock Block in Visited.Values) - { - if (FirstPos > (uint)Block.Position) - FirstPos = (uint)Block.Position; - } - - ShaderIrBlock Current = Visited[(int)FirstPos]; - - do - { - Graph[Graph.Length - Visited.Count] = Current; - - Visited.Remove(Current.Position); - - Current = Current.Next; - } - while (Current != null); - } - - return Graph; - } - - private static void FillBlock(IGalMemory Memory, ShaderIrBlock Block, long Beginning) - { - int Position = Block.Position; - - do - { - //Ignore scheduling instructions, which are written every 32 bytes. - if ((Position & 0x1f) == 0) - { - Position += 8; - - continue; - } - - uint Word0 = (uint)Memory.ReadInt32(Position + Beginning + 0); - uint Word1 = (uint)Memory.ReadInt32(Position + Beginning + 4); - - Position += 8; - - long OpCode = Word0 | (long)Word1 << 32; - - ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode); - - if (AddDbgComments) - { - string DbgOpCode = $"0x{(Position - 8):x16}: 0x{OpCode:x16} "; - - DbgOpCode += (Decode?.Method.Name ?? "???"); - - if (Decode == ShaderDecode.Bra || Decode == ShaderDecode.Ssy) - { - int Offset = ((int)(OpCode >> 20) << 8) >> 8; - - long Target = Position + Offset; - - DbgOpCode += " (0x" + Target.ToString("x16") + ")"; - } - - Block.AddNode(new ShaderIrCmnt(DbgOpCode)); - } - - if (Decode == null) - { - continue; - } - - Decode(Block, OpCode, Position); - } - while (!IsFlowChange(Block.GetLastNode())); - - Block.EndPosition = Position; - } - - private static bool IsFlowChange(ShaderIrNode Node) - { - return !NodeHasNext(GetInnermostOp(Node)); - } - - private static ShaderIrOp GetInnermostOp(ShaderIrNode Node) - { - if (Node is ShaderIrCond Cond) - { - Node = Cond.Child; - } - - return Node is ShaderIrOp Op ? Op : null; - } - - private static bool NodeHasNext(ShaderIrNode Node) - { - if (!(Node is ShaderIrOp Op)) - { - return true; - } - - return Op.Inst != ShaderIrInst.Exit && - Op.Inst != ShaderIrInst.Bra; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderHeader.cs b/Ryujinx.Graphics/Gal/Shader/ShaderHeader.cs deleted file mode 100644 index eca90fc3aa..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderHeader.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gal.Shader -{ - struct OmapTarget - { - public bool Red; - public bool Green; - public bool Blue; - public bool Alpha; - - public bool Enabled => Red || Green || Blue || Alpha; - - public bool ComponentEnabled(int Component) - { - switch (Component) - { - case 0: return Red; - case 1: return Green; - case 2: return Blue; - case 3: return Alpha; - } - - throw new ArgumentException(nameof(Component)); - } - } - - class ShaderHeader - { - public const int PointList = 1; - public const int LineStrip = 6; - public const int TriangleStrip = 7; - - public int SphType { get; private set; } - public int Version { get; private set; } - public int ShaderType { get; private set; } - public bool MrtEnable { get; private set; } - public bool KillsPixels { get; private set; } - public bool DoesGlobalStore { get; private set; } - public int SassVersion { get; private set; } - public bool DoesLoadOrStore { get; private set; } - public bool DoesFp64 { get; private set; } - public int StreamOutMask { get; private set; } - - public int ShaderLocalMemoryLowSize { get; private set; } - public int PerPatchAttributeCount { get; private set; } - - public int ShaderLocalMemoryHighSize { get; private set; } - public int ThreadsPerInputPrimitive { get; private set; } - - public int ShaderLocalMemoryCrsSize { get; private set; } - public int OutputTopology { get; private set; } - - public int MaxOutputVertexCount { get; private set; } - public int StoreReqStart { get; private set; } - public int StoreReqEnd { get; private set; } - - public OmapTarget[] OmapTargets { get; private set; } - public bool OmapSampleMask { get; private set; } - public bool OmapDepth { get; private set; } - - public ShaderHeader(IGalMemory Memory, long Position) - { - uint CommonWord0 = (uint)Memory.ReadInt32(Position + 0); - uint CommonWord1 = (uint)Memory.ReadInt32(Position + 4); - uint CommonWord2 = (uint)Memory.ReadInt32(Position + 8); - uint CommonWord3 = (uint)Memory.ReadInt32(Position + 12); - uint CommonWord4 = (uint)Memory.ReadInt32(Position + 16); - - SphType = ReadBits(CommonWord0, 0, 5); - Version = ReadBits(CommonWord0, 5, 5); - ShaderType = ReadBits(CommonWord0, 10, 4); - MrtEnable = ReadBits(CommonWord0, 14, 1) != 0; - KillsPixels = ReadBits(CommonWord0, 15, 1) != 0; - DoesGlobalStore = ReadBits(CommonWord0, 16, 1) != 0; - SassVersion = ReadBits(CommonWord0, 17, 4); - DoesLoadOrStore = ReadBits(CommonWord0, 26, 1) != 0; - DoesFp64 = ReadBits(CommonWord0, 27, 1) != 0; - StreamOutMask = ReadBits(CommonWord0, 28, 4); - - ShaderLocalMemoryLowSize = ReadBits(CommonWord1, 0, 24); - PerPatchAttributeCount = ReadBits(CommonWord1, 24, 8); - - ShaderLocalMemoryHighSize = ReadBits(CommonWord2, 0, 24); - ThreadsPerInputPrimitive = ReadBits(CommonWord2, 24, 8); - - ShaderLocalMemoryCrsSize = ReadBits(CommonWord3, 0, 24); - OutputTopology = ReadBits(CommonWord3, 24, 4); - - MaxOutputVertexCount = ReadBits(CommonWord4, 0, 12); - StoreReqStart = ReadBits(CommonWord4, 12, 8); - StoreReqEnd = ReadBits(CommonWord4, 24, 8); - - //Type 2 (fragment?) reading - uint Type2OmapTarget = (uint)Memory.ReadInt32(Position + 72); - uint Type2Omap = (uint)Memory.ReadInt32(Position + 76); - - OmapTargets = new OmapTarget[8]; - - for (int i = 0; i < OmapTargets.Length; i++) - { - int Offset = i * 4; - - OmapTargets[i] = new OmapTarget - { - Red = ReadBits(Type2OmapTarget, Offset + 0, 1) != 0, - Green = ReadBits(Type2OmapTarget, Offset + 1, 1) != 0, - Blue = ReadBits(Type2OmapTarget, Offset + 2, 1) != 0, - Alpha = ReadBits(Type2OmapTarget, Offset + 3, 1) != 0 - }; - } - - OmapSampleMask = ReadBits(Type2Omap, 0, 1) != 0; - OmapDepth = ReadBits(Type2Omap, 1, 1) != 0; - } - - public int DepthRegister - { - get - { - int Count = 0; - - for (int Index = 0; Index < OmapTargets.Length; Index++) - { - for (int Component = 0; Component < 4; Component++) - { - if (OmapTargets[Index].ComponentEnabled(Component)) - { - Count++; - } - } - } - - // Depth register is always two registers after the last color output - return Count + 1; - } - } - - private static int ReadBits(uint Word, int Offset, int BitWidth) - { - uint Mask = (1u << BitWidth) - 1u; - - return (int)((Word >> Offset) & Mask); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIpaMode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIpaMode.cs deleted file mode 100644 index b3713fa483..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIpaMode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - enum ShaderIpaMode - { - Pass = 0, - None = 1, - Constant = 2, - Sc = 3 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs deleted file mode 100644 index 00f8f6a5e5..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrAsg : ShaderIrNode - { - public ShaderIrNode Dst { get; set; } - public ShaderIrNode Src { get; set; } - - public ShaderIrAsg(ShaderIrNode Dst, ShaderIrNode Src) - { - this.Dst = Dst; - this.Src = Src; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs deleted file mode 100644 index 782f96261b..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrBlock - { - public int Position { get; set; } - public int EndPosition { get; set; } - - public ShaderIrBlock Next { get; set; } - public ShaderIrBlock Branch { get; set; } - - public List Sources { get; private set; } - - public List Nodes { get; private set; } - - public ShaderIrBlock(int Position) - { - this.Position = Position; - - Sources = new List(); - - Nodes = new List(); - } - - public void AddNode(ShaderIrNode Node) - { - Nodes.Add(Node); - } - - public ShaderIrNode[] GetNodes() - { - return Nodes.ToArray(); - } - - public ShaderIrNode GetLastNode() - { - if (Nodes.Count > 0) - { - return Nodes[Nodes.Count - 1]; - } - - return null; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrCmnt.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrCmnt.cs deleted file mode 100644 index 03031ec5b7..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrCmnt.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrCmnt : ShaderIrNode - { - public string Comment { get; private set; } - - public ShaderIrCmnt(string Comment) - { - this.Comment = Comment; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs deleted file mode 100644 index 8fb01660cf..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrCond : ShaderIrNode - { - public ShaderIrNode Pred { get; set; } - public ShaderIrNode Child { get; set; } - - public bool Not { get; private set; } - - public ShaderIrCond(ShaderIrNode Pred, ShaderIrNode Child, bool Not) - { - this.Pred = Pred; - this.Child = Child; - this.Not = Not; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs deleted file mode 100644 index 35dea61216..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs +++ /dev/null @@ -1,93 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - enum ShaderIrInst - { - Invalid, - - B_Start, - Band, - Bnot, - Bor, - Bxor, - B_End, - - F_Start, - Ceil, - - Fabs, - Fadd, - Fceq, - Fcequ, - Fcge, - Fcgeu, - Fcgt, - Fcgtu, - Fclamp, - Fcle, - Fcleu, - Fclt, - Fcltu, - Fcnan, - Fcne, - Fcneu, - Fcnum, - Fcos, - Fex2, - Ffma, - Flg2, - Floor, - Fmax, - Fmin, - Fmul, - Fneg, - Frcp, - Frsq, - Fsin, - Fsqrt, - Ftos, - Ftou, - Ipa, - Texb, - Texs, - Trunc, - F_End, - - I_Start, - Abs, - Add, - And, - Asr, - Ceq, - Cge, - Cgt, - Clamps, - Clampu, - Cle, - Clt, - Cne, - Lsl, - Lsr, - Max, - Min, - Mul, - Neg, - Not, - Or, - Stof, - Sub, - Texq, - Txlf, - Utof, - Xor, - I_End, - - Bra, - Exit, - Kil, - Ssy, - Sync, - - Emit, - Cut - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs deleted file mode 100644 index afb7503be8..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrMeta { } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaIpa.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaIpa.cs deleted file mode 100644 index 3b884621b0..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaIpa.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrMetaIpa : ShaderIrMeta - { - public ShaderIpaMode Mode { get; private set; } - - public ShaderIrMetaIpa(ShaderIpaMode Mode) - { - this.Mode = Mode; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs deleted file mode 100644 index 82f3bb774a..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrMetaTex : ShaderIrMeta - { - public int Elem { get; private set; } - - public ShaderIrMetaTex(int Elem) - { - this.Elem = Elem; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs deleted file mode 100644 index 92871137f6..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrMetaTexq : ShaderIrMeta - { - public ShaderTexqInfo Info { get; private set; } - - public int Elem { get; private set; } - - public ShaderIrMetaTexq(ShaderTexqInfo Info, int Elem) - { - this.Info = Info; - this.Elem = Elem; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs deleted file mode 100644 index 2648164a11..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrNode { } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs deleted file mode 100644 index 12a6123c31..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrOp : ShaderIrNode - { - public ShaderIrInst Inst { get; private set; } - public ShaderIrNode OperandA { get; set; } - public ShaderIrNode OperandB { get; set; } - public ShaderIrNode OperandC { get; set; } - public ShaderIrMeta MetaData { get; set; } - - public ShaderIrOp( - ShaderIrInst Inst, - ShaderIrNode OperandA = null, - ShaderIrNode OperandB = null, - ShaderIrNode OperandC = null, - ShaderIrMeta MetaData = null) - { - this.Inst = Inst; - this.OperandA = OperandA; - this.OperandB = OperandB; - this.OperandC = OperandC; - this.MetaData = MetaData; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs deleted file mode 100644 index f17d9c0e62..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrOperAbuf : ShaderIrNode - { - public int Offs { get; private set; } - - public ShaderIrNode Vertex { get; private set; } - - public ShaderIrOperAbuf(int Offs, ShaderIrNode Vertex) - { - this.Offs = Offs; - this.Vertex = Vertex; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs deleted file mode 100644 index b040c5c63e..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrOperCbuf : ShaderIrNode - { - public int Index { get; private set; } - public int Pos { get; set; } - - public ShaderIrNode Offs { get; private set; } - - public ShaderIrOperCbuf(int Index, int Pos, ShaderIrNode Offs = null) - { - this.Index = Index; - this.Pos = Pos; - this.Offs = Offs; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs deleted file mode 100644 index b4a5cab4d4..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrOperGpr : ShaderIrNode - { - public const int ZRIndex = 0xff; - - public bool IsConst => Index == ZRIndex; - - public bool IsValidRegister => (uint)Index <= ZRIndex; - - public int Index { get; set; } - public int HalfPart { get; set; } - - public ShaderRegisterSize RegisterSize { get; private set; } - - public ShaderIrOperGpr(int Index) - { - this.Index = Index; - - RegisterSize = ShaderRegisterSize.Single; - } - - public ShaderIrOperGpr(int Index, int HalfPart) - { - this.Index = Index; - this.HalfPart = HalfPart; - - RegisterSize = ShaderRegisterSize.Half; - } - - public static ShaderIrOperGpr MakeTemporary(int Index = 0) - { - return new ShaderIrOperGpr(0x100 + Index); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs deleted file mode 100644 index ba2c2c9b24..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrOperImm : ShaderIrNode - { - public int Value { get; private set; } - - public ShaderIrOperImm(int Value) - { - this.Value = Value; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs deleted file mode 100644 index 3c27e48361..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrOperImmf : ShaderIrNode - { - public float Value { get; private set; } - - public ShaderIrOperImmf(float Value) - { - this.Value = Value; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs deleted file mode 100644 index 74cca0efef..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - class ShaderIrOperPred : ShaderIrNode - { - public const int UnusedIndex = 0x7; - public const int NeverExecute = 0xf; - - public bool IsConst => Index >= UnusedIndex; - - public int Index { get; set; } - - public ShaderIrOperPred(int Index) - { - this.Index = Index; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs deleted file mode 100644 index 177e36c3e1..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gal.Shader -{ - static class ShaderOpCodeTable - { - private const int EncodingBits = 14; - - private class ShaderDecodeEntry - { - public ShaderDecodeFunc Func; - - public int XBits; - - public ShaderDecodeEntry(ShaderDecodeFunc Func, int XBits) - { - this.Func = Func; - this.XBits = XBits; - } - } - - private static ShaderDecodeEntry[] OpCodes; - - static ShaderOpCodeTable() - { - OpCodes = new ShaderDecodeEntry[1 << EncodingBits]; - -#region Instructions - Set("0100110000000x", ShaderDecode.Bfe_C); - Set("0011100x00000x", ShaderDecode.Bfe_I); - Set("0101110000000x", ShaderDecode.Bfe_R); - Set("111000100100xx", ShaderDecode.Bra); - Set("111000110000xx", ShaderDecode.Exit); - Set("0100110010101x", ShaderDecode.F2f_C); - Set("0011100x10101x", ShaderDecode.F2f_I); - Set("0101110010101x", ShaderDecode.F2f_R); - Set("0100110010110x", ShaderDecode.F2i_C); - Set("0011100x10110x", ShaderDecode.F2i_I); - Set("0101110010110x", ShaderDecode.F2i_R); - Set("0100110001011x", ShaderDecode.Fadd_C); - Set("0011100x01011x", ShaderDecode.Fadd_I); - Set("000010xxxxxxxx", ShaderDecode.Fadd_I32); - Set("0101110001011x", ShaderDecode.Fadd_R); - Set("010010011xxxxx", ShaderDecode.Ffma_CR); - Set("0011001x1xxxxx", ShaderDecode.Ffma_I); - Set("010100011xxxxx", ShaderDecode.Ffma_RC); - Set("010110011xxxxx", ShaderDecode.Ffma_RR); - Set("0100110001101x", ShaderDecode.Fmul_C); - Set("0011100x01101x", ShaderDecode.Fmul_I); - Set("00011110xxxxxx", ShaderDecode.Fmul_I32); - Set("0101110001101x", ShaderDecode.Fmul_R); - Set("0100110001100x", ShaderDecode.Fmnmx_C); - Set("0011100x01100x", ShaderDecode.Fmnmx_I); - Set("0101110001100x", ShaderDecode.Fmnmx_R); - Set("0100100xxxxxxx", ShaderDecode.Fset_C); - Set("0011000xxxxxxx", ShaderDecode.Fset_I); - Set("01011000xxxxxx", ShaderDecode.Fset_R); - Set("010010111011xx", ShaderDecode.Fsetp_C); - Set("0011011x1011xx", ShaderDecode.Fsetp_I); - Set("010110111011xx", ShaderDecode.Fsetp_R); - Set("0101110100010x", ShaderDecode.Hadd2_R); - Set("0101110100001x", ShaderDecode.Hmul2_R); - Set("0100110010111x", ShaderDecode.I2f_C); - Set("0011100x10111x", ShaderDecode.I2f_I); - Set("0101110010111x", ShaderDecode.I2f_R); - Set("0100110011100x", ShaderDecode.I2i_C); - Set("0011100x11100x", ShaderDecode.I2i_I); - Set("0101110011100x", ShaderDecode.I2i_R); - Set("0100110000010x", ShaderDecode.Iadd_C); - Set("0011100000010x", ShaderDecode.Iadd_I); - Set("0001110x0xxxxx", ShaderDecode.Iadd_I32); - Set("0101110000010x", ShaderDecode.Iadd_R); - Set("010011001100xx", ShaderDecode.Iadd3_C); - Set("001110001100xx", ShaderDecode.Iadd3_I); - Set("010111001100xx", ShaderDecode.Iadd3_R); - Set("0100110000100x", ShaderDecode.Imnmx_C); - Set("0011100x00100x", ShaderDecode.Imnmx_I); - Set("0101110000100x", ShaderDecode.Imnmx_R); - Set("1110111111010x", ShaderDecode.Isberd); - Set("11100000xxxxxx", ShaderDecode.Ipa); - Set("0100110000011x", ShaderDecode.Iscadd_C); - Set("0011100x00011x", ShaderDecode.Iscadd_I); - Set("0101110000011x", ShaderDecode.Iscadd_R); - Set("010010110101xx", ShaderDecode.Iset_C); - Set("001101100101xx", ShaderDecode.Iset_I); - Set("010110110101xx", ShaderDecode.Iset_R); - Set("010010110110xx", ShaderDecode.Isetp_C); - Set("0011011x0110xx", ShaderDecode.Isetp_I); - Set("010110110110xx", ShaderDecode.Isetp_R); - Set("111000110011xx", ShaderDecode.Kil); - Set("1110111111011x", ShaderDecode.Ld_A); - Set("1110111110010x", ShaderDecode.Ld_C); - Set("0100110001000x", ShaderDecode.Lop_C); - Set("0011100001000x", ShaderDecode.Lop_I); - Set("000001xxxxxxxx", ShaderDecode.Lop_I32); - Set("0101110001000x", ShaderDecode.Lop_R); - Set("0100110010011x", ShaderDecode.Mov_C); - Set("0011100x10011x", ShaderDecode.Mov_I); - Set("000000010000xx", ShaderDecode.Mov_I32); - Set("0101110010011x", ShaderDecode.Mov_R); - Set("1111000011001x", ShaderDecode.Mov_S); - Set("0101000010000x", ShaderDecode.Mufu); - Set("1111101111100x", ShaderDecode.Out_R); - Set("0101000010010x", ShaderDecode.Psetp); - Set("0100110010010x", ShaderDecode.Rro_C); - Set("0011100x10010x", ShaderDecode.Rro_I); - Set("0101110010010x", ShaderDecode.Rro_R); - Set("0100110010100x", ShaderDecode.Sel_C); - Set("0011100010100x", ShaderDecode.Sel_I); - Set("0101110010100x", ShaderDecode.Sel_R); - Set("0100110001001x", ShaderDecode.Shl_C); - Set("0011100x01001x", ShaderDecode.Shl_I); - Set("0101110001001x", ShaderDecode.Shl_R); - Set("0100110000101x", ShaderDecode.Shr_C); - Set("0011100x00101x", ShaderDecode.Shr_I); - Set("0101110000101x", ShaderDecode.Shr_R); - Set("111000101001xx", ShaderDecode.Ssy); - Set("1110111111110x", ShaderDecode.St_A); - Set("1111000011111x", ShaderDecode.Sync); - Set("110000xxxx111x", ShaderDecode.Tex); - Set("1101111010111x", ShaderDecode.Tex_B); - Set("1101111101001x", ShaderDecode.Texq); - Set("1101x00xxxxxxx", ShaderDecode.Texs); - Set("1101101xxxxxxx", ShaderDecode.Tlds); - Set("01011111xxxxxx", ShaderDecode.Vmad); - Set("0100111xxxxxxx", ShaderDecode.Xmad_CR); - Set("0011011x00xxxx", ShaderDecode.Xmad_I); - Set("010100010xxxxx", ShaderDecode.Xmad_RC); - Set("0101101100xxxx", ShaderDecode.Xmad_RR); -#endregion - } - - private static void Set(string Encoding, ShaderDecodeFunc Func) - { - if (Encoding.Length != EncodingBits) - { - throw new ArgumentException(nameof(Encoding)); - } - - int Bit = Encoding.Length - 1; - int Value = 0; - int XMask = 0; - int XBits = 0; - - int[] XPos = new int[Encoding.Length]; - - for (int Index = 0; Index < Encoding.Length; Index++, Bit--) - { - char Chr = Encoding[Index]; - - if (Chr == '1') - { - Value |= 1 << Bit; - } - else if (Chr == 'x') - { - XMask |= 1 << Bit; - - XPos[XBits++] = Bit; - } - } - - XMask = ~XMask; - - ShaderDecodeEntry Entry = new ShaderDecodeEntry(Func, XBits); - - for (int Index = 0; Index < (1 << XBits); Index++) - { - Value &= XMask; - - for (int X = 0; X < XBits; X++) - { - Value |= ((Index >> X) & 1) << XPos[X]; - } - - if (OpCodes[Value] == null || OpCodes[Value].XBits > XBits) - { - OpCodes[Value] = Entry; - } - } - } - - public static ShaderDecodeFunc GetDecoder(long OpCode) - { - return OpCodes[(ulong)OpCode >> (64 - EncodingBits)]?.Func; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOper.cs deleted file mode 100644 index aa48548282..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOper.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - enum ShaderOper - { - CR, - Imm, - Immf, - RC, - RR - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderRegisterSize.cs b/Ryujinx.Graphics/Gal/Shader/ShaderRegisterSize.cs deleted file mode 100644 index eb37359bf4..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderRegisterSize.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - enum ShaderRegisterSize - { - Half, - Single, - Double - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs b/Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs deleted file mode 100644 index 9158662ccd..0000000000 --- a/Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ryujinx.Graphics.Gal.Shader -{ - enum ShaderTexqInfo - { - Dimension = 1, - TextureType = 2, - SamplePos = 5, - Filter = 16, - Lod = 18, - Wrap = 20, - BorderColor = 22 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs deleted file mode 100644 index ef47ca2e1b..0000000000 --- a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public class ShaderDeclInfo - { - public string Name { get; private set; } - - public int Index { get; private set; } - public bool IsCb { get; private set; } - public int Cbuf { get; private set; } - public int Size { get; private set; } - - public ShaderDeclInfo( - string Name, - int Index, - bool IsCb = false, - int Cbuf = 0, - int Size = 1) - { - this.Name = Name; - this.Index = Index; - this.IsCb = IsCb; - this.Cbuf = Cbuf; - this.Size = Size; - } - - internal void Enlarge(int NewSize) - { - if (Size < NewSize) - { - Size = NewSize; - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/ShaderDumper.cs b/Ryujinx.Graphics/Gal/ShaderDumper.cs index 21e92491a7..726447e426 100644 --- a/Ryujinx.Graphics/Gal/ShaderDumper.cs +++ b/Ryujinx.Graphics/Gal/ShaderDumper.cs @@ -5,67 +5,67 @@ namespace Ryujinx.Graphics.Gal { static class ShaderDumper { - private static string RuntimeDir; + private static string _runtimeDir; public static int DumpIndex { get; private set; } = 1; - public static void Dump(IGalMemory Memory, long Position, GalShaderType Type, string ExtSuffix = "") + public static void Dump(IGalMemory memory, long position, GalShaderType type, string extSuffix = "") { if (!IsDumpEnabled()) { return; } - string FileName = "Shader" + DumpIndex.ToString("d4") + "." + ShaderExtension(Type) + ExtSuffix + ".bin"; + string fileName = "Shader" + DumpIndex.ToString("d4") + "." + ShaderExtension(type) + extSuffix + ".bin"; - string FullPath = Path.Combine(FullDir(), FileName); - string CodePath = Path.Combine(CodeDir(), FileName); + string fullPath = Path.Combine(FullDir(), fileName); + string codePath = Path.Combine(CodeDir(), fileName); DumpIndex++; - using (FileStream FullFile = File.Create(FullPath)) - using (FileStream CodeFile = File.Create(CodePath)) + using (FileStream fullFile = File.Create(fullPath)) + using (FileStream codeFile = File.Create(codePath)) { - BinaryWriter FullWriter = new BinaryWriter(FullFile); - BinaryWriter CodeWriter = new BinaryWriter(CodeFile); + BinaryWriter fullWriter = new BinaryWriter(fullFile); + BinaryWriter codeWriter = new BinaryWriter(codeFile); for (long i = 0; i < 0x50; i += 4) { - FullWriter.Write(Memory.ReadInt32(Position + i)); + fullWriter.Write(memory.ReadInt32(position + i)); } - long Offset = 0; + long offset = 0; - ulong Instruction = 0; + ulong instruction = 0; //Dump until a NOP instruction is found - while ((Instruction >> 48 & 0xfff8) != 0x50b0) + while ((instruction >> 48 & 0xfff8) != 0x50b0) { - uint Word0 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 0); - uint Word1 = (uint)Memory.ReadInt32(Position + 0x50 + Offset + 4); + uint word0 = (uint)memory.ReadInt32(position + 0x50 + offset + 0); + uint word1 = (uint)memory.ReadInt32(position + 0x50 + offset + 4); - Instruction = Word0 | (ulong)Word1 << 32; + instruction = word0 | (ulong)word1 << 32; //Zero instructions (other kind of NOP) stop immediatly, //this is to avoid two rows of zeroes - if (Instruction == 0) + if (instruction == 0) { break; } - FullWriter.Write(Instruction); - CodeWriter.Write(Instruction); + fullWriter.Write(instruction); + codeWriter.Write(instruction); - Offset += 8; + offset += 8; } //Align to meet nvdisasm requeriments - while (Offset % 0x20 != 0) + while (offset % 0x20 != 0) { - FullWriter.Write(0); - CodeWriter.Write(0); + fullWriter.Write(0); + codeWriter.Write(0); - Offset += 4; + offset += 4; } } } @@ -87,37 +87,37 @@ namespace Ryujinx.Graphics.Gal private static string DumpDir() { - if (string.IsNullOrEmpty(RuntimeDir)) + if (string.IsNullOrEmpty(_runtimeDir)) { - int Index = 1; + int index = 1; do { - RuntimeDir = Path.Combine(GraphicsConfig.ShadersDumpPath, "Dumps" + Index.ToString("d2")); + _runtimeDir = Path.Combine(GraphicsConfig.ShadersDumpPath, "Dumps" + index.ToString("d2")); - Index++; + index++; } - while (Directory.Exists(RuntimeDir)); + while (Directory.Exists(_runtimeDir)); - Directory.CreateDirectory(RuntimeDir); + Directory.CreateDirectory(_runtimeDir); } - return RuntimeDir; + return _runtimeDir; } - private static string CreateAndReturn(string Dir) + private static string CreateAndReturn(string dir) { - if (!Directory.Exists(Dir)) + if (!Directory.Exists(dir)) { - Directory.CreateDirectory(Dir); + Directory.CreateDirectory(dir); } - return Dir; + return dir; } - private static string ShaderExtension(GalShaderType Type) + private static string ShaderExtension(GalShaderType type) { - switch (Type) + switch (type) { case GalShaderType.Vertex: return "vert"; case GalShaderType.TessControl: return "tesc"; @@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Gal case GalShaderType.Geometry: return "geom"; case GalShaderType.Fragment: return "frag"; - default: throw new ArgumentException(nameof(Type)); + default: throw new ArgumentException(nameof(type)); } } } diff --git a/Ryujinx.Graphics/Gal/ShaderException.cs b/Ryujinx.Graphics/Gal/ShaderException.cs index 9bc87ff3db..b0aff42bf5 100644 --- a/Ryujinx.Graphics/Gal/ShaderException.cs +++ b/Ryujinx.Graphics/Gal/ShaderException.cs @@ -6,6 +6,6 @@ namespace Ryujinx.Graphics.Gal { public ShaderException() : base() { } - public ShaderException(string Message) : base(Message) { } + public ShaderException(string message) : base(message) { } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/GpuMethodCall.cs b/Ryujinx.Graphics/GpuMethodCall.cs index 762d10f1d0..4a310b0751 100644 --- a/Ryujinx.Graphics/GpuMethodCall.cs +++ b/Ryujinx.Graphics/GpuMethodCall.cs @@ -10,15 +10,15 @@ namespace Ryujinx.Graphics public bool IsLastCall => MethodCount <= 1; public GpuMethodCall( - int Method, - int Argument, - int SubChannel = 0, - int MethodCount = 0) + int method, + int argument, + int subChannel = 0, + int methodCount = 0) { - this.Method = Method; - this.Argument = Argument; - this.SubChannel = SubChannel; - this.MethodCount = MethodCount; + Method = method; + Argument = argument; + SubChannel = subChannel; + MethodCount = methodCount; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/GpuResourceManager.cs b/Ryujinx.Graphics/GpuResourceManager.cs index d46129516d..740e6be86d 100644 --- a/Ryujinx.Graphics/GpuResourceManager.cs +++ b/Ryujinx.Graphics/GpuResourceManager.cs @@ -11,100 +11,129 @@ namespace Ryujinx.Graphics { None, Texture, + TextureArrayLayer, ColorBuffer, ZetaBuffer } - private NvGpu Gpu; + private NvGpu _gpu; - private HashSet[] UploadedKeys; + private HashSet[] _uploadedKeys; - private Dictionary ImageTypes; + private Dictionary _imageTypes; + private Dictionary _mirroredTextures; - public GpuResourceManager(NvGpu Gpu) + public GpuResourceManager(NvGpu gpu) { - this.Gpu = Gpu; + _gpu = gpu; - UploadedKeys = new HashSet[(int)NvGpuBufferType.Count]; + _uploadedKeys = new HashSet[(int)NvGpuBufferType.Count]; - for (int Index = 0; Index < UploadedKeys.Length; Index++) + for (int index = 0; index < _uploadedKeys.Length; index++) { - UploadedKeys[Index] = new HashSet(); + _uploadedKeys[index] = new HashSet(); } - ImageTypes = new Dictionary(); + _imageTypes = new Dictionary(); + _mirroredTextures = new Dictionary(); } - public void SendColorBuffer(NvGpuVmm Vmm, long Position, int Attachment, GalImage NewImage) + public void SendColorBuffer(NvGpuVmm vmm, long position, int attachment, GalImage newImage) { - long Size = (uint)ImageUtils.GetSize(NewImage); + long size = (uint)ImageUtils.GetSize(newImage); - ImageTypes[Position] = ImageType.ColorBuffer; + _imageTypes[position] = ImageType.ColorBuffer; - if (!TryReuse(Vmm, Position, NewImage)) + if (!TryReuse(vmm, position, newImage)) { - Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage); + _gpu.Renderer.Texture.Create(position, (int)size, newImage); } - Gpu.Renderer.RenderTarget.BindColor(Position, Attachment); + _gpu.Renderer.RenderTarget.BindColor(position, attachment); } - public void SendZetaBuffer(NvGpuVmm Vmm, long Position, GalImage NewImage) + public void SendZetaBuffer(NvGpuVmm vmm, long position, GalImage newImage) { - long Size = (uint)ImageUtils.GetSize(NewImage); + long size = (uint)ImageUtils.GetSize(newImage); - ImageTypes[Position] = ImageType.ZetaBuffer; + _imageTypes[position] = ImageType.ZetaBuffer; - if (!TryReuse(Vmm, Position, NewImage)) + if (!TryReuse(vmm, position, newImage)) { - Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage); + _gpu.Renderer.Texture.Create(position, (int)size, newImage); } - Gpu.Renderer.RenderTarget.BindZeta(Position); + _gpu.Renderer.RenderTarget.BindZeta(position); } - public void SendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage) + public void SendTexture(NvGpuVmm vmm, long position, GalImage newImage) { - PrepareSendTexture(Vmm, Position, NewImage); + PrepareSendTexture(vmm, position, newImage); - ImageTypes[Position] = ImageType.Texture; + _imageTypes[position] = ImageType.Texture; } - private void PrepareSendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage) + public bool TryGetTextureLayer(long position, out int layerIndex) { - long Size = ImageUtils.GetSize(NewImage); - - bool SkipCheck = false; - - if (ImageTypes.TryGetValue(Position, out ImageType OldType)) + if (_mirroredTextures.TryGetValue(position, out layerIndex)) { - if (OldType == ImageType.ColorBuffer || OldType == ImageType.ZetaBuffer) + ImageType type = _imageTypes[position]; + + // FIXME(thog): I'm actually unsure if we should deny all other image type, gpu testing needs to be done here. + if (type != ImageType.Texture && type != ImageType.TextureArrayLayer) + { + layerIndex = -1; + return false; + } + + return true; + } + + layerIndex = -1; + return false; + } + + public void SetTextureArrayLayer(long position, int layerIndex) + { + _imageTypes[position] = ImageType.TextureArrayLayer; + _mirroredTextures[position] = layerIndex; + } + + private void PrepareSendTexture(NvGpuVmm vmm, long position, GalImage newImage) + { + long size = ImageUtils.GetSize(newImage); + + bool skipCheck = false; + + if (_imageTypes.TryGetValue(position, out ImageType oldType)) + { + if (oldType == ImageType.ColorBuffer || oldType == ImageType.ZetaBuffer) { //Avoid data destruction - MemoryRegionModified(Vmm, Position, Size, NvGpuBufferType.Texture); + MemoryRegionModified(vmm, position, size, NvGpuBufferType.Texture); - SkipCheck = true; + skipCheck = true; } } - if (SkipCheck || !MemoryRegionModified(Vmm, Position, Size, NvGpuBufferType.Texture)) + if (skipCheck || !MemoryRegionModified(vmm, position, size, NvGpuBufferType.Texture)) { - if (TryReuse(Vmm, Position, NewImage)) + if (TryReuse(vmm, position, newImage)) { return; } } - byte[] Data = ImageUtils.ReadTexture(Vmm, NewImage, Position); + byte[] data = ImageUtils.ReadTexture(vmm, newImage, position); - Gpu.Renderer.Texture.Create(Position, Data, NewImage); + _gpu.Renderer.Texture.Create(position, data, newImage); } - private bool TryReuse(NvGpuVmm Vmm, long Position, GalImage NewImage) + private bool TryReuse(NvGpuVmm vmm, long position, GalImage newImage) { - if (Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage) && CachedImage.SizeMatches(NewImage)) + if (_gpu.Renderer.Texture.TryGetImage(position, out GalImage cachedImage) && cachedImage.TextureTarget == newImage.TextureTarget && cachedImage.SizeMatches(newImage)) { - Gpu.Renderer.RenderTarget.Reinterpret(Position, NewImage); + _gpu.Renderer.RenderTarget.Reinterpret(position, newImage); return true; } @@ -112,29 +141,29 @@ namespace Ryujinx.Graphics return false; } - public bool MemoryRegionModified(NvGpuVmm Vmm, long Position, long Size, NvGpuBufferType Type) + public bool MemoryRegionModified(NvGpuVmm vmm, long position, long size, NvGpuBufferType type) { - HashSet Uploaded = UploadedKeys[(int)Type]; + HashSet uploaded = _uploadedKeys[(int)type]; - if (!Uploaded.Add(Position)) + if (!uploaded.Add(position)) { return false; } - return Vmm.IsRegionModified(Position, Size, Type); + return vmm.IsRegionModified(position, size, type); } public void ClearPbCache() { - for (int Index = 0; Index < UploadedKeys.Length; Index++) + for (int index = 0; index < _uploadedKeys.Length; index++) { - UploadedKeys[Index].Clear(); + _uploadedKeys[index].Clear(); } } - public void ClearPbCache(NvGpuBufferType Type) + public void ClearPbCache(NvGpuBufferType type) { - UploadedKeys[(int)Type].Clear(); + _uploadedKeys[(int)type].Clear(); } } } diff --git a/Ryujinx.Graphics/Graphics3d/INvGpuEngine.cs b/Ryujinx.Graphics/Graphics3d/INvGpuEngine.cs index c2474a1718..aa0db6826f 100644 --- a/Ryujinx.Graphics/Graphics3d/INvGpuEngine.cs +++ b/Ryujinx.Graphics/Graphics3d/INvGpuEngine.cs @@ -6,6 +6,6 @@ namespace Ryujinx.Graphics.Graphics3d { int[] Registers { get; } - void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall); + void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/MacroInterpreter.cs b/Ryujinx.Graphics/Graphics3d/MacroInterpreter.cs index a124aca484..845762133c 100644 --- a/Ryujinx.Graphics/Graphics3d/MacroInterpreter.cs +++ b/Ryujinx.Graphics/Graphics3d/MacroInterpreter.cs @@ -42,78 +42,78 @@ namespace Ryujinx.Graphics.Graphics3d BitwiseNotAnd = 12 } - private NvGpuFifo PFifo; - private INvGpuEngine Engine; + private NvGpuFifo _pFifo; + private INvGpuEngine _engine; public Queue Fifo { get; private set; } - private int[] Gprs; + private int[] _gprs; - private int MethAddr; - private int MethIncr; + private int _methAddr; + private int _methIncr; - private bool Carry; + private bool _carry; - private int OpCode; + private int _opCode; - private int PipeOp; + private int _pipeOp; - private int Pc; + private int _pc; - public MacroInterpreter(NvGpuFifo PFifo, INvGpuEngine Engine) + public MacroInterpreter(NvGpuFifo pFifo, INvGpuEngine engine) { - this.PFifo = PFifo; - this.Engine = Engine; + _pFifo = pFifo; + _engine = engine; Fifo = new Queue(); - Gprs = new int[8]; + _gprs = new int[8]; } - public void Execute(NvGpuVmm Vmm, int[] Mme, int Position, int Param) + public void Execute(NvGpuVmm vmm, int[] mme, int position, int param) { Reset(); - Gprs[1] = Param; + _gprs[1] = param; - Pc = Position; + _pc = position; - FetchOpCode(Mme); + FetchOpCode(mme); - while (Step(Vmm, Mme)); + while (Step(vmm, mme)); //Due to the delay slot, we still need to execute //one more instruction before we actually exit. - Step(Vmm, Mme); + Step(vmm, mme); } private void Reset() { - for (int Index = 0; Index < Gprs.Length; Index++) + for (int index = 0; index < _gprs.Length; index++) { - Gprs[Index] = 0; + _gprs[index] = 0; } - MethAddr = 0; - MethIncr = 0; + _methAddr = 0; + _methIncr = 0; - Carry = false; + _carry = false; } - private bool Step(NvGpuVmm Vmm, int[] Mme) + private bool Step(NvGpuVmm vmm, int[] mme) { - int BaseAddr = Pc - 1; + int baseAddr = _pc - 1; - FetchOpCode(Mme); + FetchOpCode(mme); - if ((OpCode & 7) < 7) + if ((_opCode & 7) < 7) { //Operation produces a value. - AssignmentOperation AsgOp = (AssignmentOperation)((OpCode >> 4) & 7); + AssignmentOperation asgOp = (AssignmentOperation)((_opCode >> 4) & 7); - int Result = GetAluResult(); + int result = GetAluResult(); - switch (AsgOp) + switch (asgOp) { //Fetch parameter and ignore result. case AssignmentOperation.IgnoreAndFetch: @@ -126,7 +126,7 @@ namespace Ryujinx.Graphics.Graphics3d //Move result. case AssignmentOperation.Move: { - SetDstGpr(Result); + SetDstGpr(result); break; } @@ -134,9 +134,9 @@ namespace Ryujinx.Graphics.Graphics3d //Move result and use as Method Address. case AssignmentOperation.MoveAndSetMaddr: { - SetDstGpr(Result); + SetDstGpr(result); - SetMethAddr(Result); + SetMethAddr(result); break; } @@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.Graphics3d { SetDstGpr(FetchParam()); - Send(Vmm, Result); + Send(vmm, result); break; } @@ -154,9 +154,9 @@ namespace Ryujinx.Graphics.Graphics3d //Move and send result. case AssignmentOperation.MoveAndSend: { - SetDstGpr(Result); + SetDstGpr(result); - Send(Vmm, Result); + Send(vmm, result); break; } @@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Graphics3d { SetDstGpr(FetchParam()); - SetMethAddr(Result); + SetMethAddr(result); break; } @@ -174,11 +174,11 @@ namespace Ryujinx.Graphics.Graphics3d //Move result and use as Method Address, then fetch and send paramter. case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend: { - SetDstGpr(Result); + SetDstGpr(result); - SetMethAddr(Result); + SetMethAddr(result); - Send(Vmm, FetchParam()); + Send(vmm, FetchParam()); break; } @@ -186,11 +186,11 @@ namespace Ryujinx.Graphics.Graphics3d //Move result and use as Method Address, then send bits 17:12 of result. case AssignmentOperation.MoveAndSetMaddrThenSendHigh: { - SetDstGpr(Result); + SetDstGpr(result); - SetMethAddr(Result); + SetMethAddr(result); - Send(Vmm, (Result >> 12) & 0x3f); + Send(vmm, (result >> 12) & 0x3f); break; } @@ -199,50 +199,50 @@ namespace Ryujinx.Graphics.Graphics3d else { //Branch. - bool OnNotZero = ((OpCode >> 4) & 1) != 0; + bool onNotZero = ((_opCode >> 4) & 1) != 0; - bool Taken = OnNotZero + bool taken = onNotZero ? GetGprA() != 0 : GetGprA() == 0; - if (Taken) + if (taken) { - Pc = BaseAddr + GetImm(); + _pc = baseAddr + GetImm(); - bool NoDelays = (OpCode & 0x20) != 0; + bool noDelays = (_opCode & 0x20) != 0; - if (NoDelays) + if (noDelays) { - FetchOpCode(Mme); + FetchOpCode(mme); } return true; } } - bool Exit = (OpCode & 0x80) != 0; + bool exit = (_opCode & 0x80) != 0; - return !Exit; + return !exit; } - private void FetchOpCode(int[] Mme) + private void FetchOpCode(int[] mme) { - OpCode = PipeOp; + _opCode = _pipeOp; - PipeOp = Mme[Pc++]; + _pipeOp = mme[_pc++]; } private int GetAluResult() { - AluOperation Op = (AluOperation)(OpCode & 7); + AluOperation op = (AluOperation)(_opCode & 7); - switch (Op) + switch (op) { case AluOperation.AluReg: { - AluRegOperation AluOp = (AluRegOperation)((OpCode >> 17) & 0x1f); + AluRegOperation aluOp = (AluRegOperation)((_opCode >> 17) & 0x1f); - return GetAluResult(AluOp, GetGprA(), GetGprB()); + return GetAluResult(aluOp, GetGprA(), GetGprB()); } case AluOperation.AddImmediate: @@ -254,40 +254,40 @@ namespace Ryujinx.Graphics.Graphics3d case AluOperation.BitfieldExtractLslImm: case AluOperation.BitfieldExtractLslReg: { - int BfSrcBit = (OpCode >> 17) & 0x1f; - int BfSize = (OpCode >> 22) & 0x1f; - int BfDstBit = (OpCode >> 27) & 0x1f; + int bfSrcBit = (_opCode >> 17) & 0x1f; + int bfSize = (_opCode >> 22) & 0x1f; + int bfDstBit = (_opCode >> 27) & 0x1f; - int BfMask = (1 << BfSize) - 1; + int bfMask = (1 << bfSize) - 1; - int Dst = GetGprA(); - int Src = GetGprB(); + int dst = GetGprA(); + int src = GetGprB(); - switch (Op) + switch (op) { case AluOperation.BitfieldReplace: { - Src = (int)((uint)Src >> BfSrcBit) & BfMask; + src = (int)((uint)src >> bfSrcBit) & bfMask; - Dst &= ~(BfMask << BfDstBit); + dst &= ~(bfMask << bfDstBit); - Dst |= Src << BfDstBit; + dst |= src << bfDstBit; - return Dst; + return dst; } case AluOperation.BitfieldExtractLslImm: { - Src = (int)((uint)Src >> Dst) & BfMask; + src = (int)((uint)src >> dst) & bfMask; - return Src << BfDstBit; + return src << bfDstBit; } case AluOperation.BitfieldExtractLslReg: { - Src = (int)((uint)Src >> BfSrcBit) & BfMask; + src = (int)((uint)src >> bfSrcBit) & bfMask; - return Src << Dst; + return src << dst; } } @@ -300,117 +300,117 @@ namespace Ryujinx.Graphics.Graphics3d } } - throw new ArgumentException(nameof(OpCode)); + throw new ArgumentException(nameof(_opCode)); } - private int GetAluResult(AluRegOperation AluOp, int A, int B) + private int GetAluResult(AluRegOperation aluOp, int a, int b) { - switch (AluOp) + switch (aluOp) { case AluRegOperation.Add: { - ulong Result = (ulong)A + (ulong)B; + ulong result = (ulong)a + (ulong)b; - Carry = Result > 0xffffffff; + _carry = result > 0xffffffff; - return (int)Result; + return (int)result; } case AluRegOperation.AddWithCarry: { - ulong Result = (ulong)A + (ulong)B + (Carry ? 1UL : 0UL); + ulong result = (ulong)a + (ulong)b + (_carry ? 1UL : 0UL); - Carry = Result > 0xffffffff; + _carry = result > 0xffffffff; - return (int)Result; + return (int)result; } case AluRegOperation.Subtract: { - ulong Result = (ulong)A - (ulong)B; + ulong result = (ulong)a - (ulong)b; - Carry = Result < 0x100000000; + _carry = result < 0x100000000; - return (int)Result; + return (int)result; } case AluRegOperation.SubtractWithBorrow: { - ulong Result = (ulong)A - (ulong)B - (Carry ? 0UL : 1UL); + ulong result = (ulong)a - (ulong)b - (_carry ? 0UL : 1UL); - Carry = Result < 0x100000000; + _carry = result < 0x100000000; - return (int)Result; + return (int)result; } - case AluRegOperation.BitwiseExclusiveOr: return A ^ B; - case AluRegOperation.BitwiseOr: return A | B; - case AluRegOperation.BitwiseAnd: return A & B; - case AluRegOperation.BitwiseAndNot: return A & ~B; - case AluRegOperation.BitwiseNotAnd: return ~(A & B); + case AluRegOperation.BitwiseExclusiveOr: return a ^ b; + case AluRegOperation.BitwiseOr: return a | b; + case AluRegOperation.BitwiseAnd: return a & b; + case AluRegOperation.BitwiseAndNot: return a & ~b; + case AluRegOperation.BitwiseNotAnd: return ~(a & b); } - throw new ArgumentOutOfRangeException(nameof(AluOp)); + throw new ArgumentOutOfRangeException(nameof(aluOp)); } private int GetImm() { //Note: The immediate is signed, the sign-extension is intended here. - return OpCode >> 14; + return _opCode >> 14; } - private void SetMethAddr(int Value) + private void SetMethAddr(int value) { - MethAddr = (Value >> 0) & 0xfff; - MethIncr = (Value >> 12) & 0x3f; + _methAddr = (value >> 0) & 0xfff; + _methIncr = (value >> 12) & 0x3f; } - private void SetDstGpr(int Value) + private void SetDstGpr(int value) { - Gprs[(OpCode >> 8) & 7] = Value; + _gprs[(_opCode >> 8) & 7] = value; } private int GetGprA() { - return GetGprValue((OpCode >> 11) & 7); + return GetGprValue((_opCode >> 11) & 7); } private int GetGprB() { - return GetGprValue((OpCode >> 14) & 7); + return GetGprValue((_opCode >> 14) & 7); } - private int GetGprValue(int Index) + private int GetGprValue(int index) { - return Index != 0 ? Gprs[Index] : 0; + return index != 0 ? _gprs[index] : 0; } private int FetchParam() { - int Value; + int value; - if (!Fifo.TryDequeue(out Value)) + if (!Fifo.TryDequeue(out value)) { Logger.PrintWarning(LogClass.Gpu, "Macro attempted to fetch an inexistent argument."); return 0; } - return Value; + return value; } - private int Read(int Reg) + private int Read(int reg) { - return Engine.Registers[Reg]; + return _engine.Registers[reg]; } - private void Send(NvGpuVmm Vmm, int Value) + private void Send(NvGpuVmm vmm, int value) { - GpuMethodCall MethCall = new GpuMethodCall(MethAddr, Value); + GpuMethodCall methCall = new GpuMethodCall(_methAddr, value); - Engine.CallMethod(Vmm, MethCall); + _engine.CallMethod(vmm, methCall); - MethAddr += MethIncr; + _methAddr += _methIncr; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs index 55e3ebd4c4..361b170610 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs @@ -19,157 +19,240 @@ namespace Ryujinx.Graphics.Graphics3d public int[] Registers { get; private set; } - private NvGpu Gpu; + private NvGpu _gpu; - public NvGpuEngine2d(NvGpu Gpu) + public NvGpuEngine2d(NvGpu gpu) { - this.Gpu = Gpu; + _gpu = gpu; Registers = new int[0x238]; } - public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + public void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall) { - WriteRegister(MethCall); + WriteRegister(methCall); - if ((NvGpuEngine2dReg)MethCall.Method == NvGpuEngine2dReg.BlitSrcYInt) + if ((NvGpuEngine2dReg)methCall.Method == NvGpuEngine2dReg.BlitSrcYInt) { - TextureCopy(Vmm); + TextureCopy(vmm); } } - private void TextureCopy(NvGpuVmm Vmm) + private void TextureCopy(NvGpuVmm vmm) { - CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); + CopyOperation operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); - int DstFormat = ReadRegister(NvGpuEngine2dReg.DstFormat); - bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; - int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); - int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); - int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); - int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); + int dstFormat = ReadRegister(NvGpuEngine2dReg.DstFormat); + bool dstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; + int dstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); + int dstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); + int dstDepth = ReadRegister(NvGpuEngine2dReg.DstDepth); + int dstLayer = ReadRegister(NvGpuEngine2dReg.DstLayer); + int dstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); + int dstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); - int SrcFormat = ReadRegister(NvGpuEngine2dReg.SrcFormat); - bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; - int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); - int SrcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); - int SrcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch); - int SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions); + int srcFormat = ReadRegister(NvGpuEngine2dReg.SrcFormat); + bool srcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; + int srcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); + int srcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); + int srcDepth = ReadRegister(NvGpuEngine2dReg.SrcDepth); + int srcLayer = ReadRegister(NvGpuEngine2dReg.SrcLayer); + int srcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch); + int srcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions); - int DstBlitX = ReadRegister(NvGpuEngine2dReg.BlitDstX); - int DstBlitY = ReadRegister(NvGpuEngine2dReg.BlitDstY); - int DstBlitW = ReadRegister(NvGpuEngine2dReg.BlitDstW); - int DstBlitH = ReadRegister(NvGpuEngine2dReg.BlitDstH); + int dstBlitX = ReadRegister(NvGpuEngine2dReg.BlitDstX); + int dstBlitY = ReadRegister(NvGpuEngine2dReg.BlitDstY); + int dstBlitW = ReadRegister(NvGpuEngine2dReg.BlitDstW); + int dstBlitH = ReadRegister(NvGpuEngine2dReg.BlitDstH); - long BlitDuDx = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDuDxFract); - long BlitDvDy = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDvDyFract); + long blitDuDx = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDuDxFract); + long blitDvDy = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDvDyFract); - long SrcBlitX = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcXFract); - long SrcBlitY = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcYFract); + long srcBlitX = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcXFract); + long srcBlitY = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcYFract); - GalImageFormat SrcImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)SrcFormat); - GalImageFormat DstImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)DstFormat); + GalImageFormat srcImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)srcFormat); + GalImageFormat dstImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)dstFormat); - GalMemoryLayout SrcLayout = GetLayout(SrcLinear); - GalMemoryLayout DstLayout = GetLayout(DstLinear); + GalMemoryLayout srcLayout = GetLayout(srcLinear); + GalMemoryLayout dstLayout = GetLayout(dstLinear); - int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf); - int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); + int srcBlockHeight = 1 << ((srcBlkDim >> 4) & 0xf); + int dstBlockHeight = 1 << ((dstBlkDim >> 4) & 0xf); - long SrcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); - long DstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress); + long srcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); + long dstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress); - long SrcKey = Vmm.GetPhysicalAddress(SrcAddress); - long DstKey = Vmm.GetPhysicalAddress(DstAddress); + long srcKey = vmm.GetPhysicalAddress(srcAddress); + long dstKey = vmm.GetPhysicalAddress(dstAddress); - GalImage SrcTexture = new GalImage( - SrcWidth, - SrcHeight, 1, - SrcBlockHeight, - SrcLayout, - SrcImgFormat); + bool isSrcLayered = false; + bool isDstLayered = false; - GalImage DstTexture = new GalImage( - DstWidth, - DstHeight, 1, - DstBlockHeight, - DstLayout, - DstImgFormat); + GalTextureTarget srcTarget = GalTextureTarget.TwoD; - SrcTexture.Pitch = SrcPitch; - DstTexture.Pitch = DstPitch; + if (srcDepth != 0) + { + srcTarget = GalTextureTarget.TwoDArray; + srcDepth++; + isSrcLayered = true; + } + else + { + srcDepth = 1; + } - Gpu.ResourceManager.SendTexture(Vmm, SrcKey, SrcTexture); - Gpu.ResourceManager.SendTexture(Vmm, DstKey, DstTexture); + GalTextureTarget dstTarget = GalTextureTarget.TwoD; - int SrcBlitX1 = (int)(SrcBlitX >> 32); - int SrcBlitY1 = (int)(SrcBlitY >> 32); + if (dstDepth != 0) + { + dstTarget = GalTextureTarget.TwoDArray; + dstDepth++; + isDstLayered = true; + } + else + { + dstDepth = 1; + } - int SrcBlitX2 = (int)(SrcBlitX + DstBlitW * BlitDuDx >> 32); - int SrcBlitY2 = (int)(SrcBlitY + DstBlitH * BlitDvDy >> 32); + GalImage srcTexture = new GalImage( + srcWidth, + srcHeight, + 1, srcDepth, 1, + srcBlockHeight, 1, + srcLayout, + srcImgFormat, + srcTarget); - Gpu.Renderer.RenderTarget.Copy( - SrcKey, - DstKey, - SrcBlitX1, - SrcBlitY1, - SrcBlitX2, - SrcBlitY2, - DstBlitX, - DstBlitY, - DstBlitX + DstBlitW, - DstBlitY + DstBlitH); + GalImage dstTexture = new GalImage( + dstWidth, + dstHeight, + 1, dstDepth, 1, + dstBlockHeight, 1, + dstLayout, + dstImgFormat, + dstTarget); + + srcTexture.Pitch = srcPitch; + dstTexture.Pitch = dstPitch; + + long GetLayerOffset(GalImage image, int layer) + { + int targetMipLevel = image.MaxMipmapLevel <= 1 ? 1 : image.MaxMipmapLevel - 1; + return ImageUtils.GetLayerOffset(image, targetMipLevel) * layer; + } + + int srcLayerIndex = -1; + + if (isSrcLayered && _gpu.ResourceManager.TryGetTextureLayer(srcKey, out srcLayerIndex) && srcLayerIndex != 0) + { + srcKey = srcKey - GetLayerOffset(srcTexture, srcLayerIndex); + } + + int dstLayerIndex = -1; + + if (isDstLayered && _gpu.ResourceManager.TryGetTextureLayer(dstKey, out dstLayerIndex) && dstLayerIndex != 0) + { + dstKey = dstKey - GetLayerOffset(dstTexture, dstLayerIndex); + } + + _gpu.ResourceManager.SendTexture(vmm, srcKey, srcTexture); + _gpu.ResourceManager.SendTexture(vmm, dstKey, dstTexture); + + if (isSrcLayered && srcLayerIndex == -1) + { + for (int layer = 0; layer < srcTexture.LayerCount; layer++) + { + _gpu.ResourceManager.SetTextureArrayLayer(srcKey + GetLayerOffset(srcTexture, layer), layer); + } + + srcLayerIndex = 0; + } + + if (isDstLayered && dstLayerIndex == -1) + { + for (int layer = 0; layer < dstTexture.LayerCount; layer++) + { + _gpu.ResourceManager.SetTextureArrayLayer(dstKey + GetLayerOffset(dstTexture, layer), layer); + } + + dstLayerIndex = 0; + } + + int srcBlitX1 = (int)(srcBlitX >> 32); + int srcBlitY1 = (int)(srcBlitY >> 32); + + int srcBlitX2 = (int)(srcBlitX + dstBlitW * blitDuDx >> 32); + int srcBlitY2 = (int)(srcBlitY + dstBlitH * blitDvDy >> 32); + + _gpu.Renderer.RenderTarget.Copy( + srcTexture, + dstTexture, + srcKey, + dstKey, + srcLayerIndex, + dstLayerIndex, + srcBlitX1, + srcBlitY1, + srcBlitX2, + srcBlitY2, + dstBlitX, + dstBlitY, + dstBlitX + dstBlitW, + dstBlitY + dstBlitH); //Do a guest side copy aswell. This is necessary when //the texture is modified by the guest, however it doesn't //work when resources that the gpu can write to are copied, //like framebuffers. - ImageUtils.CopyTexture( - Vmm, - SrcTexture, - DstTexture, - SrcAddress, - DstAddress, - SrcBlitX1, - SrcBlitY1, - DstBlitX, - DstBlitY, - DstBlitW, - DstBlitH); - Vmm.IsRegionModified(DstKey, ImageUtils.GetSize(DstTexture), NvGpuBufferType.Texture); + // FIXME: SUPPORT MULTILAYER CORRECTLY HERE (this will cause weird stuffs on the first layer) + ImageUtils.CopyTexture( + vmm, + srcTexture, + dstTexture, + srcAddress, + dstAddress, + srcBlitX1, + srcBlitY1, + dstBlitX, + dstBlitY, + dstBlitW, + dstBlitH); + + vmm.IsRegionModified(dstKey, ImageUtils.GetSize(dstTexture), NvGpuBufferType.Texture); } - private static GalMemoryLayout GetLayout(bool Linear) + private static GalMemoryLayout GetLayout(bool linear) { - return Linear + return linear ? GalMemoryLayout.Pitch : GalMemoryLayout.BlockLinear; } - private long MakeInt64From2xInt32(NvGpuEngine2dReg Reg) + private long MakeInt64From2xInt32(NvGpuEngine2dReg reg) { return - (long)Registers[(int)Reg + 0] << 32 | - (uint)Registers[(int)Reg + 1]; + (long)Registers[(int)reg + 0] << 32 | + (uint)Registers[(int)reg + 1]; } - private void WriteRegister(GpuMethodCall MethCall) + private void WriteRegister(GpuMethodCall methCall) { - Registers[MethCall.Method] = MethCall.Argument; + Registers[methCall.Method] = methCall.Argument; } - private long ReadRegisterFixed1_31_32(NvGpuEngine2dReg Reg) + private long ReadRegisterFixed1_31_32(NvGpuEngine2dReg reg) { - long Low = (uint)ReadRegister(Reg + 0); - long High = (uint)ReadRegister(Reg + 1); + long low = (uint)ReadRegister(reg + 0); + long high = (uint)ReadRegister(reg + 1); - return Low | (High << 32); + return low | (high << 32); } - private int ReadRegister(NvGpuEngine2dReg Reg) + private int ReadRegister(NvGpuEngine2dReg reg) { - return Registers[(int)Reg]; + return Registers[(int)reg]; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs index c1c0dba29f..7747b5a3ab 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs @@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Graphics3d DstWidth = 0x86, DstHeight = 0x87, DstAddress = 0x88, + DstAddressLow = 0x89, SrcFormat = 0x8c, SrcLinear = 0x8d, SrcBlockDimensions = 0x8e, @@ -20,6 +21,7 @@ namespace Ryujinx.Graphics.Graphics3d SrcWidth = 0x92, SrcHeight = 0x93, SrcAddress = 0x94, + SrcAddressLow = 0x95, ClipEnable = 0xa4, CopyOperation = 0xab, BlitControl = 0x223, diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs index 1ca3ca1ce1..bbed642b82 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs @@ -1,6 +1,7 @@ using Ryujinx.Common; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; +using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Texture; using System; using System.Collections.Generic; @@ -11,9 +12,9 @@ namespace Ryujinx.Graphics.Graphics3d { public int[] Registers { get; private set; } - private NvGpu Gpu; + private NvGpu _gpu; - private Dictionary Methods; + private Dictionary _methods; private struct ConstBuffer { @@ -22,28 +23,33 @@ namespace Ryujinx.Graphics.Graphics3d public int Size; } - private ConstBuffer[][] ConstBuffers; + private ConstBuffer[][] _constBuffers; - // Height kept for flipping y axis - private int ViewportHeight = 0; + // Viewport dimensions kept for scissor test limits + private int _viewportX0 = 0; + private int _viewportY0 = 0; + private int _viewportX1 = 0; + private int _viewportY1 = 0; + private int _viewportWidth = 0; + private int _viewportHeight = 0; - private int CurrentInstance = 0; + private int _currentInstance = 0; - public NvGpuEngine3d(NvGpu Gpu) + public NvGpuEngine3d(NvGpu gpu) { - this.Gpu = Gpu; + _gpu = gpu; Registers = new int[0xe00]; - Methods = new Dictionary(); + _methods = new Dictionary(); - void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) + void AddMethod(int meth, int count, int stride, NvGpuMethod method) { - while (Count-- > 0) + while (count-- > 0) { - Methods.Add(Meth, Method); + _methods.Add(meth, method); - Meth += Stride; + meth += stride; } } @@ -53,11 +59,11 @@ namespace Ryujinx.Graphics.Graphics3d AddMethod(0x8e4, 16, 1, CbData); AddMethod(0x904, 5, 8, CbBind); - ConstBuffers = new ConstBuffer[6][]; + _constBuffers = new ConstBuffer[6][]; - for (int Index = 0; Index < ConstBuffers.Length; Index++) + for (int index = 0; index < _constBuffers.Length; index++) { - ConstBuffers[Index] = new ConstBuffer[18]; + _constBuffers[index] = new ConstBuffer[18]; } //Ensure that all components are enabled by default. @@ -66,220 +72,227 @@ namespace Ryujinx.Graphics.Graphics3d WriteRegister(NvGpuEngine3dReg.FrameBufferSrgb, 1); - for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + WriteRegister(NvGpuEngine3dReg.FrontFace, (int)GalFrontFace.Cw); + + for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) { - WriteRegister(NvGpuEngine3dReg.IBlendNEquationRgb + Index * 8, (int)GalBlendEquation.FuncAdd); - WriteRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb + Index * 8, (int)GalBlendFactor.One); - WriteRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb + Index * 8, (int)GalBlendFactor.Zero); - WriteRegister(NvGpuEngine3dReg.IBlendNEquationAlpha + Index * 8, (int)GalBlendEquation.FuncAdd); - WriteRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha + Index * 8, (int)GalBlendFactor.One); - WriteRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha + Index * 8, (int)GalBlendFactor.Zero); + WriteRegister(NvGpuEngine3dReg.IBlendNEquationRgb + index * 8, (int)GalBlendEquation.FuncAdd); + WriteRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb + index * 8, (int)GalBlendFactor.One); + WriteRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb + index * 8, (int)GalBlendFactor.Zero); + WriteRegister(NvGpuEngine3dReg.IBlendNEquationAlpha + index * 8, (int)GalBlendEquation.FuncAdd); + WriteRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha + index * 8, (int)GalBlendFactor.One); + WriteRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha + index * 8, (int)GalBlendFactor.Zero); } } - public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + public void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall) { - if (Methods.TryGetValue(MethCall.Method, out NvGpuMethod Method)) + if (_methods.TryGetValue(methCall.Method, out NvGpuMethod method)) { - Method(Vmm, MethCall); + method(vmm, methCall); } else { - WriteRegister(MethCall); + WriteRegister(methCall); } } - private void VertexEndGl(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void VertexEndGl(NvGpuVmm vmm, GpuMethodCall methCall) { LockCaches(); - GalPipelineState State = new GalPipelineState(); + GalPipelineState state = new GalPipelineState(); - SetFrameBuffer(State); - SetFrontFace(State); - SetCullFace(State); - SetDepth(State); - SetStencil(State); - SetScissor(State); - SetBlending(State); - SetColorMask(State); - SetPrimitiveRestart(State); + // Framebuffer must be run configured because viewport dimensions may be used in other methods + SetFrameBuffer(state); - for (int FbIndex = 0; FbIndex < 8; FbIndex++) + for (int fbIndex = 0; fbIndex < 8; fbIndex++) { - SetFrameBuffer(Vmm, FbIndex); + SetFrameBuffer(vmm, fbIndex); } - SetZeta(Vmm); + SetFrontFace(state); + SetCullFace(state); + SetDepth(state); + SetStencil(state); + SetScissor(state); + SetBlending(state); + SetColorMask(state); + SetPrimitiveRestart(state); + + SetZeta(vmm); SetRenderTargets(); - long[] Keys = UploadShaders(Vmm); + long[] keys = UploadShaders(vmm); - Gpu.Renderer.Shader.BindProgram(); + _gpu.Renderer.Shader.BindProgram(); - UploadTextures(Vmm, State, Keys); - UploadConstBuffers(Vmm, State, Keys); - UploadVertexArrays(Vmm, State); + UploadTextures(vmm, state, keys); + UploadConstBuffers(vmm, state, keys); + UploadVertexArrays(vmm, state); - DispatchRender(Vmm, State); + DispatchRender(vmm, state); UnlockCaches(); } private void LockCaches() { - Gpu.Renderer.Buffer.LockCache(); - Gpu.Renderer.Rasterizer.LockCaches(); - Gpu.Renderer.Texture.LockCache(); + _gpu.Renderer.Buffer.LockCache(); + _gpu.Renderer.Rasterizer.LockCaches(); + _gpu.Renderer.Texture.LockCache(); } private void UnlockCaches() { - Gpu.Renderer.Buffer.UnlockCache(); - Gpu.Renderer.Rasterizer.UnlockCaches(); - Gpu.Renderer.Texture.UnlockCache(); + _gpu.Renderer.Buffer.UnlockCache(); + _gpu.Renderer.Rasterizer.UnlockCaches(); + _gpu.Renderer.Texture.UnlockCache(); } - private void ClearBuffers(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void ClearBuffers(NvGpuVmm vmm, GpuMethodCall methCall) { - int Attachment = (MethCall.Argument >> 6) & 0xf; + int attachment = (methCall.Argument >> 6) & 0xf; - GalClearBufferFlags Flags = (GalClearBufferFlags)(MethCall.Argument & 0x3f); + GalClearBufferFlags flags = (GalClearBufferFlags)(methCall.Argument & 0x3f); - float Red = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 0); - float Green = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 1); - float Blue = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 2); - float Alpha = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 3); + float red = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 0); + float green = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 1); + float blue = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 2); + float alpha = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 3); - float Depth = ReadRegisterFloat(NvGpuEngine3dReg.ClearDepth); + float depth = ReadRegisterFloat(NvGpuEngine3dReg.ClearDepth); - int Stencil = ReadRegister(NvGpuEngine3dReg.ClearStencil); + int stencil = ReadRegister(NvGpuEngine3dReg.ClearStencil); - SetFrameBuffer(Vmm, Attachment); + SetFrameBuffer(vmm, attachment); - SetZeta(Vmm); + SetZeta(vmm); SetRenderTargets(); - Gpu.Renderer.RenderTarget.Bind(); + _gpu.Renderer.RenderTarget.Bind(); - Gpu.Renderer.Rasterizer.ClearBuffers(Flags, Attachment, Red, Green, Blue, Alpha, Depth, Stencil); + _gpu.Renderer.Rasterizer.ClearBuffers(flags, attachment, red, green, blue, alpha, depth, stencil); - Gpu.Renderer.Pipeline.ResetDepthMask(); - Gpu.Renderer.Pipeline.ResetColorMask(Attachment); + _gpu.Renderer.Pipeline.ResetDepthMask(); + _gpu.Renderer.Pipeline.ResetColorMask(attachment); } - private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) + private void SetFrameBuffer(NvGpuVmm vmm, int fbIndex) { - long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10); + long va = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + fbIndex * 0x10); - int SurfFormat = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + FbIndex * 0x10); + int surfFormat = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + fbIndex * 0x10); - if (VA == 0 || SurfFormat == 0) + if (va == 0 || surfFormat == 0) { - Gpu.Renderer.RenderTarget.UnbindColor(FbIndex); + _gpu.Renderer.RenderTarget.UnbindColor(fbIndex); return; } - long Key = Vmm.GetPhysicalAddress(VA); + long key = vmm.GetPhysicalAddress(va); - int Width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + FbIndex * 0x10); - int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10); + int width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + fbIndex * 0x10); + int height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + fbIndex * 0x10); - int BlockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + FbIndex * 0x10); + int arrayMode = ReadRegister(NvGpuEngine3dReg.FrameBufferNArrayMode + fbIndex * 0x10); + int layerCount = arrayMode & 0xFFFF; + int layerStride = ReadRegister(NvGpuEngine3dReg.FrameBufferNLayerStride + fbIndex * 0x10); + int baseLayer = ReadRegister(NvGpuEngine3dReg.FrameBufferNBaseLayer + fbIndex * 0x10); + int blockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + fbIndex * 0x10); - int GobBlockHeight = 1 << ((BlockDim >> 4) & 7); + int gobBlockHeight = 1 << ((blockDim >> 4) & 7); - GalMemoryLayout Layout = (GalMemoryLayout)((BlockDim >> 12) & 1); + GalMemoryLayout layout = (GalMemoryLayout)((blockDim >> 12) & 1); - float TX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + FbIndex * 8); - float TY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + FbIndex * 8); + float tx = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + fbIndex * 8); + float ty = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + fbIndex * 8); - float SX = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleX + FbIndex * 8); - float SY = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleY + FbIndex * 8); + float sx = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleX + fbIndex * 8); + float sy = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleY + fbIndex * 8); - int VpX = (int)MathF.Max(0, TX - MathF.Abs(SX)); - int VpY = (int)MathF.Max(0, TY - MathF.Abs(SY)); + _viewportX0 = (int)MathF.Max(0, tx - MathF.Abs(sx)); + _viewportY0 = (int)MathF.Max(0, ty - MathF.Abs(sy)); - int VpW = (int)(TX + MathF.Abs(SX)) - VpX; - int VpH = (int)(TY + MathF.Abs(SY)) - VpY; + _viewportX1 = (int)(tx + MathF.Abs(sx)); + _viewportY1 = (int)(ty + MathF.Abs(sy)); - GalImageFormat Format = ImageUtils.ConvertSurface((GalSurfaceFormat)SurfFormat); + GalImageFormat format = ImageUtils.ConvertSurface((GalSurfaceFormat)surfFormat); - GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format); + GalImage image = new GalImage(width, height, 1, 1, 1, gobBlockHeight, 1, layout, format, GalTextureTarget.TwoD); - Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image); + _gpu.ResourceManager.SendColorBuffer(vmm, key, fbIndex, image); - ViewportHeight = VpH; - - Gpu.Renderer.RenderTarget.SetViewport(FbIndex, VpX, VpY, VpW, VpH); + _gpu.Renderer.RenderTarget.SetViewport(fbIndex, _viewportX0, _viewportY0, _viewportX1 - _viewportX0, _viewportY1 - _viewportY0); } - private void SetFrameBuffer(GalPipelineState State) + private void SetFrameBuffer(GalPipelineState state) { - State.FramebufferSrgb = ReadRegisterBool(NvGpuEngine3dReg.FrameBufferSrgb); + state.FramebufferSrgb = ReadRegisterBool(NvGpuEngine3dReg.FrameBufferSrgb); - State.FlipX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); - State.FlipY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); + state.FlipX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); + state.FlipY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); - int ScreenYControl = ReadRegister(NvGpuEngine3dReg.ScreenYControl); + int screenYControl = ReadRegister(NvGpuEngine3dReg.ScreenYControl); - bool NegateY = (ScreenYControl & 1) != 0; + bool negateY = (screenYControl & 1) != 0; - if (NegateY) + if (negateY) { - State.FlipY = -State.FlipY; + state.FlipY = -state.FlipY; } } - private void SetZeta(NvGpuVmm Vmm) + private void SetZeta(NvGpuVmm vmm) { - long VA = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress); + long va = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress); - int ZetaFormat = ReadRegister(NvGpuEngine3dReg.ZetaFormat); + int zetaFormat = ReadRegister(NvGpuEngine3dReg.ZetaFormat); - int BlockDim = ReadRegister(NvGpuEngine3dReg.ZetaBlockDimensions); + int blockDim = ReadRegister(NvGpuEngine3dReg.ZetaBlockDimensions); - int GobBlockHeight = 1 << ((BlockDim >> 4) & 7); + int gobBlockHeight = 1 << ((blockDim >> 4) & 7); - GalMemoryLayout Layout = (GalMemoryLayout)((BlockDim >> 12) & 1); //? + GalMemoryLayout layout = (GalMemoryLayout)((blockDim >> 12) & 1); //? - bool ZetaEnable = ReadRegisterBool(NvGpuEngine3dReg.ZetaEnable); + bool zetaEnable = ReadRegisterBool(NvGpuEngine3dReg.ZetaEnable); - if (VA == 0 || ZetaFormat == 0 || !ZetaEnable) + if (va == 0 || zetaFormat == 0 || !zetaEnable) { - Gpu.Renderer.RenderTarget.UnbindZeta(); + _gpu.Renderer.RenderTarget.UnbindZeta(); return; } - long Key = Vmm.GetPhysicalAddress(VA); + long key = vmm.GetPhysicalAddress(va); - int Width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz); - int Height = ReadRegister(NvGpuEngine3dReg.ZetaVert); + int width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz); + int height = ReadRegister(NvGpuEngine3dReg.ZetaVert); - GalImageFormat Format = ImageUtils.ConvertZeta((GalZetaFormat)ZetaFormat); + GalImageFormat format = ImageUtils.ConvertZeta((GalZetaFormat)zetaFormat); - GalImage Image = new GalImage(Width, Height, 1, GobBlockHeight, Layout, Format); + // TODO: Support non 2D? + GalImage image = new GalImage(width, height, 1, 1, 1, gobBlockHeight, 1, layout, format, GalTextureTarget.TwoD); - Gpu.ResourceManager.SendZetaBuffer(Vmm, Key, Image); + _gpu.ResourceManager.SendZetaBuffer(vmm, key, image); } - private long[] UploadShaders(NvGpuVmm Vmm) + private long[] UploadShaders(NvGpuVmm vmm) { - long[] Keys = new long[5]; + long[] keys = new long[5]; - long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); + long basePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); - int Index = 1; + int index = 1; - int VpAControl = ReadRegister(NvGpuEngine3dReg.ShaderNControl); + int vpAControl = ReadRegister(NvGpuEngine3dReg.ShaderNControl); - bool VpAEnable = (VpAControl & 1) != 0; + bool vpAEnable = (vpAControl & 1) != 0; - if (VpAEnable) + if (vpAEnable) { //Note: The maxwell supports 2 vertex programs, usually //only VP B is used, but in some cases VP A is also used. @@ -287,51 +300,51 @@ namespace Ryujinx.Graphics.Graphics3d //shader stage. //The graphics abstraction layer has a special overload for this //case, which should merge the two shaders into one vertex shader. - int VpAOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset); - int VpBOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + 0x10); + int vpAOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset); + int vpBOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + 0x10); - long VpAPos = BasePosition + (uint)VpAOffset; - long VpBPos = BasePosition + (uint)VpBOffset; + long vpAPos = basePosition + (uint)vpAOffset; + long vpBPos = basePosition + (uint)vpBOffset; - Keys[(int)GalShaderType.Vertex] = VpBPos; + keys[(int)GalShaderType.Vertex] = vpBPos; - Gpu.Renderer.Shader.Create(Vmm, VpAPos, VpBPos, GalShaderType.Vertex); - Gpu.Renderer.Shader.Bind(VpBPos); + _gpu.Renderer.Shader.Create(vmm, vpAPos, vpBPos, GalShaderType.Vertex); + _gpu.Renderer.Shader.Bind(vpBPos); - Index = 2; + index = 2; } - for (; Index < 6; Index++) + for (; index < 6; index++) { - GalShaderType Type = GetTypeFromProgram(Index); + GalShaderType type = GetTypeFromProgram(index); - int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + Index * 0x10); - int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + Index * 0x10); + int control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + index * 0x10); + int offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + index * 0x10); //Note: Vertex Program (B) is always enabled. - bool Enable = (Control & 1) != 0 || Index == 1; + bool enable = (control & 1) != 0 || index == 1; - if (!Enable) + if (!enable) { - Gpu.Renderer.Shader.Unbind(Type); + _gpu.Renderer.Shader.Unbind(type); continue; } - long Key = BasePosition + (uint)Offset; + long key = basePosition + (uint)offset; - Keys[(int)Type] = Key; + keys[(int)type] = key; - Gpu.Renderer.Shader.Create(Vmm, Key, Type); - Gpu.Renderer.Shader.Bind(Key); + _gpu.Renderer.Shader.Create(vmm, key, type); + _gpu.Renderer.Shader.Bind(key); } - return Keys; + return keys; } - private static GalShaderType GetTypeFromProgram(int Program) + private static GalShaderType GetTypeFromProgram(int program) { - switch (Program) + switch (program) { case 0: case 1: return GalShaderType.Vertex; @@ -341,193 +354,238 @@ namespace Ryujinx.Graphics.Graphics3d case 5: return GalShaderType.Fragment; } - throw new ArgumentOutOfRangeException(nameof(Program)); + throw new ArgumentOutOfRangeException(nameof(program)); } - private void SetFrontFace(GalPipelineState State) + private void SetFrontFace(GalPipelineState state) { - float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); - float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); + float signX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); + float signY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); - GalFrontFace FrontFace = (GalFrontFace)ReadRegister(NvGpuEngine3dReg.FrontFace); + GalFrontFace frontFace = (GalFrontFace)ReadRegister(NvGpuEngine3dReg.FrontFace); //Flipping breaks facing. Flipping front facing too fixes it - if (SignX != SignY) + if (signX != signY) { - switch (FrontFace) + switch (frontFace) { - case GalFrontFace.CW: FrontFace = GalFrontFace.CCW; break; - case GalFrontFace.CCW: FrontFace = GalFrontFace.CW; break; + case GalFrontFace.Cw: frontFace = GalFrontFace.Ccw; break; + case GalFrontFace.Ccw: frontFace = GalFrontFace.Cw; break; } } - State.FrontFace = FrontFace; + state.FrontFace = frontFace; } - private void SetCullFace(GalPipelineState State) + private void SetCullFace(GalPipelineState state) { - State.CullFaceEnabled = ReadRegisterBool(NvGpuEngine3dReg.CullFaceEnable); + state.CullFaceEnabled = ReadRegisterBool(NvGpuEngine3dReg.CullFaceEnable); - if (State.CullFaceEnabled) + if (state.CullFaceEnabled) { - State.CullFace = (GalCullFace)ReadRegister(NvGpuEngine3dReg.CullFace); + state.CullFace = (GalCullFace)ReadRegister(NvGpuEngine3dReg.CullFace); } } - private void SetDepth(GalPipelineState State) + private void SetDepth(GalPipelineState state) { - State.DepthTestEnabled = ReadRegisterBool(NvGpuEngine3dReg.DepthTestEnable); + state.DepthTestEnabled = ReadRegisterBool(NvGpuEngine3dReg.DepthTestEnable); - State.DepthWriteEnabled = ReadRegisterBool(NvGpuEngine3dReg.DepthWriteEnable); + state.DepthWriteEnabled = ReadRegisterBool(NvGpuEngine3dReg.DepthWriteEnable); - if (State.DepthTestEnabled) + if (state.DepthTestEnabled) { - State.DepthFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.DepthTestFunction); + state.DepthFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.DepthTestFunction); } - State.DepthRangeNear = ReadRegisterFloat(NvGpuEngine3dReg.DepthRangeNNear); - State.DepthRangeFar = ReadRegisterFloat(NvGpuEngine3dReg.DepthRangeNFar); + state.DepthRangeNear = ReadRegisterFloat(NvGpuEngine3dReg.DepthRangeNNear); + state.DepthRangeFar = ReadRegisterFloat(NvGpuEngine3dReg.DepthRangeNFar); } - private void SetStencil(GalPipelineState State) + private void SetStencil(GalPipelineState state) { - State.StencilTestEnabled = ReadRegisterBool(NvGpuEngine3dReg.StencilEnable); + state.StencilTestEnabled = ReadRegisterBool(NvGpuEngine3dReg.StencilEnable); - if (State.StencilTestEnabled) + if (state.StencilTestEnabled) { - State.StencilBackFuncFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.StencilBackFuncFunc); - State.StencilBackFuncRef = ReadRegister(NvGpuEngine3dReg.StencilBackFuncRef); - State.StencilBackFuncMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilBackFuncMask); - State.StencilBackOpFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpFail); - State.StencilBackOpZFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpZFail); - State.StencilBackOpZPass = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpZPass); - State.StencilBackMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilBackMask); + state.StencilBackFuncFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.StencilBackFuncFunc); + state.StencilBackFuncRef = ReadRegister(NvGpuEngine3dReg.StencilBackFuncRef); + state.StencilBackFuncMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilBackFuncMask); + state.StencilBackOpFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpFail); + state.StencilBackOpZFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpZFail); + state.StencilBackOpZPass = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpZPass); + state.StencilBackMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilBackMask); - State.StencilFrontFuncFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.StencilFrontFuncFunc); - State.StencilFrontFuncRef = ReadRegister(NvGpuEngine3dReg.StencilFrontFuncRef); - State.StencilFrontFuncMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilFrontFuncMask); - State.StencilFrontOpFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpFail); - State.StencilFrontOpZFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpZFail); - State.StencilFrontOpZPass = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpZPass); - State.StencilFrontMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilFrontMask); + state.StencilFrontFuncFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.StencilFrontFuncFunc); + state.StencilFrontFuncRef = ReadRegister(NvGpuEngine3dReg.StencilFrontFuncRef); + state.StencilFrontFuncMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilFrontFuncMask); + state.StencilFrontOpFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpFail); + state.StencilFrontOpZFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpZFail); + state.StencilFrontOpZPass = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpZPass); + state.StencilFrontMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilFrontMask); } } - private void SetScissor(GalPipelineState State) + private void SetScissor(GalPipelineState state) { - // FIXME: Stubbed, only the first scissor test is valid without a geometry shader loaded. At time of writing geometry shaders are also stubbed. - // Once geometry shaders are fixed it should be equal to GalPipelineState.RenderTargetCount when shader loaded, otherwise equal to 1 - State.ScissorTestCount = 1; + int count = 0; - for (int Index = 0; Index < State.ScissorTestCount; Index++) + for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) { - State.ScissorTestEnabled[Index] = ReadRegisterBool(NvGpuEngine3dReg.ScissorEnable + Index * 4); + state.ScissorTestEnabled[index] = ReadRegisterBool(NvGpuEngine3dReg.ScissorEnable + index * 4); - if (State.ScissorTestEnabled[Index]) + if (state.ScissorTestEnabled[index]) { - uint ScissorHorizontal = (uint)ReadRegister(NvGpuEngine3dReg.ScissorHorizontal + Index * 4); - uint ScissorVertical = (uint)ReadRegister(NvGpuEngine3dReg.ScissorVertical + Index * 4); + uint scissorHorizontal = (uint)ReadRegister(NvGpuEngine3dReg.ScissorHorizontal + index * 4); + uint scissorVertical = (uint)ReadRegister(NvGpuEngine3dReg.ScissorVertical + index * 4); - State.ScissorTestX[Index] = (int)((ScissorHorizontal & 0xFFFF) * State.FlipX); // X, lower 16 bits - State.ScissorTestWidth[Index] = (int)((ScissorHorizontal >> 16) * State.FlipX) - State.ScissorTestX[Index]; // Width, right side is upper 16 bits + int left = (int)(scissorHorizontal & 0xFFFF); // Left, lower 16 bits + int right = (int)(scissorHorizontal >> 16); // Right, upper 16 bits - State.ScissorTestY[Index] = (int)((ScissorVertical & 0xFFFF)); // Y, lower 16 bits - State.ScissorTestHeight[Index] = (int)((ScissorVertical >> 16)) - State.ScissorTestY[Index]; // Height, top side is upper 16 bits + int bottom = (int)(scissorVertical & 0xFFFF); // Bottom, lower 16 bits + int top = (int)(scissorVertical >> 16); // Top, upper 16 bits - // Y coordinates may have to be flipped - if ((int)State.FlipY == -1) + int width = Math.Abs(right - left); + int height = Math.Abs(top - bottom); + + // If the scissor test covers the whole possible viewport, i.e. uninitialized, disable scissor test + if ((width > NvGpu.MaxViewportSize && height > NvGpu.MaxViewportSize) || width <= 0 || height <= 0) { - State.ScissorTestY[Index] = ViewportHeight - State.ScissorTestY[Index] - State.ScissorTestHeight[Index]; - - // Handle negative viewpont coordinate - if (State.ScissorTestY[Index] < 0) - { - State.ScissorTestY[Index] = 0; - } + state.ScissorTestEnabled[index] = false; + continue; } + + // Keep track of how many scissor tests are active. + // If only 1, and it's the first user should apply to all viewports + count++; + + // Flip X + if (state.FlipX == -1) + { + left = _viewportX1 - (left - _viewportX0); + right = _viewportX1 - (right - _viewportX0); + } + + // Ensure X is in the right order + if (left > right) + { + int temp = left; + left = right; + right = temp; + } + + // Flip Y + if (state.FlipY == -1) + { + bottom = _viewportY1 - (bottom - _viewportY0); + top = _viewportY1 - (top - _viewportY0); + } + + // Ensure Y is in the right order + if (bottom > top) + { + int temp = top; + top = bottom; + bottom = temp; + } + + // Handle out of active viewport dimensions + left = Math.Clamp(left, _viewportX0, _viewportX1); + right = Math.Clamp(right, _viewportX0, _viewportX1); + top = Math.Clamp(top, _viewportY0, _viewportY1); + bottom = Math.Clamp(bottom, _viewportY0, _viewportY1); + + // Save values to state + state.ScissorTestX[index] = left; + state.ScissorTestY[index] = bottom; + + state.ScissorTestWidth[index] = right - left; + state.ScissorTestHeight[index] = top - bottom; } } + + state.ScissorTestCount = count; } - private void SetBlending(GalPipelineState State) + private void SetBlending(GalPipelineState state) { - bool BlendIndependent = ReadRegisterBool(NvGpuEngine3dReg.BlendIndependent); + bool blendIndependent = ReadRegisterBool(NvGpuEngine3dReg.BlendIndependent); - State.BlendIndependent = BlendIndependent; + state.BlendIndependent = blendIndependent; - for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) { - if (BlendIndependent) + if (blendIndependent) { - State.Blends[Index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable + Index); + state.Blends[index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable + index); - if (State.Blends[Index].Enabled) + if (state.Blends[index].Enabled) { - State.Blends[Index].SeparateAlpha = ReadRegisterBool(NvGpuEngine3dReg.IBlendNSeparateAlpha + Index * 8); + state.Blends[index].SeparateAlpha = ReadRegisterBool(NvGpuEngine3dReg.IBlendNSeparateAlpha + index * 8); - State.Blends[Index].EquationRgb = ReadBlendEquation(NvGpuEngine3dReg.IBlendNEquationRgb + Index * 8); - State.Blends[Index].FuncSrcRgb = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncSrcRgb + Index * 8); - State.Blends[Index].FuncDstRgb = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncDstRgb + Index * 8); - State.Blends[Index].EquationAlpha = ReadBlendEquation(NvGpuEngine3dReg.IBlendNEquationAlpha + Index * 8); - State.Blends[Index].FuncSrcAlpha = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncSrcAlpha + Index * 8); - State.Blends[Index].FuncDstAlpha = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncDstAlpha + Index * 8); + state.Blends[index].EquationRgb = ReadBlendEquation(NvGpuEngine3dReg.IBlendNEquationRgb + index * 8); + state.Blends[index].FuncSrcRgb = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncSrcRgb + index * 8); + state.Blends[index].FuncDstRgb = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncDstRgb + index * 8); + state.Blends[index].EquationAlpha = ReadBlendEquation(NvGpuEngine3dReg.IBlendNEquationAlpha + index * 8); + state.Blends[index].FuncSrcAlpha = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncSrcAlpha + index * 8); + state.Blends[index].FuncDstAlpha = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncDstAlpha + index * 8); } } else { //It seems that even when independent blend is disabled, the first IBlend enable //register is still set to indicate whenever blend is enabled or not (?). - State.Blends[Index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable); + state.Blends[index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable); - if (State.Blends[Index].Enabled) + if (state.Blends[index].Enabled) { - State.Blends[Index].SeparateAlpha = ReadRegisterBool(NvGpuEngine3dReg.BlendSeparateAlpha); + state.Blends[index].SeparateAlpha = ReadRegisterBool(NvGpuEngine3dReg.BlendSeparateAlpha); - State.Blends[Index].EquationRgb = ReadBlendEquation(NvGpuEngine3dReg.BlendEquationRgb); - State.Blends[Index].FuncSrcRgb = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncSrcRgb); - State.Blends[Index].FuncDstRgb = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncDstRgb); - State.Blends[Index].EquationAlpha = ReadBlendEquation(NvGpuEngine3dReg.BlendEquationAlpha); - State.Blends[Index].FuncSrcAlpha = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncSrcAlpha); - State.Blends[Index].FuncDstAlpha = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncDstAlpha); + state.Blends[index].EquationRgb = ReadBlendEquation(NvGpuEngine3dReg.BlendEquationRgb); + state.Blends[index].FuncSrcRgb = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncSrcRgb); + state.Blends[index].FuncDstRgb = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncDstRgb); + state.Blends[index].EquationAlpha = ReadBlendEquation(NvGpuEngine3dReg.BlendEquationAlpha); + state.Blends[index].FuncSrcAlpha = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncSrcAlpha); + state.Blends[index].FuncDstAlpha = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncDstAlpha); } } } } - private GalBlendEquation ReadBlendEquation(NvGpuEngine3dReg Register) + private GalBlendEquation ReadBlendEquation(NvGpuEngine3dReg register) { - return (GalBlendEquation)ReadRegister(Register); + return (GalBlendEquation)ReadRegister(register); } - private GalBlendFactor ReadBlendFactor(NvGpuEngine3dReg Register) + private GalBlendFactor ReadBlendFactor(NvGpuEngine3dReg register) { - return (GalBlendFactor)ReadRegister(Register); + return (GalBlendFactor)ReadRegister(register); } - private void SetColorMask(GalPipelineState State) + private void SetColorMask(GalPipelineState state) { - bool ColorMaskCommon = ReadRegisterBool(NvGpuEngine3dReg.ColorMaskCommon); + bool colorMaskCommon = ReadRegisterBool(NvGpuEngine3dReg.ColorMaskCommon); - State.ColorMaskCommon = ColorMaskCommon; + state.ColorMaskCommon = colorMaskCommon; - for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) { - int ColorMask = ReadRegister(NvGpuEngine3dReg.ColorMaskN + (ColorMaskCommon ? 0 : Index)); + int colorMask = ReadRegister(NvGpuEngine3dReg.ColorMaskN + (colorMaskCommon ? 0 : index)); - State.ColorMasks[Index].Red = ((ColorMask >> 0) & 0xf) != 0; - State.ColorMasks[Index].Green = ((ColorMask >> 4) & 0xf) != 0; - State.ColorMasks[Index].Blue = ((ColorMask >> 8) & 0xf) != 0; - State.ColorMasks[Index].Alpha = ((ColorMask >> 12) & 0xf) != 0; + state.ColorMasks[index].Red = ((colorMask >> 0) & 0xf) != 0; + state.ColorMasks[index].Green = ((colorMask >> 4) & 0xf) != 0; + state.ColorMasks[index].Blue = ((colorMask >> 8) & 0xf) != 0; + state.ColorMasks[index].Alpha = ((colorMask >> 12) & 0xf) != 0; } } - private void SetPrimitiveRestart(GalPipelineState State) + private void SetPrimitiveRestart(GalPipelineState state) { - State.PrimitiveRestartEnabled = ReadRegisterBool(NvGpuEngine3dReg.PrimRestartEnable); + state.PrimitiveRestartEnabled = ReadRegisterBool(NvGpuEngine3dReg.PrimRestartEnable); - if (State.PrimitiveRestartEnabled) + if (state.PrimitiveRestartEnabled) { - State.PrimitiveRestartIndex = (uint)ReadRegister(NvGpuEngine3dReg.PrimRestartIndex); + state.PrimitiveRestartIndex = (uint)ReadRegister(NvGpuEngine3dReg.PrimRestartIndex); } } @@ -536,459 +594,464 @@ namespace Ryujinx.Graphics.Graphics3d //Commercial games do not seem to //bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData); - uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl)); + uint control = (uint)(ReadRegister(NvGpuEngine3dReg.RtControl)); - uint Count = Control & 0xf; + uint count = control & 0xf; - if (Count > 0) + if (count > 0) { - int[] Map = new int[Count]; + int[] map = new int[count]; - for (int Index = 0; Index < Count; Index++) + for (int index = 0; index < count; index++) { - int Shift = 4 + Index * 3; + int shift = 4 + index * 3; - Map[Index] = (int)((Control >> Shift) & 7); + map[index] = (int)((control >> shift) & 7); } - Gpu.Renderer.RenderTarget.SetMap(Map); + _gpu.Renderer.RenderTarget.SetMap(map); } else { - Gpu.Renderer.RenderTarget.SetMap(null); + _gpu.Renderer.RenderTarget.SetMap(null); } } - private void UploadTextures(NvGpuVmm Vmm, GalPipelineState State, long[] Keys) + private void UploadTextures(NvGpuVmm vmm, GalPipelineState state, long[] keys) { - long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); + long baseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); - int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); + int textureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); - List<(long, GalImage, GalTextureSampler)> UnboundTextures = new List<(long, GalImage, GalTextureSampler)>(); + List<(long, GalImage, GalTextureSampler)> unboundTextures = new List<(long, GalImage, GalTextureSampler)>(); - for (int Index = 0; Index < Keys.Length; Index++) + for (int index = 0; index < keys.Length; index++) { - foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetTextureUsage(Keys[Index])) + foreach (TextureDescriptor desc in _gpu.Renderer.Shader.GetTextureUsage(keys[index])) { - long Position; + int textureHandle; - if (DeclInfo.IsCb) + if (desc.IsBindless) { - Position = ConstBuffers[Index][DeclInfo.Cbuf].Position; + long position = _constBuffers[index][desc.CbufSlot].Position; + + textureHandle = vmm.ReadInt32(position + desc.CbufOffset * 4); } else { - Position = ConstBuffers[Index][TextureCbIndex].Position; + long position = _constBuffers[index][textureCbIndex].Position; + + textureHandle = vmm.ReadInt32(position + desc.HandleIndex * 4); } - int TextureHandle = Vmm.ReadInt32(Position + DeclInfo.Index * 4); - - UnboundTextures.Add(UploadTexture(Vmm, TextureHandle)); + unboundTextures.Add(UploadTexture(vmm, textureHandle)); } } - for (int Index = 0; Index < UnboundTextures.Count; Index++) + for (int index = 0; index < unboundTextures.Count; index++) { - (long Key, GalImage Image, GalTextureSampler Sampler) = UnboundTextures[Index]; + (long key, GalImage image, GalTextureSampler sampler) = unboundTextures[index]; - if (Key == 0) + if (key == 0) { continue; } - Gpu.Renderer.Texture.Bind(Key, Index, Image); - Gpu.Renderer.Texture.SetSampler(Sampler); + _gpu.Renderer.Texture.Bind(key, index, image); + _gpu.Renderer.Texture.SetSampler(image, sampler); } } - private (long, GalImage, GalTextureSampler) UploadTexture(NvGpuVmm Vmm, int TextureHandle) + private (long, GalImage, GalTextureSampler) UploadTexture(NvGpuVmm vmm, int textureHandle) { - if (TextureHandle == 0) + if (textureHandle == 0) { //FIXME: Some games like puyo puyo will use handles with the value 0. //This is a bug, most likely caused by sync issues. return (0, default(GalImage), default(GalTextureSampler)); } - bool LinkedTsc = ReadRegisterBool(NvGpuEngine3dReg.LinkedTsc); + bool linkedTsc = ReadRegisterBool(NvGpuEngine3dReg.LinkedTsc); - int TicIndex = (TextureHandle >> 0) & 0xfffff; + int ticIndex = (textureHandle >> 0) & 0xfffff; - int TscIndex = LinkedTsc ? TicIndex : (TextureHandle >> 20) & 0xfff; + int tscIndex = linkedTsc ? ticIndex : (textureHandle >> 20) & 0xfff; - long TicPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset); - long TscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset); + long ticPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset); + long tscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset); - TicPosition += TicIndex * 0x20; - TscPosition += TscIndex * 0x20; + ticPosition += ticIndex * 0x20; + tscPosition += tscIndex * 0x20; - GalImage Image = TextureFactory.MakeTexture(Vmm, TicPosition); + GalImage image = TextureFactory.MakeTexture(vmm, ticPosition); - GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Vmm, TscPosition); + GalTextureSampler sampler = TextureFactory.MakeSampler(_gpu, vmm, tscPosition); - long Key = Vmm.ReadInt64(TicPosition + 4) & 0xffffffffffff; + long key = vmm.ReadInt64(ticPosition + 4) & 0xffffffffffff; - if (Image.Layout == GalMemoryLayout.BlockLinear) + if (image.Layout == GalMemoryLayout.BlockLinear) { - Key &= ~0x1ffL; + key &= ~0x1ffL; } - else if (Image.Layout == GalMemoryLayout.Pitch) + else if (image.Layout == GalMemoryLayout.Pitch) { - Key &= ~0x1fL; + key &= ~0x1fL; } - Key = Vmm.GetPhysicalAddress(Key); + key = vmm.GetPhysicalAddress(key); - if (Key == -1) + if (key == -1) { //FIXME: Shouldn't ignore invalid addresses. return (0, default(GalImage), default(GalTextureSampler)); } - Gpu.ResourceManager.SendTexture(Vmm, Key, Image); + _gpu.ResourceManager.SendTexture(vmm, key, image); - return (Key, Image, Sampler); + return (key, image, sampler); } - private void UploadConstBuffers(NvGpuVmm Vmm, GalPipelineState State, long[] Keys) + private void UploadConstBuffers(NvGpuVmm vmm, GalPipelineState state, long[] keys) { - for (int Stage = 0; Stage < Keys.Length; Stage++) + for (int stage = 0; stage < keys.Length; stage++) { - foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.Shader.GetConstBufferUsage(Keys[Stage])) + foreach (CBufferDescriptor desc in _gpu.Renderer.Shader.GetConstBufferUsage(keys[stage])) { - ConstBuffer Cb = ConstBuffers[Stage][DeclInfo.Cbuf]; + ConstBuffer cb = _constBuffers[stage][desc.Slot]; - if (!Cb.Enabled) + if (!cb.Enabled) { continue; } - long Key = Vmm.GetPhysicalAddress(Cb.Position); + long key = vmm.GetPhysicalAddress(cb.Position); - if (Gpu.ResourceManager.MemoryRegionModified(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer)) + if (_gpu.ResourceManager.MemoryRegionModified(vmm, key, cb.Size, NvGpuBufferType.ConstBuffer)) { - if (Vmm.TryGetHostAddress(Cb.Position, Cb.Size, out IntPtr CbPtr)) + if (vmm.TryGetHostAddress(cb.Position, cb.Size, out IntPtr cbPtr)) { - Gpu.Renderer.Buffer.SetData(Key, Cb.Size, CbPtr); + _gpu.Renderer.Buffer.SetData(key, cb.Size, cbPtr); } else { - Gpu.Renderer.Buffer.SetData(Key, Vmm.ReadBytes(Cb.Position, Cb.Size)); + _gpu.Renderer.Buffer.SetData(key, vmm.ReadBytes(cb.Position, cb.Size)); } } - State.ConstBufferKeys[Stage][DeclInfo.Cbuf] = Key; + state.ConstBufferKeys[stage][desc.Slot] = key; } } } - private void UploadVertexArrays(NvGpuVmm Vmm, GalPipelineState State) + private void UploadVertexArrays(NvGpuVmm vmm, GalPipelineState state) { - long IbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); + long ibPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); - long IboKey = Vmm.GetPhysicalAddress(IbPosition); + long iboKey = vmm.GetPhysicalAddress(ibPosition); - int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); - int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); - int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); + int indexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); + int indexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); + int primCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); - GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); + GalPrimitiveType primType = (GalPrimitiveType)(primCtrl & 0xffff); - GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt; + GalIndexFormat indexFormat = (GalIndexFormat)indexEntryFmt; - int IndexEntrySize = 1 << IndexEntryFmt; + int indexEntrySize = 1 << indexEntryFmt; - if (IndexEntrySize > 4) + if (indexEntrySize > 4) { - throw new InvalidOperationException("Invalid index entry size \"" + IndexEntrySize + "\"!"); + throw new InvalidOperationException("Invalid index entry size \"" + indexEntrySize + "\"!"); } - if (IndexCount != 0) + if (indexCount != 0) { - int IbSize = IndexCount * IndexEntrySize; + int ibSize = indexCount * indexEntrySize; - bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IboKey, (uint)IbSize); + bool iboCached = _gpu.Renderer.Rasterizer.IsIboCached(iboKey, (uint)ibSize); - bool UsesLegacyQuads = - PrimType == GalPrimitiveType.Quads || - PrimType == GalPrimitiveType.QuadStrip; + bool usesLegacyQuads = + primType == GalPrimitiveType.Quads || + primType == GalPrimitiveType.QuadStrip; - if (!IboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index)) + if (!iboCached || _gpu.ResourceManager.MemoryRegionModified(vmm, iboKey, (uint)ibSize, NvGpuBufferType.Index)) { - if (!UsesLegacyQuads) + if (!usesLegacyQuads) { - if (Vmm.TryGetHostAddress(IbPosition, IbSize, out IntPtr IbPtr)) + if (vmm.TryGetHostAddress(ibPosition, ibSize, out IntPtr ibPtr)) { - Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, IbPtr); + _gpu.Renderer.Rasterizer.CreateIbo(iboKey, ibSize, ibPtr); } else { - Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Vmm.ReadBytes(IbPosition, IbSize)); + _gpu.Renderer.Rasterizer.CreateIbo(iboKey, ibSize, vmm.ReadBytes(ibPosition, ibSize)); } } else { - byte[] Buffer = Vmm.ReadBytes(IbPosition, IbSize); + byte[] buffer = vmm.ReadBytes(ibPosition, ibSize); - if (PrimType == GalPrimitiveType.Quads) + if (primType == GalPrimitiveType.Quads) { - Buffer = QuadHelper.ConvertQuadsToTris(Buffer, IndexEntrySize, IndexCount); + buffer = QuadHelper.ConvertQuadsToTris(buffer, indexEntrySize, indexCount); } else /* if (PrimType == GalPrimitiveType.QuadStrip) */ { - Buffer = QuadHelper.ConvertQuadStripToTris(Buffer, IndexEntrySize, IndexCount); + buffer = QuadHelper.ConvertQuadStripToTris(buffer, indexEntrySize, indexCount); } - Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Buffer); + _gpu.Renderer.Rasterizer.CreateIbo(iboKey, ibSize, buffer); } } - if (!UsesLegacyQuads) + if (!usesLegacyQuads) { - Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat); + _gpu.Renderer.Rasterizer.SetIndexArray(ibSize, indexFormat); } else { - if (PrimType == GalPrimitiveType.Quads) + if (primType == GalPrimitiveType.Quads) { - Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertSizeQuadsToTris(IbSize), IndexFormat); + _gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertSizeQuadsToTris(ibSize), indexFormat); } else /* if (PrimType == GalPrimitiveType.QuadStrip) */ { - Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertSizeQuadStripToTris(IbSize), IndexFormat); + _gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertSizeQuadStripToTris(ibSize), indexFormat); } } } - List[] Attribs = new List[32]; + List[] attribs = new List[32]; - for (int Attr = 0; Attr < 16; Attr++) + for (int attr = 0; attr < 16; attr++) { - int Packed = ReadRegister(NvGpuEngine3dReg.VertexAttribNFormat + Attr); + int packed = ReadRegister(NvGpuEngine3dReg.VertexAttribNFormat + attr); - int ArrayIndex = Packed & 0x1f; + int arrayIndex = packed & 0x1f; - if (Attribs[ArrayIndex] == null) + if (attribs[arrayIndex] == null) { - Attribs[ArrayIndex] = new List(); + attribs[arrayIndex] = new List(); } - long VbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4); + long vbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + arrayIndex * 4); - if (VbPosition == 0) + if (vbPosition == 0) { continue; } - bool IsConst = ((Packed >> 6) & 1) != 0; + bool isConst = ((packed >> 6) & 1) != 0; - int Offset = (Packed >> 7) & 0x3fff; + int offset = (packed >> 7) & 0x3fff; - GalVertexAttribSize Size = (GalVertexAttribSize)((Packed >> 21) & 0x3f); - GalVertexAttribType Type = (GalVertexAttribType)((Packed >> 27) & 0x7); + GalVertexAttribSize size = (GalVertexAttribSize)((packed >> 21) & 0x3f); + GalVertexAttribType type = (GalVertexAttribType)((packed >> 27) & 0x7); - bool IsRgba = ((Packed >> 31) & 1) != 0; + bool isRgba = ((packed >> 31) & 1) != 0; // Check vertex array is enabled to avoid out of bounds exception when reading bytes - bool Enable = (ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + ArrayIndex * 4) & 0x1000) != 0; + bool enable = (ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + arrayIndex * 4) & 0x1000) != 0; //Note: 16 is the maximum size of an attribute, //having a component size of 32-bits with 4 elements (a vec4). - if (Enable) + if (enable) { - byte[] Data = Vmm.ReadBytes(VbPosition + Offset, 16); + byte[] data = vmm.ReadBytes(vbPosition + offset, 16); - Attribs[ArrayIndex].Add(new GalVertexAttrib(Attr, IsConst, Offset, Data, Size, Type, IsRgba)); + attribs[arrayIndex].Add(new GalVertexAttrib(attr, isConst, offset, data, size, type, isRgba)); } } - State.VertexBindings = new GalVertexBinding[32]; + state.VertexBindings = new GalVertexBinding[32]; - for (int Index = 0; Index < 32; Index++) + for (int index = 0; index < 32; index++) { - if (Attribs[Index] == null) + if (attribs[index] == null) { continue; } - int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4); + int control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + index * 4); - bool Enable = (Control & 0x1000) != 0; + bool enable = (control & 0x1000) != 0; - if (!Enable) + if (!enable) { continue; } - long VbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); - long VbEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2); + long vbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + index * 4); + long vbEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + index * 2); - int VertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + Index * 4); + int vertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + index * 4); - bool Instanced = ReadRegisterBool(NvGpuEngine3dReg.VertexArrayNInstance + Index); + bool instanced = ReadRegisterBool(NvGpuEngine3dReg.VertexArrayNInstance + index); - int Stride = Control & 0xfff; + int stride = control & 0xfff; - if (Instanced && VertexDivisor != 0) + if (instanced && vertexDivisor != 0) { - VbPosition += Stride * (CurrentInstance / VertexDivisor); + vbPosition += stride * (_currentInstance / vertexDivisor); } - if (VbPosition > VbEndPos) + if (vbPosition > vbEndPos) { //Instance is invalid, ignore the draw call continue; } - long VboKey = Vmm.GetPhysicalAddress(VbPosition); + long vboKey = vmm.GetPhysicalAddress(vbPosition); - long VbSize = (VbEndPos - VbPosition) + 1; - int ModifiedVbSize = (int)VbSize; + long vbSize = (vbEndPos - vbPosition) + 1; + int modifiedVbSize = (int)vbSize; // If quads convert size to triangle length - if (Stride == 0) + if (stride == 0) { - if (PrimType == GalPrimitiveType.Quads) + if (primType == GalPrimitiveType.Quads) { - ModifiedVbSize = QuadHelper.ConvertSizeQuadsToTris(ModifiedVbSize); + modifiedVbSize = QuadHelper.ConvertSizeQuadsToTris(modifiedVbSize); } - else if (PrimType == GalPrimitiveType.QuadStrip) + else if (primType == GalPrimitiveType.QuadStrip) { - ModifiedVbSize = QuadHelper.ConvertSizeQuadStripToTris(ModifiedVbSize); + modifiedVbSize = QuadHelper.ConvertSizeQuadStripToTris(modifiedVbSize); } } - bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, ModifiedVbSize); + bool vboCached = _gpu.Renderer.Rasterizer.IsVboCached(vboKey, modifiedVbSize); - if (!VboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex)) + if (!vboCached || _gpu.ResourceManager.MemoryRegionModified(vmm, vboKey, vbSize, NvGpuBufferType.Vertex)) { - if ((PrimType == GalPrimitiveType.Quads | PrimType == GalPrimitiveType.QuadStrip) && Stride != 0) + if ((primType == GalPrimitiveType.Quads | primType == GalPrimitiveType.QuadStrip) && stride != 0) { // Convert quad buffer to triangles - byte[] data = Vmm.ReadBytes(VbPosition, VbSize); + byte[] data = vmm.ReadBytes(vbPosition, vbSize); - if (PrimType == GalPrimitiveType.Quads) + if (primType == GalPrimitiveType.Quads) { - data = QuadHelper.ConvertQuadsToTris(data, Stride, (int)(VbSize / Stride)); + data = QuadHelper.ConvertQuadsToTris(data, stride, (int)(vbSize / stride)); } else { - data = QuadHelper.ConvertQuadStripToTris(data, Stride, (int)(VbSize / Stride)); + data = QuadHelper.ConvertQuadStripToTris(data, stride, (int)(vbSize / stride)); } - Gpu.Renderer.Rasterizer.CreateVbo(VboKey, data); + _gpu.Renderer.Rasterizer.CreateVbo(vboKey, data); } - else if (Vmm.TryGetHostAddress(VbPosition, VbSize, out IntPtr VbPtr)) + else if (vmm.TryGetHostAddress(vbPosition, vbSize, out IntPtr vbPtr)) { - Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, VbPtr); + _gpu.Renderer.Rasterizer.CreateVbo(vboKey, (int)vbSize, vbPtr); } else { - Gpu.Renderer.Rasterizer.CreateVbo(VboKey, Vmm.ReadBytes(VbPosition, VbSize)); + _gpu.Renderer.Rasterizer.CreateVbo(vboKey, vmm.ReadBytes(vbPosition, vbSize)); } } - State.VertexBindings[Index].Enabled = true; - State.VertexBindings[Index].Stride = Stride; - State.VertexBindings[Index].VboKey = VboKey; - State.VertexBindings[Index].Instanced = Instanced; - State.VertexBindings[Index].Divisor = VertexDivisor; - State.VertexBindings[Index].Attribs = Attribs[Index].ToArray(); + state.VertexBindings[index].Enabled = true; + state.VertexBindings[index].Stride = stride; + state.VertexBindings[index].VboKey = vboKey; + state.VertexBindings[index].Instanced = instanced; + state.VertexBindings[index].Divisor = vertexDivisor; + state.VertexBindings[index].Attribs = attribs[index].ToArray(); } } - private void DispatchRender(NvGpuVmm Vmm, GalPipelineState State) + private void DispatchRender(NvGpuVmm vmm, GalPipelineState state) { - int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); - int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); + int indexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); + int primCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); - GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); + GalPrimitiveType primType = (GalPrimitiveType)(primCtrl & 0xffff); - bool InstanceNext = ((PrimCtrl >> 26) & 1) != 0; - bool InstanceCont = ((PrimCtrl >> 27) & 1) != 0; + bool instanceNext = ((primCtrl >> 26) & 1) != 0; + bool instanceCont = ((primCtrl >> 27) & 1) != 0; - if (InstanceNext && InstanceCont) + if (instanceNext && instanceCont) { throw new InvalidOperationException("GPU tried to increase and reset instance count at the same time"); } - if (InstanceNext) + if (instanceNext) { - CurrentInstance++; + _currentInstance++; } - else if (!InstanceCont) + else if (!instanceCont) { - CurrentInstance = 0; + _currentInstance = 0; } - State.Instance = CurrentInstance; + state.Instance = _currentInstance; - Gpu.Renderer.Pipeline.Bind(State); + _gpu.Renderer.Pipeline.Bind(state); - Gpu.Renderer.RenderTarget.Bind(); + _gpu.Renderer.RenderTarget.Bind(); - if (IndexCount != 0) + if (indexCount != 0) { - int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); - int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); - int VertexBase = ReadRegister(NvGpuEngine3dReg.VertexArrayElemBase); + int indexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); + int indexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); + int vertexBase = ReadRegister(NvGpuEngine3dReg.VertexArrayElemBase); - long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); + long indexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); - long IboKey = Vmm.GetPhysicalAddress(IndexPosition); + long iboKey = vmm.GetPhysicalAddress(indexPosition); //Quad primitive types were deprecated on OpenGL 3.x, //they are converted to a triangles index buffer on IB creation, //so we should use the triangles type here too. - if (PrimType == GalPrimitiveType.Quads || PrimType == GalPrimitiveType.QuadStrip) + if (primType == GalPrimitiveType.Quads || primType == GalPrimitiveType.QuadStrip) { //Note: We assume that index first points to the first //vertex of a quad, if it points to the middle of a //quad (First % 4 != 0 for Quads) then it will not work properly. - if (PrimType == GalPrimitiveType.Quads) + if (primType == GalPrimitiveType.Quads) { - IndexFirst = QuadHelper.ConvertSizeQuadsToTris(IndexFirst); + indexFirst = QuadHelper.ConvertSizeQuadsToTris(indexFirst); } else // QuadStrip { - IndexFirst = QuadHelper.ConvertSizeQuadStripToTris(IndexFirst); + indexFirst = QuadHelper.ConvertSizeQuadStripToTris(indexFirst); } - PrimType = GalPrimitiveType.Triangles; + primType = GalPrimitiveType.Triangles; } - Gpu.Renderer.Rasterizer.DrawElements(IboKey, IndexFirst, VertexBase, PrimType); + _gpu.Renderer.Rasterizer.DrawElements(iboKey, indexFirst, vertexBase, primType); } else { - int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); - int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); + int vertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); + int vertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); //Quad primitive types were deprecated on OpenGL 3.x, //they are converted to a triangles index buffer on IB creation, //so we should use the triangles type here too. - if (PrimType == GalPrimitiveType.Quads || PrimType == GalPrimitiveType.QuadStrip) + if (primType == GalPrimitiveType.Quads || primType == GalPrimitiveType.QuadStrip) { //Note: We assume that index first points to the first //vertex of a quad, if it points to the middle of a //quad (First % 4 != 0 for Quads) then it will not work properly. - if (PrimType == GalPrimitiveType.Quads) + if (primType == GalPrimitiveType.Quads) { - VertexFirst = QuadHelper.ConvertSizeQuadsToTris(VertexFirst); + vertexFirst = QuadHelper.ConvertSizeQuadsToTris(vertexFirst); } else // QuadStrip { - VertexFirst = QuadHelper.ConvertSizeQuadStripToTris(VertexFirst); + vertexFirst = QuadHelper.ConvertSizeQuadStripToTris(vertexFirst); } - PrimType = GalPrimitiveType.Triangles; - VertexCount = QuadHelper.ConvertSizeQuadsToTris(VertexCount); + primType = GalPrimitiveType.Triangles; + vertexCount = QuadHelper.ConvertSizeQuadsToTris(vertexCount); } - Gpu.Renderer.Rasterizer.DrawArrays(VertexFirst, VertexCount, PrimType); + _gpu.Renderer.Rasterizer.DrawArrays(vertexFirst, vertexCount, primType); } + // Reset pipeline for host OpenGL calls + _gpu.Renderer.Pipeline.Unbind(state); + //Is the GPU really clearing those registers after draw? WriteRegister(NvGpuEngine3dReg.IndexBatchFirst, 0); WriteRegister(NvGpuEngine3dReg.IndexBatchCount, 0); @@ -1001,115 +1064,115 @@ namespace Ryujinx.Graphics.Graphics3d WriteCounterAndTimestamp } - private void QueryControl(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void QueryControl(NvGpuVmm vmm, GpuMethodCall methCall) { - WriteRegister(MethCall); + WriteRegister(methCall); - long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress); + long position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress); - int Seq = Registers[(int)NvGpuEngine3dReg.QuerySequence]; - int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl]; + int seq = Registers[(int)NvGpuEngine3dReg.QuerySequence]; + int ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl]; - QueryMode Mode = (QueryMode)(Ctrl & 3); + QueryMode mode = (QueryMode)(ctrl & 3); - switch (Mode) + switch (mode) { - case QueryMode.WriteSeq: Vmm.WriteInt32(Position, Seq); break; + case QueryMode.WriteSeq: vmm.WriteInt32(position, seq); break; case QueryMode.WriteCounterAndTimestamp: { //TODO: Implement counters. - long Counter = 1; + long counter = 1; - long Timestamp = PerformanceCounter.ElapsedMilliseconds; + long timestamp = PerformanceCounter.ElapsedMilliseconds; - Vmm.WriteInt64(Position + 0, Counter); - Vmm.WriteInt64(Position + 8, Timestamp); + vmm.WriteInt64(position + 0, counter); + vmm.WriteInt64(position + 8, timestamp); break; } } } - private void CbData(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void CbData(NvGpuVmm vmm, GpuMethodCall methCall) { - long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); + long position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); - int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferOffset); + int offset = ReadRegister(NvGpuEngine3dReg.ConstBufferOffset); - Vmm.WriteInt32(Position + Offset, MethCall.Argument); + vmm.WriteInt32(position + offset, methCall.Argument); - WriteRegister(NvGpuEngine3dReg.ConstBufferOffset, Offset + 4); + WriteRegister(NvGpuEngine3dReg.ConstBufferOffset, offset + 4); - Gpu.ResourceManager.ClearPbCache(NvGpuBufferType.ConstBuffer); + _gpu.ResourceManager.ClearPbCache(NvGpuBufferType.ConstBuffer); } - private void CbBind(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void CbBind(NvGpuVmm vmm, GpuMethodCall methCall) { - int Stage = (MethCall.Method - 0x904) >> 3; + int stage = (methCall.Method - 0x904) >> 3; - int Index = MethCall.Argument; + int index = methCall.Argument; - bool Enabled = (Index & 1) != 0; + bool enabled = (index & 1) != 0; - Index = (Index >> 4) & 0x1f; + index = (index >> 4) & 0x1f; - long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); + long position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); - long CbKey = Vmm.GetPhysicalAddress(Position); + long cbKey = vmm.GetPhysicalAddress(position); - int Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); + int size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); - if (!Gpu.Renderer.Buffer.IsCached(CbKey, Size)) + if (!_gpu.Renderer.Buffer.IsCached(cbKey, size)) { - Gpu.Renderer.Buffer.Create(CbKey, Size); + _gpu.Renderer.Buffer.Create(cbKey, size); } - ConstBuffer Cb = ConstBuffers[Stage][Index]; + ConstBuffer cb = _constBuffers[stage][index]; - if (Cb.Position != Position || Cb.Enabled != Enabled || Cb.Size != Size) + if (cb.Position != position || cb.Enabled != enabled || cb.Size != size) { - ConstBuffers[Stage][Index].Position = Position; - ConstBuffers[Stage][Index].Enabled = Enabled; - ConstBuffers[Stage][Index].Size = Size; + _constBuffers[stage][index].Position = position; + _constBuffers[stage][index].Enabled = enabled; + _constBuffers[stage][index].Size = size; } } - private float GetFlipSign(NvGpuEngine3dReg Reg) + private float GetFlipSign(NvGpuEngine3dReg reg) { - return MathF.Sign(ReadRegisterFloat(Reg)); + return MathF.Sign(ReadRegisterFloat(reg)); } - private long MakeInt64From2xInt32(NvGpuEngine3dReg Reg) + private long MakeInt64From2xInt32(NvGpuEngine3dReg reg) { return - (long)Registers[(int)Reg + 0] << 32 | - (uint)Registers[(int)Reg + 1]; + (long)Registers[(int)reg + 0] << 32 | + (uint)Registers[(int)reg + 1]; } - private void WriteRegister(GpuMethodCall MethCall) + private void WriteRegister(GpuMethodCall methCall) { - Registers[MethCall.Method] = MethCall.Argument; + Registers[methCall.Method] = methCall.Argument; } - private int ReadRegister(NvGpuEngine3dReg Reg) + private int ReadRegister(NvGpuEngine3dReg reg) { - return Registers[(int)Reg]; + return Registers[(int)reg]; } - private float ReadRegisterFloat(NvGpuEngine3dReg Reg) + private float ReadRegisterFloat(NvGpuEngine3dReg reg) { - return BitConverter.Int32BitsToSingle(ReadRegister(Reg)); + return BitConverter.Int32BitsToSingle(ReadRegister(reg)); } - private bool ReadRegisterBool(NvGpuEngine3dReg Reg) + private bool ReadRegisterBool(NvGpuEngine3dReg reg) { - return (ReadRegister(Reg) & 1) != 0; + return (ReadRegister(reg) & 1) != 0; } - private void WriteRegister(NvGpuEngine3dReg Reg, int Value) + private void WriteRegister(NvGpuEngine3dReg reg, int value) { - Registers[(int)Reg] = Value; + Registers[(int)reg] = value; } } } diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs index 026b0cd198..c6596a309a 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs @@ -2,112 +2,115 @@ namespace Ryujinx.Graphics.Graphics3d { enum NvGpuEngine3dReg { - FrameBufferNAddress = 0x200, - FrameBufferNWidth = 0x202, - FrameBufferNHeight = 0x203, - FrameBufferNFormat = 0x204, - FrameBufferNBlockDim = 0x205, - ViewportNScaleX = 0x280, - ViewportNScaleY = 0x281, - ViewportNScaleZ = 0x282, - ViewportNTranslateX = 0x283, - ViewportNTranslateY = 0x284, - ViewportNTranslateZ = 0x285, - ViewportNHoriz = 0x300, - ViewportNVert = 0x301, - DepthRangeNNear = 0x302, - DepthRangeNFar = 0x303, - VertexArrayFirst = 0x35d, - VertexArrayCount = 0x35e, - ClearNColor = 0x360, - ClearDepth = 0x364, - ClearStencil = 0x368, - ScissorEnable = 0x380, - ScissorHorizontal = 0x381, - ScissorVertical = 0x382, - StencilBackFuncRef = 0x3d5, - StencilBackMask = 0x3d6, - StencilBackFuncMask = 0x3d7, - ColorMaskCommon = 0x3e4, - RTSeparateFragData = 0x3eb, - ZetaAddress = 0x3f8, - ZetaFormat = 0x3fa, - ZetaBlockDimensions = 0x3fb, - ZetaLayerStride = 0x3fc, - VertexAttribNFormat = 0x458, - RTControl = 0x487, - ZetaHoriz = 0x48a, - ZetaVert = 0x48b, - ZetaArrayMode = 0x48c, - LinkedTsc = 0x48d, - DepthTestEnable = 0x4b3, - BlendIndependent = 0x4b9, - DepthWriteEnable = 0x4ba, - DepthTestFunction = 0x4c3, - BlendSeparateAlpha = 0x4cf, - BlendEquationRgb = 0x4d0, - BlendFuncSrcRgb = 0x4d1, - BlendFuncDstRgb = 0x4d2, - BlendEquationAlpha = 0x4d3, - BlendFuncSrcAlpha = 0x4d4, - BlendFuncDstAlpha = 0x4d6, - BlendEnable = 0x4d7, - IBlendNEnable = 0x4d8, - StencilEnable = 0x4e0, - StencilFrontOpFail = 0x4e1, - StencilFrontOpZFail = 0x4e2, - StencilFrontOpZPass = 0x4e3, - StencilFrontFuncFunc = 0x4e4, - StencilFrontFuncRef = 0x4e5, - StencilFrontFuncMask = 0x4e6, - StencilFrontMask = 0x4e7, - ScreenYControl = 0x4eb, - VertexArrayElemBase = 0x50d, - VertexArrayInstBase = 0x50e, - ZetaEnable = 0x54e, - TexHeaderPoolOffset = 0x55d, - TexSamplerPoolOffset = 0x557, - StencilTwoSideEnable = 0x565, - StencilBackOpFail = 0x566, - StencilBackOpZFail = 0x567, - StencilBackOpZPass = 0x568, - StencilBackFuncFunc = 0x569, - FrameBufferSrgb = 0x56e, - ShaderAddress = 0x582, - VertexBeginGl = 0x586, - PrimRestartEnable = 0x591, - PrimRestartIndex = 0x592, - IndexArrayAddress = 0x5f2, - IndexArrayEndAddr = 0x5f4, - IndexArrayFormat = 0x5f6, - IndexBatchFirst = 0x5f7, - IndexBatchCount = 0x5f8, - VertexArrayNInstance = 0x620, - CullFaceEnable = 0x646, - FrontFace = 0x647, - CullFace = 0x648, - ColorMaskN = 0x680, - QueryAddress = 0x6c0, - QuerySequence = 0x6c2, - QueryControl = 0x6c3, - VertexArrayNControl = 0x700, - VertexArrayNAddress = 0x701, - VertexArrayNDivisor = 0x703, - IBlendNSeparateAlpha = 0x780, - IBlendNEquationRgb = 0x781, - IBlendNFuncSrcRgb = 0x782, - IBlendNFuncDstRgb = 0x783, - IBlendNEquationAlpha = 0x784, - IBlendNFuncSrcAlpha = 0x785, - IBlendNFuncDstAlpha = 0x786, - VertexArrayNEndAddr = 0x7c0, - ShaderNControl = 0x800, - ShaderNOffset = 0x801, - ShaderNMaxGprs = 0x803, - ShaderNType = 0x804, - ConstBufferSize = 0x8e0, - ConstBufferAddress = 0x8e1, - ConstBufferOffset = 0x8e3, - TextureCbIndex = 0x982 + FrameBufferNAddress = 0x200, + FrameBufferNWidth = 0x202, + FrameBufferNHeight = 0x203, + FrameBufferNFormat = 0x204, + FrameBufferNBlockDim = 0x205, + FrameBufferNArrayMode = 0x206, + FrameBufferNLayerStride = 0x207, + FrameBufferNBaseLayer = 0x208, + ViewportNScaleX = 0x280, + ViewportNScaleY = 0x281, + ViewportNScaleZ = 0x282, + ViewportNTranslateX = 0x283, + ViewportNTranslateY = 0x284, + ViewportNTranslateZ = 0x285, + ViewportNHoriz = 0x300, + ViewportNVert = 0x301, + DepthRangeNNear = 0x302, + DepthRangeNFar = 0x303, + VertexArrayFirst = 0x35d, + VertexArrayCount = 0x35e, + ClearNColor = 0x360, + ClearDepth = 0x364, + ClearStencil = 0x368, + ScissorEnable = 0x380, + ScissorHorizontal = 0x381, + ScissorVertical = 0x382, + StencilBackFuncRef = 0x3d5, + StencilBackMask = 0x3d6, + StencilBackFuncMask = 0x3d7, + ColorMaskCommon = 0x3e4, + RtSeparateFragData = 0x3eb, + ZetaAddress = 0x3f8, + ZetaFormat = 0x3fa, + ZetaBlockDimensions = 0x3fb, + ZetaLayerStride = 0x3fc, + VertexAttribNFormat = 0x458, + RtControl = 0x487, + ZetaHoriz = 0x48a, + ZetaVert = 0x48b, + ZetaArrayMode = 0x48c, + LinkedTsc = 0x48d, + DepthTestEnable = 0x4b3, + BlendIndependent = 0x4b9, + DepthWriteEnable = 0x4ba, + DepthTestFunction = 0x4c3, + BlendSeparateAlpha = 0x4cf, + BlendEquationRgb = 0x4d0, + BlendFuncSrcRgb = 0x4d1, + BlendFuncDstRgb = 0x4d2, + BlendEquationAlpha = 0x4d3, + BlendFuncSrcAlpha = 0x4d4, + BlendFuncDstAlpha = 0x4d6, + BlendEnable = 0x4d7, + IBlendNEnable = 0x4d8, + StencilEnable = 0x4e0, + StencilFrontOpFail = 0x4e1, + StencilFrontOpZFail = 0x4e2, + StencilFrontOpZPass = 0x4e3, + StencilFrontFuncFunc = 0x4e4, + StencilFrontFuncRef = 0x4e5, + StencilFrontFuncMask = 0x4e6, + StencilFrontMask = 0x4e7, + ScreenYControl = 0x4eb, + VertexArrayElemBase = 0x50d, + VertexArrayInstBase = 0x50e, + ZetaEnable = 0x54e, + TexHeaderPoolOffset = 0x55d, + TexSamplerPoolOffset = 0x557, + StencilTwoSideEnable = 0x565, + StencilBackOpFail = 0x566, + StencilBackOpZFail = 0x567, + StencilBackOpZPass = 0x568, + StencilBackFuncFunc = 0x569, + FrameBufferSrgb = 0x56e, + ShaderAddress = 0x582, + VertexBeginGl = 0x586, + PrimRestartEnable = 0x591, + PrimRestartIndex = 0x592, + IndexArrayAddress = 0x5f2, + IndexArrayEndAddr = 0x5f4, + IndexArrayFormat = 0x5f6, + IndexBatchFirst = 0x5f7, + IndexBatchCount = 0x5f8, + VertexArrayNInstance = 0x620, + CullFaceEnable = 0x646, + FrontFace = 0x647, + CullFace = 0x648, + ColorMaskN = 0x680, + QueryAddress = 0x6c0, + QuerySequence = 0x6c2, + QueryControl = 0x6c3, + VertexArrayNControl = 0x700, + VertexArrayNAddress = 0x701, + VertexArrayNDivisor = 0x703, + IBlendNSeparateAlpha = 0x780, + IBlendNEquationRgb = 0x781, + IBlendNFuncSrcRgb = 0x782, + IBlendNFuncDstRgb = 0x783, + IBlendNEquationAlpha = 0x784, + IBlendNFuncSrcAlpha = 0x785, + IBlendNFuncDstAlpha = 0x786, + VertexArrayNEndAddr = 0x7c0, + ShaderNControl = 0x800, + ShaderNOffset = 0x801, + ShaderNMaxGprs = 0x803, + ShaderNType = 0x804, + ConstBufferSize = 0x8e0, + ConstBufferAddress = 0x8e1, + ConstBufferOffset = 0x8e3, + TextureCbIndex = 0x982 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs index d89059c0c5..45b0bbd792 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs @@ -8,180 +8,196 @@ namespace Ryujinx.Graphics.Graphics3d { public int[] Registers { get; private set; } - private NvGpu Gpu; + private NvGpu _gpu; - private Dictionary Methods; + private Dictionary _methods; - public NvGpuEngineM2mf(NvGpu Gpu) + public NvGpuEngineM2mf(NvGpu gpu) { - this.Gpu = Gpu; + _gpu = gpu; Registers = new int[0x1d6]; - Methods = new Dictionary(); + _methods = new Dictionary(); - void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) + void AddMethod(int meth, int count, int stride, NvGpuMethod method) { - while (Count-- > 0) + while (count-- > 0) { - Methods.Add(Meth, Method); + _methods.Add(meth, method); - Meth += Stride; + meth += stride; } } AddMethod(0xc0, 1, 1, Execute); } - public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + public void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall) { - if (Methods.TryGetValue(MethCall.Method, out NvGpuMethod Method)) + if (_methods.TryGetValue(methCall.Method, out NvGpuMethod method)) { - Method(Vmm, MethCall); + method(vmm, methCall); } else { - WriteRegister(MethCall); + WriteRegister(methCall); } } - private void Execute(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void Execute(NvGpuVmm vmm, GpuMethodCall methCall) { //TODO: Some registers and copy modes are still not implemented. - int Control = MethCall.Argument; + int control = methCall.Argument; - bool SrcLinear = ((Control >> 7) & 1) != 0; - bool DstLinear = ((Control >> 8) & 1) != 0; - bool Copy2d = ((Control >> 9) & 1) != 0; + bool srcLinear = ((control >> 7) & 1) != 0; + bool dstLinear = ((control >> 8) & 1) != 0; + bool copy2D = ((control >> 9) & 1) != 0; - long SrcAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.SrcAddress); - long DstAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.DstAddress); + long srcAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.SrcAddress); + long dstAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.DstAddress); - int SrcPitch = ReadRegister(NvGpuEngineM2mfReg.SrcPitch); - int DstPitch = ReadRegister(NvGpuEngineM2mfReg.DstPitch); + int srcPitch = ReadRegister(NvGpuEngineM2mfReg.SrcPitch); + int dstPitch = ReadRegister(NvGpuEngineM2mfReg.DstPitch); - int XCount = ReadRegister(NvGpuEngineM2mfReg.XCount); - int YCount = ReadRegister(NvGpuEngineM2mfReg.YCount); + int xCount = ReadRegister(NvGpuEngineM2mfReg.XCount); + int yCount = ReadRegister(NvGpuEngineM2mfReg.YCount); - int Swizzle = ReadRegister(NvGpuEngineM2mfReg.Swizzle); + int swizzle = ReadRegister(NvGpuEngineM2mfReg.Swizzle); - int DstBlkDim = ReadRegister(NvGpuEngineM2mfReg.DstBlkDim); - int DstSizeX = ReadRegister(NvGpuEngineM2mfReg.DstSizeX); - int DstSizeY = ReadRegister(NvGpuEngineM2mfReg.DstSizeY); - int DstSizeZ = ReadRegister(NvGpuEngineM2mfReg.DstSizeZ); - int DstPosXY = ReadRegister(NvGpuEngineM2mfReg.DstPosXY); - int DstPosZ = ReadRegister(NvGpuEngineM2mfReg.DstPosZ); + int dstBlkDim = ReadRegister(NvGpuEngineM2mfReg.DstBlkDim); + int dstSizeX = ReadRegister(NvGpuEngineM2mfReg.DstSizeX); + int dstSizeY = ReadRegister(NvGpuEngineM2mfReg.DstSizeY); + int dstSizeZ = ReadRegister(NvGpuEngineM2mfReg.DstSizeZ); + int dstPosXY = ReadRegister(NvGpuEngineM2mfReg.DstPosXY); + int dstPosZ = ReadRegister(NvGpuEngineM2mfReg.DstPosZ); - int SrcBlkDim = ReadRegister(NvGpuEngineM2mfReg.SrcBlkDim); - int SrcSizeX = ReadRegister(NvGpuEngineM2mfReg.SrcSizeX); - int SrcSizeY = ReadRegister(NvGpuEngineM2mfReg.SrcSizeY); - int SrcSizeZ = ReadRegister(NvGpuEngineM2mfReg.SrcSizeZ); - int SrcPosXY = ReadRegister(NvGpuEngineM2mfReg.SrcPosXY); - int SrcPosZ = ReadRegister(NvGpuEngineM2mfReg.SrcPosZ); + int srcBlkDim = ReadRegister(NvGpuEngineM2mfReg.SrcBlkDim); + int srcSizeX = ReadRegister(NvGpuEngineM2mfReg.SrcSizeX); + int srcSizeY = ReadRegister(NvGpuEngineM2mfReg.SrcSizeY); + int srcSizeZ = ReadRegister(NvGpuEngineM2mfReg.SrcSizeZ); + int srcPosXY = ReadRegister(NvGpuEngineM2mfReg.SrcPosXY); + int srcPosZ = ReadRegister(NvGpuEngineM2mfReg.SrcPosZ); - int SrcCpp = ((Swizzle >> 20) & 7) + 1; - int DstCpp = ((Swizzle >> 24) & 7) + 1; + int srcCpp = ((swizzle >> 20) & 7) + 1; + int dstCpp = ((swizzle >> 24) & 7) + 1; - int DstPosX = (DstPosXY >> 0) & 0xffff; - int DstPosY = (DstPosXY >> 16) & 0xffff; + int dstPosX = (dstPosXY >> 0) & 0xffff; + int dstPosY = (dstPosXY >> 16) & 0xffff; - int SrcPosX = (SrcPosXY >> 0) & 0xffff; - int SrcPosY = (SrcPosXY >> 16) & 0xffff; + int srcPosX = (srcPosXY >> 0) & 0xffff; + int srcPosY = (srcPosXY >> 16) & 0xffff; - int SrcBlockHeight = 1 << ((SrcBlkDim >> 4) & 0xf); - int DstBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); + int srcBlockHeight = 1 << ((srcBlkDim >> 4) & 0xf); + int dstBlockHeight = 1 << ((dstBlkDim >> 4) & 0xf); - long SrcPA = Vmm.GetPhysicalAddress(SrcAddress); - long DstPA = Vmm.GetPhysicalAddress(DstAddress); + long srcPa = vmm.GetPhysicalAddress(srcAddress); + long dstPa = vmm.GetPhysicalAddress(dstAddress); - if (Copy2d) + if (copy2D) { - if (SrcLinear) + if (srcLinear) { - SrcPosX = SrcPosY = SrcPosZ = 0; + srcPosX = srcPosY = srcPosZ = 0; } - if (DstLinear) + if (dstLinear) { - DstPosX = DstPosY = DstPosZ = 0; + dstPosX = dstPosY = dstPosZ = 0; } - if (SrcLinear && DstLinear) + if (srcLinear && dstLinear) { - for (int Y = 0; Y < YCount; Y++) + for (int y = 0; y < yCount; y++) { - int SrcOffset = (SrcPosY + Y) * SrcPitch + SrcPosX * SrcCpp; - int DstOffset = (DstPosY + Y) * DstPitch + DstPosX * DstCpp; + int srcOffset = (srcPosY + y) * srcPitch + srcPosX * srcCpp; + int dstOffset = (dstPosY + y) * dstPitch + dstPosX * dstCpp; - long Src = SrcPA + (uint)SrcOffset; - long Dst = DstPA + (uint)DstOffset; + long src = srcPa + (uint)srcOffset; + long dst = dstPa + (uint)dstOffset; - Vmm.Memory.CopyBytes(Src, Dst, XCount * SrcCpp); + vmm.Memory.CopyBytes(src, dst, xCount * srcCpp); } } else { - ISwizzle SrcSwizzle; + ISwizzle srcSwizzle; - if (SrcLinear) + if (srcLinear) { - SrcSwizzle = new LinearSwizzle(SrcPitch, SrcCpp); + srcSwizzle = new LinearSwizzle(srcPitch, srcCpp, srcSizeX, srcSizeY); } else { - SrcSwizzle = new BlockLinearSwizzle(SrcSizeX, SrcCpp, SrcBlockHeight); + srcSwizzle = new BlockLinearSwizzle( + srcSizeX, + srcSizeY, 1, + srcBlockHeight, 1, + srcCpp); } - ISwizzle DstSwizzle; + ISwizzle dstSwizzle; - if (DstLinear) + if (dstLinear) { - DstSwizzle = new LinearSwizzle(DstPitch, DstCpp); + dstSwizzle = new LinearSwizzle(dstPitch, dstCpp, srcSizeX, srcSizeY); } else { - DstSwizzle = new BlockLinearSwizzle(DstSizeX, DstCpp, DstBlockHeight); + dstSwizzle = new BlockLinearSwizzle( + dstSizeX, + dstSizeY, 1, + dstBlockHeight, 1, + dstCpp); } - for (int Y = 0; Y < YCount; Y++) - for (int X = 0; X < XCount; X++) + // Calculate the bits per pixel + int bpp = srcPitch / xCount; + + // Copying all the bits at the same time corrupts the texture, unknown why but probably because the texture isn't linear + // To avoid this we will simply loop more times to cover all the bits, + // this allows up to recalculate the memory locations for each iteration around the loop + xCount *= bpp / srcCpp; + + for (int y = 0; y < yCount; y++) + for (int x = 0; x < xCount; x++) { - int SrcOffset = SrcSwizzle.GetSwizzleOffset(SrcPosX + X, SrcPosY + Y); - int DstOffset = DstSwizzle.GetSwizzleOffset(DstPosX + X, DstPosY + Y); + int srcOffset = srcSwizzle.GetSwizzleOffset(srcPosX + x, srcPosY + y, 0); + int dstOffset = dstSwizzle.GetSwizzleOffset(dstPosX + x, dstPosY + y, 0); - long Src = SrcPA + (uint)SrcOffset; - long Dst = DstPA + (uint)DstOffset; + long src = srcPa + (uint)srcOffset; + long dst = dstPa + (uint)dstOffset; - Vmm.Memory.CopyBytes(Src, Dst, SrcCpp); + vmm.Memory.CopyBytes(src, dst, srcCpp); } } } else { - Vmm.Memory.CopyBytes(SrcPA, DstPA, XCount); + vmm.Memory.CopyBytes(srcPa, dstPa, xCount); } } - private long MakeInt64From2xInt32(NvGpuEngineM2mfReg Reg) + private long MakeInt64From2xInt32(NvGpuEngineM2mfReg reg) { return - (long)Registers[(int)Reg + 0] << 32 | - (uint)Registers[(int)Reg + 1]; + (long)Registers[(int)reg + 0] << 32 | + (uint)Registers[(int)reg + 1]; } - private void WriteRegister(GpuMethodCall MethCall) + private void WriteRegister(GpuMethodCall methCall) { - Registers[MethCall.Method] = MethCall.Argument; + Registers[methCall.Method] = methCall.Argument; } - private int ReadRegister(NvGpuEngineM2mfReg Reg) + private int ReadRegister(NvGpuEngineM2mfReg reg) { - return Registers[(int)Reg]; + return Registers[(int)reg]; } - private void WriteRegister(NvGpuEngineM2mfReg Reg, int Value) + private void WriteRegister(NvGpuEngineM2mfReg reg, int value) { - Registers[(int)Reg] = Value; + Registers[(int)reg] = value; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs index 681552556c..d24f2303d9 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs @@ -8,41 +8,41 @@ namespace Ryujinx.Graphics.Graphics3d { public int[] Registers { get; private set; } - private NvGpu Gpu; + private NvGpu _gpu; - private Dictionary Methods; + private Dictionary _methods; - private int CopyStartX; - private int CopyStartY; + private int _copyStartX; + private int _copyStartY; - private int CopyWidth; - private int CopyHeight; - private int CopyGobBlockHeight; + private int _copyWidth; + private int _copyHeight; + private int _copyGobBlockHeight; - private long CopyAddress; + private long _copyAddress; - private int CopyOffset; - private int CopySize; + private int _copyOffset; + private int _copySize; - private bool CopyLinear; + private bool _copyLinear; - private byte[] Buffer; + private byte[] _buffer; - public NvGpuEngineP2mf(NvGpu Gpu) + public NvGpuEngineP2mf(NvGpu gpu) { - this.Gpu = Gpu; + _gpu = gpu; Registers = new int[0x80]; - Methods = new Dictionary(); + _methods = new Dictionary(); - void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method) + void AddMethod(int meth, int count, int stride, NvGpuMethod method) { - while (Count-- > 0) + while (count-- > 0) { - Methods.Add(Meth, Method); + _methods.Add(meth, method); - Meth += Stride; + meth += stride; } } @@ -50,112 +50,115 @@ namespace Ryujinx.Graphics.Graphics3d AddMethod(0x6d, 1, 1, PushData); } - public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + public void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall) { - if (Methods.TryGetValue(MethCall.Method, out NvGpuMethod Method)) + if (_methods.TryGetValue(methCall.Method, out NvGpuMethod method)) { - Method(Vmm, MethCall); + method(vmm, methCall); } else { - WriteRegister(MethCall); + WriteRegister(methCall); } } - private void Execute(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void Execute(NvGpuVmm vmm, GpuMethodCall methCall) { //TODO: Some registers and copy modes are still not implemented. - int Control = MethCall.Argument; + int control = methCall.Argument; - long DstAddress = MakeInt64From2xInt32(NvGpuEngineP2mfReg.DstAddress); + long dstAddress = MakeInt64From2xInt32(NvGpuEngineP2mfReg.DstAddress); - int DstPitch = ReadRegister(NvGpuEngineP2mfReg.DstPitch); - int DstBlkDim = ReadRegister(NvGpuEngineP2mfReg.DstBlockDim); + int dstPitch = ReadRegister(NvGpuEngineP2mfReg.DstPitch); + int dstBlkDim = ReadRegister(NvGpuEngineP2mfReg.DstBlockDim); - int DstX = ReadRegister(NvGpuEngineP2mfReg.DstX); - int DstY = ReadRegister(NvGpuEngineP2mfReg.DstY); + int dstX = ReadRegister(NvGpuEngineP2mfReg.DstX); + int dstY = ReadRegister(NvGpuEngineP2mfReg.DstY); - int DstWidth = ReadRegister(NvGpuEngineP2mfReg.DstWidth); - int DstHeight = ReadRegister(NvGpuEngineP2mfReg.DstHeight); + int dstWidth = ReadRegister(NvGpuEngineP2mfReg.DstWidth); + int dstHeight = ReadRegister(NvGpuEngineP2mfReg.DstHeight); - int LineLengthIn = ReadRegister(NvGpuEngineP2mfReg.LineLengthIn); - int LineCount = ReadRegister(NvGpuEngineP2mfReg.LineCount); + int lineLengthIn = ReadRegister(NvGpuEngineP2mfReg.LineLengthIn); + int lineCount = ReadRegister(NvGpuEngineP2mfReg.LineCount); - CopyLinear = (Control & 1) != 0; + _copyLinear = (control & 1) != 0; - CopyGobBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf); + _copyGobBlockHeight = 1 << ((dstBlkDim >> 4) & 0xf); - CopyStartX = DstX; - CopyStartY = DstY; + _copyStartX = dstX; + _copyStartY = dstY; - CopyWidth = DstWidth; - CopyHeight = DstHeight; + _copyWidth = dstWidth; + _copyHeight = dstHeight; - CopyAddress = DstAddress; + _copyAddress = dstAddress; - CopyOffset = 0; - CopySize = LineLengthIn * LineCount; + _copyOffset = 0; + _copySize = lineLengthIn * lineCount; - Buffer = new byte[CopySize]; + _buffer = new byte[_copySize]; } - private void PushData(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void PushData(NvGpuVmm vmm, GpuMethodCall methCall) { - if (Buffer == null) + if (_buffer == null) { return; } - for (int Shift = 0; Shift < 32 && CopyOffset < CopySize; Shift += 8, CopyOffset++) + for (int shift = 0; shift < 32 && _copyOffset < _copySize; shift += 8, _copyOffset++) { - Buffer[CopyOffset] = (byte)(MethCall.Argument >> Shift); + _buffer[_copyOffset] = (byte)(methCall.Argument >> shift); } - if (MethCall.IsLastCall) + if (methCall.IsLastCall) { - if (CopyLinear) + if (_copyLinear) { - Vmm.WriteBytes(CopyAddress, Buffer); + vmm.WriteBytes(_copyAddress, _buffer); } else { - BlockLinearSwizzle Swizzle = new BlockLinearSwizzle(CopyWidth, 1, CopyGobBlockHeight); + BlockLinearSwizzle swizzle = new BlockLinearSwizzle( + _copyWidth, + _copyHeight, 1, + _copyGobBlockHeight, 1, 1); - int SrcOffset = 0; + int srcOffset = 0; - for (int Y = CopyStartY; Y < CopyHeight && SrcOffset < CopySize; Y++) - for (int X = CopyStartX; X < CopyWidth && SrcOffset < CopySize; X++) + for (int y = _copyStartY; y < _copyHeight && srcOffset < _copySize; y++) + for (int x = _copyStartX; x < _copyWidth && srcOffset < _copySize; x++) { - int DstOffset = Swizzle.GetSwizzleOffset(X, Y); + int dstOffset = swizzle.GetSwizzleOffset(x, y, 0); - Vmm.WriteByte(CopyAddress + DstOffset, Buffer[SrcOffset++]); + vmm.WriteByte(_copyAddress + dstOffset, _buffer[srcOffset++]); } } - Buffer = null; + _buffer = null; } } - private long MakeInt64From2xInt32(NvGpuEngineP2mfReg Reg) + private long MakeInt64From2xInt32(NvGpuEngineP2mfReg reg) { return - (long)Registers[(int)Reg + 0] << 32 | - (uint)Registers[(int)Reg + 1]; + (long)Registers[(int)reg + 0] << 32 | + (uint)Registers[(int)reg + 1]; } - private void WriteRegister(GpuMethodCall MethCall) + private void WriteRegister(GpuMethodCall methCall) { - Registers[MethCall.Method] = MethCall.Argument; + Registers[methCall.Method] = methCall.Argument; } - private int ReadRegister(NvGpuEngineP2mfReg Reg) + private int ReadRegister(NvGpuEngineP2mfReg reg) { - return Registers[(int)Reg]; + return Registers[(int)reg]; } - private void WriteRegister(NvGpuEngineP2mfReg Reg, int Value) + private void WriteRegister(NvGpuEngineP2mfReg reg, int value) { - Registers[(int)Reg] = Value; + Registers[(int)reg] = value; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuFifo.cs b/Ryujinx.Graphics/Graphics3d/NvGpuFifo.cs index f834ade78d..25c1a9cd9c 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuFifo.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuFifo.cs @@ -11,166 +11,166 @@ namespace Ryujinx.Graphics.Graphics3d //a guess here and use 256kb as the size. Increase if needed. private const int MmeWords = 256 * 256; - private NvGpu Gpu; + private NvGpu _gpu; - private NvGpuEngine[] SubChannels; + private NvGpuEngine[] _subChannels; private struct CachedMacro { public int Position { get; private set; } - private bool ExecutionPending; - private int Argument; + private bool _executionPending; + private int _argument; - private MacroInterpreter Interpreter; + private MacroInterpreter _interpreter; - public CachedMacro(NvGpuFifo PFifo, INvGpuEngine Engine, int Position) + public CachedMacro(NvGpuFifo pFifo, INvGpuEngine engine, int position) { - this.Position = Position; + Position = position; - ExecutionPending = false; - Argument = 0; + _executionPending = false; + _argument = 0; - Interpreter = new MacroInterpreter(PFifo, Engine); + _interpreter = new MacroInterpreter(pFifo, engine); } - public void StartExecution(int Argument) + public void StartExecution(int argument) { - this.Argument = Argument; + _argument = argument; - ExecutionPending = true; + _executionPending = true; } - public void Execute(NvGpuVmm Vmm, int[] Mme) + public void Execute(NvGpuVmm vmm, int[] mme) { - if (ExecutionPending) + if (_executionPending) { - ExecutionPending = false; + _executionPending = false; - Interpreter?.Execute(Vmm, Mme, Position, Argument); + _interpreter?.Execute(vmm, mme, Position, _argument); } } - public void PushArgument(int Argument) + public void PushArgument(int argument) { - Interpreter?.Fifo.Enqueue(Argument); + _interpreter?.Fifo.Enqueue(argument); } } - private int CurrMacroPosition; - private int CurrMacroBindIndex; + private int _currMacroPosition; + private int _currMacroBindIndex; - private CachedMacro[] Macros; + private CachedMacro[] _macros; - private int[] Mme; + private int[] _mme; - public NvGpuFifo(NvGpu Gpu) + public NvGpuFifo(NvGpu gpu) { - this.Gpu = Gpu; + _gpu = gpu; - SubChannels = new NvGpuEngine[8]; + _subChannels = new NvGpuEngine[8]; - Macros = new CachedMacro[MacrosCount]; + _macros = new CachedMacro[MacrosCount]; - Mme = new int[MmeWords]; + _mme = new int[MmeWords]; } - public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + public void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall) { - if ((NvGpuFifoMeth)MethCall.Method == NvGpuFifoMeth.BindChannel) + if ((NvGpuFifoMeth)methCall.Method == NvGpuFifoMeth.BindChannel) { - NvGpuEngine Engine = (NvGpuEngine)MethCall.Argument; + NvGpuEngine engine = (NvGpuEngine)methCall.Argument; - SubChannels[MethCall.SubChannel] = Engine; + _subChannels[methCall.SubChannel] = engine; } else { - switch (SubChannels[MethCall.SubChannel]) + switch (_subChannels[methCall.SubChannel]) { - case NvGpuEngine._2d: Call2dMethod (Vmm, MethCall); break; - case NvGpuEngine._3d: Call3dMethod (Vmm, MethCall); break; - case NvGpuEngine.P2mf: CallP2mfMethod(Vmm, MethCall); break; - case NvGpuEngine.M2mf: CallM2mfMethod(Vmm, MethCall); break; + case NvGpuEngine._2d: Call2dMethod (vmm, methCall); break; + case NvGpuEngine._3d: Call3dMethod (vmm, methCall); break; + case NvGpuEngine.P2mf: CallP2mfMethod(vmm, methCall); break; + case NvGpuEngine.M2mf: CallM2mfMethod(vmm, methCall); break; } } } - private void Call2dMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void Call2dMethod(NvGpuVmm vmm, GpuMethodCall methCall) { - Gpu.Engine2d.CallMethod(Vmm, MethCall); + _gpu.Engine2d.CallMethod(vmm, methCall); } - private void Call3dMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void Call3dMethod(NvGpuVmm vmm, GpuMethodCall methCall) { - if (MethCall.Method < 0x80) + if (methCall.Method < 0x80) { - switch ((NvGpuFifoMeth)MethCall.Method) + switch ((NvGpuFifoMeth)methCall.Method) { case NvGpuFifoMeth.SetMacroUploadAddress: { - CurrMacroPosition = MethCall.Argument; + _currMacroPosition = methCall.Argument; break; } case NvGpuFifoMeth.SendMacroCodeData: { - Mme[CurrMacroPosition++] = MethCall.Argument; + _mme[_currMacroPosition++] = methCall.Argument; break; } case NvGpuFifoMeth.SetMacroBindingIndex: { - CurrMacroBindIndex = MethCall.Argument; + _currMacroBindIndex = methCall.Argument; break; } case NvGpuFifoMeth.BindMacro: { - int Position = MethCall.Argument; + int position = methCall.Argument; - Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position); + _macros[_currMacroBindIndex] = new CachedMacro(this, _gpu.Engine3d, position); break; } - default: CallP2mfMethod(Vmm, MethCall); break; + default: CallP2mfMethod(vmm, methCall); break; } } - else if (MethCall.Method < 0xe00) + else if (methCall.Method < 0xe00) { - Gpu.Engine3d.CallMethod(Vmm, MethCall); + _gpu.Engine3d.CallMethod(vmm, methCall); } else { - int MacroIndex = (MethCall.Method >> 1) & MacroIndexMask; + int macroIndex = (methCall.Method >> 1) & MacroIndexMask; - if ((MethCall.Method & 1) != 0) + if ((methCall.Method & 1) != 0) { - Macros[MacroIndex].PushArgument(MethCall.Argument); + _macros[macroIndex].PushArgument(methCall.Argument); } else { - Macros[MacroIndex].StartExecution(MethCall.Argument); + _macros[macroIndex].StartExecution(methCall.Argument); } - if (MethCall.IsLastCall) + if (methCall.IsLastCall) { - Macros[MacroIndex].Execute(Vmm, Mme); + _macros[macroIndex].Execute(vmm, _mme); } } } - private void CallP2mfMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void CallP2mfMethod(NvGpuVmm vmm, GpuMethodCall methCall) { - Gpu.EngineP2mf.CallMethod(Vmm, MethCall); + _gpu.EngineP2mf.CallMethod(vmm, methCall); } - private void CallM2mfMethod(NvGpuVmm Vmm, GpuMethodCall MethCall) + private void CallM2mfMethod(NvGpuVmm vmm, GpuMethodCall methCall) { - Gpu.EngineM2mf.CallMethod(Vmm, MethCall); + _gpu.EngineM2mf.CallMethod(vmm, methCall); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuMethod.cs b/Ryujinx.Graphics/Graphics3d/NvGpuMethod.cs index 8730d1448c..23185c81fc 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuMethod.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuMethod.cs @@ -2,5 +2,5 @@ using Ryujinx.Graphics.Memory; namespace Ryujinx.Graphics.Graphics3d { - delegate void NvGpuMethod(NvGpuVmm Vmm, GpuMethodCall MethCall); + delegate void NvGpuMethod(NvGpuVmm vmm, GpuMethodCall methCall); } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs b/Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs deleted file mode 100644 index 1efa025523..0000000000 --- a/Ryujinx.Graphics/Graphics3d/Texture/ASTCDecoder.cs +++ /dev/null @@ -1,1384 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; - -namespace Ryujinx.Graphics.Texture -{ - public class ASTCDecoderException : Exception - { - public ASTCDecoderException(string ExMsg) : base(ExMsg) { } - } - - //https://github.com/GammaUNC/FasTC/blob/master/ASTCEncoder/src/Decompressor.cpp - public static class ASTCDecoder - { - struct TexelWeightParams - { - public int Width; - public int Height; - public bool DualPlane; - public int MaxWeight; - public bool Error; - public bool VoidExtentLDR; - public bool VoidExtentHDR; - - public int GetPackedBitSize() - { - // How many indices do we have? - int Indices = Height * Width; - - if (DualPlane) - { - Indices *= 2; - } - - IntegerEncoded IntEncoded = IntegerEncoded.CreateEncoding(MaxWeight); - - return IntEncoded.GetBitLength(Indices); - } - - public int GetNumWeightValues() - { - int Ret = Width * Height; - - if (DualPlane) - { - Ret *= 2; - } - - return Ret; - } - } - - public static byte[] DecodeToRGBA8888( - byte[] InputBuffer, - int BlockX, - int BlockY, - int BlockZ, - int X, - int Y, - int Z) - { - using (MemoryStream InputStream = new MemoryStream(InputBuffer)) - { - BinaryReader BinReader = new BinaryReader(InputStream); - - if (BlockX > 12 || BlockY > 12) - { - throw new ASTCDecoderException("Block size unsupported!"); - } - - if (BlockZ != 1 || Z != 1) - { - throw new ASTCDecoderException("3D compressed textures unsupported!"); - } - - using (MemoryStream OutputStream = new MemoryStream()) - { - int BlockIndex = 0; - - for (int j = 0; j < Y; j += BlockY) - { - for (int i = 0; i < X; i += BlockX) - { - int[] DecompressedData = new int[144]; - - DecompressBlock(BinReader.ReadBytes(0x10), DecompressedData, BlockX, BlockY); - - int DecompressedWidth = Math.Min(BlockX, X - i); - int DecompressedHeight = Math.Min(BlockY, Y - j); - int BaseOffsets = (j * X + i) * 4; - - for (int jj = 0; jj < DecompressedHeight; jj++) - { - OutputStream.Seek(BaseOffsets + jj * X * 4, SeekOrigin.Begin); - - byte[] OutputBuffer = new byte[DecompressedData.Length * sizeof(int)]; - Buffer.BlockCopy(DecompressedData, 0, OutputBuffer, 0, OutputBuffer.Length); - - OutputStream.Write(OutputBuffer, jj * BlockX * 4, DecompressedWidth * 4); - } - - BlockIndex++; - } - } - - return OutputStream.ToArray(); - } - } - } - - public static bool DecompressBlock( - byte[] InputBuffer, - int[] OutputBuffer, - int BlockWidth, - int BlockHeight) - { - BitArrayStream BitStream = new BitArrayStream(new BitArray(InputBuffer)); - TexelWeightParams TexelParams = DecodeBlockInfo(BitStream); - - if (TexelParams.Error) - { - throw new ASTCDecoderException("Invalid block mode"); - } - - if (TexelParams.VoidExtentLDR) - { - FillVoidExtentLDR(BitStream, OutputBuffer, BlockWidth, BlockHeight); - - return true; - } - - if (TexelParams.VoidExtentHDR) - { - throw new ASTCDecoderException("HDR void extent blocks are unsupported!"); - } - - if (TexelParams.Width > BlockWidth) - { - throw new ASTCDecoderException("Texel weight grid width should be smaller than block width"); - } - - if (TexelParams.Height > BlockHeight) - { - throw new ASTCDecoderException("Texel weight grid height should be smaller than block height"); - } - - // Read num partitions - int NumberPartitions = BitStream.ReadBits(2) + 1; - Debug.Assert(NumberPartitions <= 4); - - if (NumberPartitions == 4 && TexelParams.DualPlane) - { - throw new ASTCDecoderException("Dual plane mode is incompatible with four partition blocks"); - } - - // Based on the number of partitions, read the color endpoint mode for - // each partition. - - // Determine partitions, partition index, and color endpoint modes - int PlaneIndices = -1; - int PartitionIndex; - uint[] ColorEndpointMode = { 0, 0, 0, 0 }; - - BitArrayStream ColorEndpointStream = new BitArrayStream(new BitArray(16 * 8)); - - // Read extra config data... - uint BaseColorEndpointMode = 0; - - if (NumberPartitions == 1) - { - ColorEndpointMode[0] = (uint)BitStream.ReadBits(4); - PartitionIndex = 0; - } - else - { - PartitionIndex = BitStream.ReadBits(10); - BaseColorEndpointMode = (uint)BitStream.ReadBits(6); - } - - uint BaseMode = (BaseColorEndpointMode & 3); - - // Remaining bits are color endpoint data... - int NumberWeightBits = TexelParams.GetPackedBitSize(); - int RemainingBits = 128 - NumberWeightBits - BitStream.Position; - - // Consider extra bits prior to texel data... - uint ExtraColorEndpointModeBits = 0; - - if (BaseMode != 0) - { - switch (NumberPartitions) - { - case 2: ExtraColorEndpointModeBits += 2; break; - case 3: ExtraColorEndpointModeBits += 5; break; - case 4: ExtraColorEndpointModeBits += 8; break; - default: Debug.Assert(false); break; - } - } - - RemainingBits -= (int)ExtraColorEndpointModeBits; - - // Do we have a dual plane situation? - int PlaneSelectorBits = 0; - - if (TexelParams.DualPlane) - { - PlaneSelectorBits = 2; - } - - RemainingBits -= PlaneSelectorBits; - - // Read color data... - int ColorDataBits = RemainingBits; - - while (RemainingBits > 0) - { - int NumberBits = Math.Min(RemainingBits, 8); - int Bits = BitStream.ReadBits(NumberBits); - ColorEndpointStream.WriteBits(Bits, NumberBits); - RemainingBits -= 8; - } - - // Read the plane selection bits - PlaneIndices = BitStream.ReadBits(PlaneSelectorBits); - - // Read the rest of the CEM - if (BaseMode != 0) - { - uint ExtraColorEndpointMode = (uint)BitStream.ReadBits((int)ExtraColorEndpointModeBits); - uint TempColorEndpointMode = (ExtraColorEndpointMode << 6) | BaseColorEndpointMode; - TempColorEndpointMode >>= 2; - - bool[] C = new bool[4]; - - for (int i = 0; i < NumberPartitions; i++) - { - C[i] = (TempColorEndpointMode & 1) != 0; - TempColorEndpointMode >>= 1; - } - - byte[] M = new byte[4]; - - for (int i = 0; i < NumberPartitions; i++) - { - M[i] = (byte)(TempColorEndpointMode & 3); - TempColorEndpointMode >>= 2; - Debug.Assert(M[i] <= 3); - } - - for (int i = 0; i < NumberPartitions; i++) - { - ColorEndpointMode[i] = BaseMode; - if (!(C[i])) ColorEndpointMode[i] -= 1; - ColorEndpointMode[i] <<= 2; - ColorEndpointMode[i] |= M[i]; - } - } - else if (NumberPartitions > 1) - { - uint TempColorEndpointMode = BaseColorEndpointMode >> 2; - - for (uint i = 0; i < NumberPartitions; i++) - { - ColorEndpointMode[i] = TempColorEndpointMode; - } - } - - // Make sure everything up till here is sane. - for (int i = 0; i < NumberPartitions; i++) - { - Debug.Assert(ColorEndpointMode[i] < 16); - } - Debug.Assert(BitStream.Position + TexelParams.GetPackedBitSize() == 128); - - // Decode both color data and texel weight data - int[] ColorValues = new int[32]; // Four values * two endpoints * four maximum partitions - DecodeColorValues(ColorValues, ColorEndpointStream.ToByteArray(), ColorEndpointMode, NumberPartitions, ColorDataBits); - - ASTCPixel[][] EndPoints = new ASTCPixel[4][]; - EndPoints[0] = new ASTCPixel[2]; - EndPoints[1] = new ASTCPixel[2]; - EndPoints[2] = new ASTCPixel[2]; - EndPoints[3] = new ASTCPixel[2]; - - int ColorValuesPosition = 0; - - for (int i = 0; i < NumberPartitions; i++) - { - ComputeEndpoints(EndPoints[i], ColorValues, ColorEndpointMode[i], ref ColorValuesPosition); - } - - // Read the texel weight data. - byte[] TexelWeightData = (byte[])InputBuffer.Clone(); - - // Reverse everything - for (int i = 0; i < 8; i++) - { - byte a = ReverseByte(TexelWeightData[i]); - byte b = ReverseByte(TexelWeightData[15 - i]); - - TexelWeightData[i] = b; - TexelWeightData[15 - i] = a; - } - - // Make sure that higher non-texel bits are set to zero - int ClearByteStart = (TexelParams.GetPackedBitSize() >> 3) + 1; - TexelWeightData[ClearByteStart - 1] &= (byte)((1 << (TexelParams.GetPackedBitSize() % 8)) - 1); - - int cLen = 16 - ClearByteStart; - for (int i = ClearByteStart; i < ClearByteStart + cLen; i++) TexelWeightData[i] = 0; - - List TexelWeightValues = new List(); - BitArrayStream WeightBitStream = new BitArrayStream(new BitArray(TexelWeightData)); - - IntegerEncoded.DecodeIntegerSequence(TexelWeightValues, WeightBitStream, TexelParams.MaxWeight, TexelParams.GetNumWeightValues()); - - // Blocks can be at most 12x12, so we can have as many as 144 weights - int[][] Weights = new int[2][]; - Weights[0] = new int[144]; - Weights[1] = new int[144]; - - UnquantizeTexelWeights(Weights, TexelWeightValues, TexelParams, BlockWidth, BlockHeight); - - // Now that we have endpoints and weights, we can interpolate and generate - // the proper decoding... - for (int j = 0; j < BlockHeight; j++) - { - for (int i = 0; i < BlockWidth; i++) - { - int Partition = Select2DPartition(PartitionIndex, i, j, NumberPartitions, ((BlockHeight * BlockWidth) < 32)); - Debug.Assert(Partition < NumberPartitions); - - ASTCPixel Pixel = new ASTCPixel(0, 0, 0, 0); - for (int Component = 0; Component < 4; Component++) - { - int Component0 = EndPoints[Partition][0].GetComponent(Component); - Component0 = BitArrayStream.Replicate(Component0, 8, 16); - int Component1 = EndPoints[Partition][1].GetComponent(Component); - Component1 = BitArrayStream.Replicate(Component1, 8, 16); - - int Plane = 0; - - if (TexelParams.DualPlane && (((PlaneIndices + 1) & 3) == Component)) - { - Plane = 1; - } - - int Weight = Weights[Plane][j * BlockWidth + i]; - int FinalComponent = (Component0 * (64 - Weight) + Component1 * Weight + 32) / 64; - - if (FinalComponent == 65535) - { - Pixel.SetComponent(Component, 255); - } - else - { - double FinalComponentFloat = FinalComponent; - Pixel.SetComponent(Component, (int)(255.0 * (FinalComponentFloat / 65536.0) + 0.5)); - } - } - - OutputBuffer[j * BlockWidth + i] = Pixel.Pack(); - } - } - - return true; - } - - private static int Select2DPartition(int Seed, int X, int Y, int PartitionCount, bool IsSmallBlock) - { - return SelectPartition(Seed, X, Y, 0, PartitionCount, IsSmallBlock); - } - - private static int SelectPartition(int Seed, int X, int Y, int Z, int PartitionCount, bool IsSmallBlock) - { - if (PartitionCount == 1) - { - return 0; - } - - if (IsSmallBlock) - { - X <<= 1; - Y <<= 1; - Z <<= 1; - } - - Seed += (PartitionCount - 1) * 1024; - - int RightNum = Hash52((uint)Seed); - byte Seed01 = (byte)(RightNum & 0xF); - byte Seed02 = (byte)((RightNum >> 4) & 0xF); - byte Seed03 = (byte)((RightNum >> 8) & 0xF); - byte Seed04 = (byte)((RightNum >> 12) & 0xF); - byte Seed05 = (byte)((RightNum >> 16) & 0xF); - byte Seed06 = (byte)((RightNum >> 20) & 0xF); - byte Seed07 = (byte)((RightNum >> 24) & 0xF); - byte Seed08 = (byte)((RightNum >> 28) & 0xF); - byte Seed09 = (byte)((RightNum >> 18) & 0xF); - byte Seed10 = (byte)((RightNum >> 22) & 0xF); - byte Seed11 = (byte)((RightNum >> 26) & 0xF); - byte Seed12 = (byte)(((RightNum >> 30) | (RightNum << 2)) & 0xF); - - Seed01 *= Seed01; Seed02 *= Seed02; - Seed03 *= Seed03; Seed04 *= Seed04; - Seed05 *= Seed05; Seed06 *= Seed06; - Seed07 *= Seed07; Seed08 *= Seed08; - Seed09 *= Seed09; Seed10 *= Seed10; - Seed11 *= Seed11; Seed12 *= Seed12; - - int SeedHash1, SeedHash2, SeedHash3; - - if ((Seed & 1) != 0) - { - SeedHash1 = (Seed & 2) != 0 ? 4 : 5; - SeedHash2 = (PartitionCount == 3) ? 6 : 5; - } - else - { - SeedHash1 = (PartitionCount == 3) ? 6 : 5; - SeedHash2 = (Seed & 2) != 0 ? 4 : 5; - } - - SeedHash3 = (Seed & 0x10) != 0 ? SeedHash1 : SeedHash2; - - Seed01 >>= SeedHash1; Seed02 >>= SeedHash2; Seed03 >>= SeedHash1; Seed04 >>= SeedHash2; - Seed05 >>= SeedHash1; Seed06 >>= SeedHash2; Seed07 >>= SeedHash1; Seed08 >>= SeedHash2; - Seed09 >>= SeedHash3; Seed10 >>= SeedHash3; Seed11 >>= SeedHash3; Seed12 >>= SeedHash3; - - int a = Seed01 * X + Seed02 * Y + Seed11 * Z + (RightNum >> 14); - int b = Seed03 * X + Seed04 * Y + Seed12 * Z + (RightNum >> 10); - int c = Seed05 * X + Seed06 * Y + Seed09 * Z + (RightNum >> 6); - int d = Seed07 * X + Seed08 * Y + Seed10 * Z + (RightNum >> 2); - - a &= 0x3F; b &= 0x3F; c &= 0x3F; d &= 0x3F; - - if (PartitionCount < 4) d = 0; - if (PartitionCount < 3) c = 0; - - if (a >= b && a >= c && a >= d) return 0; - else if (b >= c && b >= d) return 1; - else if (c >= d) return 2; - return 3; - } - - static int Hash52(uint Val) - { - Val ^= Val >> 15; Val -= Val << 17; Val += Val << 7; Val += Val << 4; - Val ^= Val >> 5; Val += Val << 16; Val ^= Val >> 7; Val ^= Val >> 3; - Val ^= Val << 6; Val ^= Val >> 17; - - return (int)Val; - } - - static void UnquantizeTexelWeights( - int[][] OutputBuffer, - List Weights, - TexelWeightParams TexelParams, - int BlockWidth, - int BlockHeight) - { - int WeightIndices = 0; - int[][] Unquantized = new int[2][]; - Unquantized[0] = new int[144]; - Unquantized[1] = new int[144]; - - for (int i = 0; i < Weights.Count; i++) - { - Unquantized[0][WeightIndices] = UnquantizeTexelWeight(Weights[i]); - - if (TexelParams.DualPlane) - { - i++; - Unquantized[1][WeightIndices] = UnquantizeTexelWeight(Weights[i]); - - if (i == Weights.Count) - { - break; - } - } - - if (++WeightIndices >= (TexelParams.Width * TexelParams.Height)) break; - } - - // Do infill if necessary (Section C.2.18) ... - int Ds = (1024 + (BlockWidth / 2)) / (BlockWidth - 1); - int Dt = (1024 + (BlockHeight / 2)) / (BlockHeight - 1); - - int PlaneScale = TexelParams.DualPlane ? 2 : 1; - - for (int Plane = 0; Plane < PlaneScale; Plane++) - { - for (int t = 0; t < BlockHeight; t++) - { - for (int s = 0; s < BlockWidth; s++) - { - int cs = Ds * s; - int ct = Dt * t; - - int gs = (cs * (TexelParams.Width - 1) + 32) >> 6; - int gt = (ct * (TexelParams.Height - 1) + 32) >> 6; - - int js = gs >> 4; - int fs = gs & 0xF; - - int jt = gt >> 4; - int ft = gt & 0x0F; - - int w11 = (fs * ft + 8) >> 4; - int w10 = ft - w11; - int w01 = fs - w11; - int w00 = 16 - fs - ft + w11; - - int v0 = js + jt * TexelParams.Width; - - int p00 = 0; - int p01 = 0; - int p10 = 0; - int p11 = 0; - - if (v0 < (TexelParams.Width * TexelParams.Height)) - { - p00 = Unquantized[Plane][v0]; - } - - if (v0 + 1 < (TexelParams.Width * TexelParams.Height)) - { - p01 = Unquantized[Plane][v0 + 1]; - } - - if (v0 + TexelParams.Width < (TexelParams.Width * TexelParams.Height)) - { - p10 = Unquantized[Plane][v0 + TexelParams.Width]; - } - - if (v0 + TexelParams.Width + 1 < (TexelParams.Width * TexelParams.Height)) - { - p11 = Unquantized[Plane][v0 + TexelParams.Width + 1]; - } - - OutputBuffer[Plane][t * BlockWidth + s] = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4; - } - } - } - } - - static int UnquantizeTexelWeight(IntegerEncoded IntEncoded) - { - int BitValue = IntEncoded.BitValue; - int BitLength = IntEncoded.NumberBits; - - int A = BitArrayStream.Replicate(BitValue & 1, 1, 7); - int B = 0, C = 0, D = 0; - - int Result = 0; - - switch (IntEncoded.GetEncoding()) - { - case IntegerEncoded.EIntegerEncoding.JustBits: - Result = BitArrayStream.Replicate(BitValue, BitLength, 6); - break; - - case IntegerEncoded.EIntegerEncoding.Trit: - { - D = IntEncoded.TritValue; - Debug.Assert(D < 3); - - switch (BitLength) - { - case 0: - { - int[] Results = { 0, 32, 63 }; - Result = Results[D]; - - break; - } - - case 1: - { - C = 50; - break; - } - - case 2: - { - C = 23; - int b = (BitValue >> 1) & 1; - B = (b << 6) | (b << 2) | b; - - break; - } - - case 3: - { - C = 11; - int cb = (BitValue >> 1) & 3; - B = (cb << 5) | cb; - - break; - } - - default: - throw new ASTCDecoderException("Invalid trit encoding for texel weight"); - } - - break; - } - - case IntegerEncoded.EIntegerEncoding.Quint: - { - D = IntEncoded.QuintValue; - Debug.Assert(D < 5); - - switch (BitLength) - { - case 0: - { - int[] Results = { 0, 16, 32, 47, 63 }; - Result = Results[D]; - - break; - } - - case 1: - { - C = 28; - - break; - } - - case 2: - { - C = 13; - int b = (BitValue >> 1) & 1; - B = (b << 6) | (b << 1); - - break; - } - - default: - throw new ASTCDecoderException("Invalid quint encoding for texel weight"); - } - - break; - } - } - - if (IntEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits && BitLength > 0) - { - // Decode the value... - Result = D * C + B; - Result ^= A; - Result = (A & 0x20) | (Result >> 2); - } - - Debug.Assert(Result < 64); - - // Change from [0,63] to [0,64] - if (Result > 32) - { - Result += 1; - } - - return Result; - } - - static byte ReverseByte(byte b) - { - // Taken from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits - return (byte)((((b) * 0x80200802L) & 0x0884422110L) * 0x0101010101L >> 32); - } - - static uint[] ReadUintColorValues(int Number, int[] ColorValues, ref int ColorValuesPosition) - { - uint[] Ret = new uint[Number]; - - for (int i = 0; i < Number; i++) - { - Ret[i] = (uint)ColorValues[ColorValuesPosition++]; - } - - return Ret; - } - - static int[] ReadIntColorValues(int Number, int[] ColorValues, ref int ColorValuesPosition) - { - int[] Ret = new int[Number]; - - for (int i = 0; i < Number; i++) - { - Ret[i] = ColorValues[ColorValuesPosition++]; - } - - return Ret; - } - - static void ComputeEndpoints( - ASTCPixel[] EndPoints, - int[] ColorValues, - uint ColorEndpointMode, - ref int ColorValuesPosition) - { - switch (ColorEndpointMode) - { - case 0: - { - uint[] Val = ReadUintColorValues(2, ColorValues, ref ColorValuesPosition); - - EndPoints[0] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[0], (short)Val[0]); - EndPoints[1] = new ASTCPixel(0xFF, (short)Val[1], (short)Val[1], (short)Val[1]); - - break; - } - - - case 1: - { - uint[] Val = ReadUintColorValues(2, ColorValues, ref ColorValuesPosition); - int L0 = (int)((Val[0] >> 2) | (Val[1] & 0xC0)); - int L1 = (int)Math.Max(L0 + (Val[1] & 0x3F), 0xFFU); - - EndPoints[0] = new ASTCPixel(0xFF, (short)L0, (short)L0, (short)L0); - EndPoints[1] = new ASTCPixel(0xFF, (short)L1, (short)L1, (short)L1); - - break; - } - - case 4: - { - uint[] Val = ReadUintColorValues(4, ColorValues, ref ColorValuesPosition); - - EndPoints[0] = new ASTCPixel((short)Val[2], (short)Val[0], (short)Val[0], (short)Val[0]); - EndPoints[1] = new ASTCPixel((short)Val[3], (short)Val[1], (short)Val[1], (short)Val[1]); - - break; - } - - case 5: - { - int[] Val = ReadIntColorValues(4, ColorValues, ref ColorValuesPosition); - - BitArrayStream.BitTransferSigned(ref Val[1], ref Val[0]); - BitArrayStream.BitTransferSigned(ref Val[3], ref Val[2]); - - EndPoints[0] = new ASTCPixel((short)Val[2], (short)Val[0], (short)Val[0], (short)Val[0]); - EndPoints[1] = new ASTCPixel((short)(Val[2] + Val[3]), (short)(Val[0] + Val[1]), (short)(Val[0] + Val[1]), (short)(Val[0] + Val[1])); - - EndPoints[0].ClampByte(); - EndPoints[1].ClampByte(); - - break; - } - - case 6: - { - uint[] Val = ReadUintColorValues(4, ColorValues, ref ColorValuesPosition); - - EndPoints[0] = new ASTCPixel(0xFF, (short)(Val[0] * Val[3] >> 8), (short)(Val[1] * Val[3] >> 8), (short)(Val[2] * Val[3] >> 8)); - EndPoints[1] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[1], (short)Val[2]); - - break; - } - - case 8: - { - uint[] Val = ReadUintColorValues(6, ColorValues, ref ColorValuesPosition); - - if (Val[1] + Val[3] + Val[5] >= Val[0] + Val[2] + Val[4]) - { - EndPoints[0] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[2], (short)Val[4]); - EndPoints[1] = new ASTCPixel(0xFF, (short)Val[1], (short)Val[3], (short)Val[5]); - } - else - { - EndPoints[0] = ASTCPixel.BlueContract(0xFF, (short)Val[1], (short)Val[3], (short)Val[5]); - EndPoints[1] = ASTCPixel.BlueContract(0xFF, (short)Val[0], (short)Val[2], (short)Val[4]); - } - - break; - } - - case 9: - { - int[] Val = ReadIntColorValues(6, ColorValues, ref ColorValuesPosition); - - BitArrayStream.BitTransferSigned(ref Val[1], ref Val[0]); - BitArrayStream.BitTransferSigned(ref Val[3], ref Val[2]); - BitArrayStream.BitTransferSigned(ref Val[5], ref Val[4]); - - if (Val[1] + Val[3] + Val[5] >= 0) - { - EndPoints[0] = new ASTCPixel(0xFF, (short)Val[0], (short)Val[2], (short)Val[4]); - EndPoints[1] = new ASTCPixel(0xFF, (short)(Val[0] + Val[1]), (short)(Val[2] + Val[3]), (short)(Val[4] + Val[5])); - } - else - { - EndPoints[0] = ASTCPixel.BlueContract(0xFF, Val[0] + Val[1], Val[2] + Val[3], Val[4] + Val[5]); - EndPoints[1] = ASTCPixel.BlueContract(0xFF, Val[0], Val[2], Val[4]); - } - - EndPoints[0].ClampByte(); - EndPoints[1].ClampByte(); - - break; - } - - case 10: - { - uint[] Val = ReadUintColorValues(6, ColorValues, ref ColorValuesPosition); - - EndPoints[0] = new ASTCPixel((short)Val[4], (short)(Val[0] * Val[3] >> 8), (short)(Val[1] * Val[3] >> 8), (short)(Val[2] * Val[3] >> 8)); - EndPoints[1] = new ASTCPixel((short)Val[5], (short)Val[0], (short)Val[1], (short)Val[2]); - - break; - } - - case 12: - { - uint[] Val = ReadUintColorValues(8, ColorValues, ref ColorValuesPosition); - - if (Val[1] + Val[3] + Val[5] >= Val[0] + Val[2] + Val[4]) - { - EndPoints[0] = new ASTCPixel((short)Val[6], (short)Val[0], (short)Val[2], (short)Val[4]); - EndPoints[1] = new ASTCPixel((short)Val[7], (short)Val[1], (short)Val[3], (short)Val[5]); - } - else - { - EndPoints[0] = ASTCPixel.BlueContract((short)Val[7], (short)Val[1], (short)Val[3], (short)Val[5]); - EndPoints[1] = ASTCPixel.BlueContract((short)Val[6], (short)Val[0], (short)Val[2], (short)Val[4]); - } - - break; - } - - case 13: - { - int[] Val = ReadIntColorValues(8, ColorValues, ref ColorValuesPosition); - - BitArrayStream.BitTransferSigned(ref Val[1], ref Val[0]); - BitArrayStream.BitTransferSigned(ref Val[3], ref Val[2]); - BitArrayStream.BitTransferSigned(ref Val[5], ref Val[4]); - BitArrayStream.BitTransferSigned(ref Val[7], ref Val[6]); - - if (Val[1] + Val[3] + Val[5] >= 0) - { - EndPoints[0] = new ASTCPixel((short)Val[6], (short)Val[0], (short)Val[2], (short)Val[4]); - EndPoints[1] = new ASTCPixel((short)(Val[7] + Val[6]), (short)(Val[0] + Val[1]), (short)(Val[2] + Val[3]), (short)(Val[4] + Val[5])); - } - else - { - EndPoints[0] = ASTCPixel.BlueContract(Val[6] + Val[7], Val[0] + Val[1], Val[2] + Val[3], Val[4] + Val[5]); - EndPoints[1] = ASTCPixel.BlueContract(Val[6], Val[0], Val[2], Val[4]); - } - - EndPoints[0].ClampByte(); - EndPoints[1].ClampByte(); - - break; - } - - default: - throw new ASTCDecoderException("Unsupported color endpoint mode (is it HDR?)"); - } - } - - static void DecodeColorValues( - int[] OutputValues, - byte[] InputData, - uint[] Modes, - int NumberPartitions, - int NumberBitsForColorData) - { - // First figure out how many color values we have - int NumberValues = 0; - - for (int i = 0; i < NumberPartitions; i++) - { - NumberValues += (int)((Modes[i] >> 2) + 1) << 1; - } - - // Then based on the number of values and the remaining number of bits, - // figure out the max value for each of them... - int Range = 256; - - while (--Range > 0) - { - IntegerEncoded IntEncoded = IntegerEncoded.CreateEncoding(Range); - int BitLength = IntEncoded.GetBitLength(NumberValues); - - if (BitLength <= NumberBitsForColorData) - { - // Find the smallest possible range that matches the given encoding - while (--Range > 0) - { - IntegerEncoded NewIntEncoded = IntegerEncoded.CreateEncoding(Range); - if (!NewIntEncoded.MatchesEncoding(IntEncoded)) - { - break; - } - } - - // Return to last matching range. - Range++; - break; - } - } - - // We now have enough to decode our integer sequence. - List IntegerEncodedSequence = new List(); - BitArrayStream ColorBitStream = new BitArrayStream(new BitArray(InputData)); - - IntegerEncoded.DecodeIntegerSequence(IntegerEncodedSequence, ColorBitStream, Range, NumberValues); - - // Once we have the decoded values, we need to dequantize them to the 0-255 range - // This procedure is outlined in ASTC spec C.2.13 - int OutputIndices = 0; - - foreach (IntegerEncoded IntEncoded in IntegerEncodedSequence) - { - int BitLength = IntEncoded.NumberBits; - int BitValue = IntEncoded.BitValue; - - Debug.Assert(BitLength >= 1); - - int A = 0, B = 0, C = 0, D = 0; - // A is just the lsb replicated 9 times. - A = BitArrayStream.Replicate(BitValue & 1, 1, 9); - - switch (IntEncoded.GetEncoding()) - { - case IntegerEncoded.EIntegerEncoding.JustBits: - { - OutputValues[OutputIndices++] = BitArrayStream.Replicate(BitValue, BitLength, 8); - - break; - } - - case IntegerEncoded.EIntegerEncoding.Trit: - { - D = IntEncoded.TritValue; - - switch (BitLength) - { - case 1: - { - C = 204; - - break; - } - - case 2: - { - C = 93; - // B = b000b0bb0 - int b = (BitValue >> 1) & 1; - B = (b << 8) | (b << 4) | (b << 2) | (b << 1); - - break; - } - - case 3: - { - C = 44; - // B = cb000cbcb - int cb = (BitValue >> 1) & 3; - B = (cb << 7) | (cb << 2) | cb; - - break; - } - - - case 4: - { - C = 22; - // B = dcb000dcb - int dcb = (BitValue >> 1) & 7; - B = (dcb << 6) | dcb; - - break; - } - - case 5: - { - C = 11; - // B = edcb000ed - int edcb = (BitValue >> 1) & 0xF; - B = (edcb << 5) | (edcb >> 2); - - break; - } - - case 6: - { - C = 5; - // B = fedcb000f - int fedcb = (BitValue >> 1) & 0x1F; - B = (fedcb << 4) | (fedcb >> 4); - - break; - } - - default: - throw new ASTCDecoderException("Unsupported trit encoding for color values!"); - } - - break; - } - - case IntegerEncoded.EIntegerEncoding.Quint: - { - D = IntEncoded.QuintValue; - - switch (BitLength) - { - case 1: - { - C = 113; - - break; - } - - case 2: - { - C = 54; - // B = b0000bb00 - int b = (BitValue >> 1) & 1; - B = (b << 8) | (b << 3) | (b << 2); - - break; - } - - case 3: - { - C = 26; - // B = cb0000cbc - int cb = (BitValue >> 1) & 3; - B = (cb << 7) | (cb << 1) | (cb >> 1); - - break; - } - - case 4: - { - C = 13; - // B = dcb0000dc - int dcb = (BitValue >> 1) & 7; - B = (dcb << 6) | (dcb >> 1); - - break; - } - - case 5: - { - C = 6; - // B = edcb0000e - int edcb = (BitValue >> 1) & 0xF; - B = (edcb << 5) | (edcb >> 3); - - break; - } - - default: - throw new ASTCDecoderException("Unsupported quint encoding for color values!"); - } - break; - } - } - - if (IntEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits) - { - int T = D * C + B; - T ^= A; - T = (A & 0x80) | (T >> 2); - - OutputValues[OutputIndices++] = T; - } - } - - // Make sure that each of our values is in the proper range... - for (int i = 0; i < NumberValues; i++) - { - Debug.Assert(OutputValues[i] <= 255); - } - } - - static void FillVoidExtentLDR(BitArrayStream BitStream, int[] OutputBuffer, int BlockWidth, int BlockHeight) - { - // Don't actually care about the void extent, just read the bits... - for (int i = 0; i < 4; ++i) - { - BitStream.ReadBits(13); - } - - // Decode the RGBA components and renormalize them to the range [0, 255] - ushort R = (ushort)BitStream.ReadBits(16); - ushort G = (ushort)BitStream.ReadBits(16); - ushort B = (ushort)BitStream.ReadBits(16); - ushort A = (ushort)BitStream.ReadBits(16); - - int RGBA = (R >> 8) | (G & 0xFF00) | ((B) & 0xFF00) << 8 | ((A) & 0xFF00) << 16; - - for (int j = 0; j < BlockHeight; j++) - { - for (int i = 0; i < BlockWidth; i++) - { - OutputBuffer[j * BlockWidth + i] = RGBA; - } - } - } - - static TexelWeightParams DecodeBlockInfo(BitArrayStream BitStream) - { - TexelWeightParams TexelParams = new TexelWeightParams(); - - // Read the entire block mode all at once - ushort ModeBits = (ushort)BitStream.ReadBits(11); - - // Does this match the void extent block mode? - if ((ModeBits & 0x01FF) == 0x1FC) - { - if ((ModeBits & 0x200) != 0) - { - TexelParams.VoidExtentHDR = true; - } - else - { - TexelParams.VoidExtentLDR = true; - } - - // Next two bits must be one. - if ((ModeBits & 0x400) == 0 || BitStream.ReadBits(1) == 0) - { - TexelParams.Error = true; - } - - return TexelParams; - } - - // First check if the last four bits are zero - if ((ModeBits & 0xF) == 0) - { - TexelParams.Error = true; - return TexelParams; - } - - // If the last two bits are zero, then if bits - // [6-8] are all ones, this is also reserved. - if ((ModeBits & 0x3) == 0 && (ModeBits & 0x1C0) == 0x1C0) - { - TexelParams.Error = true; - - return TexelParams; - } - - // Otherwise, there is no error... Figure out the layout - // of the block mode. Layout is determined by a number - // between 0 and 9 corresponding to table C.2.8 of the - // ASTC spec. - int Layout = 0; - - if ((ModeBits & 0x1) != 0 || (ModeBits & 0x2) != 0) - { - // layout is in [0-4] - if ((ModeBits & 0x8) != 0) - { - // layout is in [2-4] - if ((ModeBits & 0x4) != 0) - { - // layout is in [3-4] - if ((ModeBits & 0x100) != 0) - { - Layout = 4; - } - else - { - Layout = 3; - } - } - else - { - Layout = 2; - } - } - else - { - // layout is in [0-1] - if ((ModeBits & 0x4) != 0) - { - Layout = 1; - } - else - { - Layout = 0; - } - } - } - else - { - // layout is in [5-9] - if ((ModeBits & 0x100) != 0) - { - // layout is in [7-9] - if ((ModeBits & 0x80) != 0) - { - // layout is in [7-8] - Debug.Assert((ModeBits & 0x40) == 0); - - if ((ModeBits & 0x20) != 0) - { - Layout = 8; - } - else - { - Layout = 7; - } - } - else - { - Layout = 9; - } - } - else - { - // layout is in [5-6] - if ((ModeBits & 0x80) != 0) - { - Layout = 6; - } - else - { - Layout = 5; - } - } - } - - Debug.Assert(Layout < 10); - - // Determine R - int R = (ModeBits >> 4) & 1; - if (Layout < 5) - { - R |= (ModeBits & 0x3) << 1; - } - else - { - R |= (ModeBits & 0xC) >> 1; - } - - Debug.Assert(2 <= R && R <= 7); - - // Determine width & height - switch (Layout) - { - case 0: - { - int A = (ModeBits >> 5) & 0x3; - int B = (ModeBits >> 7) & 0x3; - - TexelParams.Width = B + 4; - TexelParams.Height = A + 2; - - break; - } - - case 1: - { - int A = (ModeBits >> 5) & 0x3; - int B = (ModeBits >> 7) & 0x3; - - TexelParams.Width = B + 8; - TexelParams.Height = A + 2; - - break; - } - - case 2: - { - int A = (ModeBits >> 5) & 0x3; - int B = (ModeBits >> 7) & 0x3; - - TexelParams.Width = A + 2; - TexelParams.Height = B + 8; - - break; - } - - case 3: - { - int A = (ModeBits >> 5) & 0x3; - int B = (ModeBits >> 7) & 0x1; - - TexelParams.Width = A + 2; - TexelParams.Height = B + 6; - - break; - } - - case 4: - { - int A = (ModeBits >> 5) & 0x3; - int B = (ModeBits >> 7) & 0x1; - - TexelParams.Width = B + 2; - TexelParams.Height = A + 2; - - break; - } - - case 5: - { - int A = (ModeBits >> 5) & 0x3; - - TexelParams.Width = 12; - TexelParams.Height = A + 2; - - break; - } - - case 6: - { - int A = (ModeBits >> 5) & 0x3; - - TexelParams.Width = A + 2; - TexelParams.Height = 12; - - break; - } - - case 7: - { - TexelParams.Width = 6; - TexelParams.Height = 10; - - break; - } - - case 8: - { - TexelParams.Width = 10; - TexelParams.Height = 6; - break; - } - - case 9: - { - int A = (ModeBits >> 5) & 0x3; - int B = (ModeBits >> 9) & 0x3; - - TexelParams.Width = A + 6; - TexelParams.Height = B + 6; - - break; - } - - default: - //Don't know this layout... - TexelParams.Error = true; - break; - } - - // Determine whether or not we're using dual planes - // and/or high precision layouts. - bool D = ((Layout != 9) && ((ModeBits & 0x400) != 0)); - bool H = (Layout != 9) && ((ModeBits & 0x200) != 0); - - if (H) - { - int[] MaxWeights = { 9, 11, 15, 19, 23, 31 }; - TexelParams.MaxWeight = MaxWeights[R - 2]; - } - else - { - int[] MaxWeights = { 1, 2, 3, 4, 5, 7 }; - TexelParams.MaxWeight = MaxWeights[R - 2]; - } - - TexelParams.DualPlane = D; - - return TexelParams; - } - } -} diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ASTCPixel.cs b/Ryujinx.Graphics/Graphics3d/Texture/ASTCPixel.cs deleted file mode 100644 index c43eaf9380..0000000000 --- a/Ryujinx.Graphics/Graphics3d/Texture/ASTCPixel.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Diagnostics; - -namespace Ryujinx.Graphics.Texture -{ - class ASTCPixel - { - public short R { get; set; } - public short G { get; set; } - public short B { get; set; } - public short A { get; set; } - - byte[] BitDepth = new byte[4]; - - public ASTCPixel(short _A, short _R, short _G, short _B) - { - A = _A; - R = _R; - G = _G; - B = _B; - - for (int i = 0; i < 4; i++) - BitDepth[i] = 8; - } - - public void ClampByte() - { - R = Math.Min(Math.Max(R, (short)0), (short)255); - G = Math.Min(Math.Max(G, (short)0), (short)255); - B = Math.Min(Math.Max(B, (short)0), (short)255); - A = Math.Min(Math.Max(A, (short)0), (short)255); - } - - public short GetComponent(int Index) - { - switch(Index) - { - case 0: return A; - case 1: return R; - case 2: return G; - case 3: return B; - } - - return 0; - } - - public void SetComponent(int Index, int Value) - { - switch (Index) - { - case 0: - A = (short)Value; - break; - case 1: - R = (short)Value; - break; - case 2: - G = (short)Value; - break; - case 3: - B = (short)Value; - break; - } - } - - public void ChangeBitDepth(byte[] Depth) - { - for(int i = 0; i< 4; i++) - { - int Value = ChangeBitDepth(GetComponent(i), BitDepth[i], Depth[i]); - - SetComponent(i, Value); - BitDepth[i] = Depth[i]; - } - } - - short ChangeBitDepth(short Value, byte OldDepth, byte NewDepth) - { - Debug.Assert(NewDepth <= 8); - Debug.Assert(OldDepth <= 8); - - if (OldDepth == NewDepth) - { - // Do nothing - return Value; - } - else if (OldDepth == 0 && NewDepth != 0) - { - return (short)((1 << NewDepth) - 1); - } - else if (NewDepth > OldDepth) - { - return (short)BitArrayStream.Replicate(Value, OldDepth, NewDepth); - } - else - { - // oldDepth > newDepth - if (NewDepth == 0) - { - return 0xFF; - } - else - { - byte BitsWasted = (byte)(OldDepth - NewDepth); - short TempValue = Value; - - TempValue = (short)((TempValue + (1 << (BitsWasted - 1))) >> BitsWasted); - TempValue = Math.Min(Math.Max((short)0, TempValue), (short)((1 << NewDepth) - 1)); - - return (byte)(TempValue); - } - } - } - - public int Pack() - { - ASTCPixel NewPixel = new ASTCPixel(A, R, G, B); - byte[] eightBitDepth = { 8, 8, 8, 8 }; - - NewPixel.ChangeBitDepth(eightBitDepth); - - return (byte)NewPixel.A << 24 | - (byte)NewPixel.B << 16 | - (byte)NewPixel.G << 8 | - (byte)NewPixel.R << 0; - } - - // Adds more precision to the blue channel as described - // in C.2.14 - public static ASTCPixel BlueContract(int a, int r, int g, int b) - { - return new ASTCPixel((short)(a), - (short)((r + b) >> 1), - (short)((g + b) >> 1), - (short)(b)); - } - } -} diff --git a/Ryujinx.Graphics/Graphics3d/Texture/AstcDecoder.cs b/Ryujinx.Graphics/Graphics3d/Texture/AstcDecoder.cs new file mode 100644 index 0000000000..99b166f33e --- /dev/null +++ b/Ryujinx.Graphics/Graphics3d/Texture/AstcDecoder.cs @@ -0,0 +1,1385 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; + +namespace Ryujinx.Graphics.Texture +{ + public class AstcDecoderException : Exception + { + public AstcDecoderException(string exMsg) : base(exMsg) { } + } + + //https://github.com/GammaUNC/FasTC/blob/master/ASTCEncoder/src/Decompressor.cpp + public static class AstcDecoder + { + struct TexelWeightParams + { + public int Width; + public int Height; + public bool DualPlane; + public int MaxWeight; + public bool Error; + public bool VoidExtentLdr; + public bool VoidExtentHdr; + + public int GetPackedBitSize() + { + // How many indices do we have? + int indices = Height * Width; + + if (DualPlane) + { + indices *= 2; + } + + IntegerEncoded intEncoded = IntegerEncoded.CreateEncoding(MaxWeight); + + return intEncoded.GetBitLength(indices); + } + + public int GetNumWeightValues() + { + int ret = Width * Height; + + if (DualPlane) + { + ret *= 2; + } + + return ret; + } + } + + public static byte[] DecodeToRgba8888( + byte[] inputBuffer, + int blockX, + int blockY, + int blockZ, + int x, + int y, + int z) + { + using (MemoryStream inputStream = new MemoryStream(inputBuffer)) + { + BinaryReader binReader = new BinaryReader(inputStream); + + if (blockX > 12 || blockY > 12) + { + throw new AstcDecoderException("Block size unsupported!"); + } + + if (blockZ != 1 || z != 1) + { + // TODO: Support 3D textures? + throw new AstcDecoderException("3D compressed textures unsupported!"); + } + + using (MemoryStream outputStream = new MemoryStream()) + { + int blockIndex = 0; + + for (int j = 0; j < y; j += blockY) + { + for (int i = 0; i < x; i += blockX) + { + int[] decompressedData = new int[144]; + + DecompressBlock(binReader.ReadBytes(0x10), decompressedData, blockX, blockY); + + int decompressedWidth = Math.Min(blockX, x - i); + int decompressedHeight = Math.Min(blockY, y - j); + int baseOffsets = (j * x + i) * 4; + + for (int jj = 0; jj < decompressedHeight; jj++) + { + outputStream.Seek(baseOffsets + jj * x * 4, SeekOrigin.Begin); + + byte[] outputBuffer = new byte[decompressedData.Length * sizeof(int)]; + Buffer.BlockCopy(decompressedData, 0, outputBuffer, 0, outputBuffer.Length); + + outputStream.Write(outputBuffer, jj * blockX * 4, decompressedWidth * 4); + } + + blockIndex++; + } + } + + return outputStream.ToArray(); + } + } + } + + public static bool DecompressBlock( + byte[] inputBuffer, + int[] outputBuffer, + int blockWidth, + int blockHeight) + { + BitArrayStream bitStream = new BitArrayStream(new BitArray(inputBuffer)); + TexelWeightParams texelParams = DecodeBlockInfo(bitStream); + + if (texelParams.Error) + { + throw new AstcDecoderException("Invalid block mode"); + } + + if (texelParams.VoidExtentLdr) + { + FillVoidExtentLdr(bitStream, outputBuffer, blockWidth, blockHeight); + + return true; + } + + if (texelParams.VoidExtentHdr) + { + throw new AstcDecoderException("HDR void extent blocks are unsupported!"); + } + + if (texelParams.Width > blockWidth) + { + throw new AstcDecoderException("Texel weight grid width should be smaller than block width"); + } + + if (texelParams.Height > blockHeight) + { + throw new AstcDecoderException("Texel weight grid height should be smaller than block height"); + } + + // Read num partitions + int numberPartitions = bitStream.ReadBits(2) + 1; + Debug.Assert(numberPartitions <= 4); + + if (numberPartitions == 4 && texelParams.DualPlane) + { + throw new AstcDecoderException("Dual plane mode is incompatible with four partition blocks"); + } + + // Based on the number of partitions, read the color endpoint mode for + // each partition. + + // Determine partitions, partition index, and color endpoint modes + int planeIndices = -1; + int partitionIndex; + uint[] colorEndpointMode = { 0, 0, 0, 0 }; + + BitArrayStream colorEndpointStream = new BitArrayStream(new BitArray(16 * 8)); + + // Read extra config data... + uint baseColorEndpointMode = 0; + + if (numberPartitions == 1) + { + colorEndpointMode[0] = (uint)bitStream.ReadBits(4); + partitionIndex = 0; + } + else + { + partitionIndex = bitStream.ReadBits(10); + baseColorEndpointMode = (uint)bitStream.ReadBits(6); + } + + uint baseMode = (baseColorEndpointMode & 3); + + // Remaining bits are color endpoint data... + int numberWeightBits = texelParams.GetPackedBitSize(); + int remainingBits = 128 - numberWeightBits - bitStream.Position; + + // Consider extra bits prior to texel data... + uint extraColorEndpointModeBits = 0; + + if (baseMode != 0) + { + switch (numberPartitions) + { + case 2: extraColorEndpointModeBits += 2; break; + case 3: extraColorEndpointModeBits += 5; break; + case 4: extraColorEndpointModeBits += 8; break; + default: Debug.Assert(false); break; + } + } + + remainingBits -= (int)extraColorEndpointModeBits; + + // Do we have a dual plane situation? + int planeSelectorBits = 0; + + if (texelParams.DualPlane) + { + planeSelectorBits = 2; + } + + remainingBits -= planeSelectorBits; + + // Read color data... + int colorDataBits = remainingBits; + + while (remainingBits > 0) + { + int numberBits = Math.Min(remainingBits, 8); + int bits = bitStream.ReadBits(numberBits); + colorEndpointStream.WriteBits(bits, numberBits); + remainingBits -= 8; + } + + // Read the plane selection bits + planeIndices = bitStream.ReadBits(planeSelectorBits); + + // Read the rest of the CEM + if (baseMode != 0) + { + uint extraColorEndpointMode = (uint)bitStream.ReadBits((int)extraColorEndpointModeBits); + uint tempColorEndpointMode = (extraColorEndpointMode << 6) | baseColorEndpointMode; + tempColorEndpointMode >>= 2; + + bool[] c = new bool[4]; + + for (int i = 0; i < numberPartitions; i++) + { + c[i] = (tempColorEndpointMode & 1) != 0; + tempColorEndpointMode >>= 1; + } + + byte[] m = new byte[4]; + + for (int i = 0; i < numberPartitions; i++) + { + m[i] = (byte)(tempColorEndpointMode & 3); + tempColorEndpointMode >>= 2; + Debug.Assert(m[i] <= 3); + } + + for (int i = 0; i < numberPartitions; i++) + { + colorEndpointMode[i] = baseMode; + if (!(c[i])) colorEndpointMode[i] -= 1; + colorEndpointMode[i] <<= 2; + colorEndpointMode[i] |= m[i]; + } + } + else if (numberPartitions > 1) + { + uint tempColorEndpointMode = baseColorEndpointMode >> 2; + + for (uint i = 0; i < numberPartitions; i++) + { + colorEndpointMode[i] = tempColorEndpointMode; + } + } + + // Make sure everything up till here is sane. + for (int i = 0; i < numberPartitions; i++) + { + Debug.Assert(colorEndpointMode[i] < 16); + } + Debug.Assert(bitStream.Position + texelParams.GetPackedBitSize() == 128); + + // Decode both color data and texel weight data + int[] colorValues = new int[32]; // Four values * two endpoints * four maximum partitions + DecodeColorValues(colorValues, colorEndpointStream.ToByteArray(), colorEndpointMode, numberPartitions, colorDataBits); + + AstcPixel[][] endPoints = new AstcPixel[4][]; + endPoints[0] = new AstcPixel[2]; + endPoints[1] = new AstcPixel[2]; + endPoints[2] = new AstcPixel[2]; + endPoints[3] = new AstcPixel[2]; + + int colorValuesPosition = 0; + + for (int i = 0; i < numberPartitions; i++) + { + ComputeEndpoints(endPoints[i], colorValues, colorEndpointMode[i], ref colorValuesPosition); + } + + // Read the texel weight data. + byte[] texelWeightData = (byte[])inputBuffer.Clone(); + + // Reverse everything + for (int i = 0; i < 8; i++) + { + byte a = ReverseByte(texelWeightData[i]); + byte b = ReverseByte(texelWeightData[15 - i]); + + texelWeightData[i] = b; + texelWeightData[15 - i] = a; + } + + // Make sure that higher non-texel bits are set to zero + int clearByteStart = (texelParams.GetPackedBitSize() >> 3) + 1; + texelWeightData[clearByteStart - 1] &= (byte)((1 << (texelParams.GetPackedBitSize() % 8)) - 1); + + int cLen = 16 - clearByteStart; + for (int i = clearByteStart; i < clearByteStart + cLen; i++) texelWeightData[i] = 0; + + List texelWeightValues = new List(); + BitArrayStream weightBitStream = new BitArrayStream(new BitArray(texelWeightData)); + + IntegerEncoded.DecodeIntegerSequence(texelWeightValues, weightBitStream, texelParams.MaxWeight, texelParams.GetNumWeightValues()); + + // Blocks can be at most 12x12, so we can have as many as 144 weights + int[][] weights = new int[2][]; + weights[0] = new int[144]; + weights[1] = new int[144]; + + UnquantizeTexelWeights(weights, texelWeightValues, texelParams, blockWidth, blockHeight); + + // Now that we have endpoints and weights, we can interpolate and generate + // the proper decoding... + for (int j = 0; j < blockHeight; j++) + { + for (int i = 0; i < blockWidth; i++) + { + int partition = Select2dPartition(partitionIndex, i, j, numberPartitions, ((blockHeight * blockWidth) < 32)); + Debug.Assert(partition < numberPartitions); + + AstcPixel pixel = new AstcPixel(0, 0, 0, 0); + for (int component = 0; component < 4; component++) + { + int component0 = endPoints[partition][0].GetComponent(component); + component0 = BitArrayStream.Replicate(component0, 8, 16); + int component1 = endPoints[partition][1].GetComponent(component); + component1 = BitArrayStream.Replicate(component1, 8, 16); + + int plane = 0; + + if (texelParams.DualPlane && (((planeIndices + 1) & 3) == component)) + { + plane = 1; + } + + int weight = weights[plane][j * blockWidth + i]; + int finalComponent = (component0 * (64 - weight) + component1 * weight + 32) / 64; + + if (finalComponent == 65535) + { + pixel.SetComponent(component, 255); + } + else + { + double finalComponentFloat = finalComponent; + pixel.SetComponent(component, (int)(255.0 * (finalComponentFloat / 65536.0) + 0.5)); + } + } + + outputBuffer[j * blockWidth + i] = pixel.Pack(); + } + } + + return true; + } + + private static int Select2dPartition(int seed, int x, int y, int partitionCount, bool isSmallBlock) + { + return SelectPartition(seed, x, y, 0, partitionCount, isSmallBlock); + } + + private static int SelectPartition(int seed, int x, int y, int z, int partitionCount, bool isSmallBlock) + { + if (partitionCount == 1) + { + return 0; + } + + if (isSmallBlock) + { + x <<= 1; + y <<= 1; + z <<= 1; + } + + seed += (partitionCount - 1) * 1024; + + int rightNum = Hash52((uint)seed); + byte seed01 = (byte)(rightNum & 0xF); + byte seed02 = (byte)((rightNum >> 4) & 0xF); + byte seed03 = (byte)((rightNum >> 8) & 0xF); + byte seed04 = (byte)((rightNum >> 12) & 0xF); + byte seed05 = (byte)((rightNum >> 16) & 0xF); + byte seed06 = (byte)((rightNum >> 20) & 0xF); + byte seed07 = (byte)((rightNum >> 24) & 0xF); + byte seed08 = (byte)((rightNum >> 28) & 0xF); + byte seed09 = (byte)((rightNum >> 18) & 0xF); + byte seed10 = (byte)((rightNum >> 22) & 0xF); + byte seed11 = (byte)((rightNum >> 26) & 0xF); + byte seed12 = (byte)(((rightNum >> 30) | (rightNum << 2)) & 0xF); + + seed01 *= seed01; seed02 *= seed02; + seed03 *= seed03; seed04 *= seed04; + seed05 *= seed05; seed06 *= seed06; + seed07 *= seed07; seed08 *= seed08; + seed09 *= seed09; seed10 *= seed10; + seed11 *= seed11; seed12 *= seed12; + + int seedHash1, seedHash2, seedHash3; + + if ((seed & 1) != 0) + { + seedHash1 = (seed & 2) != 0 ? 4 : 5; + seedHash2 = (partitionCount == 3) ? 6 : 5; + } + else + { + seedHash1 = (partitionCount == 3) ? 6 : 5; + seedHash2 = (seed & 2) != 0 ? 4 : 5; + } + + seedHash3 = (seed & 0x10) != 0 ? seedHash1 : seedHash2; + + seed01 >>= seedHash1; seed02 >>= seedHash2; seed03 >>= seedHash1; seed04 >>= seedHash2; + seed05 >>= seedHash1; seed06 >>= seedHash2; seed07 >>= seedHash1; seed08 >>= seedHash2; + seed09 >>= seedHash3; seed10 >>= seedHash3; seed11 >>= seedHash3; seed12 >>= seedHash3; + + int a = seed01 * x + seed02 * y + seed11 * z + (rightNum >> 14); + int b = seed03 * x + seed04 * y + seed12 * z + (rightNum >> 10); + int c = seed05 * x + seed06 * y + seed09 * z + (rightNum >> 6); + int d = seed07 * x + seed08 * y + seed10 * z + (rightNum >> 2); + + a &= 0x3F; b &= 0x3F; c &= 0x3F; d &= 0x3F; + + if (partitionCount < 4) d = 0; + if (partitionCount < 3) c = 0; + + if (a >= b && a >= c && a >= d) return 0; + else if (b >= c && b >= d) return 1; + else if (c >= d) return 2; + return 3; + } + + static int Hash52(uint val) + { + val ^= val >> 15; val -= val << 17; val += val << 7; val += val << 4; + val ^= val >> 5; val += val << 16; val ^= val >> 7; val ^= val >> 3; + val ^= val << 6; val ^= val >> 17; + + return (int)val; + } + + static void UnquantizeTexelWeights( + int[][] outputBuffer, + List weights, + TexelWeightParams texelParams, + int blockWidth, + int blockHeight) + { + int weightIndices = 0; + int[][] unquantized = new int[2][]; + unquantized[0] = new int[144]; + unquantized[1] = new int[144]; + + for (int i = 0; i < weights.Count; i++) + { + unquantized[0][weightIndices] = UnquantizeTexelWeight(weights[i]); + + if (texelParams.DualPlane) + { + i++; + unquantized[1][weightIndices] = UnquantizeTexelWeight(weights[i]); + + if (i == weights.Count) + { + break; + } + } + + if (++weightIndices >= (texelParams.Width * texelParams.Height)) break; + } + + // Do infill if necessary (Section C.2.18) ... + int ds = (1024 + (blockWidth / 2)) / (blockWidth - 1); + int dt = (1024 + (blockHeight / 2)) / (blockHeight - 1); + + int planeScale = texelParams.DualPlane ? 2 : 1; + + for (int plane = 0; plane < planeScale; plane++) + { + for (int t = 0; t < blockHeight; t++) + { + for (int s = 0; s < blockWidth; s++) + { + int cs = ds * s; + int ct = dt * t; + + int gs = (cs * (texelParams.Width - 1) + 32) >> 6; + int gt = (ct * (texelParams.Height - 1) + 32) >> 6; + + int js = gs >> 4; + int fs = gs & 0xF; + + int jt = gt >> 4; + int ft = gt & 0x0F; + + int w11 = (fs * ft + 8) >> 4; + int w10 = ft - w11; + int w01 = fs - w11; + int w00 = 16 - fs - ft + w11; + + int v0 = js + jt * texelParams.Width; + + int p00 = 0; + int p01 = 0; + int p10 = 0; + int p11 = 0; + + if (v0 < (texelParams.Width * texelParams.Height)) + { + p00 = unquantized[plane][v0]; + } + + if (v0 + 1 < (texelParams.Width * texelParams.Height)) + { + p01 = unquantized[plane][v0 + 1]; + } + + if (v0 + texelParams.Width < (texelParams.Width * texelParams.Height)) + { + p10 = unquantized[plane][v0 + texelParams.Width]; + } + + if (v0 + texelParams.Width + 1 < (texelParams.Width * texelParams.Height)) + { + p11 = unquantized[plane][v0 + texelParams.Width + 1]; + } + + outputBuffer[plane][t * blockWidth + s] = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4; + } + } + } + } + + static int UnquantizeTexelWeight(IntegerEncoded intEncoded) + { + int bitValue = intEncoded.BitValue; + int bitLength = intEncoded.NumberBits; + + int a = BitArrayStream.Replicate(bitValue & 1, 1, 7); + int b = 0, c = 0, d = 0; + + int result = 0; + + switch (intEncoded.GetEncoding()) + { + case IntegerEncoded.EIntegerEncoding.JustBits: + result = BitArrayStream.Replicate(bitValue, bitLength, 6); + break; + + case IntegerEncoded.EIntegerEncoding.Trit: + { + d = intEncoded.TritValue; + Debug.Assert(d < 3); + + switch (bitLength) + { + case 0: + { + int[] results = { 0, 32, 63 }; + result = results[d]; + + break; + } + + case 1: + { + c = 50; + break; + } + + case 2: + { + c = 23; + int b2 = (bitValue >> 1) & 1; + b = (b2 << 6) | (b2 << 2) | b2; + + break; + } + + case 3: + { + c = 11; + int cb = (bitValue >> 1) & 3; + b = (cb << 5) | cb; + + break; + } + + default: + throw new AstcDecoderException("Invalid trit encoding for texel weight"); + } + + break; + } + + case IntegerEncoded.EIntegerEncoding.Quint: + { + d = intEncoded.QuintValue; + Debug.Assert(d < 5); + + switch (bitLength) + { + case 0: + { + int[] results = { 0, 16, 32, 47, 63 }; + result = results[d]; + + break; + } + + case 1: + { + c = 28; + + break; + } + + case 2: + { + c = 13; + int b2 = (bitValue >> 1) & 1; + b = (b2 << 6) | (b2 << 1); + + break; + } + + default: + throw new AstcDecoderException("Invalid quint encoding for texel weight"); + } + + break; + } + } + + if (intEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits && bitLength > 0) + { + // Decode the value... + result = d * c + b; + result ^= a; + result = (a & 0x20) | (result >> 2); + } + + Debug.Assert(result < 64); + + // Change from [0,63] to [0,64] + if (result > 32) + { + result += 1; + } + + return result; + } + + static byte ReverseByte(byte b) + { + // Taken from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits + return (byte)((((b) * 0x80200802L) & 0x0884422110L) * 0x0101010101L >> 32); + } + + static uint[] ReadUintColorValues(int number, int[] colorValues, ref int colorValuesPosition) + { + uint[] ret = new uint[number]; + + for (int i = 0; i < number; i++) + { + ret[i] = (uint)colorValues[colorValuesPosition++]; + } + + return ret; + } + + static int[] ReadIntColorValues(int number, int[] colorValues, ref int colorValuesPosition) + { + int[] ret = new int[number]; + + for (int i = 0; i < number; i++) + { + ret[i] = colorValues[colorValuesPosition++]; + } + + return ret; + } + + static void ComputeEndpoints( + AstcPixel[] endPoints, + int[] colorValues, + uint colorEndpointMode, + ref int colorValuesPosition) + { + switch (colorEndpointMode) + { + case 0: + { + uint[] val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); + + endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[0], (short)val[0]); + endPoints[1] = new AstcPixel(0xFF, (short)val[1], (short)val[1], (short)val[1]); + + break; + } + + + case 1: + { + uint[] val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); + int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0)); + int l1 = (int)Math.Max(l0 + (val[1] & 0x3F), 0xFFU); + + endPoints[0] = new AstcPixel(0xFF, (short)l0, (short)l0, (short)l0); + endPoints[1] = new AstcPixel(0xFF, (short)l1, (short)l1, (short)l1); + + break; + } + + case 4: + { + uint[] val = ReadUintColorValues(4, colorValues, ref colorValuesPosition); + + endPoints[0] = new AstcPixel((short)val[2], (short)val[0], (short)val[0], (short)val[0]); + endPoints[1] = new AstcPixel((short)val[3], (short)val[1], (short)val[1], (short)val[1]); + + break; + } + + case 5: + { + int[] val = ReadIntColorValues(4, colorValues, ref colorValuesPosition); + + BitArrayStream.BitTransferSigned(ref val[1], ref val[0]); + BitArrayStream.BitTransferSigned(ref val[3], ref val[2]); + + endPoints[0] = new AstcPixel((short)val[2], (short)val[0], (short)val[0], (short)val[0]); + endPoints[1] = new AstcPixel((short)(val[2] + val[3]), (short)(val[0] + val[1]), (short)(val[0] + val[1]), (short)(val[0] + val[1])); + + endPoints[0].ClampByte(); + endPoints[1].ClampByte(); + + break; + } + + case 6: + { + uint[] val = ReadUintColorValues(4, colorValues, ref colorValuesPosition); + + endPoints[0] = new AstcPixel(0xFF, (short)(val[0] * val[3] >> 8), (short)(val[1] * val[3] >> 8), (short)(val[2] * val[3] >> 8)); + endPoints[1] = new AstcPixel(0xFF, (short)val[0], (short)val[1], (short)val[2]); + + break; + } + + case 8: + { + uint[] val = ReadUintColorValues(6, colorValues, ref colorValuesPosition); + + if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4]) + { + endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[2], (short)val[4]); + endPoints[1] = new AstcPixel(0xFF, (short)val[1], (short)val[3], (short)val[5]); + } + else + { + endPoints[0] = AstcPixel.BlueContract(0xFF, (short)val[1], (short)val[3], (short)val[5]); + endPoints[1] = AstcPixel.BlueContract(0xFF, (short)val[0], (short)val[2], (short)val[4]); + } + + break; + } + + case 9: + { + int[] val = ReadIntColorValues(6, colorValues, ref colorValuesPosition); + + BitArrayStream.BitTransferSigned(ref val[1], ref val[0]); + BitArrayStream.BitTransferSigned(ref val[3], ref val[2]); + BitArrayStream.BitTransferSigned(ref val[5], ref val[4]); + + if (val[1] + val[3] + val[5] >= 0) + { + endPoints[0] = new AstcPixel(0xFF, (short)val[0], (short)val[2], (short)val[4]); + endPoints[1] = new AstcPixel(0xFF, (short)(val[0] + val[1]), (short)(val[2] + val[3]), (short)(val[4] + val[5])); + } + else + { + endPoints[0] = AstcPixel.BlueContract(0xFF, val[0] + val[1], val[2] + val[3], val[4] + val[5]); + endPoints[1] = AstcPixel.BlueContract(0xFF, val[0], val[2], val[4]); + } + + endPoints[0].ClampByte(); + endPoints[1].ClampByte(); + + break; + } + + case 10: + { + uint[] val = ReadUintColorValues(6, colorValues, ref colorValuesPosition); + + endPoints[0] = new AstcPixel((short)val[4], (short)(val[0] * val[3] >> 8), (short)(val[1] * val[3] >> 8), (short)(val[2] * val[3] >> 8)); + endPoints[1] = new AstcPixel((short)val[5], (short)val[0], (short)val[1], (short)val[2]); + + break; + } + + case 12: + { + uint[] val = ReadUintColorValues(8, colorValues, ref colorValuesPosition); + + if (val[1] + val[3] + val[5] >= val[0] + val[2] + val[4]) + { + endPoints[0] = new AstcPixel((short)val[6], (short)val[0], (short)val[2], (short)val[4]); + endPoints[1] = new AstcPixel((short)val[7], (short)val[1], (short)val[3], (short)val[5]); + } + else + { + endPoints[0] = AstcPixel.BlueContract((short)val[7], (short)val[1], (short)val[3], (short)val[5]); + endPoints[1] = AstcPixel.BlueContract((short)val[6], (short)val[0], (short)val[2], (short)val[4]); + } + + break; + } + + case 13: + { + int[] val = ReadIntColorValues(8, colorValues, ref colorValuesPosition); + + BitArrayStream.BitTransferSigned(ref val[1], ref val[0]); + BitArrayStream.BitTransferSigned(ref val[3], ref val[2]); + BitArrayStream.BitTransferSigned(ref val[5], ref val[4]); + BitArrayStream.BitTransferSigned(ref val[7], ref val[6]); + + if (val[1] + val[3] + val[5] >= 0) + { + endPoints[0] = new AstcPixel((short)val[6], (short)val[0], (short)val[2], (short)val[4]); + endPoints[1] = new AstcPixel((short)(val[7] + val[6]), (short)(val[0] + val[1]), (short)(val[2] + val[3]), (short)(val[4] + val[5])); + } + else + { + endPoints[0] = AstcPixel.BlueContract(val[6] + val[7], val[0] + val[1], val[2] + val[3], val[4] + val[5]); + endPoints[1] = AstcPixel.BlueContract(val[6], val[0], val[2], val[4]); + } + + endPoints[0].ClampByte(); + endPoints[1].ClampByte(); + + break; + } + + default: + throw new AstcDecoderException("Unsupported color endpoint mode (is it HDR?)"); + } + } + + static void DecodeColorValues( + int[] outputValues, + byte[] inputData, + uint[] modes, + int numberPartitions, + int numberBitsForColorData) + { + // First figure out how many color values we have + int numberValues = 0; + + for (int i = 0; i < numberPartitions; i++) + { + numberValues += (int)((modes[i] >> 2) + 1) << 1; + } + + // Then based on the number of values and the remaining number of bits, + // figure out the max value for each of them... + int range = 256; + + while (--range > 0) + { + IntegerEncoded intEncoded = IntegerEncoded.CreateEncoding(range); + int bitLength = intEncoded.GetBitLength(numberValues); + + if (bitLength <= numberBitsForColorData) + { + // Find the smallest possible range that matches the given encoding + while (--range > 0) + { + IntegerEncoded newIntEncoded = IntegerEncoded.CreateEncoding(range); + if (!newIntEncoded.MatchesEncoding(intEncoded)) + { + break; + } + } + + // Return to last matching range. + range++; + break; + } + } + + // We now have enough to decode our integer sequence. + List integerEncodedSequence = new List(); + BitArrayStream colorBitStream = new BitArrayStream(new BitArray(inputData)); + + IntegerEncoded.DecodeIntegerSequence(integerEncodedSequence, colorBitStream, range, numberValues); + + // Once we have the decoded values, we need to dequantize them to the 0-255 range + // This procedure is outlined in ASTC spec C.2.13 + int outputIndices = 0; + + foreach (IntegerEncoded intEncoded in integerEncodedSequence) + { + int bitLength = intEncoded.NumberBits; + int bitValue = intEncoded.BitValue; + + Debug.Assert(bitLength >= 1); + + int a = 0, b = 0, c = 0, d = 0; + // A is just the lsb replicated 9 times. + a = BitArrayStream.Replicate(bitValue & 1, 1, 9); + + switch (intEncoded.GetEncoding()) + { + case IntegerEncoded.EIntegerEncoding.JustBits: + { + outputValues[outputIndices++] = BitArrayStream.Replicate(bitValue, bitLength, 8); + + break; + } + + case IntegerEncoded.EIntegerEncoding.Trit: + { + d = intEncoded.TritValue; + + switch (bitLength) + { + case 1: + { + c = 204; + + break; + } + + case 2: + { + c = 93; + // B = b000b0bb0 + int b2 = (bitValue >> 1) & 1; + b = (b2 << 8) | (b2 << 4) | (b2 << 2) | (b2 << 1); + + break; + } + + case 3: + { + c = 44; + // B = cb000cbcb + int cb = (bitValue >> 1) & 3; + b = (cb << 7) | (cb << 2) | cb; + + break; + } + + + case 4: + { + c = 22; + // B = dcb000dcb + int dcb = (bitValue >> 1) & 7; + b = (dcb << 6) | dcb; + + break; + } + + case 5: + { + c = 11; + // B = edcb000ed + int edcb = (bitValue >> 1) & 0xF; + b = (edcb << 5) | (edcb >> 2); + + break; + } + + case 6: + { + c = 5; + // B = fedcb000f + int fedcb = (bitValue >> 1) & 0x1F; + b = (fedcb << 4) | (fedcb >> 4); + + break; + } + + default: + throw new AstcDecoderException("Unsupported trit encoding for color values!"); + } + + break; + } + + case IntegerEncoded.EIntegerEncoding.Quint: + { + d = intEncoded.QuintValue; + + switch (bitLength) + { + case 1: + { + c = 113; + + break; + } + + case 2: + { + c = 54; + // B = b0000bb00 + int b2 = (bitValue >> 1) & 1; + b = (b2 << 8) | (b2 << 3) | (b2 << 2); + + break; + } + + case 3: + { + c = 26; + // B = cb0000cbc + int cb = (bitValue >> 1) & 3; + b = (cb << 7) | (cb << 1) | (cb >> 1); + + break; + } + + case 4: + { + c = 13; + // B = dcb0000dc + int dcb = (bitValue >> 1) & 7; + b = (dcb << 6) | (dcb >> 1); + + break; + } + + case 5: + { + c = 6; + // B = edcb0000e + int edcb = (bitValue >> 1) & 0xF; + b = (edcb << 5) | (edcb >> 3); + + break; + } + + default: + throw new AstcDecoderException("Unsupported quint encoding for color values!"); + } + break; + } + } + + if (intEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits) + { + int T = d * c + b; + T ^= a; + T = (a & 0x80) | (T >> 2); + + outputValues[outputIndices++] = T; + } + } + + // Make sure that each of our values is in the proper range... + for (int i = 0; i < numberValues; i++) + { + Debug.Assert(outputValues[i] <= 255); + } + } + + static void FillVoidExtentLdr(BitArrayStream bitStream, int[] outputBuffer, int blockWidth, int blockHeight) + { + // Don't actually care about the void extent, just read the bits... + for (int i = 0; i < 4; ++i) + { + bitStream.ReadBits(13); + } + + // Decode the RGBA components and renormalize them to the range [0, 255] + ushort r = (ushort)bitStream.ReadBits(16); + ushort g = (ushort)bitStream.ReadBits(16); + ushort b = (ushort)bitStream.ReadBits(16); + ushort a = (ushort)bitStream.ReadBits(16); + + int rgba = (r >> 8) | (g & 0xFF00) | ((b) & 0xFF00) << 8 | ((a) & 0xFF00) << 16; + + for (int j = 0; j < blockHeight; j++) + { + for (int i = 0; i < blockWidth; i++) + { + outputBuffer[j * blockWidth + i] = rgba; + } + } + } + + static TexelWeightParams DecodeBlockInfo(BitArrayStream bitStream) + { + TexelWeightParams texelParams = new TexelWeightParams(); + + // Read the entire block mode all at once + ushort modeBits = (ushort)bitStream.ReadBits(11); + + // Does this match the void extent block mode? + if ((modeBits & 0x01FF) == 0x1FC) + { + if ((modeBits & 0x200) != 0) + { + texelParams.VoidExtentHdr = true; + } + else + { + texelParams.VoidExtentLdr = true; + } + + // Next two bits must be one. + if ((modeBits & 0x400) == 0 || bitStream.ReadBits(1) == 0) + { + texelParams.Error = true; + } + + return texelParams; + } + + // First check if the last four bits are zero + if ((modeBits & 0xF) == 0) + { + texelParams.Error = true; + return texelParams; + } + + // If the last two bits are zero, then if bits + // [6-8] are all ones, this is also reserved. + if ((modeBits & 0x3) == 0 && (modeBits & 0x1C0) == 0x1C0) + { + texelParams.Error = true; + + return texelParams; + } + + // Otherwise, there is no error... Figure out the layout + // of the block mode. Layout is determined by a number + // between 0 and 9 corresponding to table C.2.8 of the + // ASTC spec. + int layout = 0; + + if ((modeBits & 0x1) != 0 || (modeBits & 0x2) != 0) + { + // layout is in [0-4] + if ((modeBits & 0x8) != 0) + { + // layout is in [2-4] + if ((modeBits & 0x4) != 0) + { + // layout is in [3-4] + if ((modeBits & 0x100) != 0) + { + layout = 4; + } + else + { + layout = 3; + } + } + else + { + layout = 2; + } + } + else + { + // layout is in [0-1] + if ((modeBits & 0x4) != 0) + { + layout = 1; + } + else + { + layout = 0; + } + } + } + else + { + // layout is in [5-9] + if ((modeBits & 0x100) != 0) + { + // layout is in [7-9] + if ((modeBits & 0x80) != 0) + { + // layout is in [7-8] + Debug.Assert((modeBits & 0x40) == 0); + + if ((modeBits & 0x20) != 0) + { + layout = 8; + } + else + { + layout = 7; + } + } + else + { + layout = 9; + } + } + else + { + // layout is in [5-6] + if ((modeBits & 0x80) != 0) + { + layout = 6; + } + else + { + layout = 5; + } + } + } + + Debug.Assert(layout < 10); + + // Determine R + int r = (modeBits >> 4) & 1; + if (layout < 5) + { + r |= (modeBits & 0x3) << 1; + } + else + { + r |= (modeBits & 0xC) >> 1; + } + + Debug.Assert(2 <= r && r <= 7); + + // Determine width & height + switch (layout) + { + case 0: + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 7) & 0x3; + + texelParams.Width = b + 4; + texelParams.Height = a + 2; + + break; + } + + case 1: + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 7) & 0x3; + + texelParams.Width = b + 8; + texelParams.Height = a + 2; + + break; + } + + case 2: + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 7) & 0x3; + + texelParams.Width = a + 2; + texelParams.Height = b + 8; + + break; + } + + case 3: + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 7) & 0x1; + + texelParams.Width = a + 2; + texelParams.Height = b + 6; + + break; + } + + case 4: + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 7) & 0x1; + + texelParams.Width = b + 2; + texelParams.Height = a + 2; + + break; + } + + case 5: + { + int a = (modeBits >> 5) & 0x3; + + texelParams.Width = 12; + texelParams.Height = a + 2; + + break; + } + + case 6: + { + int a = (modeBits >> 5) & 0x3; + + texelParams.Width = a + 2; + texelParams.Height = 12; + + break; + } + + case 7: + { + texelParams.Width = 6; + texelParams.Height = 10; + + break; + } + + case 8: + { + texelParams.Width = 10; + texelParams.Height = 6; + break; + } + + case 9: + { + int a = (modeBits >> 5) & 0x3; + int b = (modeBits >> 9) & 0x3; + + texelParams.Width = a + 6; + texelParams.Height = b + 6; + + break; + } + + default: + //Don't know this layout... + texelParams.Error = true; + break; + } + + // Determine whether or not we're using dual planes + // and/or high precision layouts. + bool d = ((layout != 9) && ((modeBits & 0x400) != 0)); + bool h = (layout != 9) && ((modeBits & 0x200) != 0); + + if (h) + { + int[] maxWeights = { 9, 11, 15, 19, 23, 31 }; + texelParams.MaxWeight = maxWeights[r - 2]; + } + else + { + int[] maxWeights = { 1, 2, 3, 4, 5, 7 }; + texelParams.MaxWeight = maxWeights[r - 2]; + } + + texelParams.DualPlane = d; + + return texelParams; + } + } +} diff --git a/Ryujinx.Graphics/Graphics3d/Texture/AstcPixel.cs b/Ryujinx.Graphics/Graphics3d/Texture/AstcPixel.cs new file mode 100644 index 0000000000..cd30accab3 --- /dev/null +++ b/Ryujinx.Graphics/Graphics3d/Texture/AstcPixel.cs @@ -0,0 +1,138 @@ +using System; +using System.Diagnostics; + +namespace Ryujinx.Graphics.Texture +{ + class AstcPixel + { + public short R { get; set; } + public short G { get; set; } + public short B { get; set; } + public short A { get; set; } + + byte[] _bitDepth = new byte[4]; + + public AstcPixel(short a, short r, short g, short b) + { + A = a; + R = r; + G = g; + B = b; + + for (int i = 0; i < 4; i++) + _bitDepth[i] = 8; + } + + public void ClampByte() + { + R = Math.Min(Math.Max(R, (short)0), (short)255); + G = Math.Min(Math.Max(G, (short)0), (short)255); + B = Math.Min(Math.Max(B, (short)0), (short)255); + A = Math.Min(Math.Max(A, (short)0), (short)255); + } + + public short GetComponent(int index) + { + switch(index) + { + case 0: return A; + case 1: return R; + case 2: return G; + case 3: return B; + } + + return 0; + } + + public void SetComponent(int index, int value) + { + switch (index) + { + case 0: + A = (short)value; + break; + case 1: + R = (short)value; + break; + case 2: + G = (short)value; + break; + case 3: + B = (short)value; + break; + } + } + + public void ChangeBitDepth(byte[] depth) + { + for(int i = 0; i< 4; i++) + { + int value = ChangeBitDepth(GetComponent(i), _bitDepth[i], depth[i]); + + SetComponent(i, value); + _bitDepth[i] = depth[i]; + } + } + + short ChangeBitDepth(short value, byte oldDepth, byte newDepth) + { + Debug.Assert(newDepth <= 8); + Debug.Assert(oldDepth <= 8); + + if (oldDepth == newDepth) + { + // Do nothing + return value; + } + else if (oldDepth == 0 && newDepth != 0) + { + return (short)((1 << newDepth) - 1); + } + else if (newDepth > oldDepth) + { + return (short)BitArrayStream.Replicate(value, oldDepth, newDepth); + } + else + { + // oldDepth > newDepth + if (newDepth == 0) + { + return 0xFF; + } + else + { + byte bitsWasted = (byte)(oldDepth - newDepth); + short tempValue = value; + + tempValue = (short)((tempValue + (1 << (bitsWasted - 1))) >> bitsWasted); + tempValue = Math.Min(Math.Max((short)0, tempValue), (short)((1 << newDepth) - 1)); + + return (byte)(tempValue); + } + } + } + + public int Pack() + { + AstcPixel newPixel = new AstcPixel(A, R, G, B); + byte[] eightBitDepth = { 8, 8, 8, 8 }; + + newPixel.ChangeBitDepth(eightBitDepth); + + return (byte)newPixel.A << 24 | + (byte)newPixel.B << 16 | + (byte)newPixel.G << 8 | + (byte)newPixel.R << 0; + } + + // Adds more precision to the blue channel as described + // in C.2.14 + public static AstcPixel BlueContract(int a, int r, int g, int b) + { + return new AstcPixel((short)(a), + (short)((r + b) >> 1), + (short)((g + b) >> 1), + (short)(b)); + } + } +} diff --git a/Ryujinx.Graphics/Graphics3d/Texture/BitArrayStream.cs b/Ryujinx.Graphics/Graphics3d/Texture/BitArrayStream.cs index 2a8ed09105..24069d722b 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/BitArrayStream.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/BitArrayStream.cs @@ -9,103 +9,103 @@ namespace Ryujinx.Graphics.Texture public int Position { get; private set; } - public BitArrayStream(BitArray BitArray) + public BitArrayStream(BitArray bitArray) { - BitsArray = BitArray; + BitsArray = bitArray; Position = 0; } - public short ReadBits(int Length) + public short ReadBits(int length) { - int RetValue = 0; - for (int i = Position; i < Position + Length; i++) + int retValue = 0; + for (int i = Position; i < Position + length; i++) { if (BitsArray[i]) { - RetValue |= 1 << (i - Position); + retValue |= 1 << (i - Position); } } - Position += Length; - return (short)RetValue; + Position += length; + return (short)retValue; } - public int ReadBits(int Start, int End) + public int ReadBits(int start, int end) { - int RetValue = 0; - for (int i = Start; i <= End; i++) + int retValue = 0; + for (int i = start; i <= end; i++) { if (BitsArray[i]) { - RetValue |= 1 << (i - Start); + retValue |= 1 << (i - start); } } - return RetValue; + return retValue; } - public int ReadBit(int Index) + public int ReadBit(int index) { - return Convert.ToInt32(BitsArray[Index]); + return Convert.ToInt32(BitsArray[index]); } - public void WriteBits(int Value, int Length) + public void WriteBits(int value, int length) { - for (int i = Position; i < Position + Length; i++) + for (int i = Position; i < Position + length; i++) { - BitsArray[i] = ((Value >> (i - Position)) & 1) != 0; + BitsArray[i] = ((value >> (i - Position)) & 1) != 0; } - Position += Length; + Position += length; } public byte[] ToByteArray() { - byte[] RetArray = new byte[(BitsArray.Length + 7) / 8]; - BitsArray.CopyTo(RetArray, 0); - return RetArray; + byte[] retArray = new byte[(BitsArray.Length + 7) / 8]; + BitsArray.CopyTo(retArray, 0); + return retArray; } - public static int Replicate(int Value, int NumberBits, int ToBit) + public static int Replicate(int value, int numberBits, int toBit) { - if (NumberBits == 0) return 0; - if (ToBit == 0) return 0; + if (numberBits == 0) return 0; + if (toBit == 0) return 0; - int TempValue = Value & ((1 << NumberBits) - 1); - int RetValue = TempValue; - int ResLength = NumberBits; + int tempValue = value & ((1 << numberBits) - 1); + int retValue = tempValue; + int resLength = numberBits; - while (ResLength < ToBit) + while (resLength < toBit) { - int Comp = 0; - if (NumberBits > ToBit - ResLength) + int comp = 0; + if (numberBits > toBit - resLength) { - int NewShift = ToBit - ResLength; - Comp = NumberBits - NewShift; - NumberBits = NewShift; + int newShift = toBit - resLength; + comp = numberBits - newShift; + numberBits = newShift; } - RetValue <<= NumberBits; - RetValue |= TempValue >> Comp; - ResLength += NumberBits; + retValue <<= numberBits; + retValue |= tempValue >> comp; + resLength += numberBits; } - return RetValue; + return retValue; } - public static int PopCnt(int Number) + public static int PopCnt(int number) { - int Counter; - for (Counter = 0; Number != 0; Counter++) + int counter; + for (counter = 0; number != 0; counter++) { - Number &= Number - 1; + number &= number - 1; } - return Counter; + return counter; } public static void Swap(ref T lhs, ref T rhs) { - T Temp = lhs; + T temp = lhs; lhs = rhs; - rhs = Temp; + rhs = temp; } // Transfers a bit as described in C.2.14 diff --git a/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs index 9451291e9a..682f7d671d 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs @@ -1,59 +1,186 @@ +using Ryujinx.Common; using System; namespace Ryujinx.Graphics.Texture { class BlockLinearSwizzle : ISwizzle { - private int BhShift; - private int BppShift; - private int BhMask; + private const int GobWidth = 64; + private const int GobHeight = 8; - private int XShift; - private int GobStride; + private const int GobSize = GobWidth * GobHeight; - public BlockLinearSwizzle(int Width, int Bpp, int BlockHeight = 16) + private int _texWidth; + private int _texHeight; + private int _texDepth; + private int _texGobBlockHeight; + private int _texGobBlockDepth; + private int _texBpp; + + private int _bhMask; + private int _bdMask; + + private int _bhShift; + private int _bdShift; + private int _bppShift; + + private int _xShift; + + private int _robSize; + private int _sliceSize; + + private int _baseOffset; + + public BlockLinearSwizzle( + int width, + int height, + int depth, + int gobBlockHeight, + int gobBlockDepth, + int bpp) { - BhMask = (BlockHeight * 8) - 1; + _texWidth = width; + _texHeight = height; + _texDepth = depth; + _texGobBlockHeight = gobBlockHeight; + _texGobBlockDepth = gobBlockDepth; + _texBpp = bpp; - BhShift = CountLsbZeros(BlockHeight * 8); - BppShift = CountLsbZeros(Bpp); + _bppShift = BitUtils.CountTrailingZeros32(bpp); - int WidthInGobs = (int)MathF.Ceiling(Width * Bpp / 64f); - - GobStride = 512 * BlockHeight * WidthInGobs; - - XShift = CountLsbZeros(512 * BlockHeight); + SetMipLevel(0); } - private int CountLsbZeros(int Value) + public void SetMipLevel(int level) { - int Count = 0; + _baseOffset = GetMipOffset(level); - while (((Value >> Count) & 1) == 0) + int width = Math.Max(1, _texWidth >> level); + int height = Math.Max(1, _texHeight >> level); + int depth = Math.Max(1, _texDepth >> level); + + GobBlockSizes gbSizes = AdjustGobBlockSizes(height, depth); + + _bhMask = gbSizes.Height - 1; + _bdMask = gbSizes.Depth - 1; + + _bhShift = BitUtils.CountTrailingZeros32(gbSizes.Height); + _bdShift = BitUtils.CountTrailingZeros32(gbSizes.Depth); + + _xShift = BitUtils.CountTrailingZeros32(GobSize * gbSizes.Height * gbSizes.Depth); + + RobAndSliceSizes gsSizes = GetRobAndSliceSizes(width, height, gbSizes); + + _robSize = gsSizes.RobSize; + _sliceSize = gsSizes.SliceSize; + } + + public int GetImageSize(int mipsCount) + { + int size = GetMipOffset(mipsCount); + + size = (size + 0x1fff) & ~0x1fff; + + return size; + } + + public int GetMipOffset(int level) + { + int totalSize = 0; + + for (int index = 0; index < level; index++) { - Count++; + int width = Math.Max(1, _texWidth >> index); + int height = Math.Max(1, _texHeight >> index); + int depth = Math.Max(1, _texDepth >> index); + + GobBlockSizes gbSizes = AdjustGobBlockSizes(height, depth); + + RobAndSliceSizes rsSizes = GetRobAndSliceSizes(width, height, gbSizes); + + totalSize += BitUtils.DivRoundUp(depth, gbSizes.Depth) * rsSizes.SliceSize; } - return Count; + return totalSize; } - public int GetSwizzleOffset(int X, int Y) + private struct GobBlockSizes { - X <<= BppShift; + public int Height; + public int Depth; - int Position = (Y >> BhShift) * GobStride; + public GobBlockSizes(int gobBlockHeight, int gobBlockDepth) + { + Height = gobBlockHeight; + Depth = gobBlockDepth; + } + } - Position += (X >> 6) << XShift; + private GobBlockSizes AdjustGobBlockSizes(int height, int depth) + { + int gobBlockHeight = _texGobBlockHeight; + int gobBlockDepth = _texGobBlockDepth; - Position += ((Y & BhMask) >> 3) << 9; + int pow2Height = BitUtils.Pow2RoundUp(height); + int pow2Depth = BitUtils.Pow2RoundUp(depth); - Position += ((X & 0x3f) >> 5) << 8; - Position += ((Y & 0x07) >> 1) << 6; - Position += ((X & 0x1f) >> 4) << 5; - Position += ((Y & 0x01) >> 0) << 4; - Position += ((X & 0x0f) >> 0) << 0; + while (gobBlockHeight * GobHeight > pow2Height && gobBlockHeight > 1) + { + gobBlockHeight >>= 1; + } - return Position; + while (gobBlockDepth > pow2Depth && gobBlockDepth > 1) + { + gobBlockDepth >>= 1; + } + + return new GobBlockSizes(gobBlockHeight, gobBlockDepth); + } + + private struct RobAndSliceSizes + { + public int RobSize; + public int SliceSize; + + public RobAndSliceSizes(int robSize, int sliceSize) + { + RobSize = robSize; + SliceSize = sliceSize; + } + } + + private RobAndSliceSizes GetRobAndSliceSizes(int width, int height, GobBlockSizes gbSizes) + { + int widthInGobs = BitUtils.DivRoundUp(width * _texBpp, GobWidth); + + int robSize = GobSize * gbSizes.Height * gbSizes.Depth * widthInGobs; + + int sliceSize = BitUtils.DivRoundUp(height, gbSizes.Height * GobHeight) * robSize; + + return new RobAndSliceSizes(robSize, sliceSize); + } + + public int GetSwizzleOffset(int x, int y, int z) + { + x <<= _bppShift; + + int yh = y / GobHeight; + + int position = (z >> _bdShift) * _sliceSize + (yh >> _bhShift) * _robSize; + + position += (x / GobWidth) << _xShift; + + position += (yh & _bhMask) * GobSize; + + position += ((z & _bdMask) * GobSize) << _bhShift; + + position += ((x & 0x3f) >> 5) << 8; + position += ((y & 0x07) >> 1) << 6; + position += ((x & 0x1f) >> 4) << 5; + position += ((y & 0x01) >> 0) << 4; + position += ((x & 0x0f) >> 0) << 0; + + return _baseOffset + position; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs index 583fc20c53..fae3eada87 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs @@ -2,6 +2,12 @@ namespace Ryujinx.Graphics.Texture { interface ISwizzle { - int GetSwizzleOffset(int X, int Y); + int GetSwizzleOffset(int x, int y, int z); + + void SetMipLevel(int level); + + int GetMipOffset(int level); + + int GetImageSize(int mipsCount); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs b/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs index f958e1de81..bd9b4327c3 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs @@ -1,4 +1,5 @@ using ChocolArm64.Memory; +using OpenTK.Graphics.OpenGL; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; using System; @@ -23,15 +24,17 @@ namespace Ryujinx.Graphics.Texture public int BytesPerPixel { get; private set; } public int BlockWidth { get; private set; } public int BlockHeight { get; private set; } + public int BlockDepth { get; private set; } public TargetBuffer Target { get; private set; } - public ImageDescriptor(int BytesPerPixel, int BlockWidth, int BlockHeight, TargetBuffer Target) + public ImageDescriptor(int bytesPerPixel, int blockWidth, int blockHeight, int blockDepth, TargetBuffer target) { - this.BytesPerPixel = BytesPerPixel; - this.BlockWidth = BlockWidth; - this.BlockHeight = BlockHeight; - this.Target = Target; + BytesPerPixel = bytesPerPixel; + BlockWidth = blockWidth; + BlockHeight = blockHeight; + BlockDepth = blockDepth; + Target = target; } } @@ -42,26 +45,26 @@ namespace Ryujinx.Graphics.Texture private const GalImageFormat Float = GalImageFormat.Float; private const GalImageFormat Srgb = GalImageFormat.Srgb; - private static readonly Dictionary s_TextureTable = + private static readonly Dictionary TextureTable = new Dictionary() { - { GalTextureFormat.RGBA32, GalImageFormat.RGBA32 | Sint | Uint | Float }, - { GalTextureFormat.RGBA16, GalImageFormat.RGBA16 | Snorm | Unorm | Sint | Uint | Float }, - { GalTextureFormat.RG32, GalImageFormat.RG32 | Sint | Uint | Float }, - { GalTextureFormat.RGBA8, GalImageFormat.RGBA8 | Snorm | Unorm | Sint | Uint | Srgb }, - { GalTextureFormat.RGB10A2, GalImageFormat.RGB10A2 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.RG8, GalImageFormat.RG8 | Snorm | Unorm | Sint | Uint }, + { GalTextureFormat.Rgba32, GalImageFormat.Rgba32 | Sint | Uint | Float }, + { GalTextureFormat.Rgba16, GalImageFormat.Rgba16 | Snorm | Unorm | Sint | Uint | Float }, + { GalTextureFormat.Rg32, GalImageFormat.Rg32 | Sint | Uint | Float }, + { GalTextureFormat.Rgba8, GalImageFormat.Rgba8 | Snorm | Unorm | Sint | Uint | Srgb }, + { GalTextureFormat.Rgb10A2, GalImageFormat.Rgb10A2 | Snorm | Unorm | Sint | Uint }, + { GalTextureFormat.Rg8, GalImageFormat.Rg8 | Snorm | Unorm | Sint | Uint }, { GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Float }, { GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.RG16, GalImageFormat.RG16 | Snorm | Unorm | Sint | Float }, + { GalTextureFormat.Rg16, GalImageFormat.Rg16 | Snorm | Unorm | Sint | Float }, { GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Float }, - { GalTextureFormat.RGBA4, GalImageFormat.RGBA4 | Unorm }, - { GalTextureFormat.RGB5A1, GalImageFormat.RGB5A1 | Unorm }, - { GalTextureFormat.RGB565, GalImageFormat.RGB565 | Unorm }, + { GalTextureFormat.Rgba4, GalImageFormat.Rgba4 | Unorm }, + { GalTextureFormat.Rgb5A1, GalImageFormat.Rgb5A1 | Unorm }, + { GalTextureFormat.Rgb565, GalImageFormat.Rgb565 | Unorm }, { GalTextureFormat.R11G11B10F, GalImageFormat.R11G11B10 | Float }, { GalTextureFormat.D24S8, GalImageFormat.D24S8 | Unorm | Uint }, { GalTextureFormat.D32F, GalImageFormat.D32 | Float }, - { GalTextureFormat.D32FX24S8, GalImageFormat.D32S8 | Float }, + { GalTextureFormat.D32Fx24S8, GalImageFormat.D32S8 | Float }, { GalTextureFormat.D16, GalImageFormat.D16 | Unorm }, //Compressed formats @@ -89,129 +92,129 @@ namespace Ryujinx.Graphics.Texture { GalTextureFormat.Astc2D10x6, GalImageFormat.Astc2D10x6 | Unorm | Srgb } }; - private static readonly Dictionary s_ImageTable = + private static readonly Dictionary ImageTable = new Dictionary() { - { GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGBX8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R32, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGBA4, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BGR5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGB5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RGB565, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BGR565, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.RG16, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.RG8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R16, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R8, new ImageDescriptor(1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, TargetBuffer.Color) }, - { GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, TargetBuffer.Color) }, - { GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, TargetBuffer.Color) }, - { GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, TargetBuffer.Color) }, - { GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, TargetBuffer.Color) }, - { GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, TargetBuffer.Color) }, - { GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, TargetBuffer.Color) }, - { GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, TargetBuffer.Color) }, - { GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) }, + { GalImageFormat.Rgba32, new ImageDescriptor(16, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.Rgba16, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.Rg32, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.Rgbx8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.Rgba8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.Bgra8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.Rgb10A2, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R32, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.Rgba4, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.Bgr5A1, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.Rgb5A1, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.Rgb565, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.Bgr565, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.Rg16, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.Rg8, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R16, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R8, new ImageDescriptor(1, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, 1, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, 1, TargetBuffer.Color) }, - { GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) }, - { GalImageFormat.D24, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) }, - { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) }, - { GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) }, - { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) } + { GalImageFormat.D16, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Depth) }, + { GalImageFormat.D24, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Depth) }, + { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.DepthStencil) }, + { GalImageFormat.D32, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Depth) }, + { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.DepthStencil) } }; public static GalImageFormat ConvertTexture( - GalTextureFormat Format, - GalTextureType RType, - GalTextureType GType, - GalTextureType BType, - GalTextureType AType, - bool ConvSrgb) + GalTextureFormat format, + GalTextureType rType, + GalTextureType gType, + GalTextureType bType, + GalTextureType aType, + bool convSrgb) { - if (!s_TextureTable.TryGetValue(Format, out GalImageFormat ImageFormat)) + if (!TextureTable.TryGetValue(format, out GalImageFormat imageFormat)) { - throw new NotImplementedException($"Format 0x{((int)Format):x} not implemented!"); + throw new NotImplementedException($"Format 0x{((int)format):x} not implemented!"); } - if (!HasDepth(ImageFormat) && (RType != GType || RType != BType || RType != AType)) + if (!HasDepth(imageFormat) && (rType != gType || rType != bType || rType != aType)) { - throw new NotImplementedException($"Per component types are not implemented!"); + throw new NotImplementedException("Per component types are not implemented!"); } - GalImageFormat FormatType = ConvSrgb ? Srgb : GetFormatType(RType); + GalImageFormat formatType = convSrgb ? Srgb : GetFormatType(rType); - GalImageFormat CombinedFormat = (ImageFormat & GalImageFormat.FormatMask) | FormatType; + GalImageFormat combinedFormat = (imageFormat & GalImageFormat.FormatMask) | formatType; - if (!ImageFormat.HasFlag(FormatType)) + if (!imageFormat.HasFlag(formatType)) { - throw new NotImplementedException($"Format \"{CombinedFormat}\" not implemented!"); + throw new NotImplementedException($"Format \"{combinedFormat}\" not implemented!"); } - return CombinedFormat; + return combinedFormat; } - public static GalImageFormat ConvertSurface(GalSurfaceFormat Format) + public static GalImageFormat ConvertSurface(GalSurfaceFormat format) { - switch (Format) + switch (format) { - case GalSurfaceFormat.RGBA32Float: return GalImageFormat.RGBA32 | Float; - case GalSurfaceFormat.RGBA32Uint: return GalImageFormat.RGBA32 | Uint; - case GalSurfaceFormat.RGBA16Float: return GalImageFormat.RGBA16 | Float; - case GalSurfaceFormat.RGBA16Unorm: return GalImageFormat.RGBA16 | Unorm; - case GalSurfaceFormat.RG32Float: return GalImageFormat.RG32 | Float; - case GalSurfaceFormat.RG32Sint: return GalImageFormat.RG32 | Sint; - case GalSurfaceFormat.RG32Uint: return GalImageFormat.RG32 | Uint; - case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.BGRA8 | Unorm; - case GalSurfaceFormat.BGRA8Srgb: return GalImageFormat.BGRA8 | Srgb; - case GalSurfaceFormat.RGB10A2Unorm: return GalImageFormat.RGB10A2 | Unorm; - case GalSurfaceFormat.RGBA8Unorm: return GalImageFormat.RGBA8 | Unorm; - case GalSurfaceFormat.RGBA8Srgb: return GalImageFormat.RGBA8 | Srgb; - case GalSurfaceFormat.RGBA8Snorm: return GalImageFormat.RGBA8 | Snorm; - case GalSurfaceFormat.RG16Snorm: return GalImageFormat.RG16 | Snorm; - case GalSurfaceFormat.RG16Unorm: return GalImageFormat.RG16 | Unorm; - case GalSurfaceFormat.RG16Sint: return GalImageFormat.RG16 | Sint; - case GalSurfaceFormat.RG16Float: return GalImageFormat.RG16 | Float; + case GalSurfaceFormat.Rgba32Float: return GalImageFormat.Rgba32 | Float; + case GalSurfaceFormat.Rgba32Uint: return GalImageFormat.Rgba32 | Uint; + case GalSurfaceFormat.Rgba16Float: return GalImageFormat.Rgba16 | Float; + case GalSurfaceFormat.Rgba16Unorm: return GalImageFormat.Rgba16 | Unorm; + case GalSurfaceFormat.Rg32Float: return GalImageFormat.Rg32 | Float; + case GalSurfaceFormat.Rg32Sint: return GalImageFormat.Rg32 | Sint; + case GalSurfaceFormat.Rg32Uint: return GalImageFormat.Rg32 | Uint; + case GalSurfaceFormat.Bgra8Unorm: return GalImageFormat.Bgra8 | Unorm; + case GalSurfaceFormat.Bgra8Srgb: return GalImageFormat.Bgra8 | Srgb; + case GalSurfaceFormat.Rgb10A2Unorm: return GalImageFormat.Rgb10A2 | Unorm; + case GalSurfaceFormat.Rgba8Unorm: return GalImageFormat.Rgba8 | Unorm; + case GalSurfaceFormat.Rgba8Srgb: return GalImageFormat.Rgba8 | Srgb; + case GalSurfaceFormat.Rgba8Snorm: return GalImageFormat.Rgba8 | Snorm; + case GalSurfaceFormat.Rg16Snorm: return GalImageFormat.Rg16 | Snorm; + case GalSurfaceFormat.Rg16Unorm: return GalImageFormat.Rg16 | Unorm; + case GalSurfaceFormat.Rg16Sint: return GalImageFormat.Rg16 | Sint; + case GalSurfaceFormat.Rg16Float: return GalImageFormat.Rg16 | Float; case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.R11G11B10 | Float; case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Float; case GalSurfaceFormat.R32Uint: return GalImageFormat.R32 | Uint; - case GalSurfaceFormat.RG8Unorm: return GalImageFormat.RG8 | Unorm; - case GalSurfaceFormat.RG8Snorm: return GalImageFormat.RG8 | Snorm; + case GalSurfaceFormat.Rg8Unorm: return GalImageFormat.Rg8 | Unorm; + case GalSurfaceFormat.Rg8Snorm: return GalImageFormat.Rg8 | Snorm; case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Float; case GalSurfaceFormat.R16Unorm: return GalImageFormat.R16 | Unorm; case GalSurfaceFormat.R16Uint: return GalImageFormat.R16 | Uint; case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm; case GalSurfaceFormat.R8Uint: return GalImageFormat.R8 | Uint; - case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.RGB565 | Unorm; - case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.BGR5A1 | Unorm; - case GalSurfaceFormat.RGBX8Unorm: return GalImageFormat.RGBX8 | Unorm; + case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.Rgb565 | Unorm; + case GalSurfaceFormat.Bgr5A1Unorm: return GalImageFormat.Bgr5A1 | Unorm; + case GalSurfaceFormat.Rgbx8Unorm: return GalImageFormat.Rgbx8 | Unorm; } - throw new NotImplementedException(Format.ToString()); + throw new NotImplementedException(format.ToString()); } - public static GalImageFormat ConvertZeta(GalZetaFormat Format) + public static GalImageFormat ConvertZeta(GalZetaFormat format) { - switch (Format) + switch (format) { case GalZetaFormat.D32Float: return GalImageFormat.D32 | Float; case GalZetaFormat.S8D24Unorm: return GalImageFormat.D24S8 | Unorm; @@ -221,218 +224,268 @@ namespace Ryujinx.Graphics.Texture case GalZetaFormat.D32S8X24Float: return GalImageFormat.D32S8 | Float; } - throw new NotImplementedException(Format.ToString()); + throw new NotImplementedException(format.ToString()); } - public static byte[] ReadTexture(IMemory Memory, GalImage Image, long Position) + public static byte[] ReadTexture(IMemory memory, GalImage image, long position) { - MemoryManager CpuMemory; + MemoryManager cpuMemory; - if (Memory is NvGpuVmm Vmm) + if (memory is NvGpuVmm vmm) { - CpuMemory = Vmm.Memory; + cpuMemory = vmm.Memory; } else { - CpuMemory = (MemoryManager)Memory; + cpuMemory = (MemoryManager)memory; } - ISwizzle Swizzle = TextureHelper.GetSwizzle(Image); + ISwizzle swizzle = TextureHelper.GetSwizzle(image); - ImageDescriptor Desc = GetImageDescriptor(Image.Format); + ImageDescriptor desc = GetImageDescriptor(image.Format); - (int Width, int Height) = GetImageSizeInBlocks(Image); + (int width, int height, int depth) = GetImageSizeInBlocks(image); - int BytesPerPixel = Desc.BytesPerPixel; + int bytesPerPixel = desc.BytesPerPixel; //Note: Each row of the texture needs to be aligned to 4 bytes. - int Pitch = (Width * BytesPerPixel + 3) & ~3; + int pitch = (width * bytesPerPixel + 3) & ~3; - byte[] Data = new byte[Height * Pitch]; - for (int Y = 0; Y < Height; Y++) + int dataLayerSize = height * pitch * depth; + byte[] data = new byte[dataLayerSize * image.LayerCount]; + + int targetMipLevel = image.MaxMipmapLevel <= 1 ? 1 : image.MaxMipmapLevel - 1; + int layerOffset = GetLayerOffset(image, targetMipLevel); + + for (int layer = 0; layer < image.LayerCount; layer++) { - int OutOffs = Y * Pitch; - - for (int X = 0; X < Width; X++) + for (int z = 0; z < depth; z++) { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + for (int y = 0; y < height; y++) + { + int outOffs = (dataLayerSize * layer) + y * pitch + (z * width * height * bytesPerPixel); - CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel); + for (int x = 0; x < width; x++) + { + long offset = (uint)swizzle.GetSwizzleOffset(x, y, z); - OutOffs += BytesPerPixel; + cpuMemory.ReadBytes(position + (layerOffset * layer) + offset, data, outOffs, bytesPerPixel); + + outOffs += bytesPerPixel; + } + } } } - return Data; + return data; } - public static void WriteTexture(NvGpuVmm Vmm, GalImage Image, long Position, byte[] Data) + public static void WriteTexture(NvGpuVmm vmm, GalImage image, long position, byte[] data) { - ISwizzle Swizzle = TextureHelper.GetSwizzle(Image); + ISwizzle swizzle = TextureHelper.GetSwizzle(image); - ImageDescriptor Desc = GetImageDescriptor(Image.Format); + ImageDescriptor desc = GetImageDescriptor(image.Format); - (int Width, int Height) = ImageUtils.GetImageSizeInBlocks(Image); + (int width, int height, int depth) = GetImageSizeInBlocks(image); - int BytesPerPixel = Desc.BytesPerPixel; + int bytesPerPixel = desc.BytesPerPixel; - int InOffs = 0; + int inOffs = 0; - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) + for (int z = 0; z < depth; z++) + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + long offset = (uint)swizzle.GetSwizzleOffset(x, y, z); - Vmm.Memory.WriteBytes(Position + Offset, Data, InOffs, BytesPerPixel); + vmm.Memory.WriteBytes(position + offset, data, inOffs, bytesPerPixel); - InOffs += BytesPerPixel; + inOffs += bytesPerPixel; } } + // TODO: Support non 2D public static bool CopyTexture( - NvGpuVmm Vmm, - GalImage SrcImage, - GalImage DstImage, - long SrcAddress, - long DstAddress, - int SrcX, - int SrcY, - int DstX, - int DstY, - int Width, - int Height) + NvGpuVmm vmm, + GalImage srcImage, + GalImage dstImage, + long srcAddress, + long dstAddress, + int srcX, + int srcY, + int dstX, + int dstY, + int width, + int height) { - ISwizzle SrcSwizzle = TextureHelper.GetSwizzle(SrcImage); - ISwizzle DstSwizzle = TextureHelper.GetSwizzle(DstImage); + ISwizzle srcSwizzle = TextureHelper.GetSwizzle(srcImage); + ISwizzle dstSwizzle = TextureHelper.GetSwizzle(dstImage); - ImageDescriptor Desc = GetImageDescriptor(SrcImage.Format); + ImageDescriptor desc = GetImageDescriptor(srcImage.Format); - if (GetImageDescriptor(DstImage.Format).BytesPerPixel != Desc.BytesPerPixel) + if (GetImageDescriptor(dstImage.Format).BytesPerPixel != desc.BytesPerPixel) { return false; } - int BytesPerPixel = Desc.BytesPerPixel; + int bytesPerPixel = desc.BytesPerPixel; - for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) { - long SrcOffset = (uint)SrcSwizzle.GetSwizzleOffset(SrcX + X, SrcY + Y); - long DstOffset = (uint)DstSwizzle.GetSwizzleOffset(DstX + X, DstY + Y); + long srcOffset = (uint)srcSwizzle.GetSwizzleOffset(srcX + x, srcY + y, 0); + long dstOffset = (uint)dstSwizzle.GetSwizzleOffset(dstX + x, dstY + y, 0); - byte[] Texel = Vmm.ReadBytes(SrcAddress + SrcOffset, BytesPerPixel); + byte[] texel = vmm.ReadBytes(srcAddress + srcOffset, bytesPerPixel); - Vmm.WriteBytes(DstAddress + DstOffset, Texel); + vmm.WriteBytes(dstAddress + dstOffset, texel); } return true; } - public static int GetSize(GalImage Image) + public static int GetSize(GalImage image) { - ImageDescriptor Desc = GetImageDescriptor(Image.Format); + ImageDescriptor desc = GetImageDescriptor(image.Format); - int Width = DivRoundUp(Image.Width, Desc.BlockWidth); - int Height = DivRoundUp(Image.Height, Desc.BlockHeight); + int componentCount = GetCoordsCountTextureTarget(image.TextureTarget); - return Desc.BytesPerPixel * Width * Height; - } + if (IsArray(image.TextureTarget)) + componentCount--; - public static int GetPitch(GalImageFormat Format, int Width) - { - ImageDescriptor Desc = GetImageDescriptor(Format); + int width = DivRoundUp(image.Width, desc.BlockWidth); + int height = DivRoundUp(image.Height, desc.BlockHeight); + int depth = DivRoundUp(image.Depth, desc.BlockDepth); - int Pitch = Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth); - - Pitch = (Pitch + 0x1f) & ~0x1f; - - return Pitch; - } - - public static int GetBlockWidth(GalImageFormat Format) - { - return GetImageDescriptor(Format).BlockWidth; - } - - public static int GetBlockHeight(GalImageFormat Format) - { - return GetImageDescriptor(Format).BlockHeight; - } - - public static int GetAlignedWidth(GalImage Image) - { - ImageDescriptor Desc = GetImageDescriptor(Image.Format); - - int AlignMask; - - if (Image.Layout == GalMemoryLayout.BlockLinear) + switch (componentCount) { - AlignMask = Image.TileWidth * (64 / Desc.BytesPerPixel) - 1; + case 1: + return desc.BytesPerPixel * width * image.LayerCount; + case 2: + return desc.BytesPerPixel * width * height * image.LayerCount; + case 3: + return desc.BytesPerPixel * width * height * depth * image.LayerCount; + default: + throw new InvalidOperationException($"Invalid component count: {componentCount}"); + } + } + + public static int GetGpuSize(GalImage image, bool forcePitch = false) + { + return TextureHelper.GetSwizzle(image).GetImageSize(image.MaxMipmapLevel) * image.LayerCount; + } + + public static int GetLayerOffset(GalImage image, int mipLevel) + { + if (mipLevel <= 0) + { + mipLevel = 1; + } + + return TextureHelper.GetSwizzle(image).GetMipOffset(mipLevel); + } + + public static int GetPitch(GalImageFormat format, int width) + { + ImageDescriptor desc = GetImageDescriptor(format); + + int pitch = desc.BytesPerPixel * DivRoundUp(width, desc.BlockWidth); + + pitch = (pitch + 0x1f) & ~0x1f; + + return pitch; + } + + public static int GetBlockWidth(GalImageFormat format) + { + return GetImageDescriptor(format).BlockWidth; + } + + public static int GetBlockHeight(GalImageFormat format) + { + return GetImageDescriptor(format).BlockHeight; + } + + public static int GetBlockDepth(GalImageFormat format) + { + return GetImageDescriptor(format).BlockDepth; + } + + public static int GetAlignedWidth(GalImage image) + { + ImageDescriptor desc = GetImageDescriptor(image.Format); + + int alignMask; + + if (image.Layout == GalMemoryLayout.BlockLinear) + { + alignMask = image.TileWidth * (64 / desc.BytesPerPixel) - 1; } else { - AlignMask = (32 / Desc.BytesPerPixel) - 1; + alignMask = (32 / desc.BytesPerPixel) - 1; } - return (Image.Width + AlignMask) & ~AlignMask; + return (image.Width + alignMask) & ~alignMask; } - public static (int Width, int Height) GetImageSizeInBlocks(GalImage Image) + public static (int Width, int Height, int Depth) GetImageSizeInBlocks(GalImage image) { - ImageDescriptor Desc = GetImageDescriptor(Image.Format); + ImageDescriptor desc = GetImageDescriptor(image.Format); - return (DivRoundUp(Image.Width, Desc.BlockWidth), - DivRoundUp(Image.Height, Desc.BlockHeight)); + return (DivRoundUp(image.Width, desc.BlockWidth), + DivRoundUp(image.Height, desc.BlockHeight), + DivRoundUp(image.Depth, desc.BlockDepth)); } - public static int GetBytesPerPixel(GalImageFormat Format) + public static int GetBytesPerPixel(GalImageFormat format) { - return GetImageDescriptor(Format).BytesPerPixel; + return GetImageDescriptor(format).BytesPerPixel; } - private static int DivRoundUp(int LHS, int RHS) + private static int DivRoundUp(int lhs, int rhs) { - return (LHS + (RHS - 1)) / RHS; + return (lhs + (rhs - 1)) / rhs; } - public static bool HasColor(GalImageFormat Format) + public static bool HasColor(GalImageFormat format) { - return (GetImageDescriptor(Format).Target & TargetBuffer.Color) != 0; + return (GetImageDescriptor(format).Target & TargetBuffer.Color) != 0; } - public static bool HasDepth(GalImageFormat Format) + public static bool HasDepth(GalImageFormat format) { - return (GetImageDescriptor(Format).Target & TargetBuffer.Depth) != 0; + return (GetImageDescriptor(format).Target & TargetBuffer.Depth) != 0; } - public static bool HasStencil(GalImageFormat Format) + public static bool HasStencil(GalImageFormat format) { - return (GetImageDescriptor(Format).Target & TargetBuffer.Stencil) != 0; + return (GetImageDescriptor(format).Target & TargetBuffer.Stencil) != 0; } - public static bool IsCompressed(GalImageFormat Format) + public static bool IsCompressed(GalImageFormat format) { - ImageDescriptor Desc = GetImageDescriptor(Format); + ImageDescriptor desc = GetImageDescriptor(format); - return (Desc.BlockWidth | Desc.BlockHeight) != 1; + return (desc.BlockWidth | desc.BlockHeight) != 1; } - private static ImageDescriptor GetImageDescriptor(GalImageFormat Format) + private static ImageDescriptor GetImageDescriptor(GalImageFormat format) { - GalImageFormat PixelFormat = Format & GalImageFormat.FormatMask; + GalImageFormat pixelFormat = format & GalImageFormat.FormatMask; - if (s_ImageTable.TryGetValue(PixelFormat, out ImageDescriptor Descriptor)) + if (ImageTable.TryGetValue(pixelFormat, out ImageDescriptor descriptor)) { - return Descriptor; + return descriptor; } - throw new NotImplementedException($"Format \"{PixelFormat}\" not implemented!"); + throw new NotImplementedException($"Format \"{pixelFormat}\" not implemented!"); } - private static GalImageFormat GetFormatType(GalTextureType Type) + private static GalImageFormat GetFormatType(GalTextureType type) { - switch (Type) + switch (type) { case GalTextureType.Snorm: return Snorm; case GalTextureType.Unorm: return Unorm; @@ -440,7 +493,68 @@ namespace Ryujinx.Graphics.Texture case GalTextureType.Uint: return Uint; case GalTextureType.Float: return Float; - default: throw new NotImplementedException(((int)Type).ToString()); + default: throw new NotImplementedException(((int)type).ToString()); + } + } + + public static TextureTarget GetTextureTarget(GalTextureTarget galTextureTarget) + { + switch (galTextureTarget) + { + case GalTextureTarget.OneD: + return TextureTarget.Texture1D; + case GalTextureTarget.TwoD: + case GalTextureTarget.TwoDNoMipMap: + return TextureTarget.Texture2D; + case GalTextureTarget.ThreeD: + return TextureTarget.Texture3D; + case GalTextureTarget.OneDArray: + return TextureTarget.Texture1DArray; + case GalTextureTarget.OneDBuffer: + return TextureTarget.TextureBuffer; + case GalTextureTarget.TwoDArray: + return TextureTarget.Texture2DArray; + case GalTextureTarget.CubeMap: + return TextureTarget.TextureCubeMap; + case GalTextureTarget.CubeArray: + return TextureTarget.TextureCubeMapArray; + default: + throw new NotSupportedException($"Texture target {galTextureTarget} currently not supported!"); + } + } + + public static bool IsArray(GalTextureTarget textureTarget) + { + switch (textureTarget) + { + case GalTextureTarget.OneDArray: + case GalTextureTarget.TwoDArray: + case GalTextureTarget.CubeArray: + return true; + default: + return false; + } + } + + public static int GetCoordsCountTextureTarget(GalTextureTarget textureTarget) + { + switch (textureTarget) + { + case GalTextureTarget.OneD: + return 1; + case GalTextureTarget.OneDArray: + case GalTextureTarget.OneDBuffer: + case GalTextureTarget.TwoD: + case GalTextureTarget.TwoDNoMipMap: + return 2; + case GalTextureTarget.ThreeD: + case GalTextureTarget.TwoDArray: + case GalTextureTarget.CubeMap: + return 3; + case GalTextureTarget.CubeArray: + return 4; + default: + throw new NotImplementedException($"TextureTarget.{textureTarget} not implemented yet."); } } } diff --git a/Ryujinx.Graphics/Graphics3d/Texture/IntegerEncoded.cs b/Ryujinx.Graphics/Graphics3d/Texture/IntegerEncoded.cs index 683cb77049..e6d67058e0 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/IntegerEncoded.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/IntegerEncoded.cs @@ -12,81 +12,81 @@ namespace Ryujinx.Graphics.Texture Trit } - EIntegerEncoding Encoding; + EIntegerEncoding _encoding; public int NumberBits { get; private set; } public int BitValue { get; private set; } public int TritValue { get; private set; } public int QuintValue { get; private set; } - public IntegerEncoded(EIntegerEncoding _Encoding, int NumBits) + public IntegerEncoded(EIntegerEncoding encoding, int numBits) { - Encoding = _Encoding; - NumberBits = NumBits; + _encoding = encoding; + NumberBits = numBits; BitValue = 0; TritValue = 0; QuintValue = 0; } - public bool MatchesEncoding(IntegerEncoded Other) + public bool MatchesEncoding(IntegerEncoded other) { - return Encoding == Other.Encoding && NumberBits == Other.NumberBits; + return _encoding == other._encoding && NumberBits == other.NumberBits; } public EIntegerEncoding GetEncoding() { - return Encoding; + return _encoding; } - public int GetBitLength(int NumberVals) + public int GetBitLength(int numberVals) { - int TotalBits = NumberBits * NumberVals; - if (Encoding == EIntegerEncoding.Trit) + int totalBits = NumberBits * numberVals; + if (_encoding == EIntegerEncoding.Trit) { - TotalBits += (NumberVals * 8 + 4) / 5; + totalBits += (numberVals * 8 + 4) / 5; } - else if (Encoding == EIntegerEncoding.Quint) + else if (_encoding == EIntegerEncoding.Quint) { - TotalBits += (NumberVals * 7 + 2) / 3; + totalBits += (numberVals * 7 + 2) / 3; } - return TotalBits; + return totalBits; } - public static IntegerEncoded CreateEncoding(int MaxVal) + public static IntegerEncoded CreateEncoding(int maxVal) { - while (MaxVal > 0) + while (maxVal > 0) { - int Check = MaxVal + 1; + int check = maxVal + 1; // Is maxVal a power of two? - if ((Check & (Check - 1)) == 0) + if ((check & (check - 1)) == 0) { - return new IntegerEncoded(EIntegerEncoding.JustBits, BitArrayStream.PopCnt(MaxVal)); + return new IntegerEncoded(EIntegerEncoding.JustBits, BitArrayStream.PopCnt(maxVal)); } // Is maxVal of the type 3*2^n - 1? - if ((Check % 3 == 0) && ((Check / 3) & ((Check / 3) - 1)) == 0) + if ((check % 3 == 0) && ((check / 3) & ((check / 3) - 1)) == 0) { - return new IntegerEncoded(EIntegerEncoding.Trit, BitArrayStream.PopCnt(Check / 3 - 1)); + return new IntegerEncoded(EIntegerEncoding.Trit, BitArrayStream.PopCnt(check / 3 - 1)); } // Is maxVal of the type 5*2^n - 1? - if ((Check % 5 == 0) && ((Check / 5) & ((Check / 5) - 1)) == 0) + if ((check % 5 == 0) && ((check / 5) & ((check / 5) - 1)) == 0) { - return new IntegerEncoded(EIntegerEncoding.Quint, BitArrayStream.PopCnt(Check / 5 - 1)); + return new IntegerEncoded(EIntegerEncoding.Quint, BitArrayStream.PopCnt(check / 5 - 1)); } // Apparently it can't be represented with a bounded integer sequence... // just iterate. - MaxVal--; + maxVal--; } return new IntegerEncoded(EIntegerEncoding.JustBits, 0); } public static void DecodeTritBlock( - BitArrayStream BitStream, - List ListIntegerEncoded, - int NumberBitsPerValue) + BitArrayStream bitStream, + List listIntegerEncoded, + int numberBitsPerValue) { // Implement the algorithm in section C.2.12 int[] m = new int[5]; @@ -95,170 +95,170 @@ namespace Ryujinx.Graphics.Texture // Read the trit encoded block according to // table C.2.14 - m[0] = BitStream.ReadBits(NumberBitsPerValue); - T = BitStream.ReadBits(2); - m[1] = BitStream.ReadBits(NumberBitsPerValue); - T |= BitStream.ReadBits(2) << 2; - m[2] = BitStream.ReadBits(NumberBitsPerValue); - T |= BitStream.ReadBits(1) << 4; - m[3] = BitStream.ReadBits(NumberBitsPerValue); - T |= BitStream.ReadBits(2) << 5; - m[4] = BitStream.ReadBits(NumberBitsPerValue); - T |= BitStream.ReadBits(1) << 7; + m[0] = bitStream.ReadBits(numberBitsPerValue); + T = bitStream.ReadBits(2); + m[1] = bitStream.ReadBits(numberBitsPerValue); + T |= bitStream.ReadBits(2) << 2; + m[2] = bitStream.ReadBits(numberBitsPerValue); + T |= bitStream.ReadBits(1) << 4; + m[3] = bitStream.ReadBits(numberBitsPerValue); + T |= bitStream.ReadBits(2) << 5; + m[4] = bitStream.ReadBits(numberBitsPerValue); + T |= bitStream.ReadBits(1) << 7; - int C = 0; + int c = 0; - BitArrayStream Tb = new BitArrayStream(new BitArray(new int[] { T })); - if (Tb.ReadBits(2, 4) == 7) + BitArrayStream tb = new BitArrayStream(new BitArray(new int[] { T })); + if (tb.ReadBits(2, 4) == 7) { - C = (Tb.ReadBits(5, 7) << 2) | Tb.ReadBits(0, 1); + c = (tb.ReadBits(5, 7) << 2) | tb.ReadBits(0, 1); t[4] = t[3] = 2; } else { - C = Tb.ReadBits(0, 4); - if (Tb.ReadBits(5, 6) == 3) + c = tb.ReadBits(0, 4); + if (tb.ReadBits(5, 6) == 3) { t[4] = 2; - t[3] = Tb.ReadBit(7); + t[3] = tb.ReadBit(7); } else { - t[4] = Tb.ReadBit(7); - t[3] = Tb.ReadBits(5, 6); + t[4] = tb.ReadBit(7); + t[3] = tb.ReadBits(5, 6); } } - BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C })); - if (Cb.ReadBits(0, 1) == 3) + BitArrayStream cb = new BitArrayStream(new BitArray(new int[] { c })); + if (cb.ReadBits(0, 1) == 3) { t[2] = 2; - t[1] = Cb.ReadBit(4); - t[0] = (Cb.ReadBit(3) << 1) | (Cb.ReadBit(2) & ~Cb.ReadBit(3)); + t[1] = cb.ReadBit(4); + t[0] = (cb.ReadBit(3) << 1) | (cb.ReadBit(2) & ~cb.ReadBit(3)); } - else if (Cb.ReadBits(2, 3) == 3) + else if (cb.ReadBits(2, 3) == 3) { t[2] = 2; t[1] = 2; - t[0] = Cb.ReadBits(0, 1); + t[0] = cb.ReadBits(0, 1); } else { - t[2] = Cb.ReadBit(4); - t[1] = Cb.ReadBits(2, 3); - t[0] = (Cb.ReadBit(1) << 1) | (Cb.ReadBit(0) & ~Cb.ReadBit(1)); + t[2] = cb.ReadBit(4); + t[1] = cb.ReadBits(2, 3); + t[0] = (cb.ReadBit(1) << 1) | (cb.ReadBit(0) & ~cb.ReadBit(1)); } for (int i = 0; i < 5; i++) { - IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Trit, NumberBitsPerValue) + IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Trit, numberBitsPerValue) { BitValue = m[i], TritValue = t[i] }; - ListIntegerEncoded.Add(IntEncoded); + listIntegerEncoded.Add(intEncoded); } } public static void DecodeQuintBlock( - BitArrayStream BitStream, - List ListIntegerEncoded, - int NumberBitsPerValue) + BitArrayStream bitStream, + List listIntegerEncoded, + int numberBitsPerValue) { // Implement the algorithm in section C.2.12 int[] m = new int[3]; - int[] q = new int[3]; - int Q; + int[] qa = new int[3]; + int q; // Read the trit encoded block according to // table C.2.15 - m[0] = BitStream.ReadBits(NumberBitsPerValue); - Q = BitStream.ReadBits(3); - m[1] = BitStream.ReadBits(NumberBitsPerValue); - Q |= BitStream.ReadBits(2) << 3; - m[2] = BitStream.ReadBits(NumberBitsPerValue); - Q |= BitStream.ReadBits(2) << 5; + m[0] = bitStream.ReadBits(numberBitsPerValue); + q = bitStream.ReadBits(3); + m[1] = bitStream.ReadBits(numberBitsPerValue); + q |= bitStream.ReadBits(2) << 3; + m[2] = bitStream.ReadBits(numberBitsPerValue); + q |= bitStream.ReadBits(2) << 5; - BitArrayStream Qb = new BitArrayStream(new BitArray(new int[] { Q })); - if (Qb.ReadBits(1, 2) == 3 && Qb.ReadBits(5, 6) == 0) + BitArrayStream qb = new BitArrayStream(new BitArray(new int[] { q })); + if (qb.ReadBits(1, 2) == 3 && qb.ReadBits(5, 6) == 0) { - q[0] = q[1] = 4; - q[2] = (Qb.ReadBit(0) << 2) | ((Qb.ReadBit(4) & ~Qb.ReadBit(0)) << 1) | (Qb.ReadBit(3) & ~Qb.ReadBit(0)); + qa[0] = qa[1] = 4; + qa[2] = (qb.ReadBit(0) << 2) | ((qb.ReadBit(4) & ~qb.ReadBit(0)) << 1) | (qb.ReadBit(3) & ~qb.ReadBit(0)); } else { - int C = 0; - if (Qb.ReadBits(1, 2) == 3) + int c = 0; + if (qb.ReadBits(1, 2) == 3) { - q[2] = 4; - C = (Qb.ReadBits(3, 4) << 3) | ((~Qb.ReadBits(5, 6) & 3) << 1) | Qb.ReadBit(0); + qa[2] = 4; + c = (qb.ReadBits(3, 4) << 3) | ((~qb.ReadBits(5, 6) & 3) << 1) | qb.ReadBit(0); } else { - q[2] = Qb.ReadBits(5, 6); - C = Qb.ReadBits(0, 4); + qa[2] = qb.ReadBits(5, 6); + c = qb.ReadBits(0, 4); } - BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C })); - if (Cb.ReadBits(0, 2) == 5) + BitArrayStream cb = new BitArrayStream(new BitArray(new int[] { c })); + if (cb.ReadBits(0, 2) == 5) { - q[1] = 4; - q[0] = Cb.ReadBits(3, 4); + qa[1] = 4; + qa[0] = cb.ReadBits(3, 4); } else { - q[1] = Cb.ReadBits(3, 4); - q[0] = Cb.ReadBits(0, 2); + qa[1] = cb.ReadBits(3, 4); + qa[0] = cb.ReadBits(0, 2); } } for (int i = 0; i < 3; i++) { - IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Quint, NumberBitsPerValue) + IntegerEncoded intEncoded = new IntegerEncoded(EIntegerEncoding.Quint, numberBitsPerValue) { BitValue = m[i], - QuintValue = q[i] + QuintValue = qa[i] }; - ListIntegerEncoded.Add(IntEncoded); + listIntegerEncoded.Add(intEncoded); } } public static void DecodeIntegerSequence( - List DecodeIntegerSequence, - BitArrayStream BitStream, - int MaxRange, - int NumberValues) + List decodeIntegerSequence, + BitArrayStream bitStream, + int maxRange, + int numberValues) { // Determine encoding parameters - IntegerEncoded IntEncoded = CreateEncoding(MaxRange); + IntegerEncoded intEncoded = CreateEncoding(maxRange); // Start decoding - int NumberValuesDecoded = 0; - while (NumberValuesDecoded < NumberValues) + int numberValuesDecoded = 0; + while (numberValuesDecoded < numberValues) { - switch (IntEncoded.GetEncoding()) + switch (intEncoded.GetEncoding()) { case EIntegerEncoding.Quint: { - DecodeQuintBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits); - NumberValuesDecoded += 3; + DecodeQuintBlock(bitStream, decodeIntegerSequence, intEncoded.NumberBits); + numberValuesDecoded += 3; break; } case EIntegerEncoding.Trit: { - DecodeTritBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits); - NumberValuesDecoded += 5; + DecodeTritBlock(bitStream, decodeIntegerSequence, intEncoded.NumberBits); + numberValuesDecoded += 5; break; } case EIntegerEncoding.JustBits: { - IntEncoded.BitValue = BitStream.ReadBits(IntEncoded.NumberBits); - DecodeIntegerSequence.Add(IntEncoded); - NumberValuesDecoded++; + intEncoded.BitValue = bitStream.ReadBits(intEncoded.NumberBits); + decodeIntegerSequence.Add(intEncoded); + numberValuesDecoded++; break; } diff --git a/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs index ef468e27b5..fb1bd0985f 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs @@ -1,19 +1,45 @@ +using System; + namespace Ryujinx.Graphics.Texture { class LinearSwizzle : ISwizzle { - private int Pitch; - private int Bpp; + private int _pitch; + private int _bpp; - public LinearSwizzle(int Pitch, int Bpp) + private int _sliceSize; + + public LinearSwizzle(int pitch, int bpp, int width, int height) { - this.Pitch = Pitch; - this.Bpp = Bpp; + _pitch = pitch; + _bpp = bpp; + _sliceSize = width * height * bpp; } - public int GetSwizzleOffset(int X, int Y) + public void SetMipLevel(int level) { - return X * Bpp + Y * Pitch; + throw new NotImplementedException(); + } + + public int GetMipOffset(int level) + { + if (level == 1) + return _sliceSize; + throw new NotImplementedException(); + } + + public int GetImageSize(int mipsCount) + { + int size = GetMipOffset(mipsCount); + + size = (size + 0x1fff) & ~0x1fff; + + return size; + } + + public int GetSwizzleOffset(int x, int y, int z) + { + return z * _sliceSize + x * _bpp + y * _pitch; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs index 1f2d625ec4..28c9050253 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs @@ -6,112 +6,161 @@ namespace Ryujinx.Graphics.Texture { static class TextureFactory { - public static GalImage MakeTexture(NvGpuVmm Vmm, long TicPosition) + public static GalImage MakeTexture(NvGpuVmm vmm, long ticPosition) { - int[] Tic = ReadWords(Vmm, TicPosition, 8); + int[] tic = ReadWords(vmm, ticPosition, 8); - GalImageFormat Format = GetImageFormat(Tic); + GalImageFormat format = GetImageFormat(tic); - GalTextureSource XSource = (GalTextureSource)((Tic[0] >> 19) & 7); - GalTextureSource YSource = (GalTextureSource)((Tic[0] >> 22) & 7); - GalTextureSource ZSource = (GalTextureSource)((Tic[0] >> 25) & 7); - GalTextureSource WSource = (GalTextureSource)((Tic[0] >> 28) & 7); + GalTextureTarget textureTarget = (GalTextureTarget)((tic[4] >> 23) & 0xF); - TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7); + GalTextureSource xSource = (GalTextureSource)((tic[0] >> 19) & 7); + GalTextureSource ySource = (GalTextureSource)((tic[0] >> 22) & 7); + GalTextureSource zSource = (GalTextureSource)((tic[0] >> 25) & 7); + GalTextureSource wSource = (GalTextureSource)((tic[0] >> 28) & 7); - GalMemoryLayout Layout; + TextureSwizzle swizzle = (TextureSwizzle)((tic[2] >> 21) & 7); - if (Swizzle == TextureSwizzle.BlockLinear || - Swizzle == TextureSwizzle.BlockLinearColorKey) + int maxMipmapLevel = (tic[3] >> 28) & 0xF + 1; + + GalMemoryLayout layout; + + if (swizzle == TextureSwizzle.BlockLinear || + swizzle == TextureSwizzle.BlockLinearColorKey) { - Layout = GalMemoryLayout.BlockLinear; + layout = GalMemoryLayout.BlockLinear; } else { - Layout = GalMemoryLayout.Pitch; + layout = GalMemoryLayout.Pitch; } - int BlockHeightLog2 = (Tic[3] >> 3) & 7; - int TileWidthLog2 = (Tic[3] >> 10) & 7; + int gobBlockHeightLog2 = (tic[3] >> 3) & 7; + int gobBlockDepthLog2 = (tic[3] >> 6) & 7; + int tileWidthLog2 = (tic[3] >> 10) & 7; - int BlockHeight = 1 << BlockHeightLog2; - int TileWidth = 1 << TileWidthLog2; + int gobBlockHeight = 1 << gobBlockHeightLog2; + int gobBlockDepth = 1 << gobBlockDepthLog2; + int tileWidth = 1 << tileWidthLog2; - int Width = (Tic[4] & 0xffff) + 1; - int Height = (Tic[5] & 0xffff) + 1; + int width = ((tic[4] >> 0) & 0xffff) + 1; + int height = ((tic[5] >> 0) & 0xffff) + 1; + int depth = ((tic[5] >> 16) & 0x3fff) + 1; - GalImage Image = new GalImage( - Width, - Height, - TileWidth, - BlockHeight, - Layout, - Format, - XSource, - YSource, - ZSource, - WSource); + int layoutCount = 1; - if (Layout == GalMemoryLayout.Pitch) + // TODO: check this + if (ImageUtils.IsArray(textureTarget)) { - Image.Pitch = (Tic[3] & 0xffff) << 5; + layoutCount = depth; + depth = 1; } - return Image; + if (textureTarget == GalTextureTarget.OneD) + { + height = 1; + } + + if (textureTarget == GalTextureTarget.TwoD || textureTarget == GalTextureTarget.OneD) + { + depth = 1; + } + else if (textureTarget == GalTextureTarget.CubeMap) + { + // FIXME: This is a bit hacky but I guess it's fine for now + layoutCount = 6; + depth = 1; + } + else if (textureTarget == GalTextureTarget.CubeArray) + { + // FIXME: This is a really really hacky but I guess it's fine for now + layoutCount *= 6; + depth = 1; + } + + GalImage image = new GalImage( + width, + height, + depth, + layoutCount, + tileWidth, + gobBlockHeight, + gobBlockDepth, + layout, + format, + textureTarget, + maxMipmapLevel, + xSource, + ySource, + zSource, + wSource); + + if (layout == GalMemoryLayout.Pitch) + { + image.Pitch = (tic[3] & 0xffff) << 5; + } + + return image; } - public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition) + public static GalTextureSampler MakeSampler(NvGpu gpu, NvGpuVmm vmm, long tscPosition) { - int[] Tsc = ReadWords(Vmm, TscPosition, 8); + int[] tsc = ReadWords(vmm, tscPosition, 8); - GalTextureWrap AddressU = (GalTextureWrap)((Tsc[0] >> 0) & 7); - GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7); - GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7); + GalTextureWrap addressU = (GalTextureWrap)((tsc[0] >> 0) & 7); + GalTextureWrap addressV = (GalTextureWrap)((tsc[0] >> 3) & 7); + GalTextureWrap addressP = (GalTextureWrap)((tsc[0] >> 6) & 7); - GalTextureFilter MagFilter = (GalTextureFilter) ((Tsc[1] >> 0) & 3); - GalTextureFilter MinFilter = (GalTextureFilter) ((Tsc[1] >> 4) & 3); - GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3); + bool depthCompare = ((tsc[0] >> 9) & 1) == 1; - GalColorF BorderColor = new GalColorF( - BitConverter.Int32BitsToSingle(Tsc[4]), - BitConverter.Int32BitsToSingle(Tsc[5]), - BitConverter.Int32BitsToSingle(Tsc[6]), - BitConverter.Int32BitsToSingle(Tsc[7])); + DepthCompareFunc depthCompareFunc = (DepthCompareFunc)((tsc[0] >> 10) & 7); + + GalTextureFilter magFilter = (GalTextureFilter) ((tsc[1] >> 0) & 3); + GalTextureFilter minFilter = (GalTextureFilter) ((tsc[1] >> 4) & 3); + GalTextureMipFilter mipFilter = (GalTextureMipFilter)((tsc[1] >> 6) & 3); + + GalColorF borderColor = new GalColorF( + BitConverter.Int32BitsToSingle(tsc[4]), + BitConverter.Int32BitsToSingle(tsc[5]), + BitConverter.Int32BitsToSingle(tsc[6]), + BitConverter.Int32BitsToSingle(tsc[7])); return new GalTextureSampler( - AddressU, - AddressV, - AddressP, - MinFilter, - MagFilter, - MipFilter, - BorderColor); + addressU, + addressV, + addressP, + minFilter, + magFilter, + mipFilter, + borderColor, + depthCompare, + depthCompareFunc); } - private static GalImageFormat GetImageFormat(int[] Tic) + private static GalImageFormat GetImageFormat(int[] tic) { - GalTextureType RType = (GalTextureType)((Tic[0] >> 7) & 7); - GalTextureType GType = (GalTextureType)((Tic[0] >> 10) & 7); - GalTextureType BType = (GalTextureType)((Tic[0] >> 13) & 7); - GalTextureType AType = (GalTextureType)((Tic[0] >> 16) & 7); + GalTextureType rType = (GalTextureType)((tic[0] >> 7) & 7); + GalTextureType gType = (GalTextureType)((tic[0] >> 10) & 7); + GalTextureType bType = (GalTextureType)((tic[0] >> 13) & 7); + GalTextureType aType = (GalTextureType)((tic[0] >> 16) & 7); - GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f); + GalTextureFormat format = (GalTextureFormat)(tic[0] & 0x7f); - bool ConvSrgb = ((Tic[4] >> 22) & 1) != 0; + bool convSrgb = ((tic[4] >> 22) & 1) != 0; - return ImageUtils.ConvertTexture(Format, RType, GType, BType, AType, ConvSrgb); + return ImageUtils.ConvertTexture(format, rType, gType, bType, aType, convSrgb); } - private static int[] ReadWords(NvGpuVmm Vmm, long Position, int Count) + private static int[] ReadWords(NvGpuVmm vmm, long position, int count) { - int[] Words = new int[Count]; + int[] words = new int[count]; - for (int Index = 0; Index < Count; Index++, Position += 4) + for (int index = 0; index < count; index++, position += 4) { - Words[Index] = Vmm.ReadInt32(Position); + words[index] = vmm.ReadInt32(position); } - return Words; + return words; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs index 6ac91d8b59..1de81008ee 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs @@ -1,4 +1,5 @@ using ChocolArm64.Memory; +using Ryujinx.Common; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; @@ -6,37 +7,47 @@ namespace Ryujinx.Graphics.Texture { static class TextureHelper { - public static ISwizzle GetSwizzle(GalImage Image) + public static ISwizzle GetSwizzle(GalImage image) { - int BlockWidth = ImageUtils.GetBlockWidth (Image.Format); - int BytesPerPixel = ImageUtils.GetBytesPerPixel(Image.Format); + int blockWidth = ImageUtils.GetBlockWidth (image.Format); + int blockHeight = ImageUtils.GetBlockHeight (image.Format); + int blockDepth = ImageUtils.GetBlockDepth (image.Format); + int bytesPerPixel = ImageUtils.GetBytesPerPixel(image.Format); - int Width = (Image.Width + (BlockWidth - 1)) / BlockWidth; + int width = BitUtils.DivRoundUp(image.Width, blockWidth); + int height = BitUtils.DivRoundUp(image.Height, blockHeight); + int depth = BitUtils.DivRoundUp(image.Depth, blockDepth); - if (Image.Layout == GalMemoryLayout.BlockLinear) + if (image.Layout == GalMemoryLayout.BlockLinear) { - int AlignMask = Image.TileWidth * (64 / BytesPerPixel) - 1; + int alignMask = image.TileWidth * (64 / bytesPerPixel) - 1; - Width = (Width + AlignMask) & ~AlignMask; + width = (width + alignMask) & ~alignMask; - return new BlockLinearSwizzle(Width, BytesPerPixel, Image.GobBlockHeight); + return new BlockLinearSwizzle( + width, + height, + depth, + image.GobBlockHeight, + image.GobBlockDepth, + bytesPerPixel); } else { - return new LinearSwizzle(Image.Pitch, BytesPerPixel); + return new LinearSwizzle(image.Pitch, bytesPerPixel, width, height); } } public static (MemoryManager Memory, long Position) GetMemoryAndPosition( - IMemory Memory, - long Position) + IMemory memory, + long position) { - if (Memory is NvGpuVmm Vmm) + if (memory is NvGpuVmm vmm) { - return (Vmm.Memory, Vmm.GetPhysicalAddress(Position)); + return (vmm.Memory, vmm.GetPhysicalAddress(position)); } - return ((MemoryManager)Memory, Position); + return ((MemoryManager)memory, position); } } } diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureSwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureSwizzle.cs index c67a5367ec..2cc426ab9d 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/TextureSwizzle.cs +++ b/Ryujinx.Graphics/Graphics3d/Texture/TextureSwizzle.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Texture { public enum TextureSwizzle { - _1dBuffer = 0, + _1DBuffer = 0, PitchColorKey = 1, Pitch = 2, BlockLinear = 3, diff --git a/Ryujinx.Graphics/Memory/NvGpuVmm.cs b/Ryujinx.Graphics/Memory/NvGpuVmm.cs index 7fdef4734a..ac1b765ace 100644 --- a/Ryujinx.Graphics/Memory/NvGpuVmm.cs +++ b/Ryujinx.Graphics/Memory/NvGpuVmm.cs @@ -8,177 +8,177 @@ namespace Ryujinx.Graphics.Memory { public const long AddrSize = 1L << 40; - private const int PTLvl0Bits = 14; - private const int PTLvl1Bits = 14; - private const int PTPageBits = 12; + private const int PtLvl0Bits = 14; + private const int PtLvl1Bits = 14; + private const int PtPageBits = 12; - private const int PTLvl0Size = 1 << PTLvl0Bits; - private const int PTLvl1Size = 1 << PTLvl1Bits; - public const int PageSize = 1 << PTPageBits; + private const int PtLvl0Size = 1 << PtLvl0Bits; + private const int PtLvl1Size = 1 << PtLvl1Bits; + public const int PageSize = 1 << PtPageBits; - private const int PTLvl0Mask = PTLvl0Size - 1; - private const int PTLvl1Mask = PTLvl1Size - 1; + private const int PtLvl0Mask = PtLvl0Size - 1; + private const int PtLvl1Mask = PtLvl1Size - 1; public const int PageMask = PageSize - 1; - private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; - private const int PTLvl1Bit = PTPageBits; + private const int PtLvl0Bit = PtPageBits + PtLvl1Bits; + private const int PtLvl1Bit = PtPageBits; public MemoryManager Memory { get; private set; } - private NvGpuVmmCache Cache; + private NvGpuVmmCache _cache; private const long PteUnmapped = -1; private const long PteReserved = -2; - private long[][] PageTable; + private long[][] _pageTable; - public NvGpuVmm(MemoryManager Memory) + public NvGpuVmm(MemoryManager memory) { - this.Memory = Memory; + Memory = memory; - Cache = new NvGpuVmmCache(Memory); + _cache = new NvGpuVmmCache(memory); - PageTable = new long[PTLvl0Size][]; + _pageTable = new long[PtLvl0Size][]; } - public long Map(long PA, long VA, long Size) + public long Map(long pa, long va, long size) { - lock (PageTable) + lock (_pageTable) { - for (long Offset = 0; Offset < Size; Offset += PageSize) + for (long offset = 0; offset < size; offset += PageSize) { - SetPte(VA + Offset, PA + Offset); + SetPte(va + offset, pa + offset); } } - return VA; + return va; } - public long Map(long PA, long Size) + public long Map(long pa, long size) { - lock (PageTable) + lock (_pageTable) { - long VA = GetFreePosition(Size); + long va = GetFreePosition(size); - if (VA != -1) + if (va != -1) { - for (long Offset = 0; Offset < Size; Offset += PageSize) + for (long offset = 0; offset < size; offset += PageSize) { - SetPte(VA + Offset, PA + Offset); + SetPte(va + offset, pa + offset); } } - return VA; + return va; } } - public long MapLow(long PA, long Size) + public long MapLow(long pa, long size) { - lock (PageTable) + lock (_pageTable) { - long VA = GetFreePosition(Size, 1, PageSize); + long va = GetFreePosition(size, 1, PageSize); - if (VA != -1 && (ulong)VA <= uint.MaxValue && (ulong)(VA + Size) <= uint.MaxValue) + if (va != -1 && (ulong)va <= uint.MaxValue && (ulong)(va + size) <= uint.MaxValue) { - for (long Offset = 0; Offset < Size; Offset += PageSize) + for (long offset = 0; offset < size; offset += PageSize) { - SetPte(VA + Offset, PA + Offset); + SetPte(va + offset, pa + offset); } } else { - VA = -1; + va = -1; } - return VA; + return va; } } - public long ReserveFixed(long VA, long Size) + public long ReserveFixed(long va, long size) { - lock (PageTable) + lock (_pageTable) { - for (long Offset = 0; Offset < Size; Offset += PageSize) + for (long offset = 0; offset < size; offset += PageSize) { - if (IsPageInUse(VA + Offset)) + if (IsPageInUse(va + offset)) { return -1; } } - for (long Offset = 0; Offset < Size; Offset += PageSize) + for (long offset = 0; offset < size; offset += PageSize) { - SetPte(VA + Offset, PteReserved); + SetPte(va + offset, PteReserved); } } - return VA; + return va; } - public long Reserve(long Size, long Align) + public long Reserve(long size, long align) { - lock (PageTable) + lock (_pageTable) { - long Position = GetFreePosition(Size, Align); + long position = GetFreePosition(size, align); - if (Position != -1) + if (position != -1) { - for (long Offset = 0; Offset < Size; Offset += PageSize) + for (long offset = 0; offset < size; offset += PageSize) { - SetPte(Position + Offset, PteReserved); + SetPte(position + offset, PteReserved); } } - return Position; + return position; } } - public void Free(long VA, long Size) + public void Free(long va, long size) { - lock (PageTable) + lock (_pageTable) { - for (long Offset = 0; Offset < Size; Offset += PageSize) + for (long offset = 0; offset < size; offset += PageSize) { - SetPte(VA + Offset, PteUnmapped); + SetPte(va + offset, PteUnmapped); } } } - private long GetFreePosition(long Size, long Align = 1, long Start = 1L << 32) + private long GetFreePosition(long size, long align = 1, long start = 1L << 32) { //Note: Address 0 is not considered valid by the driver, //when 0 is returned it's considered a mapping error. - long Position = Start; - long FreeSize = 0; + long position = start; + long freeSize = 0; - if (Align < 1) + if (align < 1) { - Align = 1; + align = 1; } - Align = (Align + PageMask) & ~PageMask; + align = (align + PageMask) & ~PageMask; - while (Position + FreeSize < AddrSize) + while (position + freeSize < AddrSize) { - if (!IsPageInUse(Position + FreeSize)) + if (!IsPageInUse(position + freeSize)) { - FreeSize += PageSize; + freeSize += PageSize; - if (FreeSize >= Size) + if (freeSize >= size) { - return Position; + return position; } } else { - Position += FreeSize + PageSize; - FreeSize = 0; + position += freeSize + PageSize; + freeSize = 0; - long Remainder = Position % Align; + long remainder = position % align; - if (Remainder != 0) + if (remainder != 0) { - Position = (Position - Remainder) + Align; + position = (position - remainder) + align; } } } @@ -186,23 +186,23 @@ namespace Ryujinx.Graphics.Memory return -1; } - public long GetPhysicalAddress(long VA) + public long GetPhysicalAddress(long va) { - long BasePos = GetPte(VA); + long basePos = GetPte(va); - if (BasePos < 0) + if (basePos < 0) { return -1; } - return BasePos + (VA & PageMask); + return basePos + (va & PageMask); } - public bool IsRegionFree(long VA, long Size) + public bool IsRegionFree(long va, long size) { - for (long Offset = 0; Offset < Size; Offset += PageSize) + for (long offset = 0; offset < size; offset += PageSize) { - if (IsPageInUse(VA + Offset)) + if (IsPageInUse(va + offset)) { return false; } @@ -211,189 +211,189 @@ namespace Ryujinx.Graphics.Memory return true; } - private bool IsPageInUse(long VA) + private bool IsPageInUse(long va) { - if (VA >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) + if (va >> PtLvl0Bits + PtLvl1Bits + PtPageBits != 0) { return false; } - long L0 = (VA >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (VA >> PTLvl1Bit) & PTLvl1Mask; + long l0 = (va >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (va >> PtLvl1Bit) & PtLvl1Mask; - if (PageTable[L0] == null) + if (_pageTable[l0] == null) { return false; } - return PageTable[L0][L1] != PteUnmapped; + return _pageTable[l0][l1] != PteUnmapped; } - private long GetPte(long Position) + private long GetPte(long position) { - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; - if (PageTable[L0] == null) + if (_pageTable[l0] == null) { return -1; } - return PageTable[L0][L1]; + return _pageTable[l0][l1]; } - private void SetPte(long Position, long TgtAddr) + private void SetPte(long position, long tgtAddr) { - long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; - long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; + long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; + long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; - if (PageTable[L0] == null) + if (_pageTable[l0] == null) { - PageTable[L0] = new long[PTLvl1Size]; + _pageTable[l0] = new long[PtLvl1Size]; - for (int Index = 0; Index < PTLvl1Size; Index++) + for (int index = 0; index < PtLvl1Size; index++) { - PageTable[L0][Index] = PteUnmapped; + _pageTable[l0][index] = PteUnmapped; } } - PageTable[L0][L1] = TgtAddr; + _pageTable[l0][l1] = tgtAddr; } - public bool IsRegionModified(long PA, long Size, NvGpuBufferType BufferType) + public bool IsRegionModified(long pa, long size, NvGpuBufferType bufferType) { - return Cache.IsRegionModified(PA, Size, BufferType); + return _cache.IsRegionModified(pa, size, bufferType); } - public bool TryGetHostAddress(long Position, long Size, out IntPtr Ptr) + public bool TryGetHostAddress(long position, long size, out IntPtr ptr) { - return Memory.TryGetHostAddress(GetPhysicalAddress(Position), Size, out Ptr); + return Memory.TryGetHostAddress(GetPhysicalAddress(position), size, out ptr); } - public byte ReadByte(long Position) + public byte ReadByte(long position) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - return Memory.ReadByte(Position); + return Memory.ReadByte(position); } - public ushort ReadUInt16(long Position) + public ushort ReadUInt16(long position) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - return Memory.ReadUInt16(Position); + return Memory.ReadUInt16(position); } - public uint ReadUInt32(long Position) + public uint ReadUInt32(long position) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - return Memory.ReadUInt32(Position); + return Memory.ReadUInt32(position); } - public ulong ReadUInt64(long Position) + public ulong ReadUInt64(long position) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - return Memory.ReadUInt64(Position); + return Memory.ReadUInt64(position); } - public sbyte ReadSByte(long Position) + public sbyte ReadSByte(long position) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - return Memory.ReadSByte(Position); + return Memory.ReadSByte(position); } - public short ReadInt16(long Position) + public short ReadInt16(long position) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - return Memory.ReadInt16(Position); + return Memory.ReadInt16(position); } - public int ReadInt32(long Position) + public int ReadInt32(long position) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - return Memory.ReadInt32(Position); + return Memory.ReadInt32(position); } - public long ReadInt64(long Position) + public long ReadInt64(long position) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - return Memory.ReadInt64(Position); + return Memory.ReadInt64(position); } - public byte[] ReadBytes(long Position, long Size) + public byte[] ReadBytes(long position, long size) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - return Memory.ReadBytes(Position, Size); + return Memory.ReadBytes(position, size); } - public void WriteByte(long Position, byte Value) + public void WriteByte(long position, byte value) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - Memory.WriteByte(Position, Value); + Memory.WriteByte(position, value); } - public void WriteUInt16(long Position, ushort Value) + public void WriteUInt16(long position, ushort value) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - Memory.WriteUInt16(Position, Value); + Memory.WriteUInt16(position, value); } - public void WriteUInt32(long Position, uint Value) + public void WriteUInt32(long position, uint value) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - Memory.WriteUInt32(Position, Value); + Memory.WriteUInt32(position, value); } - public void WriteUInt64(long Position, ulong Value) + public void WriteUInt64(long position, ulong value) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - Memory.WriteUInt64(Position, Value); + Memory.WriteUInt64(position, value); } - public void WriteSByte(long Position, sbyte Value) + public void WriteSByte(long position, sbyte value) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - Memory.WriteSByte(Position, Value); + Memory.WriteSByte(position, value); } - public void WriteInt16(long Position, short Value) + public void WriteInt16(long position, short value) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - Memory.WriteInt16(Position, Value); + Memory.WriteInt16(position, value); } - public void WriteInt32(long Position, int Value) + public void WriteInt32(long position, int value) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - Memory.WriteInt32(Position, Value); + Memory.WriteInt32(position, value); } - public void WriteInt64(long Position, long Value) + public void WriteInt64(long position, long value) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - Memory.WriteInt64(Position, Value); + Memory.WriteInt64(position, value); } - public void WriteBytes(long Position, byte[] Data) + public void WriteBytes(long position, byte[] data) { - Position = GetPhysicalAddress(Position); + position = GetPhysicalAddress(position); - Memory.WriteBytes(Position, Data); + Memory.WriteBytes(position, data); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs b/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs index 2f50463ded..ab5ea288c6 100644 --- a/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs +++ b/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs @@ -1,4 +1,3 @@ -using ChocolArm64.Events; using ChocolArm64.Memory; using System.Collections.Concurrent; @@ -11,7 +10,7 @@ namespace Ryujinx.Graphics.Memory private const long PageSize = MemoryManager.PageSize; private const long PageMask = MemoryManager.PageMask; - private ConcurrentDictionary[] CachedPages; + private ConcurrentDictionary[] _cachedPages; private MemoryManager _memory; @@ -19,43 +18,40 @@ namespace Ryujinx.Graphics.Memory { _memory = memory; - _memory.ObservedAccess += MemoryAccessHandler; - - CachedPages = new ConcurrentDictionary[1 << 20]; - } - - private void MemoryAccessHandler(object sender, MemoryAccessEventArgs e) - { - long pa = _memory.GetPhysicalAddress(e.Position); - - CachedPages[pa >> PageBits]?.Clear(); + _cachedPages = new ConcurrentDictionary[1 << 20]; } public bool IsRegionModified(long position, long size, NvGpuBufferType bufferType) { - long pa = _memory.GetPhysicalAddress(position); + long va = position; - long addr = pa; + long pa = _memory.GetPhysicalAddress(va); - long endAddr = (addr + size + PageMask) & ~PageMask; + long endAddr = (va + size + PageMask) & ~PageMask; + + long addrTruncated = va & ~PageMask; + + bool modified = _memory.IsRegionModified(addrTruncated, endAddr - addrTruncated); int newBuffMask = 1 << (int)bufferType; - _memory.StartObservingRegion(position, size); - long cachedPagesCount = 0; - while (addr < endAddr) + while (va < endAddr) { - long page = addr >> PageBits; + long page = _memory.GetPhysicalAddress(va) >> PageBits; - ConcurrentDictionary dictionary = CachedPages[page]; + ConcurrentDictionary dictionary = _cachedPages[page]; if (dictionary == null) { dictionary = new ConcurrentDictionary(); - CachedPages[page] = dictionary; + _cachedPages[page] = dictionary; + } + else if (modified) + { + _cachedPages[page].Clear(); } if (dictionary.TryGetValue(pa, out int currBuffMask)) @@ -74,10 +70,10 @@ namespace Ryujinx.Graphics.Memory dictionary[pa] = newBuffMask; } - addr += PageSize; + va += PageSize; } - return cachedPagesCount != (endAddr - pa + PageMask) >> PageBits; + return cachedPagesCount != (endAddr - addrTruncated) >> PageBits; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpu.cs b/Ryujinx.Graphics/NvGpu.cs index 3fd91f74a5..baac0b2d2a 100644 --- a/Ryujinx.Graphics/NvGpu.cs +++ b/Ryujinx.Graphics/NvGpu.cs @@ -8,6 +8,8 @@ namespace Ryujinx.Graphics { public class NvGpu { + public const int MaxViewportSize = 0x3FFF; + public IGalRenderer Renderer { get; private set; } public GpuResourceManager ResourceManager { get; private set; } @@ -20,13 +22,13 @@ namespace Ryujinx.Graphics internal NvGpuEngineM2mf EngineM2mf { get; private set; } internal NvGpuEngineP2mf EngineP2mf { get; private set; } - private CdmaProcessor CdmaProcessor; + private CdmaProcessor _cdmaProcessor; internal VideoDecoder VideoDecoder { get; private set; } internal VideoImageComposer VideoImageComposer { get; private set; } - public NvGpu(IGalRenderer Renderer) + public NvGpu(IGalRenderer renderer) { - this.Renderer = Renderer; + Renderer = renderer; ResourceManager = new GpuResourceManager(this); @@ -38,22 +40,22 @@ namespace Ryujinx.Graphics EngineM2mf = new NvGpuEngineM2mf(this); EngineP2mf = new NvGpuEngineP2mf(this); - CdmaProcessor = new CdmaProcessor(this); + _cdmaProcessor = new CdmaProcessor(this); VideoDecoder = new VideoDecoder(this); VideoImageComposer = new VideoImageComposer(this); } - public void PushCommandBuffer(NvGpuVmm Vmm, int[] CmdBuffer) + public void PushCommandBuffer(NvGpuVmm vmm, int[] cmdBuffer) { - lock (CdmaProcessor) + lock (_cdmaProcessor) { - CdmaProcessor.PushCommands(Vmm, CmdBuffer); + _cdmaProcessor.PushCommands(vmm, cmdBuffer); } } public void UninitializeVideoDecoder() { - lock (CdmaProcessor) + lock (_cdmaProcessor) { FFmpegWrapper.Uninitialize(); } diff --git a/Ryujinx.Graphics/QuadHelper.cs b/Ryujinx.Graphics/QuadHelper.cs index d5fea9abd5..49c679e3dc 100644 --- a/Ryujinx.Graphics/QuadHelper.cs +++ b/Ryujinx.Graphics/QuadHelper.cs @@ -4,33 +4,33 @@ namespace Ryujinx.Graphics { static class QuadHelper { - public static int ConvertSizeQuadsToTris(int Size) + public static int ConvertSizeQuadsToTris(int size) { - return Size <= 0 ? 0 : (Size / 4) * 6; + return size <= 0 ? 0 : (size / 4) * 6; } - public static int ConvertSizeQuadStripToTris(int Size) + public static int ConvertSizeQuadStripToTris(int size) { - return Size <= 1 ? 0 : ((Size - 2) / 2) * 6; + return size <= 1 ? 0 : ((size - 2) / 2) * 6; } - public static byte[] ConvertQuadsToTris(byte[] Data, int EntrySize, int Count) + public static byte[] ConvertQuadsToTris(byte[] data, int entrySize, int count) { - int PrimitivesCount = Count / 4; + int primitivesCount = count / 4; - int QuadPrimSize = 4 * EntrySize; - int TrisPrimSize = 6 * EntrySize; + int quadPrimSize = 4 * entrySize; + int trisPrimSize = 6 * entrySize; - byte[] Output = new byte[PrimitivesCount * 6 * EntrySize]; + byte[] output = new byte[primitivesCount * 6 * entrySize]; - for (int Prim = 0; Prim < PrimitivesCount; Prim++) + for (int prim = 0; prim < primitivesCount; prim++) { - void AssignIndex(int Src, int Dst, int CopyCount = 1) + void AssignIndex(int src, int dst, int copyCount = 1) { - Src = Prim * QuadPrimSize + Src * EntrySize; - Dst = Prim * TrisPrimSize + Dst * EntrySize; + src = prim * quadPrimSize + src * entrySize; + dst = prim * trisPrimSize + dst * entrySize; - Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize); + Buffer.BlockCopy(data, src, output, dst, copyCount * entrySize); } //0 1 2 -> 0 1 2. @@ -43,26 +43,26 @@ namespace Ryujinx.Graphics AssignIndex(0, 5); } - return Output; + return output; } - public static byte[] ConvertQuadStripToTris(byte[] Data, int EntrySize, int Count) + public static byte[] ConvertQuadStripToTris(byte[] data, int entrySize, int count) { - int PrimitivesCount = (Count - 2) / 2; + int primitivesCount = (count - 2) / 2; - int QuadPrimSize = 2 * EntrySize; - int TrisPrimSize = 6 * EntrySize; + int quadPrimSize = 2 * entrySize; + int trisPrimSize = 6 * entrySize; - byte[] Output = new byte[PrimitivesCount * 6 * EntrySize]; + byte[] output = new byte[primitivesCount * 6 * entrySize]; - for (int Prim = 0; Prim < PrimitivesCount; Prim++) + for (int prim = 0; prim < primitivesCount; prim++) { - void AssignIndex(int Src, int Dst, int CopyCount = 1) + void AssignIndex(int src, int dst, int copyCount = 1) { - Src = Prim * QuadPrimSize + Src * EntrySize + 2 * EntrySize; - Dst = Prim * TrisPrimSize + Dst * EntrySize; + src = prim * quadPrimSize + src * entrySize + 2 * entrySize; + dst = prim * trisPrimSize + dst * entrySize; - Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize); + Buffer.BlockCopy(data, src, output, dst, copyCount * entrySize); } //-2 -1 0 -> 0 1 2. @@ -75,7 +75,7 @@ namespace Ryujinx.Graphics AssignIndex(-2, 5); } - return Output; + return output; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CBufferDescriptor.cs b/Ryujinx.Graphics/Shader/CBufferDescriptor.cs new file mode 100644 index 0000000000..f99665e162 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CBufferDescriptor.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Shader +{ + public struct CBufferDescriptor + { + public string Name { get; } + + public int Slot { get; } + + public CBufferDescriptor(string name, int slot) + { + Name = name; + Slot = slot; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/CodeGenContext.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/CodeGenContext.cs new file mode 100644 index 0000000000..ce5d7b949e --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/CodeGenContext.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; +using System.Text; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl +{ + class CodeGenContext + { + private const string Tab = " "; + + public ShaderConfig Config { get; } + + public List CBufferDescriptors { get; } + public List TextureDescriptors { get; } + + public OperandManager OperandManager { get; } + + private StringBuilder _sb; + + private int _level; + + private string _identation; + + public CodeGenContext(ShaderConfig config) + { + Config = config; + + CBufferDescriptors = new List(); + TextureDescriptors = new List(); + + OperandManager = new OperandManager(); + + _sb = new StringBuilder(); + } + + public void AppendLine() + { + _sb.AppendLine(); + } + + public void AppendLine(string str) + { + _sb.AppendLine(_identation + str); + } + + public string GetCode() + { + return _sb.ToString(); + } + + public void EnterScope() + { + AppendLine("{"); + + _level++; + + UpdateIdentation(); + } + + public void LeaveScope(string suffix = "") + { + if (_level == 0) + { + return; + } + + _level--; + + UpdateIdentation(); + + AppendLine("}" + suffix); + } + + private void UpdateIdentation() + { + _identation = GetIdentation(_level); + } + + private static string GetIdentation(int level) + { + string identation = string.Empty; + + for (int index = 0; index < level; index++) + { + identation += Tab; + } + + return identation; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Declarations.cs new file mode 100644 index 0000000000..5412d87281 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Declarations.cs @@ -0,0 +1,206 @@ +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl +{ + static class Declarations + { + public static void Declare(CodeGenContext context, StructuredProgramInfo info) + { + context.AppendLine("#version 420 core"); + + context.AppendLine(); + + context.AppendLine($"const int {DefaultNames.UndefinedName} = 0;"); + + context.AppendLine(); + + if (context.Config.Type == GalShaderType.Geometry) + { + context.AppendLine("layout (points) in;"); + context.AppendLine("layout (triangle_strip, max_vertices = 4) out;"); + + context.AppendLine(); + } + + context.AppendLine("layout (std140) uniform Extra"); + + context.EnterScope(); + + context.AppendLine("vec2 flip;"); + context.AppendLine("int instance;"); + + context.LeaveScope(";"); + + context.AppendLine(); + + if (info.CBuffers.Count != 0) + { + DeclareUniforms(context, info); + + context.AppendLine(); + } + + if (info.Samplers.Count != 0) + { + DeclareSamplers(context, info); + + context.AppendLine(); + } + + if (info.IAttributes.Count != 0) + { + DeclareInputAttributes(context, info); + + context.AppendLine(); + } + + if (info.OAttributes.Count != 0) + { + DeclareOutputAttributes(context, info); + + context.AppendLine(); + } + } + + public static void DeclareLocals(CodeGenContext context, StructuredProgramInfo info) + { + foreach (AstOperand decl in info.Locals) + { + string name = context.OperandManager.DeclareLocal(decl); + + context.AppendLine(GetVarTypeName(decl.VarType) + " " + name + ";"); + } + } + + private static string GetVarTypeName(VariableType type) + { + switch (type) + { + case VariableType.Bool: return "bool"; + case VariableType.F32: return "float"; + case VariableType.S32: return "int"; + case VariableType.U32: return "uint"; + } + + throw new ArgumentException($"Invalid variable type \"{type}\"."); + } + + private static void DeclareUniforms(CodeGenContext context, StructuredProgramInfo info) + { + foreach (int cbufSlot in info.CBuffers.OrderBy(x => x)) + { + string ubName = OperandManager.GetShaderStagePrefix(context.Config.Type); + + ubName += "_" + DefaultNames.UniformNamePrefix + cbufSlot; + + context.CBufferDescriptors.Add(new CBufferDescriptor(ubName, cbufSlot)); + + context.AppendLine("layout (std140) uniform " + ubName); + + context.EnterScope(); + + string ubSize = "[" + NumberFormatter.FormatInt(context.Config.MaxCBufferSize / 16) + "]"; + + context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Type, cbufSlot) + ubSize + ";"); + + context.LeaveScope(";"); + } + } + + private static void DeclareSamplers(CodeGenContext context, StructuredProgramInfo info) + { + Dictionary samplers = new Dictionary(); + + foreach (AstTextureOperation texOp in info.Samplers.OrderBy(x => x.Handle)) + { + string samplerName = OperandManager.GetSamplerName(context.Config.Type, texOp); + + if (!samplers.TryAdd(samplerName, texOp)) + { + continue; + } + + string samplerTypeName = GetSamplerTypeName(texOp.Type); + + context.AppendLine("uniform " + samplerTypeName + " " + samplerName + ";"); + } + + foreach (KeyValuePair kv in samplers) + { + string samplerName = kv.Key; + + AstTextureOperation texOp = kv.Value; + + TextureDescriptor desc; + + if ((texOp.Flags & TextureFlags.Bindless) != 0) + { + AstOperand operand = texOp.GetSource(0) as AstOperand; + + desc = new TextureDescriptor(samplerName, operand.CbufSlot, operand.CbufOffset); + } + else + { + desc = new TextureDescriptor(samplerName, texOp.Handle); + } + + context.TextureDescriptors.Add(desc); + } + } + + private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info) + { + string suffix = context.Config.Type == GalShaderType.Geometry ? "[]" : string.Empty; + + foreach (int attr in info.IAttributes.OrderBy(x => x)) + { + context.AppendLine($"layout (location = {attr}) in vec4 {DefaultNames.IAttributePrefix}{attr}{suffix};"); + } + } + + private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info) + { + foreach (int attr in info.OAttributes.OrderBy(x => x)) + { + context.AppendLine($"layout (location = {attr}) out vec4 {DefaultNames.OAttributePrefix}{attr};"); + } + } + + private static string GetSamplerTypeName(TextureType type) + { + string typeName; + + switch (type & TextureType.Mask) + { + case TextureType.Texture1D: typeName = "sampler1D"; break; + case TextureType.Texture2D: typeName = "sampler2D"; break; + case TextureType.Texture3D: typeName = "sampler3D"; break; + case TextureType.TextureCube: typeName = "samplerCube"; break; + + default: throw new ArgumentException($"Invalid sampler type \"{type}\"."); + } + + if ((type & TextureType.Multisample) != 0) + { + typeName += "MS"; + } + + if ((type & TextureType.Array) != 0) + { + typeName += "Array"; + } + + if ((type & TextureType.Shadow) != 0) + { + typeName += "Shadow"; + } + + return typeName; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/DefaultNames.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/DefaultNames.cs new file mode 100644 index 0000000000..1d3939fb79 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/DefaultNames.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl +{ + static class DefaultNames + { + public const string LocalNamePrefix = "temp"; + + public const string SamplerNamePrefix = "tex"; + + public const string IAttributePrefix = "in_attr"; + public const string OAttributePrefix = "out_attr"; + + public const string UniformNamePrefix = "c"; + public const string UniformNameSuffix = "data"; + + public const string UndefinedName = "undef"; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslGenerator.cs new file mode 100644 index 0000000000..4edbda8b9e --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslGenerator.cs @@ -0,0 +1,133 @@ +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using System; + +using static Ryujinx.Graphics.Shader.CodeGen.Glsl.TypeConversion; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl +{ + static class GlslGenerator + { + public static GlslProgram Generate(StructuredProgramInfo info, ShaderConfig config) + { + CodeGenContext context = new CodeGenContext(config); + + Declarations.Declare(context, info); + + PrintMainBlock(context, info); + + return new GlslProgram( + context.CBufferDescriptors.ToArray(), + context.TextureDescriptors.ToArray(), + context.GetCode()); + } + + private static void PrintMainBlock(CodeGenContext context, StructuredProgramInfo info) + { + context.AppendLine("void main()"); + + context.EnterScope(); + + Declarations.DeclareLocals(context, info); + + PrintBlock(context, info.MainBlock); + + context.LeaveScope(); + } + + private static void PrintBlock(CodeGenContext context, AstBlock block) + { + AstBlockVisitor visitor = new AstBlockVisitor(block); + + visitor.BlockEntered += (sender, e) => + { + switch (e.Block.Type) + { + case AstBlockType.DoWhile: + context.AppendLine("do"); + break; + + case AstBlockType.Else: + context.AppendLine("else"); + break; + + case AstBlockType.ElseIf: + context.AppendLine($"else if ({GetCondExpr(context, e.Block.Condition)})"); + break; + + case AstBlockType.If: + context.AppendLine($"if ({GetCondExpr(context, e.Block.Condition)})"); + break; + + default: throw new InvalidOperationException($"Found unexpected block type \"{e.Block.Type}\"."); + } + + context.EnterScope(); + }; + + visitor.BlockLeft += (sender, e) => + { + context.LeaveScope(); + + if (e.Block.Type == AstBlockType.DoWhile) + { + context.AppendLine($"while ({GetCondExpr(context, e.Block.Condition)});"); + } + }; + + foreach (IAstNode node in visitor.Visit()) + { + if (node is AstOperation operation) + { + if (operation.Inst == Instruction.Return) + { + PrepareForReturn(context); + } + + context.AppendLine(InstGen.GetExpression(context, operation) + ";"); + } + else if (node is AstAssignment assignment) + { + VariableType srcType = OperandManager.GetNodeDestType(assignment.Source); + VariableType dstType = OperandManager.GetNodeDestType(assignment.Destination); + + string dest; + + if (assignment.Destination is AstOperand operand && operand.Type == OperandType.Attribute) + { + dest = OperandManager.GetOutAttributeName(operand, context.Config.Type); + } + else + { + dest = InstGen.GetExpression(context, assignment.Destination); + } + + string src = ReinterpretCast(context, assignment.Source, srcType, dstType); + + context.AppendLine(dest + " = " + src + ";"); + } + else + { + throw new InvalidOperationException($"Found unexpected node type \"{node?.GetType().Name ?? "null"}\"."); + } + } + } + + private static string GetCondExpr(CodeGenContext context, IAstNode cond) + { + VariableType srcType = OperandManager.GetNodeDestType(cond); + + return ReinterpretCast(context, cond, srcType, VariableType.Bool); + } + + private static void PrepareForReturn(CodeGenContext context) + { + if (context.Config.Type == GalShaderType.Vertex) + { + context.AppendLine("gl_Position.xy *= flip;"); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslProgram.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslProgram.cs new file mode 100644 index 0000000000..e616aa1f81 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslProgram.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl +{ + class GlslProgram + { + public CBufferDescriptor[] CBufferDescriptors { get; } + public TextureDescriptor[] TextureDescriptors { get; } + + public string Code { get; } + + public GlslProgram( + CBufferDescriptor[] cBufferDescs, + TextureDescriptor[] textureDescs, + string code) + { + CBufferDescriptors = cBufferDescs; + TextureDescriptors = textureDescs; + Code = code; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGen.cs new file mode 100644 index 0000000000..b0b2ec1a99 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -0,0 +1,110 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using System; + +using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; +using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions +{ + static class InstGen + { + public static string GetExpression(CodeGenContext context, IAstNode node) + { + if (node is AstOperation operation) + { + return GetExpression(context, operation); + } + else if (node is AstOperand operand) + { + return context.OperandManager.GetExpression(operand, context.Config.Type); + } + + throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); + } + + private static string GetExpression(CodeGenContext context, AstOperation operation) + { + Instruction inst = operation.Inst; + + InstInfo info = GetInstructionInfo(inst); + + if ((info.Type & InstType.Call) != 0) + { + int arity = (int)(info.Type & InstType.ArityMask); + + string args = string.Empty; + + for (int argIndex = 0; argIndex < arity; argIndex++) + { + if (argIndex != 0) + { + args += ", "; + } + + VariableType dstType = GetSrcVarType(inst, argIndex); + + args += GetSoureExpr(context, operation.GetSource(argIndex), dstType); + } + + return info.OpName + "(" + args + ")"; + } + else if ((info.Type & InstType.Op) != 0) + { + string op = info.OpName; + + int arity = (int)(info.Type & InstType.ArityMask); + + string[] expr = new string[arity]; + + for (int index = 0; index < arity; index++) + { + IAstNode src = operation.GetSource(index); + + string srcExpr = GetSoureExpr(context, src, GetSrcVarType(inst, index)); + + bool isLhs = arity == 2 && index == 0; + + expr[index] = Enclose(srcExpr, src, inst, info, isLhs); + } + + switch (arity) + { + case 0: + return op; + + case 1: + return op + expr[0]; + + case 2: + return $"{expr[0]} {op} {expr[1]}"; + + case 3: + return $"{expr[0]} {op[0]} {expr[1]} {op[1]} {expr[2]}"; + } + } + else if ((info.Type & InstType.Special) != 0) + { + switch (inst) + { + case Instruction.LoadConstant: + return InstGenMemory.LoadConstant(context, operation); + + case Instruction.PackHalf2x16: + return InstGenPacking.PackHalf2x16(context, operation); + + case Instruction.TextureSample: + return InstGenMemory.TextureSample(context, operation); + + case Instruction.TextureSize: + return InstGenMemory.TextureSize(context, operation); + + case Instruction.UnpackHalf2x16: + return InstGenPacking.UnpackHalf2x16(context, operation); + } + } + + throw new InvalidOperationException($"Unexpected instruction type \"{info.Type}\"."); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs new file mode 100644 index 0000000000..0b86007293 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -0,0 +1,170 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; + +using static Ryujinx.Graphics.Shader.CodeGen.Glsl.TypeConversion; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions +{ + static class InstGenHelper + { + private static InstInfo[] _infoTbl; + + static InstGenHelper() + { + _infoTbl = new InstInfo[(int)Instruction.Count]; + + Add(Instruction.Absolute, InstType.CallUnary, "abs"); + Add(Instruction.Add, InstType.OpBinaryCom, "+", 2); + Add(Instruction.BitfieldExtractS32, InstType.CallTernary, "bitfieldExtract"); + Add(Instruction.BitfieldExtractU32, InstType.CallTernary, "bitfieldExtract"); + Add(Instruction.BitfieldInsert, InstType.CallQuaternary, "bitfieldInsert"); + Add(Instruction.BitfieldReverse, InstType.CallUnary, "bitfieldReverse"); + Add(Instruction.BitwiseAnd, InstType.OpBinaryCom, "&", 6); + Add(Instruction.BitwiseExclusiveOr, InstType.OpBinaryCom, "^", 7); + Add(Instruction.BitwiseNot, InstType.OpUnary, "~", 0); + Add(Instruction.BitwiseOr, InstType.OpBinaryCom, "|", 8); + Add(Instruction.Ceiling, InstType.CallUnary, "ceil"); + Add(Instruction.Clamp, InstType.CallTernary, "clamp"); + Add(Instruction.ClampU32, InstType.CallTernary, "clamp"); + Add(Instruction.CompareEqual, InstType.OpBinaryCom, "==", 5); + Add(Instruction.CompareGreater, InstType.OpBinary, ">", 4); + Add(Instruction.CompareGreaterOrEqual, InstType.OpBinary, ">=", 4); + Add(Instruction.CompareGreaterOrEqualU32, InstType.OpBinary, ">=", 4); + Add(Instruction.CompareGreaterU32, InstType.OpBinary, ">", 4); + Add(Instruction.CompareLess, InstType.OpBinary, "<", 4); + Add(Instruction.CompareLessOrEqual, InstType.OpBinary, "<=", 4); + Add(Instruction.CompareLessOrEqualU32, InstType.OpBinary, "<=", 4); + Add(Instruction.CompareLessU32, InstType.OpBinary, "<", 4); + Add(Instruction.CompareNotEqual, InstType.OpBinaryCom, "!=", 5); + Add(Instruction.ConditionalSelect, InstType.OpTernary, "?:", 12); + Add(Instruction.ConvertFPToS32, InstType.CallUnary, "int"); + Add(Instruction.ConvertS32ToFP, InstType.CallUnary, "float"); + Add(Instruction.ConvertU32ToFP, InstType.CallUnary, "float"); + Add(Instruction.Cosine, InstType.CallUnary, "cos"); + Add(Instruction.Discard, InstType.OpNullary, "discard"); + Add(Instruction.Divide, InstType.OpBinary, "/", 1); + Add(Instruction.EmitVertex, InstType.CallNullary, "EmitVertex"); + Add(Instruction.EndPrimitive, InstType.CallNullary, "EndPrimitive"); + Add(Instruction.ExponentB2, InstType.CallUnary, "exp2"); + Add(Instruction.Floor, InstType.CallUnary, "floor"); + Add(Instruction.FusedMultiplyAdd, InstType.CallTernary, "fma"); + Add(Instruction.IsNan, InstType.CallUnary, "isnan"); + Add(Instruction.LoadConstant, InstType.Special); + Add(Instruction.LogarithmB2, InstType.CallUnary, "log2"); + Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9); + Add(Instruction.LogicalExclusiveOr, InstType.OpBinaryCom, "^^", 10); + Add(Instruction.LogicalNot, InstType.OpUnary, "!", 0); + Add(Instruction.LogicalOr, InstType.OpBinaryCom, "||", 11); + Add(Instruction.LoopBreak, InstType.OpNullary, "break"); + Add(Instruction.LoopContinue, InstType.OpNullary, "continue"); + Add(Instruction.PackHalf2x16, InstType.Special); + Add(Instruction.ShiftLeft, InstType.OpBinary, "<<", 3); + Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>", 3); + Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>", 3); + Add(Instruction.Maximum, InstType.CallBinary, "max"); + Add(Instruction.MaximumU32, InstType.CallBinary, "max"); + Add(Instruction.Minimum, InstType.CallBinary, "min"); + Add(Instruction.MinimumU32, InstType.CallBinary, "min"); + Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1); + Add(Instruction.Negate, InstType.OpUnary, "-", 0); + Add(Instruction.ReciprocalSquareRoot, InstType.CallUnary, "inversesqrt"); + Add(Instruction.Return, InstType.OpNullary, "return"); + Add(Instruction.Sine, InstType.CallUnary, "sin"); + Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); + Add(Instruction.Subtract, InstType.OpBinary, "-", 2); + Add(Instruction.TextureSample, InstType.Special); + Add(Instruction.TextureSize, InstType.Special); + Add(Instruction.Truncate, InstType.CallUnary, "trunc"); + Add(Instruction.UnpackHalf2x16, InstType.Special); + } + + private static void Add(Instruction inst, InstType flags, string opName = null, int precedence = 0) + { + _infoTbl[(int)inst] = new InstInfo(flags, opName, precedence); + } + + public static InstInfo GetInstructionInfo(Instruction inst) + { + return _infoTbl[(int)(inst & Instruction.Mask)]; + } + + public static string GetSoureExpr(CodeGenContext context, IAstNode node, VariableType dstType) + { + return ReinterpretCast(context, node, OperandManager.GetNodeDestType(node), dstType); + } + + public static string Enclose(string expr, IAstNode node, Instruction pInst, bool isLhs) + { + InstInfo pInfo = GetInstructionInfo(pInst); + + return Enclose(expr, node, pInst, pInfo, isLhs); + } + + public static string Enclose(string expr, IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs = false) + { + if (NeedsParenthesis(node, pInst, pInfo, isLhs)) + { + expr = "(" + expr + ")"; + } + + return expr; + } + + public static bool NeedsParenthesis(IAstNode node, Instruction pInst, InstInfo pInfo, bool isLhs) + { + //If the node isn't a operation, then it can only be a operand, + //and those never needs to be surrounded in parenthesis. + if (!(node is AstOperation operation)) + { + //This is sort of a special case, if this is a negative constant, + //and it is consumed by a unary operation, we need to put on the parenthesis, + //as in GLSL a sequence like --2 or ~-1 is not valid. + if (IsNegativeConst(node) && pInfo.Type == InstType.OpUnary) + { + return true; + } + + return false; + } + + if ((pInfo.Type & (InstType.Call | InstType.Special)) != 0) + { + return false; + } + + InstInfo info = _infoTbl[(int)(operation.Inst & Instruction.Mask)]; + + if ((info.Type & (InstType.Call | InstType.Special)) != 0) + { + return false; + } + + if (info.Precedence < pInfo.Precedence) + { + return false; + } + + if (info.Precedence == pInfo.Precedence && isLhs) + { + return false; + } + + if (pInst == operation.Inst && info.Type == InstType.OpBinaryCom) + { + return false; + } + + return true; + } + + private static bool IsNegativeConst(IAstNode node) + { + if (!(node is AstOperand operand)) + { + return false; + } + + return operand.Type == OperandType.Constant && operand.Value < 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs new file mode 100644 index 0000000000..79f80057ff --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -0,0 +1,244 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; + +using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; +using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions +{ + static class InstGenMemory + { + public static string LoadConstant(CodeGenContext context, AstOperation operation) + { + IAstNode src1 = operation.GetSource(1); + + string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1)); + + offsetExpr = Enclose(offsetExpr, src1, Instruction.ShiftRightS32, isLhs: true); + + return OperandManager.GetConstantBufferName(operation.GetSource(0), offsetExpr, context.Config.Type); + } + + public static string TextureSample(CodeGenContext context, AstOperation operation) + { + AstTextureOperation texOp = (AstTextureOperation)operation; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0; + bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0; + bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; + bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; + bool isArray = (texOp.Type & TextureType.Array) != 0; + bool isMultisample = (texOp.Type & TextureType.Multisample) != 0; + bool isShadow = (texOp.Type & TextureType.Shadow) != 0; + + string texCall = intCoords ? "texelFetch" : "texture"; + + if (isGather) + { + texCall += "Gather"; + } + else if (hasLodLevel && !intCoords) + { + texCall += "Lod"; + } + + if (hasOffset) + { + texCall += "Offset"; + } + else if (hasOffsets) + { + texCall += "Offsets"; + } + + string samplerName = OperandManager.GetSamplerName(context.Config.Type, texOp); + + texCall += "(" + samplerName; + + int coordsCount = texOp.Type.GetCoordsCount(); + + int pCount = coordsCount; + + int arrayIndexElem = -1; + + if (isArray) + { + arrayIndexElem = pCount++; + } + + //The sampler 1D shadow overload expects a + //dummy value on the middle of the vector, who knows why... + bool hasDummy1DShadowElem = texOp.Type == (TextureType.Texture1D | TextureType.Shadow); + + if (hasDummy1DShadowElem) + { + pCount++; + } + + if (isShadow && !isGather) + { + pCount++; + } + + //On textureGather*, the comparison value is + //always specified as an extra argument. + bool hasExtraCompareArg = isShadow && isGather; + + if (pCount == 5) + { + pCount = 4; + + hasExtraCompareArg = true; + } + + int srcIndex = isBindless ? 1 : 0; + + string Src(VariableType type) + { + return GetSoureExpr(context, texOp.GetSource(srcIndex++), type); + } + + void Append(string str) + { + texCall += ", " + str; + } + + VariableType coordType = intCoords ? VariableType.S32 : VariableType.F32; + + string AssemblePVector(int count) + { + if (count > 1) + { + string[] elems = new string[count]; + + for (int index = 0; index < count; index++) + { + if (arrayIndexElem == index) + { + elems[index] = Src(VariableType.S32); + + if (!intCoords) + { + elems[index] = "float(" + elems[index] + ")"; + } + } + else if (index == 1 && hasDummy1DShadowElem) + { + elems[index] = NumberFormatter.FormatFloat(0); + } + else + { + elems[index] = Src(coordType); + } + } + + string prefix = intCoords ? "i" : string.Empty; + + return prefix + "vec" + count + "(" + string.Join(", ", elems) + ")"; + } + else + { + return Src(coordType); + } + } + + Append(AssemblePVector(pCount)); + + if (hasExtraCompareArg) + { + Append(Src(VariableType.F32)); + } + + if (isMultisample) + { + Append(Src(VariableType.S32)); + } + else if (hasLodLevel) + { + Append(Src(coordType)); + } + + string AssembleOffsetVector(int count) + { + if (count > 1) + { + string[] elems = new string[count]; + + for (int index = 0; index < count; index++) + { + elems[index] = Src(VariableType.S32); + } + + return "ivec" + count + "(" + string.Join(", ", elems) + ")"; + } + else + { + return Src(VariableType.S32); + } + } + + if (hasOffset) + { + Append(AssembleOffsetVector(coordsCount)); + } + else if (hasOffsets) + { + texCall += $", ivec{coordsCount}[4]("; + + texCall += AssembleOffsetVector(coordsCount) + ", "; + texCall += AssembleOffsetVector(coordsCount) + ", "; + texCall += AssembleOffsetVector(coordsCount) + ", "; + texCall += AssembleOffsetVector(coordsCount) + ")"; + } + + if (hasLodBias) + { + Append(Src(VariableType.F32)); + } + + //textureGather* optional extra component index, + //not needed for shadow samplers. + if (isGather && !isShadow) + { + Append(Src(VariableType.S32)); + } + + texCall += ")" + (isGather || !isShadow ? GetMask(texOp.ComponentMask) : ""); + + return texCall; + } + + public static string TextureSize(CodeGenContext context, AstOperation operation) + { + AstTextureOperation texOp = (AstTextureOperation)operation; + + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + + string samplerName = OperandManager.GetSamplerName(context.Config.Type, texOp); + + IAstNode src0 = operation.GetSource(isBindless ? 1 : 0); + + string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0)); + + return $"textureSize({samplerName}, {src0Expr}){GetMask(texOp.ComponentMask)}"; + } + + private static string GetMask(int compMask) + { + string mask = "."; + + for (int index = 0; index < 4; index++) + { + if ((compMask & (1 << index)) != 0) + { + mask += "rgba".Substring(index, 1); + } + } + + return mask; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs new file mode 100644 index 0000000000..4a40032c57 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs @@ -0,0 +1,45 @@ +using Ryujinx.Graphics.Shader.StructuredIr; + +using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; +using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions +{ + static class InstGenPacking + { + public static string PackHalf2x16(CodeGenContext context, AstOperation operation) + { + IAstNode src0 = operation.GetSource(0); + IAstNode src1 = operation.GetSource(1); + + string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0)); + string src1Expr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1)); + + return $"packHalf2x16(vec2({src0Expr}, {src1Expr}))"; + } + + public static string UnpackHalf2x16(CodeGenContext context, AstOperation operation) + { + IAstNode src = operation.GetSource(0); + + string srcExpr = GetSoureExpr(context, src, GetSrcVarType(operation.Inst, 0)); + + return $"unpackHalf2x16({srcExpr}){GetMask(operation.ComponentMask)}"; + } + + private static string GetMask(int compMask) + { + string mask = "."; + + for (int index = 0; index < 2; index++) + { + if ((compMask & (1 << index)) != 0) + { + mask += "xy".Substring(index, 1); + } + } + + return mask; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstInfo.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstInfo.cs new file mode 100644 index 0000000000..fc9aef7ec3 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstInfo.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions +{ + struct InstInfo + { + public InstType Type { get; } + + public string OpName { get; } + + public int Precedence { get; } + + public InstInfo(InstType type, string opName, int precedence) + { + Type = type; + OpName = opName; + Precedence = precedence; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstType.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstType.cs new file mode 100644 index 0000000000..7d38a9d269 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstType.cs @@ -0,0 +1,27 @@ +using System; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions +{ + [Flags] + enum InstType + { + OpNullary = Op | 0, + OpUnary = Op | 1, + OpBinary = Op | 2, + OpTernary = Op | 3, + OpBinaryCom = OpBinary | Comutative, + + CallNullary = Call | 0, + CallUnary = Call | 1, + CallBinary = Call | 2, + CallTernary = Call | 3, + CallQuaternary = Call | 4, + + Comutative = 1 << 8, + Op = 1 << 9, + Call = 1 << 10, + Special = 1 << 11, + + ArityMask = 0xff + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/NumberFormatter.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/NumberFormatter.cs new file mode 100644 index 0000000000..2ec44277c3 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/NumberFormatter.cs @@ -0,0 +1,104 @@ +using Ryujinx.Graphics.Shader.StructuredIr; +using System; +using System.Globalization; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl +{ + static class NumberFormatter + { + private const int MaxDecimal = 256; + + public static bool TryFormat(int value, VariableType dstType, out string formatted) + { + if (dstType == VariableType.F32) + { + return TryFormatFloat(BitConverter.Int32BitsToSingle(value), out formatted); + } + else if (dstType == VariableType.S32) + { + formatted = FormatInt(value); + } + else if (dstType == VariableType.U32) + { + formatted = FormatUint((uint)value); + } + else if (dstType == VariableType.Bool) + { + formatted = value != 0 ? "true" : "false"; + } + else + { + throw new ArgumentException($"Invalid variable type \"{dstType}\"."); + } + + return true; + } + + public static string FormatFloat(float value) + { + if (!TryFormatFloat(value, out string formatted)) + { + throw new ArgumentException("Failed to convert float value to string."); + } + + return formatted; + } + + public static bool TryFormatFloat(float value, out string formatted) + { + if (float.IsNaN(value) || float.IsInfinity(value)) + { + formatted = null; + + return false; + } + + formatted = value.ToString("G9", CultureInfo.InvariantCulture); + + if (!(formatted.Contains('.') || + formatted.Contains('e') || + formatted.Contains('E'))) + { + formatted += ".0"; + } + + return true; + } + + public static string FormatInt(int value, VariableType dstType) + { + if (dstType == VariableType.S32) + { + return FormatInt(value); + } + else if (dstType == VariableType.U32) + { + return FormatUint((uint)value); + } + else + { + throw new ArgumentException($"Invalid variable type \"{dstType}\"."); + } + } + + public static string FormatInt(int value) + { + if (value <= MaxDecimal && value >= -MaxDecimal) + { + return value.ToString(CultureInfo.InvariantCulture); + } + + return "0x" + value.ToString("X", CultureInfo.InvariantCulture); + } + + public static string FormatUint(uint value) + { + if (value <= MaxDecimal && value >= 0) + { + return value.ToString(CultureInfo.InvariantCulture) + "u"; + } + + return "0x" + value.ToString("X", CultureInfo.InvariantCulture) + "u"; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/OperandManager.cs new file mode 100644 index 0000000000..debba42843 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/OperandManager.cs @@ -0,0 +1,239 @@ +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using System; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl +{ + class OperandManager + { + private static string[] _stagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" }; + + private struct BuiltInAttribute + { + public string Name { get; } + + public VariableType Type { get; } + + public BuiltInAttribute(string name, VariableType type) + { + Name = name; + Type = type; + } + } + + private static Dictionary _builtInAttributes = + new Dictionary() + { + { AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) }, + { AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) }, + { AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) }, + { AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) }, + { AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) }, + { AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) }, + { AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) }, + { AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) }, + { AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) }, + { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) }, + { AttributeConsts.InstanceId, new BuiltInAttribute("instance", VariableType.S32) }, + { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) }, + { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, + { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) } + }; + + private Dictionary _locals; + + public OperandManager() + { + _locals = new Dictionary(); + } + + public string DeclareLocal(AstOperand operand) + { + string name = $"{DefaultNames.LocalNamePrefix}_{_locals.Count}"; + + _locals.Add(operand, name); + + return name; + } + + public string GetExpression(AstOperand operand, GalShaderType shaderType) + { + switch (operand.Type) + { + case OperandType.Attribute: + return GetAttributeName(operand, shaderType); + + case OperandType.Constant: + return NumberFormatter.FormatInt(operand.Value); + + case OperandType.ConstantBuffer: + return GetConstantBufferName(operand, shaderType); + + case OperandType.LocalVariable: + return _locals[operand]; + + case OperandType.Undefined: + return DefaultNames.UndefinedName; + } + + throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."); + } + + public static string GetConstantBufferName(AstOperand cbuf, GalShaderType shaderType) + { + string ubName = GetUbName(shaderType, cbuf.CbufSlot); + + ubName += "[" + (cbuf.CbufOffset >> 2) + "]"; + + return ubName + "." + GetSwizzleMask(cbuf.CbufOffset & 3); + } + + public static string GetConstantBufferName(IAstNode slot, string offsetExpr, GalShaderType shaderType) + { + //Non-constant slots are not supported. + //It is expected that upstream stages are never going to generate non-constant + //slot access. + AstOperand operand = (AstOperand)slot; + + string ubName = GetUbName(shaderType, operand.Value); + + string index0 = "[" + offsetExpr + " >> 4]"; + string index1 = "[" + offsetExpr + " >> 2 & 3]"; + + return ubName + index0 + index1; + } + + public static string GetOutAttributeName(AstOperand attr, GalShaderType shaderType) + { + return GetAttributeName(attr, shaderType, isOutAttr: true); + } + + private static string GetAttributeName(AstOperand attr, GalShaderType shaderType, bool isOutAttr = false) + { + int value = attr.Value; + + string swzMask = GetSwizzleMask((value >> 2) & 3); + + if (value >= AttributeConsts.UserAttributeBase && + value < AttributeConsts.UserAttributeEnd) + { + value -= AttributeConsts.UserAttributeBase; + + string prefix = isOutAttr + ? DefaultNames.OAttributePrefix + : DefaultNames.IAttributePrefix; + + string name = $"{prefix}{(value >> 4)}"; + + if (shaderType == GalShaderType.Geometry && !isOutAttr) + { + name += "[0]"; + } + + name += "." + swzMask; + + return name; + } + else + { + if (value >= AttributeConsts.FragmentOutputColorBase && + value < AttributeConsts.FragmentOutputColorEnd) + { + value -= AttributeConsts.FragmentOutputColorBase; + + return $"{DefaultNames.OAttributePrefix}{(value >> 4)}.{swzMask}"; + } + else if (_builtInAttributes.TryGetValue(value & ~3, out BuiltInAttribute builtInAttr)) + { + //TODO: There must be a better way to handle this... + if (shaderType == GalShaderType.Fragment) + { + switch (value & ~3) + { + case AttributeConsts.PositionX: return "gl_FragCoord.x"; + case AttributeConsts.PositionY: return "gl_FragCoord.y"; + case AttributeConsts.PositionZ: return "gl_FragCoord.z"; + case AttributeConsts.PositionW: return "1.0"; + } + } + + string name = builtInAttr.Name; + + if (shaderType == GalShaderType.Geometry && !isOutAttr) + { + name = "gl_in[0]." + name; + } + + return name; + } + } + + return DefaultNames.UndefinedName; + } + + public static string GetUbName(GalShaderType shaderType, int slot) + { + string ubName = OperandManager.GetShaderStagePrefix(shaderType); + + ubName += "_" + DefaultNames.UniformNamePrefix + slot; + + return ubName + "_" + DefaultNames.UniformNameSuffix; + } + + public static string GetSamplerName(GalShaderType shaderType, AstTextureOperation texOp) + { + string suffix; + + if ((texOp.Flags & TextureFlags.Bindless) != 0) + { + AstOperand operand = texOp.GetSource(0) as AstOperand; + + suffix = "_cb" + operand.CbufSlot + "_" + operand.CbufOffset; + } + else + { + suffix = (texOp.Handle - 8).ToString(); + } + + return GetShaderStagePrefix(shaderType) + "_" + DefaultNames.SamplerNamePrefix + suffix; + } + + public static string GetShaderStagePrefix(GalShaderType shaderType) + { + return _stagePrefixes[(int)shaderType]; + } + + private static string GetSwizzleMask(int value) + { + return "xyzw".Substring(value, 1); + } + + public static VariableType GetNodeDestType(IAstNode node) + { + if (node is AstOperation operation) + { + return GetDestVarType(operation.Inst); + } + else if (node is AstOperand operand) + { + if (operand.Type == OperandType.Attribute) + { + if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr)) + { + return builtInAttr.Type; + } + } + + return OperandInfo.GetVarType(operand); + } + else + { + throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/TypeConversion.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/TypeConversion.cs new file mode 100644 index 0000000000..7adc5ad339 --- /dev/null +++ b/Ryujinx.Graphics/Shader/CodeGen/Glsl/TypeConversion.cs @@ -0,0 +1,85 @@ +using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using System; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl +{ + static class TypeConversion + { + public static string ReinterpretCast( + CodeGenContext context, + IAstNode node, + VariableType srcType, + VariableType dstType) + { + if (node is AstOperand operand && operand.Type == OperandType.Constant) + { + if (NumberFormatter.TryFormat(operand.Value, dstType, out string formatted)) + { + return formatted; + } + } + + string expr = InstGen.GetExpression(context, node); + + return ReinterpretCast(expr, node, srcType, dstType); + } + + private static string ReinterpretCast(string expr, IAstNode node, VariableType srcType, VariableType dstType) + { + if (srcType == dstType) + { + return expr; + } + + if (srcType == VariableType.F32) + { + switch (dstType) + { + case VariableType.S32: return $"floatBitsToInt({expr})"; + case VariableType.U32: return $"floatBitsToUint({expr})"; + } + } + else if (dstType == VariableType.F32) + { + switch (srcType) + { + case VariableType.Bool: return $"intBitsToFloat({ReinterpretBoolToInt(expr, node, VariableType.S32)})"; + case VariableType.S32: return $"intBitsToFloat({expr})"; + case VariableType.U32: return $"uintBitsToFloat({expr})"; + } + } + else if (srcType == VariableType.Bool) + { + return ReinterpretBoolToInt(expr, node, dstType); + } + else if (dstType == VariableType.Bool) + { + expr = InstGenHelper.Enclose(expr, node, Instruction.CompareNotEqual, isLhs: true); + + return $"({expr} != 0)"; + } + else if (dstType == VariableType.S32) + { + return $"int({expr})"; + } + else if (dstType == VariableType.U32) + { + return $"uint({expr})"; + } + + throw new ArgumentException($"Invalid reinterpret cast from \"{srcType}\" to \"{dstType}\"."); + } + + private static string ReinterpretBoolToInt(string expr, IAstNode node, VariableType dstType) + { + string trueExpr = NumberFormatter.FormatInt(IrConsts.True, dstType); + string falseExpr = NumberFormatter.FormatInt(IrConsts.False, dstType); + + expr = InstGenHelper.Enclose(expr, node, Instruction.ConditionalSelect, isLhs: false); + + return $"({expr} ? {trueExpr} : {falseExpr})"; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/BitfieldExtensions.cs b/Ryujinx.Graphics/Shader/Decoders/BitfieldExtensions.cs new file mode 100644 index 0000000000..3bb9bc1f4c --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/BitfieldExtensions.cs @@ -0,0 +1,25 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + static class BitfieldExtensions + { + public static bool Extract(this int value, int lsb) + { + return ((int)(value >> lsb) & 1) != 0; + } + + public static int Extract(this int value, int lsb, int length) + { + return (int)(value >> lsb) & (int)(uint.MaxValue >> (32 - length)); + } + + public static bool Extract(this long value, int lsb) + { + return ((int)(value >> lsb) & 1) != 0; + } + + public static int Extract(this long value, int lsb, int length) + { + return (int)(value >> lsb) & (int)(uint.MaxValue >> (32 - length)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/Block.cs b/Ryujinx.Graphics/Shader/Decoders/Block.cs new file mode 100644 index 0000000000..b5e610d713 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/Block.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class Block + { + public ulong Address { get; set; } + public ulong EndAddress { get; set; } + + public Block Next { get; set; } + public Block Branch { get; set; } + + public List OpCodes { get; } + public List SsyOpCodes { get; } + + public Block(ulong address) + { + Address = address; + + OpCodes = new List(); + SsyOpCodes = new List(); + } + + public void Split(Block rightBlock) + { + int splitIndex = BinarySearch(OpCodes, rightBlock.Address); + + if (OpCodes[splitIndex].Address < rightBlock.Address) + { + splitIndex++; + } + + int splitCount = OpCodes.Count - splitIndex; + + if (splitCount <= 0) + { + throw new ArgumentException("Can't split at right block address."); + } + + rightBlock.EndAddress = EndAddress; + + rightBlock.Next = Next; + rightBlock.Branch = Branch; + + rightBlock.OpCodes.AddRange(OpCodes.GetRange(splitIndex, splitCount)); + + rightBlock.UpdateSsyOpCodes(); + + EndAddress = rightBlock.Address; + + Next = rightBlock; + Branch = null; + + OpCodes.RemoveRange(splitIndex, splitCount); + + UpdateSsyOpCodes(); + } + + private static int BinarySearch(List opCodes, ulong address) + { + int left = 0; + int middle = 0; + int right = opCodes.Count - 1; + + while (left <= right) + { + int size = right - left; + + middle = left + (size >> 1); + + OpCode opCode = opCodes[middle]; + + if (address == opCode.Address) + { + break; + } + + if (address < opCode.Address) + { + right = middle - 1; + } + else + { + left = middle + 1; + } + } + + return middle; + } + + public OpCode GetLastOp() + { + if (OpCodes.Count != 0) + { + return OpCodes[OpCodes.Count - 1]; + } + + return null; + } + + public void UpdateSsyOpCodes() + { + SsyOpCodes.Clear(); + + for (int index = 0; index < OpCodes.Count; index++) + { + if (!(OpCodes[index] is OpCodeSsy op)) + { + continue; + } + + SsyOpCodes.Add(op); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/Condition.cs b/Ryujinx.Graphics/Shader/Decoders/Condition.cs new file mode 100644 index 0000000000..10400f94ac --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/Condition.cs @@ -0,0 +1,45 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum Condition + { + Less = 1 << 0, + Equal = 1 << 1, + Greater = 1 << 2, + Nan = 1 << 3, + Unsigned = 1 << 4, + + Never = 0, + + LessOrEqual = Less | Equal, + NotEqual = Less | Greater, + GreaterOrEqual = Greater | Equal, + Number = Greater | Equal | Less, + + LessUnordered = Less | Nan, + EqualUnordered = Equal | Nan, + LessOrEqualUnordered = LessOrEqual | Nan, + GreaterUnordered = Greater | Nan, + NotEqualUnordered = NotEqual | Nan, + GreaterOrEqualUnordered = GreaterOrEqual | Nan, + + Always = 0xf, + + Off = Unsigned | Never, + Lower = Unsigned | Less, + Sff = Unsigned | Equal, + LowerOrSame = Unsigned | LessOrEqual, + Higher = Unsigned | Greater, + Sft = Unsigned | NotEqual, + HigherOrSame = Unsigned | GreaterOrEqual, + Oft = Unsigned | Always, + + CsmTa = 0x18, + CsmTr = 0x19, + CsmMx = 0x1a, + FcsmTa = 0x1b, + FcsmTr = 0x1c, + FcsmMx = 0x1d, + Rle = 0x1e, + Rgt = 0x1f + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/ConditionalOperation.cs b/Ryujinx.Graphics/Shader/Decoders/ConditionalOperation.cs new file mode 100644 index 0000000000..4fc31e842b --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/ConditionalOperation.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum ConditionalOperation + { + False = 0, + True = 1, + Zero = 2, + NotZero = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/Decoder.cs b/Ryujinx.Graphics/Shader/Decoders/Decoder.cs new file mode 100644 index 0000000000..86df3b203d --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/Decoder.cs @@ -0,0 +1,406 @@ +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Shader.Instructions; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + static class Decoder + { + private const long HeaderSize = 0x50; + + private delegate object OpActivator(InstEmitter emitter, ulong address, long opCode); + + private static ConcurrentDictionary _opActivators; + + static Decoder() + { + _opActivators = new ConcurrentDictionary(); + } + + public static Block[] Decode(IGalMemory memory, ulong address) + { + List blocks = new List(); + + Queue workQueue = new Queue(); + + Dictionary visited = new Dictionary(); + + Block GetBlock(ulong blkAddress) + { + if (!visited.TryGetValue(blkAddress, out Block block)) + { + block = new Block(blkAddress); + + workQueue.Enqueue(block); + + visited.Add(blkAddress, block); + } + + return block; + } + + ulong startAddress = address + HeaderSize; + + GetBlock(startAddress); + + while (workQueue.TryDequeue(out Block currBlock)) + { + //Check if the current block is inside another block. + if (BinarySearch(blocks, currBlock.Address, out int nBlkIndex)) + { + Block nBlock = blocks[nBlkIndex]; + + if (nBlock.Address == currBlock.Address) + { + throw new InvalidOperationException("Found duplicate block address on the list."); + } + + nBlock.Split(currBlock); + + blocks.Insert(nBlkIndex + 1, currBlock); + + continue; + } + + //If we have a block after the current one, set the limit address. + ulong limitAddress = ulong.MaxValue; + + if (nBlkIndex != blocks.Count) + { + Block nBlock = blocks[nBlkIndex]; + + int nextIndex = nBlkIndex + 1; + + if (nBlock.Address < currBlock.Address && nextIndex < blocks.Count) + { + limitAddress = blocks[nextIndex].Address; + } + else if (nBlock.Address > currBlock.Address) + { + limitAddress = blocks[nBlkIndex].Address; + } + } + + FillBlock(memory, currBlock, limitAddress, startAddress); + + if (currBlock.OpCodes.Count != 0) + { + foreach (OpCodeSsy ssyOp in currBlock.SsyOpCodes) + { + GetBlock(ssyOp.GetAbsoluteAddress()); + } + + //Set child blocks. "Branch" is the block the branch instruction + //points to (when taken), "Next" is the block at the next address, + //executed when the branch is not taken. For Unconditional Branches + //or end of program, Next is null. + OpCode lastOp = currBlock.GetLastOp(); + + if (lastOp is OpCodeBranch op) + { + currBlock.Branch = GetBlock(op.GetAbsoluteAddress()); + } + + if (!IsUnconditionalBranch(lastOp)) + { + currBlock.Next = GetBlock(currBlock.EndAddress); + } + } + + //Insert the new block on the list (sorted by address). + if (blocks.Count != 0) + { + Block nBlock = blocks[nBlkIndex]; + + blocks.Insert(nBlkIndex + (nBlock.Address < currBlock.Address ? 1 : 0), currBlock); + } + else + { + blocks.Add(currBlock); + } + } + + foreach (Block ssyBlock in blocks.Where(x => x.SsyOpCodes.Count != 0)) + { + for (int ssyIndex = 0; ssyIndex < ssyBlock.SsyOpCodes.Count; ssyIndex++) + { + PropagateSsy(visited, ssyBlock, ssyIndex); + } + } + + return blocks.ToArray(); + } + + private static bool BinarySearch(List blocks, ulong address, out int index) + { + index = 0; + + int left = 0; + int right = blocks.Count - 1; + + while (left <= right) + { + int size = right - left; + + int middle = left + (size >> 1); + + Block block = blocks[middle]; + + index = middle; + + if (address >= block.Address && address < block.EndAddress) + { + return true; + } + + if (address < block.Address) + { + right = middle - 1; + } + else + { + left = middle + 1; + } + } + + return false; + } + + private static void FillBlock( + IGalMemory memory, + Block block, + ulong limitAddress, + ulong startAddress) + { + ulong address = block.Address; + + do + { + if (address >= limitAddress) + { + break; + } + + //Ignore scheduling instructions, which are written every 32 bytes. + if (((address - startAddress) & 0x1f) == 0) + { + address += 8; + + continue; + } + + uint word0 = (uint)memory.ReadInt32((long)(address + 0)); + uint word1 = (uint)memory.ReadInt32((long)(address + 4)); + + ulong opAddress = address; + + address += 8; + + long opCode = word0 | (long)word1 << 32; + + (InstEmitter emitter, Type opCodeType) = OpCodeTable.GetEmitter(opCode); + + if (emitter == null) + { + //TODO: Warning, illegal encoding. + continue; + } + + OpCode op = MakeOpCode(opCodeType, emitter, opAddress, opCode); + + block.OpCodes.Add(op); + } + while (!IsBranch(block.GetLastOp())); + + block.EndAddress = address; + + block.UpdateSsyOpCodes(); + } + + private static bool IsUnconditionalBranch(OpCode opCode) + { + return IsUnconditional(opCode) && IsBranch(opCode); + } + + private static bool IsUnconditional(OpCode opCode) + { + if (opCode is OpCodeExit op && op.Condition != Condition.Always) + { + return false; + } + + return opCode.Predicate.Index == RegisterConsts.PredicateTrueIndex && !opCode.InvertPredicate; + } + + private static bool IsBranch(OpCode opCode) + { + return (opCode is OpCodeBranch && opCode.Emitter != InstEmit.Ssy) || + opCode is OpCodeSync || + opCode is OpCodeExit; + } + + private static OpCode MakeOpCode(Type type, InstEmitter emitter, ulong address, long opCode) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + OpActivator createInstance = _opActivators.GetOrAdd(type, CacheOpActivator); + + return (OpCode)createInstance(emitter, address, opCode); + } + + private static OpActivator CacheOpActivator(Type type) + { + Type[] argTypes = new Type[] { typeof(InstEmitter), typeof(ulong), typeof(long) }; + + DynamicMethod mthd = new DynamicMethod($"Make{type.Name}", type, argTypes); + + ILGenerator generator = mthd.GetILGenerator(); + + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldarg_2); + generator.Emit(OpCodes.Newobj, type.GetConstructor(argTypes)); + generator.Emit(OpCodes.Ret); + + return (OpActivator)mthd.CreateDelegate(typeof(OpActivator)); + } + + private struct PathBlockState + { + public Block Block { get; } + + private enum RestoreType + { + None, + PopSsy, + PushSync + } + + private RestoreType _restoreType; + + private ulong _restoreValue; + + public bool ReturningFromVisit => _restoreType != RestoreType.None; + + public PathBlockState(Block block) + { + Block = block; + _restoreType = RestoreType.None; + _restoreValue = 0; + } + + public PathBlockState(int oldSsyStackSize) + { + Block = null; + _restoreType = RestoreType.PopSsy; + _restoreValue = (ulong)oldSsyStackSize; + } + + public PathBlockState(ulong syncAddress) + { + Block = null; + _restoreType = RestoreType.PushSync; + _restoreValue = syncAddress; + } + + public void RestoreStackState(Stack ssyStack) + { + if (_restoreType == RestoreType.PushSync) + { + ssyStack.Push(_restoreValue); + } + else if (_restoreType == RestoreType.PopSsy) + { + while (ssyStack.Count > (uint)_restoreValue) + { + ssyStack.Pop(); + } + } + } + } + + private static void PropagateSsy(Dictionary blocks, Block ssyBlock, int ssyIndex) + { + OpCodeSsy ssyOp = ssyBlock.SsyOpCodes[ssyIndex]; + + Stack workQueue = new Stack(); + + HashSet visited = new HashSet(); + + Stack ssyStack = new Stack(); + + void Push(PathBlockState pbs) + { + if (pbs.Block == null || visited.Add(pbs.Block)) + { + workQueue.Push(pbs); + } + } + + Push(new PathBlockState(ssyBlock)); + + while (workQueue.TryPop(out PathBlockState pbs)) + { + if (pbs.ReturningFromVisit) + { + pbs.RestoreStackState(ssyStack); + + continue; + } + + Block current = pbs.Block; + + int ssyOpCodesCount = current.SsyOpCodes.Count; + + if (ssyOpCodesCount != 0) + { + Push(new PathBlockState(ssyStack.Count)); + + for (int index = ssyIndex; index < ssyOpCodesCount; index++) + { + ssyStack.Push(current.SsyOpCodes[index].GetAbsoluteAddress()); + } + } + + ssyIndex = 0; + + if (current.Next != null) + { + Push(new PathBlockState(current.Next)); + } + + if (current.Branch != null) + { + Push(new PathBlockState(current.Branch)); + } + else if (current.GetLastOp() is OpCodeSync op) + { + ulong syncAddress = ssyStack.Pop(); + + if (ssyStack.Count == 0) + { + ssyStack.Push(syncAddress); + + op.Targets.Add(ssyOp, op.Targets.Count); + + ssyOp.Syncs.TryAdd(op, Local()); + } + else + { + Push(new PathBlockState(syncAddress)); + Push(new PathBlockState(blocks[syncAddress])); + } + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/DecoderHelper.cs b/Ryujinx.Graphics/Shader/Decoders/DecoderHelper.cs new file mode 100644 index 0000000000..fd0a45e82b --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/DecoderHelper.cs @@ -0,0 +1,58 @@ +using System; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + static class DecoderHelper + { + public static int DecodeS20Immediate(long opCode) + { + int imm = opCode.Extract(20, 19); + + bool negate = opCode.Extract(56); + + if (negate) + { + imm = -imm; + } + + return imm; + } + + public static int Decode2xF10Immediate(long opCode) + { + int immH0 = opCode.Extract(20, 9); + int immH1 = opCode.Extract(30, 9); + + bool negateH0 = opCode.Extract(29); + bool negateH1 = opCode.Extract(56); + + if (negateH0) + { + immH0 |= 1 << 9; + } + + if (negateH1) + { + immH1 |= 1 << 9; + } + + return immH1 << 22 | immH0 << 6; + } + + public static float DecodeF20Immediate(long opCode) + { + int imm = opCode.Extract(20, 19); + + bool negate = opCode.Extract(56); + + imm <<= 12; + + if (negate) + { + imm |= 1 << 31; + } + + return BitConverter.Int32BitsToSingle(imm); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/FPHalfSwizzle.cs b/Ryujinx.Graphics/Shader/Decoders/FPHalfSwizzle.cs new file mode 100644 index 0000000000..3ddf17cfcc --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/FPHalfSwizzle.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum FPHalfSwizzle + { + FP16 = 0, + FP32 = 1, + DupH0 = 2, + DupH1 = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/FPType.cs b/Ryujinx.Graphics/Shader/Decoders/FPType.cs new file mode 100644 index 0000000000..e602ad45fa --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/FPType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum FPType + { + FP16 = 1, + FP32 = 2, + FP64 = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/FmulScale.cs b/Ryujinx.Graphics/Shader/Decoders/FmulScale.cs new file mode 100644 index 0000000000..c35c6e489e --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/FmulScale.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum FmulScale + { + None = 0, + Divide2 = 1, + Divide4 = 2, + Divide8 = 3, + Multiply8 = 4, + Multiply4 = 5, + Multiply2 = 6 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCode.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCode.cs new file mode 100644 index 0000000000..dd6ad79a2e --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCode.cs @@ -0,0 +1,16 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCode + { + InstEmitter Emitter { get; } + + ulong Address { get; } + long RawOpCode { get; } + + Register Predicate { get; } + + bool InvertPredicate { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeAlu.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeAlu.cs new file mode 100644 index 0000000000..d840d49d1b --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeAlu.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeAlu : IOpCodeRd, IOpCodeRa + { + Register Predicate39 { get; } + + bool InvertP { get; } + bool Extended { get; } + bool SetCondCode { get; } + bool Saturate { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeCbuf.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeCbuf.cs new file mode 100644 index 0000000000..42a174514c --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeCbuf.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeCbuf : IOpCode + { + int Offset { get; } + int Slot { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeFArith.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeFArith.cs new file mode 100644 index 0000000000..d68ccf593b --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeFArith.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeFArith : IOpCodeAlu + { + RoundingMode RoundingMode { get; } + + FmulScale Scale { get; } + + bool FlushToZero { get; } + bool AbsoluteA { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeHfma.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeHfma.cs new file mode 100644 index 0000000000..4638f66086 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeHfma.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeHfma : IOpCode + { + bool NegateB { get; } + bool NegateC { get; } + bool Saturate { get; } + + FPHalfSwizzle SwizzleA { get; } + FPHalfSwizzle SwizzleB { get; } + FPHalfSwizzle SwizzleC { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeImm.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeImm.cs new file mode 100644 index 0000000000..9cfcd69b05 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeImm.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeImm : IOpCode + { + int Immediate { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeImmF.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeImmF.cs new file mode 100644 index 0000000000..629eff7973 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeImmF.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeImmF : IOpCode + { + float Immediate { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeLop.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeLop.cs new file mode 100644 index 0000000000..62c87bf435 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeLop.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeLop : IOpCodeAlu + { + LogicalOperation LogicalOp { get; } + + bool InvertA { get; } + bool InvertB { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeRa.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeRa.cs new file mode 100644 index 0000000000..e5902110e5 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeRa.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeRa : IOpCode + { + Register Ra { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeRc.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeRc.cs new file mode 100644 index 0000000000..bb806b95c9 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeRc.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeRc : IOpCode + { + Register Rc { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeRd.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeRd.cs new file mode 100644 index 0000000000..099c4061ab --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeRd.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeRd : IOpCode + { + Register Rd { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeReg.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeReg.cs new file mode 100644 index 0000000000..3ed157e821 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeReg.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeReg : IOpCode + { + Register Rb { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeRegCbuf.cs b/Ryujinx.Graphics/Shader/Decoders/IOpCodeRegCbuf.cs new file mode 100644 index 0000000000..429f01bb7f --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IOpCodeRegCbuf.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + interface IOpCodeRegCbuf : IOpCodeRc + { + int Offset { get; } + int Slot { get; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IntegerCondition.cs b/Ryujinx.Graphics/Shader/Decoders/IntegerCondition.cs new file mode 100644 index 0000000000..a1937c2f52 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IntegerCondition.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum IntegerCondition + { + Less = 1 << 0, + Equal = 1 << 1, + Greater = 1 << 2, + + Never = 0, + + LessOrEqual = Less | Equal, + NotEqual = Less | Greater, + GreaterOrEqual = Greater | Equal, + Number = Greater | Equal | Less, + + Always = 7 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IntegerHalfPart.cs b/Ryujinx.Graphics/Shader/Decoders/IntegerHalfPart.cs new file mode 100644 index 0000000000..b779f44d4b --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IntegerHalfPart.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum IntegerHalfPart + { + B32 = 0, + H0 = 1, + H1 = 2 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IntegerShift.cs b/Ryujinx.Graphics/Shader/Decoders/IntegerShift.cs new file mode 100644 index 0000000000..ce4d9f3bb6 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IntegerShift.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum IntegerShift + { + NoShift = 0, + ShiftRight = 1, + ShiftLeft = 2 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IntegerSize.cs b/Ryujinx.Graphics/Shader/Decoders/IntegerSize.cs new file mode 100644 index 0000000000..70fdfc3dc9 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IntegerSize.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum IntegerSize + { + U8 = 0, + S8 = 1, + U16 = 2, + S16 = 3, + B32 = 4, + B64 = 5 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IntegerType.cs b/Ryujinx.Graphics/Shader/Decoders/IntegerType.cs new file mode 100644 index 0000000000..46734dbe4a --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/IntegerType.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum IntegerType + { + U8 = 0, + U16 = 1, + U32 = 2, + U64 = 3, + S8 = 4, + S16 = 5, + S32 = 6, + S64 = 7 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/LogicalOperation.cs b/Ryujinx.Graphics/Shader/Decoders/LogicalOperation.cs new file mode 100644 index 0000000000..5221442510 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/LogicalOperation.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum LogicalOperation + { + And = 0, + Or = 1, + ExclusiveOr = 2, + Passthrough = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/MufuOperation.cs b/Ryujinx.Graphics/Shader/Decoders/MufuOperation.cs new file mode 100644 index 0000000000..88bd1f5cea --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/MufuOperation.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum MufuOperation + { + Cosine = 0, + Sine = 1, + ExponentB2 = 2, + LogarithmB2 = 3, + Reciprocal = 4, + ReciprocalSquareRoot = 5, + Reciprocal64H = 6, + ReciprocalSquareRoot64H = 7, + SquareRoot = 8 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCode.cs b/Ryujinx.Graphics/Shader/Decoders/OpCode.cs new file mode 100644 index 0000000000..b0f2ffdc32 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCode.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCode + { + public InstEmitter Emitter { get; } + + public ulong Address { get; } + public long RawOpCode { get; } + + public Register Predicate { get; protected set; } + + public bool InvertPredicate { get; protected set; } + + //When inverted, the always true predicate == always false. + public bool NeverExecute => Predicate.Index == RegisterConsts.PredicateTrueIndex && InvertPredicate; + + public OpCode(InstEmitter emitter, ulong address, long opCode) + { + Emitter = emitter; + Address = address; + RawOpCode = opCode; + + Predicate = new Register(opCode.Extract(16, 3), RegisterType.Predicate); + + InvertPredicate = opCode.Extract(19); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAlu.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeAlu.cs new file mode 100644 index 0000000000..15fbb9af79 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeAlu.cs @@ -0,0 +1,34 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeAlu : OpCode, IOpCodeAlu, IOpCodeRc + { + public Register Rd { get; } + public Register Ra { get; } + public Register Rc { get; } + public Register Predicate39 { get; } + + public int ByteSelection { get; } + + public bool InvertP { get; } + public bool Extended { get; protected set; } + public bool SetCondCode { get; protected set; } + public bool Saturate { get; protected set; } + + public OpCodeAlu(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); + Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); + Rc = new Register(opCode.Extract(39, 8), RegisterType.Gpr); + Predicate39 = new Register(opCode.Extract(39, 3), RegisterType.Predicate); + + ByteSelection = opCode.Extract(41, 2); + + InvertP = opCode.Extract(42); + Extended = opCode.Extract(43); + SetCondCode = opCode.Extract(47); + Saturate = opCode.Extract(50); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluCbuf.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluCbuf.cs new file mode 100644 index 0000000000..9c12798947 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluCbuf.cs @@ -0,0 +1,16 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeAluCbuf : OpCodeAlu, IOpCodeCbuf + { + public int Offset { get; } + public int Slot { get; } + + public OpCodeAluCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Offset = opCode.Extract(20, 14); + Slot = opCode.Extract(34, 5); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm.cs new file mode 100644 index 0000000000..a407fc6bf8 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeAluImm : OpCodeAlu, IOpCodeImm + { + public int Immediate { get; } + + public OpCodeAluImm(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Immediate = DecoderHelper.DecodeS20Immediate(opCode); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm2x10.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm2x10.cs new file mode 100644 index 0000000000..9aeb32bd4d --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm2x10.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeAluImm2x10 : OpCodeAlu, IOpCodeImm + { + public int Immediate { get; } + + public OpCodeAluImm2x10(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Immediate = DecoderHelper.Decode2xF10Immediate(opCode); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm32.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm32.cs new file mode 100644 index 0000000000..5941e0b9a2 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm32.cs @@ -0,0 +1,18 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeAluImm32 : OpCodeAlu, IOpCodeImm + { + public int Immediate { get; } + + public OpCodeAluImm32(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Immediate = opCode.Extract(20, 32); + + SetCondCode = opCode.Extract(52); + Extended = opCode.Extract(53); + Saturate = opCode.Extract(54); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluReg.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluReg.cs new file mode 100644 index 0000000000..13b96a3ae8 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluReg.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeAluReg : OpCodeAlu, IOpCodeReg + { + public Register Rb { get; protected set; } + + public OpCodeAluReg(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluRegCbuf.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluRegCbuf.cs new file mode 100644 index 0000000000..6cf6bd2e22 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeAluRegCbuf.cs @@ -0,0 +1,18 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeAluRegCbuf : OpCodeAluReg, IOpCodeRegCbuf + { + public int Offset { get; } + public int Slot { get; } + + public OpCodeAluRegCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Offset = opCode.Extract(20, 14); + Slot = opCode.Extract(34, 5); + + Rb = new Register(opCode.Extract(39, 8), RegisterType.Gpr); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAttribute.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeAttribute.cs new file mode 100644 index 0000000000..fd8e63fcfb --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeAttribute.cs @@ -0,0 +1,16 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeAttribute : OpCodeAluReg + { + public int AttributeOffset { get; } + public int Count { get; } + + public OpCodeAttribute(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + AttributeOffset = opCode.Extract(20, 10); + Count = opCode.Extract(47, 2) + 1; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeBranch.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeBranch.cs new file mode 100644 index 0000000000..25941b3967 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeBranch.cs @@ -0,0 +1,19 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeBranch : OpCode + { + public int Offset { get; } + + public OpCodeBranch(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Offset = ((int)(opCode >> 20) << 8) >> 8; + } + + public ulong GetAbsoluteAddress() + { + return (ulong)((long)Address + (long)Offset + 8); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeExit.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeExit.cs new file mode 100644 index 0000000000..d50903eb4f --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeExit.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeExit : OpCode + { + public Condition Condition { get; } + + public OpCodeExit(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Condition = (Condition)opCode.Extract(0, 5); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArith.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArith.cs new file mode 100644 index 0000000000..c88f7f0ee3 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArith.cs @@ -0,0 +1,24 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeFArith : OpCodeAlu, IOpCodeFArith + { + public RoundingMode RoundingMode { get; } + + public FmulScale Scale { get; } + + public bool FlushToZero { get; } + public bool AbsoluteA { get; } + + public OpCodeFArith(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + RoundingMode = (RoundingMode)opCode.Extract(39, 2); + + Scale = (FmulScale)opCode.Extract(41, 3); + + FlushToZero = opCode.Extract(44); + AbsoluteA = opCode.Extract(46); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithCbuf.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithCbuf.cs new file mode 100644 index 0000000000..5486bb0b07 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithCbuf.cs @@ -0,0 +1,16 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeFArithCbuf : OpCodeFArith, IOpCodeCbuf + { + public int Offset { get; } + public int Slot { get; } + + public OpCodeFArithCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Offset = opCode.Extract(20, 14); + Slot = opCode.Extract(34, 5); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithImm.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithImm.cs new file mode 100644 index 0000000000..1bb6f42552 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithImm.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeFArithImm : OpCodeFArith, IOpCodeImmF + { + public float Immediate { get; } + + public OpCodeFArithImm(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Immediate = DecoderHelper.DecodeF20Immediate(opCode); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithImm32.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithImm32.cs new file mode 100644 index 0000000000..ec9da6f302 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithImm32.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.Shader.Instructions; +using System; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeFArithImm32 : OpCodeAlu, IOpCodeFArith, IOpCodeImmF + { + public RoundingMode RoundingMode => RoundingMode.ToNearest; + + public FmulScale Scale => FmulScale.None; + + public bool FlushToZero { get; } + public bool AbsoluteA { get; } + + public float Immediate { get; } + + public OpCodeFArithImm32(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + int imm = opCode.Extract(20, 32); + + Immediate = BitConverter.Int32BitsToSingle(imm); + + SetCondCode = opCode.Extract(52); + AbsoluteA = opCode.Extract(54); + FlushToZero = opCode.Extract(55); + + Saturate = false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithReg.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithReg.cs new file mode 100644 index 0000000000..55cf448597 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithReg.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeFArithReg : OpCodeFArith, IOpCodeReg + { + public Register Rb { get; protected set; } + + public OpCodeFArithReg(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithRegCbuf.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithRegCbuf.cs new file mode 100644 index 0000000000..315c2c8b15 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithRegCbuf.cs @@ -0,0 +1,16 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeFArithRegCbuf : OpCodeFArith, IOpCodeRegCbuf + { + public int Offset { get; } + public int Slot { get; } + + public OpCodeFArithRegCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Offset = opCode.Extract(20, 14); + Slot = opCode.Extract(34, 5); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFsetImm.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeFsetImm.cs new file mode 100644 index 0000000000..cb5f155e82 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeFsetImm.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeFsetImm : OpCodeSet, IOpCodeImmF + { + public float Immediate { get; } + + public OpCodeFsetImm(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Immediate = DecoderHelper.DecodeF20Immediate(opCode); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfma.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfma.cs new file mode 100644 index 0000000000..32f3cd7ab8 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfma.cs @@ -0,0 +1,22 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeHfma : OpCode, IOpCodeRd, IOpCodeRa, IOpCodeRc + { + public Register Rd { get; } + public Register Ra { get; } + public Register Rc { get; protected set; } + + public FPHalfSwizzle SwizzleA { get; } + + public OpCodeHfma(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); + Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); + Rc = new Register(opCode.Extract(39, 8), RegisterType.Gpr); + + SwizzleA = (FPHalfSwizzle)opCode.Extract(47, 2); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaCbuf.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaCbuf.cs new file mode 100644 index 0000000000..33768c7d01 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaCbuf.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeHfmaCbuf : OpCodeHfma, IOpCodeHfma, IOpCodeCbuf + { + public int Offset { get; } + public int Slot { get; } + + public bool NegateB { get; } + public bool NegateC { get; } + public bool Saturate { get; } + + public FPHalfSwizzle SwizzleB => FPHalfSwizzle.FP32; + public FPHalfSwizzle SwizzleC { get; } + + public OpCodeHfmaCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Offset = opCode.Extract(20, 14); + Slot = opCode.Extract(34, 5); + + NegateC = opCode.Extract(51); + Saturate = opCode.Extract(52); + + SwizzleC = (FPHalfSwizzle)opCode.Extract(53, 2); + + NegateB = opCode.Extract(56); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaImm2x10.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaImm2x10.cs new file mode 100644 index 0000000000..80a5a14089 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaImm2x10.cs @@ -0,0 +1,26 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeHfmaImm2x10 : OpCodeHfma, IOpCodeHfma, IOpCodeImm + { + public int Immediate { get; } + + public bool NegateB => false; + public bool NegateC { get; } + public bool Saturate { get; } + + public FPHalfSwizzle SwizzleB => FPHalfSwizzle.FP16; + public FPHalfSwizzle SwizzleC { get; } + + public OpCodeHfmaImm2x10(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Immediate = DecoderHelper.Decode2xF10Immediate(opCode); + + NegateC = opCode.Extract(51); + Saturate = opCode.Extract(52); + + SwizzleC = (FPHalfSwizzle)opCode.Extract(53, 2); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaImm32.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaImm32.cs new file mode 100644 index 0000000000..05eb9ffe0a --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaImm32.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeHfmaImm32 : OpCodeHfma, IOpCodeHfma, IOpCodeImm + { + public int Immediate { get; } + + public bool NegateB => false; + public bool NegateC { get; } + public bool Saturate => false; + + public FPHalfSwizzle SwizzleB => FPHalfSwizzle.FP16; + public FPHalfSwizzle SwizzleC => FPHalfSwizzle.FP16; + + public OpCodeHfmaImm32(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Immediate = opCode.Extract(20, 32); + + NegateC = opCode.Extract(52); + + Rc = Rd; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaReg.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaReg.cs new file mode 100644 index 0000000000..714c89dead --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaReg.cs @@ -0,0 +1,29 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeHfmaReg : OpCodeHfma, IOpCodeHfma, IOpCodeReg + { + public Register Rb { get; } + + public bool NegateB { get; } + public bool NegateC { get; } + public bool Saturate { get; } + + public FPHalfSwizzle SwizzleB { get; } + public FPHalfSwizzle SwizzleC { get; } + + public OpCodeHfmaReg(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr); + + SwizzleB = (FPHalfSwizzle)opCode.Extract(28, 2); + + NegateC = opCode.Extract(30); + NegateB = opCode.Extract(31); + Saturate = opCode.Extract(32); + + SwizzleC = (FPHalfSwizzle)opCode.Extract(35, 2); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaRegCbuf.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaRegCbuf.cs new file mode 100644 index 0000000000..c0001908ce --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaRegCbuf.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeHfmaRegCbuf : OpCodeHfma, IOpCodeHfma, IOpCodeRegCbuf + { + public int Offset { get; } + public int Slot { get; } + + public bool NegateB { get; } + public bool NegateC { get; } + public bool Saturate { get; } + + public FPHalfSwizzle SwizzleB { get; } + public FPHalfSwizzle SwizzleC => FPHalfSwizzle.FP32; + + public OpCodeHfmaRegCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Offset = opCode.Extract(20, 14); + Slot = opCode.Extract(34, 5); + + NegateC = opCode.Extract(51); + Saturate = opCode.Extract(52); + + SwizzleB = (FPHalfSwizzle)opCode.Extract(53, 2); + + NegateB = opCode.Extract(56); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeIpa.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeIpa.cs new file mode 100644 index 0000000000..e21095a325 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeIpa.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeIpa : OpCodeAluReg + { + public int AttributeOffset { get; } + + public OpCodeIpa(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + AttributeOffset = opCode.Extract(28, 10); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLdc.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeLdc.cs new file mode 100644 index 0000000000..cc9f065828 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeLdc.cs @@ -0,0 +1,26 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeLdc : OpCode, IOpCodeRd, IOpCodeRa, IOpCodeCbuf + { + public Register Rd { get; } + public Register Ra { get; } + + public int Offset { get; } + public int Slot { get; } + + public IntegerSize Size { get; } + + public OpCodeLdc(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); + Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); + + Offset = opCode.Extract(22, 14); + Slot = opCode.Extract(36, 5); + + Size = (IntegerSize)opCode.Extract(48, 3); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLop.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeLop.cs new file mode 100644 index 0000000000..c5f903451b --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeLop.cs @@ -0,0 +1,28 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeLop : OpCodeAlu, IOpCodeLop + { + public bool InvertA { get; protected set; } + public bool InvertB { get; protected set; } + + public LogicalOperation LogicalOp { get; } + + public ConditionalOperation CondOp { get; } + + public Register Predicate48 { get; } + + public OpCodeLop(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + InvertA = opCode.Extract(39); + InvertB = opCode.Extract(40); + + LogicalOp = (LogicalOperation)opCode.Extract(41, 2); + + CondOp = (ConditionalOperation)opCode.Extract(44, 2); + + Predicate48 = new Register(opCode.Extract(48, 3), RegisterType.Predicate); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLopCbuf.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeLopCbuf.cs new file mode 100644 index 0000000000..f174733c42 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeLopCbuf.cs @@ -0,0 +1,16 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeLopCbuf : OpCodeLop, IOpCodeCbuf + { + public int Offset { get; } + public int Slot { get; } + + public OpCodeLopCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Offset = opCode.Extract(20, 14); + Slot = opCode.Extract(34, 5); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLopImm.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeLopImm.cs new file mode 100644 index 0000000000..a2f091a2c4 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeLopImm.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeLopImm : OpCodeLop, IOpCodeImm + { + public int Immediate { get; } + + public OpCodeLopImm(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Immediate = DecoderHelper.DecodeS20Immediate(opCode); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLopImm32.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeLopImm32.cs new file mode 100644 index 0000000000..cb48f3a615 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeLopImm32.cs @@ -0,0 +1,22 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeLopImm32 : OpCodeAluImm32, IOpCodeLop, IOpCodeImm + { + public LogicalOperation LogicalOp { get; } + + public bool InvertA { get; } + public bool InvertB { get; } + + public OpCodeLopImm32(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + LogicalOp = (LogicalOperation)opCode.Extract(53, 2); + + InvertA = opCode.Extract(55); + InvertB = opCode.Extract(56); + + Extended = opCode.Extract(57); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLopReg.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeLopReg.cs new file mode 100644 index 0000000000..5f43db72b5 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeLopReg.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeLopReg : OpCodeLop, IOpCodeReg + { + public Register Rb { get; } + + public OpCodeLopReg(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodePsetp.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodePsetp.cs new file mode 100644 index 0000000000..729e3207e8 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodePsetp.cs @@ -0,0 +1,20 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodePsetp : OpCodeSet + { + public Register Predicate12 { get; } + public Register Predicate29 { get; } + + public LogicalOperation LogicalOpAB { get; } + + public OpCodePsetp(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Predicate12 = new Register(opCode.Extract(12, 3), RegisterType.Predicate); + Predicate29 = new Register(opCode.Extract(29, 3), RegisterType.Predicate); + + LogicalOpAB = (LogicalOperation)opCode.Extract(24, 2); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSet.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeSet.cs new file mode 100644 index 0000000000..cd6773a13c --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeSet.cs @@ -0,0 +1,26 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeSet : OpCodeAlu + { + public Register Predicate0 { get; } + public Register Predicate3 { get; } + + public bool NegateP { get; } + + public LogicalOperation LogicalOp { get; } + + public bool FlushToZero { get; } + + public OpCodeSet(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Predicate0 = new Register(opCode.Extract(0, 3), RegisterType.Predicate); + Predicate3 = new Register(opCode.Extract(3, 3), RegisterType.Predicate); + + LogicalOp = (LogicalOperation)opCode.Extract(45, 2); + + FlushToZero = opCode.Extract(47); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSetCbuf.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeSetCbuf.cs new file mode 100644 index 0000000000..4f3dbd7412 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeSetCbuf.cs @@ -0,0 +1,16 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeSetCbuf : OpCodeSet, IOpCodeCbuf + { + public int Offset { get; } + public int Slot { get; } + + public OpCodeSetCbuf(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Offset = opCode.Extract(20, 14); + Slot = opCode.Extract(34, 5); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSetImm.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeSetImm.cs new file mode 100644 index 0000000000..bc63b9f476 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeSetImm.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeSetImm : OpCodeSet, IOpCodeImm + { + public int Immediate { get; } + + public OpCodeSetImm(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Immediate = DecoderHelper.DecodeS20Immediate(opCode); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSetReg.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeSetReg.cs new file mode 100644 index 0000000000..bbdee19622 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeSetReg.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeSetReg : OpCodeSet, IOpCodeReg + { + public Register Rb { get; protected set; } + + public OpCodeSetReg(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSsy.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeSsy.cs new file mode 100644 index 0000000000..499c070689 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeSsy.cs @@ -0,0 +1,20 @@ +using Ryujinx.Graphics.Shader.Instructions; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeSsy : OpCodeBranch + { + public Dictionary Syncs { get; } + + public OpCodeSsy(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Syncs = new Dictionary(); + + Predicate = new Register(RegisterConsts.PredicateTrueIndex, RegisterType.Predicate); + + InvertPredicate = false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSync.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeSync.cs new file mode 100644 index 0000000000..081d08a0de --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeSync.cs @@ -0,0 +1,15 @@ +using Ryujinx.Graphics.Shader.Instructions; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeSync : OpCode + { + public Dictionary Targets { get; } + + public OpCodeSync(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Targets = new Dictionary(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTable.cs new file mode 100644 index 0000000000..d588ce8ee8 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTable.cs @@ -0,0 +1,216 @@ +using Ryujinx.Graphics.Shader.Instructions; +using System; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + static class OpCodeTable + { + private const int EncodingBits = 14; + + private class TableEntry + { + public InstEmitter Emitter { get; } + + public Type OpCodeType { get; } + + public int XBits { get; } + + public TableEntry(InstEmitter emitter, Type opCodeType, int xBits) + { + Emitter = emitter; + OpCodeType = opCodeType; + XBits = xBits; + } + } + + private static TableEntry[] _opCodes; + + static OpCodeTable() + { + _opCodes = new TableEntry[1 << EncodingBits]; + +#region Instructions + Set("1110111111011x", InstEmit.Ald, typeof(OpCodeAttribute)); + Set("1110111111110x", InstEmit.Ast, typeof(OpCodeAttribute)); + Set("0100110000000x", InstEmit.Bfe, typeof(OpCodeAluCbuf)); + Set("0011100x00000x", InstEmit.Bfe, typeof(OpCodeAluImm)); + Set("0101110000000x", InstEmit.Bfe, typeof(OpCodeAluReg)); + Set("111000100100xx", InstEmit.Bra, typeof(OpCodeBranch)); + Set("111000110000xx", InstEmit.Exit, typeof(OpCodeExit)); + Set("0100110010101x", InstEmit.F2F, typeof(OpCodeFArithCbuf)); + Set("0011100x10101x", InstEmit.F2F, typeof(OpCodeFArithImm)); + Set("0101110010101x", InstEmit.F2F, typeof(OpCodeFArithReg)); + Set("0100110010110x", InstEmit.F2I, typeof(OpCodeFArithCbuf)); + Set("0011100x10110x", InstEmit.F2I, typeof(OpCodeFArithImm)); + Set("0101110010110x", InstEmit.F2I, typeof(OpCodeFArithReg)); + Set("0100110001011x", InstEmit.Fadd, typeof(OpCodeFArithCbuf)); + Set("0011100x01011x", InstEmit.Fadd, typeof(OpCodeFArithImm)); + Set("000010xxxxxxxx", InstEmit.Fadd, typeof(OpCodeFArithImm32)); + Set("0101110001011x", InstEmit.Fadd, typeof(OpCodeFArithReg)); + Set("010010011xxxxx", InstEmit.Ffma, typeof(OpCodeFArithCbuf)); + Set("0011001x1xxxxx", InstEmit.Ffma, typeof(OpCodeFArithImm)); + Set("010100011xxxxx", InstEmit.Ffma, typeof(OpCodeFArithRegCbuf)); + Set("010110011xxxxx", InstEmit.Ffma, typeof(OpCodeFArithReg)); + Set("0100110001100x", InstEmit.Fmnmx, typeof(OpCodeFArithCbuf)); + Set("0011100x01100x", InstEmit.Fmnmx, typeof(OpCodeFArithImm)); + Set("0101110001100x", InstEmit.Fmnmx, typeof(OpCodeFArithReg)); + Set("0100110001101x", InstEmit.Fmul, typeof(OpCodeFArithCbuf)); + Set("0011100x01101x", InstEmit.Fmul, typeof(OpCodeFArithImm)); + Set("00011110xxxxxx", InstEmit.Fmul, typeof(OpCodeFArithImm32)); + Set("0101110001101x", InstEmit.Fmul, typeof(OpCodeFArithReg)); + Set("0100100xxxxxxx", InstEmit.Fset, typeof(OpCodeSetCbuf)); + Set("0011000xxxxxxx", InstEmit.Fset, typeof(OpCodeFsetImm)); + Set("01011000xxxxxx", InstEmit.Fset, typeof(OpCodeSetReg)); + Set("010010111011xx", InstEmit.Fsetp, typeof(OpCodeSetCbuf)); + Set("0011011x1011xx", InstEmit.Fsetp, typeof(OpCodeFsetImm)); + Set("010110111011xx", InstEmit.Fsetp, typeof(OpCodeSetReg)); + Set("0111101x1xxxxx", InstEmit.Hadd2, typeof(OpCodeAluCbuf)); + Set("0111101x0xxxxx", InstEmit.Hadd2, typeof(OpCodeAluImm2x10)); + Set("0010110xxxxxxx", InstEmit.Hadd2, typeof(OpCodeAluImm32)); + Set("0101110100010x", InstEmit.Hadd2, typeof(OpCodeAluReg)); + Set("01110xxx1xxxxx", InstEmit.Hfma2, typeof(OpCodeHfmaCbuf)); + Set("01110xxx0xxxxx", InstEmit.Hfma2, typeof(OpCodeHfmaImm2x10)); + Set("0010100xxxxxxx", InstEmit.Hfma2, typeof(OpCodeHfmaImm32)); + Set("0101110100000x", InstEmit.Hfma2, typeof(OpCodeHfmaReg)); + Set("01100xxx1xxxxx", InstEmit.Hfma2, typeof(OpCodeHfmaRegCbuf)); + Set("0111100x1xxxxx", InstEmit.Hmul2, typeof(OpCodeAluCbuf)); + Set("0111100x0xxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm2x10)); + Set("0010101xxxxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm32)); + Set("0101110100001x", InstEmit.Hmul2, typeof(OpCodeAluReg)); + Set("0100110010111x", InstEmit.I2F, typeof(OpCodeAluCbuf)); + Set("0011100x10111x", InstEmit.I2F, typeof(OpCodeAluImm)); + Set("0101110010111x", InstEmit.I2F, typeof(OpCodeAluReg)); + Set("0100110011100x", InstEmit.I2I, typeof(OpCodeAluCbuf)); + Set("0011100x11100x", InstEmit.I2I, typeof(OpCodeAluImm)); + Set("0101110011100x", InstEmit.I2I, typeof(OpCodeAluReg)); + Set("0100110000010x", InstEmit.Iadd, typeof(OpCodeAluCbuf)); + Set("0011100000010x", InstEmit.Iadd, typeof(OpCodeAluImm)); + Set("0001110x0xxxxx", InstEmit.Iadd, typeof(OpCodeAluImm32)); + Set("0101110000010x", InstEmit.Iadd, typeof(OpCodeAluReg)); + Set("010011001100xx", InstEmit.Iadd3, typeof(OpCodeAluCbuf)); + Set("001110001100xx", InstEmit.Iadd3, typeof(OpCodeAluImm)); + Set("010111001100xx", InstEmit.Iadd3, typeof(OpCodeAluReg)); + Set("0100110000100x", InstEmit.Imnmx, typeof(OpCodeAluCbuf)); + Set("0011100x00100x", InstEmit.Imnmx, typeof(OpCodeAluImm)); + Set("0101110000100x", InstEmit.Imnmx, typeof(OpCodeAluReg)); + Set("11100000xxxxxx", InstEmit.Ipa, typeof(OpCodeIpa)); + Set("0100110000011x", InstEmit.Iscadd, typeof(OpCodeAluCbuf)); + Set("0011100x00011x", InstEmit.Iscadd, typeof(OpCodeAluImm)); + Set("000101xxxxxxxx", InstEmit.Iscadd, typeof(OpCodeAluImm32)); + Set("0101110000011x", InstEmit.Iscadd, typeof(OpCodeAluReg)); + Set("010010110101xx", InstEmit.Iset, typeof(OpCodeSetCbuf)); + Set("001101100101xx", InstEmit.Iset, typeof(OpCodeSetImm)); + Set("010110110101xx", InstEmit.Iset, typeof(OpCodeSetReg)); + Set("010010110110xx", InstEmit.Isetp, typeof(OpCodeSetCbuf)); + Set("0011011x0110xx", InstEmit.Isetp, typeof(OpCodeSetImm)); + Set("010110110110xx", InstEmit.Isetp, typeof(OpCodeSetReg)); + Set("111000110011xx", InstEmit.Kil, typeof(OpCodeExit)); + Set("1110111110010x", InstEmit.Ldc, typeof(OpCodeLdc)); + Set("0100110001000x", InstEmit.Lop, typeof(OpCodeLopCbuf)); + Set("0011100001000x", InstEmit.Lop, typeof(OpCodeLopImm)); + Set("000001xxxxxxxx", InstEmit.Lop, typeof(OpCodeLopImm32)); + Set("0101110001000x", InstEmit.Lop, typeof(OpCodeLopReg)); + Set("0010000xxxxxxx", InstEmit.Lop3, typeof(OpCodeLopCbuf)); + Set("001111xxxxxxxx", InstEmit.Lop3, typeof(OpCodeLopImm)); + Set("0101101111100x", InstEmit.Lop3, typeof(OpCodeLopReg)); + Set("0100110010011x", InstEmit.Mov, typeof(OpCodeAluCbuf)); + Set("0011100x10011x", InstEmit.Mov, typeof(OpCodeAluImm)); + Set("000000010000xx", InstEmit.Mov, typeof(OpCodeAluImm32)); + Set("0101110010011x", InstEmit.Mov, typeof(OpCodeAluReg)); + Set("0101000010000x", InstEmit.Mufu, typeof(OpCodeFArith)); + Set("1111101111100x", InstEmit.Out, typeof(OpCode)); + Set("0101000010010x", InstEmit.Psetp, typeof(OpCodePsetp)); + Set("0100110010010x", InstEmit.Rro, typeof(OpCodeFArithCbuf)); + Set("0011100x10010x", InstEmit.Rro, typeof(OpCodeFArithImm)); + Set("0101110010010x", InstEmit.Rro, typeof(OpCodeFArithReg)); + Set("0100110010100x", InstEmit.Sel, typeof(OpCodeAluCbuf)); + Set("0011100010100x", InstEmit.Sel, typeof(OpCodeAluImm)); + Set("0101110010100x", InstEmit.Sel, typeof(OpCodeAluReg)); + Set("0100110001001x", InstEmit.Shl, typeof(OpCodeAluCbuf)); + Set("0011100x01001x", InstEmit.Shl, typeof(OpCodeAluImm)); + Set("0101110001001x", InstEmit.Shl, typeof(OpCodeAluReg)); + Set("0100110000101x", InstEmit.Shr, typeof(OpCodeAluCbuf)); + Set("0011100x00101x", InstEmit.Shr, typeof(OpCodeAluImm)); + Set("0101110000101x", InstEmit.Shr, typeof(OpCodeAluReg)); + Set("111000101001xx", InstEmit.Ssy, typeof(OpCodeSsy)); + Set("1111000011111x", InstEmit.Sync, typeof(OpCodeSync)); + Set("110000xxxx111x", InstEmit.Tex, typeof(OpCodeTex)); + Set("1101111010111x", InstEmit.Tex_B, typeof(OpCodeTex)); + Set("1101x00xxxxxxx", InstEmit.Texs, typeof(OpCodeTexs)); + Set("1101x01xxxxxxx", InstEmit.Texs, typeof(OpCodeTlds)); + Set("1101x11100xxxx", InstEmit.Texs, typeof(OpCodeTld4s)); + Set("11011100xx111x", InstEmit.Tld, typeof(OpCodeTld)); + Set("11011101xx111x", InstEmit.Tld_B, typeof(OpCodeTld)); + Set("110010xxxx111x", InstEmit.Tld4, typeof(OpCodeTld4)); + Set("1101111101001x", InstEmit.Txq, typeof(OpCodeTex)); + Set("1101111101010x", InstEmit.Txq_B, typeof(OpCodeTex)); + Set("0100111xxxxxxx", InstEmit.Xmad, typeof(OpCodeAluCbuf)); + Set("0011011x00xxxx", InstEmit.Xmad, typeof(OpCodeAluImm)); + Set("010100010xxxxx", InstEmit.Xmad, typeof(OpCodeAluRegCbuf)); + Set("0101101100xxxx", InstEmit.Xmad, typeof(OpCodeAluReg)); +#endregion + } + + private static void Set(string encoding, InstEmitter emitter, Type opCodeType) + { + if (encoding.Length != EncodingBits) + { + throw new ArgumentException(nameof(encoding)); + } + + int bit = encoding.Length - 1; + int value = 0; + int xMask = 0; + int xBits = 0; + + int[] xPos = new int[encoding.Length]; + + for (int index = 0; index < encoding.Length; index++, bit--) + { + char chr = encoding[index]; + + if (chr == '1') + { + value |= 1 << bit; + } + else if (chr == 'x') + { + xMask |= 1 << bit; + + xPos[xBits++] = bit; + } + } + + xMask = ~xMask; + + TableEntry entry = new TableEntry(emitter, opCodeType, xBits); + + for (int index = 0; index < (1 << xBits); index++) + { + value &= xMask; + + for (int X = 0; X < xBits; X++) + { + value |= ((index >> X) & 1) << xPos[X]; + } + + if (_opCodes[value] == null || _opCodes[value].XBits > xBits) + { + _opCodes[value] = entry; + } + } + } + + public static (InstEmitter emitter, Type opCodeType) GetEmitter(long OpCode) + { + TableEntry entry = _opCodes[(ulong)OpCode >> (64 - EncodingBits)]; + + if (entry != null) + { + return (entry.Emitter, entry.OpCodeType); + } + + return (null, null); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTex.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTex.cs new file mode 100644 index 0000000000..da8756b91c --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTex.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeTex : OpCodeTexture + { + public OpCodeTex(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + HasDepthCompare = opCode.Extract(50); + + HasOffset = opCode.Extract(54); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTexs.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTexs.cs new file mode 100644 index 0000000000..0822c4c074 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTexs.cs @@ -0,0 +1,11 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeTexs : OpCodeTextureScalar + { + public TextureScalarType Type => (TextureScalarType)RawType; + + public OpCodeTexs(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTexture.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTexture.cs new file mode 100644 index 0000000000..7a7e8f46e7 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTexture.cs @@ -0,0 +1,42 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeTexture : OpCode + { + public Register Rd { get; } + public Register Ra { get; } + public Register Rb { get; } + + public bool IsArray { get; } + + public TextureDimensions Dimensions { get; } + + public int ComponentMask { get; } + + public int Immediate { get; } + + public TextureLodMode LodMode { get; protected set; } + + public bool HasOffset { get; protected set; } + public bool HasDepthCompare { get; protected set; } + public bool IsMultisample { get; protected set; } + + public OpCodeTexture(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); + Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); + Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr); + + IsArray = opCode.Extract(28); + + Dimensions = (TextureDimensions)opCode.Extract(29, 2); + + ComponentMask = opCode.Extract(31, 4); + + Immediate = opCode.Extract(36, 13); + + LodMode = (TextureLodMode)opCode.Extract(55, 3); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTextureScalar.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTextureScalar.cs new file mode 100644 index 0000000000..4389f45319 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTextureScalar.cs @@ -0,0 +1,61 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeTextureScalar : OpCode + { +#region "Component mask LUT" + private const int ____ = 0x0; + private const int R___ = 0x1; + private const int _G__ = 0x2; + private const int RG__ = 0x3; + private const int __B_ = 0x4; + private const int RGB_ = 0x7; + private const int ___A = 0x8; + private const int R__A = 0x9; + private const int _G_A = 0xa; + private const int RG_A = 0xb; + private const int __BA = 0xc; + private const int R_BA = 0xd; + private const int _GBA = 0xe; + private const int RGBA = 0xf; + + private static int[,] _maskLut = new int[,] + { + { R___, _G__, __B_, ___A, RG__, R__A, _G_A, __BA }, + { RGB_, RG_A, R_BA, _GBA, RGBA, ____, ____, ____ } + }; +#endregion + + public Register Rd0 { get; } + public Register Ra { get; } + public Register Rb { get; } + public Register Rd1 { get; } + + public int Immediate { get; } + + public int ComponentMask { get; } + + protected int RawType; + + public bool IsFp16 { get; } + + public OpCodeTextureScalar(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rd0 = new Register(opCode.Extract(0, 8), RegisterType.Gpr); + Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); + Rb = new Register(opCode.Extract(20, 8), RegisterType.Gpr); + Rd1 = new Register(opCode.Extract(28, 8), RegisterType.Gpr); + + Immediate = opCode.Extract(36, 13); + + int compSel = opCode.Extract(50, 3); + + RawType = opCode.Extract(53, 4); + + IsFp16 = !opCode.Extract(59); + + ComponentMask = _maskLut[Rd1.IsRZ ? 0 : 1, compSel]; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTld.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld.cs new file mode 100644 index 0000000000..61bd900b2d --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld.cs @@ -0,0 +1,20 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeTld : OpCodeTexture + { + public OpCodeTld(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + HasOffset = opCode.Extract(35); + + IsMultisample = opCode.Extract(50); + + bool isLL = opCode.Extract(55); + + LodMode = isLL + ? TextureLodMode.LodLevel + : TextureLodMode.LodZero; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4.cs new file mode 100644 index 0000000000..485edf936b --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4.cs @@ -0,0 +1,20 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeTld4 : OpCodeTexture + { + public TextureGatherOffset Offset { get; } + + public int GatherCompIndex { get; } + + public OpCodeTld4(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + HasDepthCompare = opCode.Extract(50); + + Offset = (TextureGatherOffset)opCode.Extract(54, 2); + + GatherCompIndex = opCode.Extract(56, 2); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4s.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4s.cs new file mode 100644 index 0000000000..0d7b84606a --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4s.cs @@ -0,0 +1,20 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeTld4s : OpCodeTextureScalar + { + public bool HasDepthCompare { get; } + public bool HasOffset { get; } + + public int GatherCompIndex { get; } + + public OpCodeTld4s(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + HasDepthCompare = opCode.Extract(50); + HasOffset = opCode.Extract(51); + + GatherCompIndex = opCode.Extract(52, 2); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTlds.cs b/Ryujinx.Graphics/Shader/Decoders/OpCodeTlds.cs new file mode 100644 index 0000000000..e117721e9e --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/OpCodeTlds.cs @@ -0,0 +1,11 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeTlds : OpCodeTextureScalar + { + public TexelLoadScalarType Type => (TexelLoadScalarType)RawType; + + public OpCodeTlds(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/Register.cs b/Ryujinx.Graphics/Shader/Decoders/Register.cs new file mode 100644 index 0000000000..30840d8c03 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/Register.cs @@ -0,0 +1,36 @@ +using System; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + struct Register : IEquatable + { + public int Index { get; } + + public RegisterType Type { get; } + + public bool IsRZ => Type == RegisterType.Gpr && Index == RegisterConsts.RegisterZeroIndex; + public bool IsPT => Type == RegisterType.Predicate && Index == RegisterConsts.PredicateTrueIndex; + + public Register(int index, RegisterType type) + { + Index = index; + Type = type; + } + + public override int GetHashCode() + { + return (ushort)Index | ((ushort)Type << 16); + } + + public override bool Equals(object obj) + { + return obj is Register reg && Equals(reg); + } + + public bool Equals(Register other) + { + return other.Index == Index && + other.Type == Type; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/RegisterConsts.cs b/Ryujinx.Graphics/Shader/Decoders/RegisterConsts.cs new file mode 100644 index 0000000000..d381f9543e --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/RegisterConsts.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + static class RegisterConsts + { + public const int GprsCount = 255; + public const int PredsCount = 7; + public const int FlagsCount = 4; + public const int TotalCount = GprsCount + PredsCount + FlagsCount; + + public const int RegisterZeroIndex = GprsCount; + public const int PredicateTrueIndex = PredsCount; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/RegisterType.cs b/Ryujinx.Graphics/Shader/Decoders/RegisterType.cs new file mode 100644 index 0000000000..648f816a24 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/RegisterType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum RegisterType + { + Flag, + Gpr, + Predicate, + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/RoundingMode.cs b/Ryujinx.Graphics/Shader/Decoders/RoundingMode.cs new file mode 100644 index 0000000000..13bb08dc82 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/RoundingMode.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum RoundingMode + { + ToNearest = 0, + TowardsNegativeInfinity = 1, + TowardsPositiveInfinity = 2, + TowardsZero = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/TexelLoadScalarType.cs b/Ryujinx.Graphics/Shader/Decoders/TexelLoadScalarType.cs new file mode 100644 index 0000000000..cef5778a55 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/TexelLoadScalarType.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum TexelLoadScalarType + { + Texture1DLodZero = 0x0, + Texture1DLodLevel = 0x1, + Texture2DLodZero = 0x2, + Texture2DLodZeroOffset = 0x4, + Texture2DLodLevel = 0x5, + Texture2DLodZeroMultisample = 0x6, + Texture3DLodZero = 0x7, + Texture2DArrayLodZero = 0x8, + Texture2DLodLevelOffset = 0xc + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/TextureDimensions.cs b/Ryujinx.Graphics/Shader/Decoders/TextureDimensions.cs new file mode 100644 index 0000000000..dbdf1927fb --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/TextureDimensions.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum TextureDimensions + { + Texture1D = 0, + Texture2D = 1, + Texture3D = 2, + TextureCube = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/TextureGatherOffset.cs b/Ryujinx.Graphics/Shader/Decoders/TextureGatherOffset.cs new file mode 100644 index 0000000000..4e9ade26a4 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/TextureGatherOffset.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum TextureGatherOffset + { + None = 0, + Offset = 1, + Offsets = 2 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/TextureLodMode.cs b/Ryujinx.Graphics/Shader/Decoders/TextureLodMode.cs new file mode 100644 index 0000000000..0cc6f71432 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/TextureLodMode.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum TextureLodMode + { + None = 0, + LodZero = 1, + LodBias = 2, + LodLevel = 3, + LodBiasA = 4, //? + LodLevelA = 5 //? + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/TextureProperty.cs b/Ryujinx.Graphics/Shader/Decoders/TextureProperty.cs new file mode 100644 index 0000000000..ea35b1d1cb --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/TextureProperty.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum TextureProperty + { + Dimensions = 0x1, + Type = 0x2, + SamplePos = 0x5, + Filter = 0xa, + Lod = 0xc, + Wrap = 0xe, + BorderColor = 0x10 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/TextureScalarType.cs b/Ryujinx.Graphics/Shader/Decoders/TextureScalarType.cs new file mode 100644 index 0000000000..0055174b4e --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/TextureScalarType.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum TextureScalarType + { + Texture1DLodZero = 0x0, + Texture2D = 0x1, + Texture2DLodZero = 0x2, + Texture2DLodLevel = 0x3, + Texture2DDepthCompare = 0x4, + Texture2DLodLevelDepthCompare = 0x5, + Texture2DLodZeroDepthCompare = 0x6, + Texture2DArray = 0x7, + Texture2DArrayLodZero = 0x8, + Texture2DArrayLodZeroDepthCompare = 0x9, + Texture3D = 0xa, + Texture3DLodZero = 0xb, + TextureCube = 0xc, + TextureCubeLodLevel = 0xd + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/XmadCMode.cs b/Ryujinx.Graphics/Shader/Decoders/XmadCMode.cs new file mode 100644 index 0000000000..949a2ef70a --- /dev/null +++ b/Ryujinx.Graphics/Shader/Decoders/XmadCMode.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum XmadCMode + { + Cfull = 0, + Clo = 1, + Chi = 2, + Csfu = 3, + Cbcc = 4 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs new file mode 100644 index 0000000000..f7815e2330 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs @@ -0,0 +1,684 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using System; + +using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; +using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void Bfe(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + bool isReverse = op.RawOpCode.Extract(40); + bool isSigned = op.RawOpCode.Extract(48); + + Operand srcA = GetSrcA(context); + Operand srcB = GetSrcB(context); + + if (isReverse) + { + srcA = context.BitfieldReverse(srcA); + } + + Operand position = context.BitwiseAnd(srcB, Const(0xff)); + + Operand size = context.BitfieldExtractU32(srcB, Const(8), Const(8)); + + Operand res = isSigned + ? context.BitfieldExtractS32(srcA, position, size) + : context.BitfieldExtractU32(srcA, position, size); + + context.Copy(GetDest(context), res); + + //TODO: CC, X, corner cases + } + + public static void Iadd(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + bool negateA = false, negateB = false; + + if (!(op is OpCodeAluImm32)) + { + negateB = op.RawOpCode.Extract(48); + negateA = op.RawOpCode.Extract(49); + } + + Operand srcA = context.INegate(GetSrcA(context), negateA); + Operand srcB = context.INegate(GetSrcB(context), negateB); + + Operand res = context.IAdd(srcA, srcB); + + bool isSubtraction = negateA || negateB; + + if (op.Extended) + { + //Add carry, or subtract borrow. + res = context.IAdd(res, isSubtraction + ? context.BitwiseNot(GetCF(context)) + : context.BitwiseAnd(GetCF(context), Const(1))); + } + + SetIaddFlags(context, res, srcA, srcB, op.SetCondCode, op.Extended, isSubtraction); + + context.Copy(GetDest(context), res); + } + + public static void Iadd3(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + IntegerHalfPart partC = (IntegerHalfPart)op.RawOpCode.Extract(31, 2); + IntegerHalfPart partB = (IntegerHalfPart)op.RawOpCode.Extract(33, 2); + IntegerHalfPart partA = (IntegerHalfPart)op.RawOpCode.Extract(35, 2); + + IntegerShift mode = (IntegerShift)op.RawOpCode.Extract(37, 2); + + bool negateC = op.RawOpCode.Extract(49); + bool negateB = op.RawOpCode.Extract(50); + bool negateA = op.RawOpCode.Extract(51); + + Operand Extend(Operand src, IntegerHalfPart part) + { + if (!(op is OpCodeAluReg) || part == IntegerHalfPart.B32) + { + return src; + } + + if (part == IntegerHalfPart.H0) + { + return context.BitwiseAnd(src, Const(0xffff)); + } + else if (part == IntegerHalfPart.H1) + { + return context.ShiftRightU32(src, Const(16)); + } + else + { + //TODO: Warning. + } + + return src; + } + + Operand srcA = context.INegate(Extend(GetSrcA(context), partA), negateA); + Operand srcB = context.INegate(Extend(GetSrcB(context), partB), negateB); + Operand srcC = context.INegate(Extend(GetSrcC(context), partC), negateC); + + Operand res = context.IAdd(srcA, srcB); + + if (op is OpCodeAluReg && mode != IntegerShift.NoShift) + { + if (mode == IntegerShift.ShiftLeft) + { + res = context.ShiftLeft(res, Const(16)); + } + else if (mode == IntegerShift.ShiftRight) + { + res = context.ShiftRightU32(res, Const(16)); + } + else + { + //TODO: Warning. + } + } + + res = context.IAdd(res, srcC); + + context.Copy(GetDest(context), res); + + //TODO: CC, X, corner cases + } + + public static void Imnmx(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + bool isSignedInt = op.RawOpCode.Extract(48); + + Operand srcA = GetSrcA(context); + Operand srcB = GetSrcB(context); + + Operand resMin = isSignedInt + ? context.IMinimumS32(srcA, srcB) + : context.IMinimumU32(srcA, srcB); + + Operand resMax = isSignedInt + ? context.IMaximumS32(srcA, srcB) + : context.IMaximumU32(srcA, srcB); + + Operand pred = GetPredicate39(context); + + Operand dest = GetDest(context); + + context.Copy(dest, context.ConditionalSelect(pred, resMin, resMax)); + + SetZnFlags(context, dest, op.SetCondCode); + + //TODO: X flags. + } + + public static void Iscadd(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + bool negateA = false, negateB = false; + + if (!(op is OpCodeAluImm32)) + { + negateB = op.RawOpCode.Extract(48); + negateA = op.RawOpCode.Extract(49); + } + + int shift = op is OpCodeAluImm32 + ? op.RawOpCode.Extract(53, 5) + : op.RawOpCode.Extract(39, 5); + + Operand srcA = GetSrcA(context); + Operand srcB = GetSrcB(context); + + srcA = context.ShiftLeft(srcA, Const(shift)); + + srcA = context.INegate(srcA, negateA); + srcB = context.INegate(srcB, negateB); + + Operand res = context.IAdd(srcA, srcB); + + context.Copy(GetDest(context), res); + + //TODO: CC, X + } + + public static void Iset(EmitterContext context) + { + OpCodeSet op = (OpCodeSet)context.CurrOp; + + bool boolFloat = op.RawOpCode.Extract(44); + bool isSigned = op.RawOpCode.Extract(48); + + IntegerCondition cmpOp = (IntegerCondition)op.RawOpCode.Extract(49, 3); + + Operand srcA = GetSrcA(context); + Operand srcB = GetSrcB(context); + + Operand res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned); + + Operand pred = GetPredicate39(context); + + res = GetPredLogicalOp(context, op.LogicalOp, res, pred); + + Operand dest = GetDest(context); + + if (boolFloat) + { + context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0))); + } + else + { + context.Copy(dest, res); + } + + //TODO: CC, X + } + + public static void Isetp(EmitterContext context) + { + OpCodeSet op = (OpCodeSet)context.CurrOp; + + bool isSigned = op.RawOpCode.Extract(48); + + IntegerCondition cmpOp = (IntegerCondition)op.RawOpCode.Extract(49, 3); + + Operand srcA = GetSrcA(context); + Operand srcB = GetSrcB(context); + + Operand p0Res = GetIntComparison(context, cmpOp, srcA, srcB, isSigned); + + Operand p1Res = context.BitwiseNot(p0Res); + + Operand pred = GetPredicate39(context); + + p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred); + p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred); + + context.Copy(Register(op.Predicate3), p0Res); + context.Copy(Register(op.Predicate0), p1Res); + } + + public static void Lop(EmitterContext context) + { + IOpCodeLop op = (IOpCodeLop)context.CurrOp; + + Operand srcA = context.BitwiseNot(GetSrcA(context), op.InvertA); + Operand srcB = context.BitwiseNot(GetSrcB(context), op.InvertB); + + Operand res = srcB; + + switch (op.LogicalOp) + { + case LogicalOperation.And: res = context.BitwiseAnd (srcA, srcB); break; + case LogicalOperation.Or: res = context.BitwiseOr (srcA, srcB); break; + case LogicalOperation.ExclusiveOr: res = context.BitwiseExclusiveOr(srcA, srcB); break; + } + + EmitLopPredWrite(context, op, res); + + Operand dest = GetDest(context); + + context.Copy(dest, res); + + SetZnFlags(context, dest, op.SetCondCode, op.Extended); + } + + public static void Lop3(EmitterContext context) + { + IOpCodeLop op = (IOpCodeLop)context.CurrOp; + + Operand srcA = GetSrcA(context); + Operand srcB = GetSrcB(context); + Operand srcC = GetSrcC(context); + + bool regVariant = op is OpCodeLopReg; + + int truthTable = regVariant + ? op.RawOpCode.Extract(28, 8) + : op.RawOpCode.Extract(48, 8); + + Operand res = Lop3Expression.GetFromTruthTable(context, srcA, srcB, srcC, truthTable); + + if (regVariant) + { + EmitLopPredWrite(context, op, res); + } + + Operand dest = GetDest(context); + + context.Copy(dest, res); + + SetZnFlags(context, dest, op.SetCondCode, op.Extended); + } + + public static void Psetp(EmitterContext context) + { + OpCodePsetp op = (OpCodePsetp)context.CurrOp; + + bool invertA = op.RawOpCode.Extract(15); + bool invertB = op.RawOpCode.Extract(32); + + Operand srcA = context.BitwiseNot(Register(op.Predicate12), invertA); + Operand srcB = context.BitwiseNot(Register(op.Predicate29), invertB); + + Operand p0Res = GetPredLogicalOp(context, op.LogicalOpAB, srcA, srcB); + + Operand p1Res = context.BitwiseNot(p0Res); + + Operand pred = GetPredicate39(context); + + p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred); + p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred); + + context.Copy(Register(op.Predicate3), p0Res); + context.Copy(Register(op.Predicate0), p1Res); + } + + public static void Rro(EmitterContext context) + { + //This is the range reduction operator, + //we translate it as a simple move, as it + //should be always followed by a matching + //MUFU instruction. + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + bool negateB = op.RawOpCode.Extract(45); + bool absoluteB = op.RawOpCode.Extract(49); + + Operand srcB = GetSrcB(context); + + srcB = context.FPAbsNeg(srcB, absoluteB, negateB); + + context.Copy(GetDest(context), srcB); + } + + public static void Shl(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + bool isMasked = op.RawOpCode.Extract(39); + + Operand srcB = GetSrcB(context); + + if (isMasked) + { + srcB = context.BitwiseAnd(srcB, Const(0x1f)); + } + + Operand res = context.ShiftLeft(GetSrcA(context), srcB); + + if (!isMasked) + { + //Clamped shift value. + Operand isLessThan32 = context.ICompareLessUnsigned(srcB, Const(32)); + + res = context.ConditionalSelect(isLessThan32, res, Const(0)); + } + + //TODO: X, CC + + context.Copy(GetDest(context), res); + } + + public static void Shr(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + bool isMasked = op.RawOpCode.Extract(39); + bool isReverse = op.RawOpCode.Extract(40); + bool isSigned = op.RawOpCode.Extract(48); + + Operand srcA = GetSrcA(context); + Operand srcB = GetSrcB(context); + + if (isReverse) + { + srcA = context.BitfieldReverse(srcA); + } + + if (isMasked) + { + srcB = context.BitwiseAnd(srcB, Const(0x1f)); + } + + Operand res = isSigned + ? context.ShiftRightS32(srcA, srcB) + : context.ShiftRightU32(srcA, srcB); + + if (!isMasked) + { + //Clamped shift value. + Operand resShiftBy32; + + if (isSigned) + { + resShiftBy32 = context.ShiftRightS32(srcA, Const(31)); + } + else + { + resShiftBy32 = Const(0); + } + + Operand isLessThan32 = context.ICompareLessUnsigned(srcB, Const(32)); + + res = context.ConditionalSelect(isLessThan32, res, resShiftBy32); + } + + //TODO: X, CC + + context.Copy(GetDest(context), res); + } + + public static void Xmad(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + bool signedA = context.CurrOp.RawOpCode.Extract(48); + bool signedB = context.CurrOp.RawOpCode.Extract(49); + bool highA = context.CurrOp.RawOpCode.Extract(53); + bool highB = false; + + XmadCMode mode; + + if (op is OpCodeAluReg) + { + highB = context.CurrOp.RawOpCode.Extract(35); + + mode = (XmadCMode)context.CurrOp.RawOpCode.Extract(50, 3); + } + else + { + mode = (XmadCMode)context.CurrOp.RawOpCode.Extract(50, 2); + + if (!(op is OpCodeAluImm)) + { + highB = context.CurrOp.RawOpCode.Extract(52); + } + } + + Operand srcA = GetSrcA(context); + Operand srcB = GetSrcB(context); + Operand srcC = GetSrcC(context); + + //XMAD immediates are 16-bits unsigned integers. + if (srcB.Type == OperandType.Constant) + { + srcB = Const(srcB.Value & 0xffff); + } + + Operand Extend16To32(Operand src, bool high, bool signed) + { + if (signed && high) + { + return context.ShiftRightS32(src, Const(16)); + } + else if (signed) + { + return context.BitfieldExtractS32(src, Const(0), Const(16)); + } + else if (high) + { + return context.ShiftRightU32(src, Const(16)); + } + else + { + return context.BitwiseAnd(src, Const(0xffff)); + } + } + + srcA = Extend16To32(srcA, highA, signedA); + srcB = Extend16To32(srcB, highB, signedB); + + bool productShiftLeft = false; + bool merge = false; + + if (!(op is OpCodeAluRegCbuf)) + { + productShiftLeft = context.CurrOp.RawOpCode.Extract(36); + merge = context.CurrOp.RawOpCode.Extract(37); + } + + bool extended; + + if ((op is OpCodeAluReg) || (op is OpCodeAluImm)) + { + extended = context.CurrOp.RawOpCode.Extract(38); + } + else + { + extended = context.CurrOp.RawOpCode.Extract(54); + } + + Operand res = context.IMultiply(srcA, srcB); + + if (productShiftLeft) + { + res = context.ShiftLeft(res, Const(16)); + } + + switch (mode) + { + case XmadCMode.Cfull: break; + + case XmadCMode.Clo: srcC = Extend16To32(srcC, high: false, signed: false); break; + case XmadCMode.Chi: srcC = Extend16To32(srcC, high: true, signed: false); break; + + case XmadCMode.Cbcc: + { + srcC = context.IAdd(srcC, context.ShiftLeft(GetSrcB(context), Const(16))); + + break; + } + + case XmadCMode.Csfu: + { + Operand signAdjustA = context.ShiftLeft(context.ShiftRightU32(srcA, Const(31)), Const(16)); + Operand signAdjustB = context.ShiftLeft(context.ShiftRightU32(srcB, Const(31)), Const(16)); + + srcC = context.ISubtract(srcC, context.IAdd(signAdjustA, signAdjustB)); + + break; + } + + default: /* TODO: Warning */ break; + } + + Operand product = res; + + if (extended) + { + //Add with carry. + res = context.IAdd(res, context.BitwiseAnd(GetCF(context), Const(1))); + } + else + { + //Add (no carry in). + res = context.IAdd(res, srcC); + } + + SetIaddFlags(context, res, product, srcC, op.SetCondCode, extended); + + if (merge) + { + res = context.BitwiseAnd(res, Const(0xffff)); + res = context.BitwiseOr(res, context.ShiftLeft(GetSrcB(context), Const(16))); + } + + context.Copy(GetDest(context), res); + } + + private static Operand GetIntComparison( + EmitterContext context, + IntegerCondition cond, + Operand srcA, + Operand srcB, + bool isSigned) + { + Operand res; + + if (cond == IntegerCondition.Always) + { + res = Const(IrConsts.True); + } + else if (cond == IntegerCondition.Never) + { + res = Const(IrConsts.False); + } + else + { + Instruction inst; + + switch (cond) + { + case IntegerCondition.Less: inst = Instruction.CompareLessU32; break; + case IntegerCondition.Equal: inst = Instruction.CompareEqual; break; + case IntegerCondition.LessOrEqual: inst = Instruction.CompareLessOrEqualU32; break; + case IntegerCondition.Greater: inst = Instruction.CompareGreaterU32; break; + case IntegerCondition.NotEqual: inst = Instruction.CompareNotEqual; break; + case IntegerCondition.GreaterOrEqual: inst = Instruction.CompareGreaterOrEqualU32; break; + + default: throw new InvalidOperationException($"Unexpected condition \"{cond}\"."); + } + + if (isSigned) + { + switch (cond) + { + case IntegerCondition.Less: inst = Instruction.CompareLess; break; + case IntegerCondition.LessOrEqual: inst = Instruction.CompareLessOrEqual; break; + case IntegerCondition.Greater: inst = Instruction.CompareGreater; break; + case IntegerCondition.GreaterOrEqual: inst = Instruction.CompareGreaterOrEqual; break; + } + } + + res = context.Add(inst, Local(), srcA, srcB); + } + + return res; + } + + private static void EmitLopPredWrite(EmitterContext context, IOpCodeLop op, Operand result) + { + if (op is OpCodeLop opLop && !opLop.Predicate48.IsPT) + { + Operand pRes; + + if (opLop.CondOp == ConditionalOperation.False) + { + pRes = Const(IrConsts.False); + } + else if (opLop.CondOp == ConditionalOperation.True) + { + pRes = Const(IrConsts.True); + } + else if (opLop.CondOp == ConditionalOperation.Zero) + { + pRes = context.ICompareEqual(result, Const(0)); + } + else /* if (opLop.CondOp == ConditionalOperation.NotZero) */ + { + pRes = context.ICompareNotEqual(result, Const(0)); + } + + context.Copy(Register(opLop.Predicate48), pRes); + } + } + + private static void SetIaddFlags( + EmitterContext context, + Operand res, + Operand srcA, + Operand srcB, + bool setCC, + bool extended, + bool isSubtraction = false) + { + if (!setCC) + { + return; + } + + if (!extended || isSubtraction) + { + //C = d < a + context.Copy(GetCF(context), context.ICompareLessUnsigned(res, srcA)); + } + else + { + //C = (d == a && CIn) || d < a + Operand tempC0 = context.ICompareEqual (res, srcA); + Operand tempC1 = context.ICompareLessUnsigned(res, srcA); + + tempC0 = context.BitwiseAnd(tempC0, GetCF(context)); + + context.Copy(GetCF(context), context.BitwiseOr(tempC0, tempC1)); + } + + //V = (d ^ a) & ~(a ^ b) < 0 + Operand tempV0 = context.BitwiseExclusiveOr(res, srcA); + Operand tempV1 = context.BitwiseExclusiveOr(srcA, srcB); + + tempV1 = context.BitwiseNot(tempV1); + + Operand tempV = context.BitwiseAnd(tempV0, tempV1); + + context.Copy(GetVF(context), context.ICompareLess(tempV, Const(0))); + + SetZnFlags(context, res, setCC: true, extended: extended); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitAluHelper.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitAluHelper.cs new file mode 100644 index 0000000000..b5bde1a1c1 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitAluHelper.cs @@ -0,0 +1,88 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using System; + +using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static class InstEmitAluHelper + { + public static int GetIntMin(IntegerType type) + { + switch (type) + { + case IntegerType.U8: return byte.MinValue; + case IntegerType.S8: return sbyte.MinValue; + case IntegerType.U16: return ushort.MinValue; + case IntegerType.S16: return short.MinValue; + case IntegerType.U32: return (int)uint.MinValue; + case IntegerType.S32: return int.MinValue; + } + + throw new ArgumentException($"The type \"{type}\" is not a supported int type."); + } + + public static int GetIntMax(IntegerType type) + { + switch (type) + { + case IntegerType.U8: return byte.MaxValue; + case IntegerType.S8: return sbyte.MaxValue; + case IntegerType.U16: return ushort.MaxValue; + case IntegerType.S16: return short.MaxValue; + case IntegerType.U32: return unchecked((int)uint.MaxValue); + case IntegerType.S32: return int.MaxValue; + } + + throw new ArgumentException($"The type \"{type}\" is not a supported int type."); + } + + public static Operand GetPredLogicalOp( + EmitterContext context, + LogicalOperation logicalOp, + Operand input, + Operand pred) + { + switch (logicalOp) + { + case LogicalOperation.And: return context.BitwiseAnd (input, pred); + case LogicalOperation.Or: return context.BitwiseOr (input, pred); + case LogicalOperation.ExclusiveOr: return context.BitwiseExclusiveOr(input, pred); + } + + return input; + } + + public static void SetZnFlags(EmitterContext context, Operand dest, bool setCC, bool extended = false) + { + if (!setCC) + { + return; + } + + if (extended) + { + //When the operation is extended, it means we are doing + //the operation on a long word with any number of bits, + //so we need to AND the zero flag from result with the + //previous result when extended is specified, to ensure + //we have ZF set only if all words are zero, and not just + //the last one. + Operand oldZF = GetZF(context); + + Operand res = context.BitwiseAnd(context.ICompareEqual(dest, Const(0)), oldZF); + + context.Copy(GetZF(context), res); + } + else + { + context.Copy(GetZF(context), context.ICompareEqual(dest, Const(0))); + } + + context.Copy(GetNF(context), context.ICompareLess(dest, Const(0))); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitConversion.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitConversion.cs new file mode 100644 index 0000000000..f5e9af0365 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitConversion.cs @@ -0,0 +1,213 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; + +using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; +using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void F2F(EmitterContext context) + { + OpCodeFArith op = (OpCodeFArith)context.CurrOp; + + FPType srcType = (FPType)op.RawOpCode.Extract(8, 2); + FPType dstType = (FPType)op.RawOpCode.Extract(10, 2); + + bool pass = op.RawOpCode.Extract(40); + bool negateB = op.RawOpCode.Extract(45); + bool absoluteB = op.RawOpCode.Extract(49); + + pass &= op.RoundingMode == RoundingMode.TowardsNegativeInfinity; + + Operand srcB = context.FPAbsNeg(GetSrcB(context, srcType), absoluteB, negateB); + + if (!pass) + { + switch (op.RoundingMode) + { + case RoundingMode.TowardsNegativeInfinity: + srcB = context.FPFloor(srcB); + break; + + case RoundingMode.TowardsPositiveInfinity: + srcB = context.FPCeiling(srcB); + break; + + case RoundingMode.TowardsZero: + srcB = context.FPTruncate(srcB); + break; + } + } + + srcB = context.FPSaturate(srcB, op.Saturate); + + WriteFP(context, dstType, srcB); + + //TODO: CC. + } + + public static void F2I(EmitterContext context) + { + OpCodeFArith op = (OpCodeFArith)context.CurrOp; + + IntegerType intType = (IntegerType)op.RawOpCode.Extract(8, 2); + + bool isSmallInt = intType <= IntegerType.U16; + + FPType floatType = (FPType)op.RawOpCode.Extract(10, 2); + + bool isSignedInt = op.RawOpCode.Extract(12); + bool negateB = op.RawOpCode.Extract(45); + bool absoluteB = op.RawOpCode.Extract(49); + + if (isSignedInt) + { + intType |= IntegerType.S8; + } + + Operand srcB = context.FPAbsNeg(GetSrcB(context, floatType), absoluteB, negateB); + + switch (op.RoundingMode) + { + case RoundingMode.TowardsNegativeInfinity: + srcB = context.FPFloor(srcB); + break; + + case RoundingMode.TowardsPositiveInfinity: + srcB = context.FPCeiling(srcB); + break; + + case RoundingMode.TowardsZero: + srcB = context.FPTruncate(srcB); + break; + } + + srcB = context.FPConvertToS32(srcB); + + //TODO: S/U64, conversion overflow handling. + if (intType != IntegerType.S32) + { + int min = GetIntMin(intType); + int max = GetIntMax(intType); + + srcB = isSignedInt + ? context.IClampS32(srcB, Const(min), Const(max)) + : context.IClampU32(srcB, Const(min), Const(max)); + } + + Operand dest = GetDest(context); + + context.Copy(dest, srcB); + + //TODO: CC. + } + + public static void I2F(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + FPType dstType = (FPType)op.RawOpCode.Extract(8, 2); + + IntegerType srcType = (IntegerType)op.RawOpCode.Extract(10, 2); + + bool isSmallInt = srcType <= IntegerType.U16; + + bool isSignedInt = op.RawOpCode.Extract(13); + bool negateB = op.RawOpCode.Extract(45); + bool absoluteB = op.RawOpCode.Extract(49); + + Operand srcB = context.IAbsNeg(GetSrcB(context), absoluteB, negateB); + + if (isSmallInt) + { + int size = srcType == IntegerType.U16 ? 16 : 8; + + srcB = isSignedInt + ? context.BitfieldExtractS32(srcB, Const(op.ByteSelection * 8), Const(size)) + : context.BitfieldExtractU32(srcB, Const(op.ByteSelection * 8), Const(size)); + } + + srcB = isSignedInt + ? context.IConvertS32ToFP(srcB) + : context.IConvertU32ToFP(srcB); + + WriteFP(context, dstType, srcB); + + //TODO: CC. + } + + public static void I2I(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + IntegerType dstType = (IntegerType)op.RawOpCode.Extract(8, 2); + IntegerType srcType = (IntegerType)op.RawOpCode.Extract(10, 2); + + if (srcType == IntegerType.U64 || dstType == IntegerType.U64) + { + //TODO: Warning. This instruction doesn't support 64-bits integers + } + + bool srcIsSmallInt = srcType <= IntegerType.U16; + + bool dstIsSignedInt = op.RawOpCode.Extract(12); + bool srcIsSignedInt = op.RawOpCode.Extract(13); + bool negateB = op.RawOpCode.Extract(45); + bool absoluteB = op.RawOpCode.Extract(49); + + Operand srcB = GetSrcB(context); + + if (srcIsSmallInt) + { + int size = srcType == IntegerType.U16 ? 16 : 8; + + srcB = srcIsSignedInt + ? context.BitfieldExtractS32(srcB, Const(op.ByteSelection * 8), Const(size)) + : context.BitfieldExtractU32(srcB, Const(op.ByteSelection * 8), Const(size)); + } + + srcB = context.IAbsNeg(srcB, absoluteB, negateB); + + if (op.Saturate) + { + if (dstIsSignedInt) + { + dstType |= IntegerType.S8; + } + + int min = GetIntMin(dstType); + int max = GetIntMax(dstType); + + srcB = dstIsSignedInt + ? context.IClampS32(srcB, Const(min), Const(max)) + : context.IClampU32(srcB, Const(min), Const(max)); + } + + context.Copy(GetDest(context), srcB); + + //TODO: CC. + } + + private static void WriteFP(EmitterContext context, FPType type, Operand srcB) + { + Operand dest = GetDest(context); + + if (type == FPType.FP32) + { + context.Copy(dest, srcB); + } + else if (type == FPType.FP16) + { + context.Copy(dest, context.PackHalf2x16(srcB, ConstF(0))); + } + else + { + //TODO. + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitFArith.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitFArith.cs new file mode 100644 index 0000000000..72492470ca --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitFArith.cs @@ -0,0 +1,369 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using System; + +using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper; +using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void Fadd(EmitterContext context) + { + IOpCodeFArith op = (IOpCodeFArith)context.CurrOp; + + bool absoluteA = op.AbsoluteA, absoluteB, negateA, negateB; + + if (op is OpCodeFArithImm32) + { + negateB = op.RawOpCode.Extract(53); + negateA = op.RawOpCode.Extract(56); + absoluteB = op.RawOpCode.Extract(57); + } + else + { + negateB = op.RawOpCode.Extract(45); + negateA = op.RawOpCode.Extract(48); + absoluteB = op.RawOpCode.Extract(49); + } + + Operand srcA = context.FPAbsNeg(GetSrcA(context), absoluteA, negateA); + Operand srcB = context.FPAbsNeg(GetSrcB(context), absoluteB, negateB); + + Operand dest = GetDest(context); + + context.Copy(dest, context.FPSaturate(context.FPAdd(srcA, srcB), op.Saturate)); + + SetFPZnFlags(context, dest, op.SetCondCode); + } + + public static void Ffma(EmitterContext context) + { + IOpCodeFArith op = (IOpCodeFArith)context.CurrOp; + + bool negateB = op.RawOpCode.Extract(48); + bool negateC = op.RawOpCode.Extract(49); + + Operand srcA = GetSrcA(context); + + Operand srcB = context.FPNegate(GetSrcB(context), negateB); + Operand srcC = context.FPNegate(GetSrcC(context), negateC); + + Operand dest = GetDest(context); + + context.Copy(dest, context.FPSaturate(context.FPFusedMultiplyAdd(srcA, srcB, srcC), op.Saturate)); + + SetFPZnFlags(context, dest, op.SetCondCode); + } + + public static void Fmnmx(EmitterContext context) + { + IOpCodeFArith op = (IOpCodeFArith)context.CurrOp; + + bool absoluteA = op.AbsoluteA; + bool negateB = op.RawOpCode.Extract(45); + bool negateA = op.RawOpCode.Extract(48); + bool absoluteB = op.RawOpCode.Extract(49); + + Operand srcA = context.FPAbsNeg(GetSrcA(context), absoluteA, negateA); + Operand srcB = context.FPAbsNeg(GetSrcB(context), absoluteB, negateB); + + Operand resMin = context.FPMinimum(srcA, srcB); + Operand resMax = context.FPMaximum(srcA, srcB); + + Operand pred = GetPredicate39(context); + + Operand dest = GetDest(context); + + context.Copy(dest, context.ConditionalSelect(pred, resMin, resMax)); + + SetFPZnFlags(context, dest, op.SetCondCode); + } + + public static void Fmul(EmitterContext context) + { + IOpCodeFArith op = (IOpCodeFArith)context.CurrOp; + + bool negateB = !(op is OpCodeFArithImm32) && op.RawOpCode.Extract(48); + + Operand srcA = GetSrcA(context); + + Operand srcB = context.FPNegate(GetSrcB(context), negateB); + + switch (op.Scale) + { + case FmulScale.None: break; + + case FmulScale.Divide2: srcA = context.FPDivide (srcA, ConstF(2)); break; + case FmulScale.Divide4: srcA = context.FPDivide (srcA, ConstF(4)); break; + case FmulScale.Divide8: srcA = context.FPDivide (srcA, ConstF(8)); break; + case FmulScale.Multiply2: srcA = context.FPMultiply(srcA, ConstF(2)); break; + case FmulScale.Multiply4: srcA = context.FPMultiply(srcA, ConstF(4)); break; + case FmulScale.Multiply8: srcA = context.FPMultiply(srcA, ConstF(8)); break; + + default: break; //TODO: Warning. + } + + Operand dest = GetDest(context); + + context.Copy(dest, context.FPSaturate(context.FPMultiply(srcA, srcB), op.Saturate)); + + SetFPZnFlags(context, dest, op.SetCondCode); + } + + public static void Fset(EmitterContext context) + { + OpCodeSet op = (OpCodeSet)context.CurrOp; + + Condition cmpOp = (Condition)op.RawOpCode.Extract(48, 4); + + bool negateA = op.RawOpCode.Extract(43); + bool absoluteB = op.RawOpCode.Extract(44); + bool boolFloat = op.RawOpCode.Extract(52); + bool negateB = op.RawOpCode.Extract(53); + bool absoluteA = op.RawOpCode.Extract(54); + + Operand srcA = context.FPAbsNeg(GetSrcA(context), absoluteA, negateA); + Operand srcB = context.FPAbsNeg(GetSrcB(context), absoluteB, negateB); + + Operand res = GetFPComparison(context, cmpOp, srcA, srcB); + + Operand pred = GetPredicate39(context); + + res = GetPredLogicalOp(context, op.LogicalOp, res, pred); + + Operand dest = GetDest(context); + + if (boolFloat) + { + context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0))); + } + else + { + context.Copy(dest, res); + } + + //TODO: CC, X + } + + public static void Fsetp(EmitterContext context) + { + OpCodeSet op = (OpCodeSet)context.CurrOp; + + Condition cmpOp = (Condition)op.RawOpCode.Extract(48, 4); + + bool absoluteA = op.RawOpCode.Extract(7); + bool negateA = op.RawOpCode.Extract(43); + bool absoluteB = op.RawOpCode.Extract(44); + + Operand srcA = context.FPAbsNeg (GetSrcA(context), absoluteA, negateA); + Operand srcB = context.FPAbsolute(GetSrcB(context), absoluteB); + + Operand p0Res = GetFPComparison(context, cmpOp, srcA, srcB); + + Operand p1Res = context.BitwiseNot(p0Res); + + Operand pred = GetPredicate39(context); + + p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred); + p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred); + + context.Copy(Register(op.Predicate3), p0Res); + context.Copy(Register(op.Predicate0), p1Res); + } + + public static void Hadd2(EmitterContext context) + { + Hadd2Hmul2Impl(context, isAdd: true); + } + + public static void Hfma2(EmitterContext context) + { + IOpCodeHfma op = (IOpCodeHfma)context.CurrOp; + + Operand[] srcA = GetHfmaSrcA(context); + Operand[] srcB = GetHfmaSrcB(context); + Operand[] srcC = GetHfmaSrcC(context); + + Operand[] res = new Operand[2]; + + for (int index = 0; index < res.Length; index++) + { + res[index] = context.FPFusedMultiplyAdd(srcA[index], srcB[index], srcC[index]); + + res[index] = context.FPSaturate(res[index], op.Saturate); + } + + context.Copy(GetDest(context), GetHalfPacked(context, res)); + } + + public static void Hmul2(EmitterContext context) + { + Hadd2Hmul2Impl(context, isAdd: false); + } + + private static void Hadd2Hmul2Impl(EmitterContext context, bool isAdd) + { + OpCode op = context.CurrOp; + + bool saturate = op.RawOpCode.Extract(op is OpCodeAluImm32 ? 52 : 32); + + Operand[] srcA = GetHalfSrcA(context); + Operand[] srcB = GetHalfSrcB(context); + + Operand[] res = new Operand[2]; + + for (int index = 0; index < res.Length; index++) + { + if (isAdd) + { + res[index] = context.FPAdd(srcA[index], srcB[index]); + } + else + { + res[index] = context.FPMultiply(srcA[index], srcB[index]); + } + + res[index] = context.FPSaturate(res[index], saturate); + } + + context.Copy(GetDest(context), GetHalfPacked(context, res)); + } + + public static void Mufu(EmitterContext context) + { + IOpCodeFArith op = (IOpCodeFArith)context.CurrOp; + + bool negateB = op.RawOpCode.Extract(48); + + Operand res = context.FPAbsNeg(GetSrcA(context), op.AbsoluteA, negateB); + + MufuOperation subOp = (MufuOperation)context.CurrOp.RawOpCode.Extract(20, 4); + + switch (subOp) + { + case MufuOperation.Cosine: + res = context.FPCosine(res); + break; + + case MufuOperation.Sine: + res = context.FPSine(res); + break; + + case MufuOperation.ExponentB2: + res = context.FPExponentB2(res); + break; + + case MufuOperation.LogarithmB2: + res = context.FPLogarithmB2(res); + break; + + case MufuOperation.Reciprocal: + res = context.FPReciprocal(res); + break; + + case MufuOperation.ReciprocalSquareRoot: + res = context.FPReciprocalSquareRoot(res); + break; + + case MufuOperation.SquareRoot: + res = context.FPSquareRoot(res); + break; + + default: /* TODO */ break; + } + + context.Copy(GetDest(context), context.FPSaturate(res, op.Saturate)); + } + + private static Operand GetFPComparison( + EmitterContext context, + Condition cond, + Operand srcA, + Operand srcB) + { + Operand res; + + if (cond == Condition.Always) + { + res = Const(IrConsts.True); + } + else if (cond == Condition.Never) + { + res = Const(IrConsts.False); + } + else if (cond == Condition.Nan || cond == Condition.Number) + { + res = context.BitwiseOr(context.IsNan(srcA), context.IsNan(srcB)); + + if (cond == Condition.Number) + { + res = context.BitwiseNot(res); + } + } + else + { + Instruction inst; + + switch (cond & ~Condition.Nan) + { + case Condition.Less: inst = Instruction.CompareLess; break; + case Condition.Equal: inst = Instruction.CompareEqual; break; + case Condition.LessOrEqual: inst = Instruction.CompareLessOrEqual; break; + case Condition.Greater: inst = Instruction.CompareGreater; break; + case Condition.NotEqual: inst = Instruction.CompareNotEqual; break; + case Condition.GreaterOrEqual: inst = Instruction.CompareGreaterOrEqual; break; + + default: throw new InvalidOperationException($"Unexpected condition \"{cond}\"."); + } + + res = context.Add(inst | Instruction.FP, Local(), srcA, srcB); + + if ((cond & Condition.Nan) != 0) + { + res = context.BitwiseOr(res, context.IsNan(srcA)); + res = context.BitwiseOr(res, context.IsNan(srcB)); + } + } + + return res; + } + + private static void SetFPZnFlags(EmitterContext context, Operand dest, bool setCC) + { + if (setCC) + { + context.Copy(GetZF(context), context.FPCompareEqual(dest, ConstF(0))); + context.Copy(GetNF(context), context.FPCompareLess (dest, ConstF(0))); + } + } + + private static Operand[] GetHfmaSrcA(EmitterContext context) + { + IOpCodeHfma op = (IOpCodeHfma)context.CurrOp; + + return GetHalfSources(context, GetSrcA(context), op.SwizzleA); + } + + private static Operand[] GetHfmaSrcB(EmitterContext context) + { + IOpCodeHfma op = (IOpCodeHfma)context.CurrOp; + + Operand[] operands = GetHalfSources(context, GetSrcB(context), op.SwizzleB); + + return FPAbsNeg(context, operands, false, op.NegateB); + } + + private static Operand[] GetHfmaSrcC(EmitterContext context) + { + IOpCodeHfma op = (IOpCodeHfma)context.CurrOp; + + Operand[] operands = GetHalfSources(context, GetSrcC(context), op.SwizzleC); + + return FPAbsNeg(context, operands, false, op.NegateC); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitFlow.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitFlow.cs new file mode 100644 index 0000000000..c4523f75cf --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitFlow.cs @@ -0,0 +1,107 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using System.Collections.Generic; +using System.Linq; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void Bra(EmitterContext context) + { + EmitBranch(context, context.CurrBlock.Branch.Address); + } + + public static void Exit(EmitterContext context) + { + OpCodeExit op = (OpCodeExit)context.CurrOp; + + //TODO: Figure out how this is supposed to work in the + //presence of other condition codes. + if (op.Condition == Condition.Always) + { + context.Return(); + } + } + + public static void Kil(EmitterContext context) + { + context.Discard(); + } + + public static void Ssy(EmitterContext context) + { + OpCodeSsy op = (OpCodeSsy)context.CurrOp; + + foreach (KeyValuePair kv in op.Syncs) + { + OpCodeSync opSync = kv.Key; + + Operand local = kv.Value; + + int ssyIndex = opSync.Targets[op]; + + context.Copy(local, Const(ssyIndex)); + } + } + + public static void Sync(EmitterContext context) + { + OpCodeSync op = (OpCodeSync)context.CurrOp; + + if (op.Targets.Count == 1) + { + //If we have only one target, then the SSY is basically + //a branch, we can produce better codegen for this case. + OpCodeSsy opSsy = op.Targets.Keys.First(); + + EmitBranch(context, opSsy.GetAbsoluteAddress()); + } + else + { + foreach (KeyValuePair kv in op.Targets) + { + OpCodeSsy opSsy = kv.Key; + + Operand label = context.GetLabel(opSsy.GetAbsoluteAddress()); + + Operand local = opSsy.Syncs[op]; + + int ssyIndex = kv.Value; + + context.BranchIfTrue(label, context.ICompareEqual(local, Const(ssyIndex))); + } + } + } + + private static void EmitBranch(EmitterContext context, ulong address) + { + //If we're branching to the next instruction, then the branch + //is useless and we can ignore it. + if (address == context.CurrOp.Address + 8) + { + return; + } + + Operand label = context.GetLabel(address); + + Operand pred = Register(context.CurrOp.Predicate); + + if (context.CurrOp.Predicate.IsPT) + { + context.Branch(label); + } + else if (context.CurrOp.InvertPredicate) + { + context.BranchIfFalse(label, pred); + } + else + { + context.BranchIfTrue(label, pred); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitHelper.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitHelper.cs new file mode 100644 index 0000000000..e31528d02b --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitHelper.cs @@ -0,0 +1,267 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using System; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static class InstEmitHelper + { + public static Operand GetZF(EmitterContext context) + { + return Register(0, RegisterType.Flag); + } + + public static Operand GetNF(EmitterContext context) + { + return Register(1, RegisterType.Flag); + } + + public static Operand GetCF(EmitterContext context) + { + return Register(2, RegisterType.Flag); + } + + public static Operand GetVF(EmitterContext context) + { + return Register(3, RegisterType.Flag); + } + + public static Operand GetDest(EmitterContext context) + { + return Register(((IOpCodeRd)context.CurrOp).Rd); + } + + public static Operand GetSrcA(EmitterContext context) + { + return Register(((IOpCodeRa)context.CurrOp).Ra); + } + + public static Operand GetSrcB(EmitterContext context, FPType floatType) + { + if (floatType == FPType.FP32) + { + return GetSrcB(context); + } + else if (floatType == FPType.FP16) + { + int h = context.CurrOp.RawOpCode.Extract(41, 1); + + return GetHalfSources(context, GetSrcB(context), FPHalfSwizzle.FP16)[h]; + } + else if (floatType == FPType.FP64) + { + //TODO. + } + + throw new ArgumentException($"Invalid floating point type \"{floatType}\"."); + } + + public static Operand GetSrcB(EmitterContext context) + { + switch (context.CurrOp) + { + case IOpCodeCbuf op: + return Cbuf(op.Slot, op.Offset); + + case IOpCodeImm op: + return Const(op.Immediate); + + case IOpCodeImmF op: + return ConstF(op.Immediate); + + case IOpCodeReg op: + return Register(op.Rb); + + case IOpCodeRegCbuf op: + return Register(op.Rc); + } + + throw new InvalidOperationException($"Unexpected opcode type \"{context.CurrOp.GetType().Name}\"."); + } + + public static Operand GetSrcC(EmitterContext context) + { + switch (context.CurrOp) + { + case IOpCodeRegCbuf op: + return Cbuf(op.Slot, op.Offset); + + case IOpCodeRc op: + return Register(op.Rc); + } + + throw new InvalidOperationException($"Unexpected opcode type \"{context.CurrOp.GetType().Name}\"."); + } + + public static Operand[] GetHalfSrcA(EmitterContext context) + { + OpCode op = context.CurrOp; + + bool absoluteA = false, negateA = false; + + if (op is IOpCodeCbuf || op is IOpCodeImm) + { + negateA = op.RawOpCode.Extract(43); + absoluteA = op.RawOpCode.Extract(44); + } + else if (op is IOpCodeReg) + { + absoluteA = op.RawOpCode.Extract(44); + } + else if (op is OpCodeAluImm32 && op.Emitter == InstEmit.Hadd2) + { + negateA = op.RawOpCode.Extract(56); + } + + FPHalfSwizzle swizzle = (FPHalfSwizzle)op.RawOpCode.Extract(47, 2); + + Operand[] operands = GetHalfSources(context, GetSrcA(context), swizzle); + + return FPAbsNeg(context, operands, absoluteA, negateA); + } + + public static Operand[] GetHalfSrcB(EmitterContext context) + { + OpCode op = context.CurrOp; + + FPHalfSwizzle swizzle = FPHalfSwizzle.FP16; + + bool absoluteB = false, negateB = false; + + if (op is IOpCodeReg) + { + swizzle = (FPHalfSwizzle)op.RawOpCode.Extract(28, 2); + + absoluteB = op.RawOpCode.Extract(30); + negateB = op.RawOpCode.Extract(31); + } + else if (op is IOpCodeCbuf) + { + swizzle = FPHalfSwizzle.FP32; + + absoluteB = op.RawOpCode.Extract(54); + } + + Operand[] operands = GetHalfSources(context, GetSrcB(context), swizzle); + + return FPAbsNeg(context, operands, absoluteB, negateB); + } + + public static Operand[] FPAbsNeg(EmitterContext context, Operand[] operands, bool abs, bool neg) + { + for (int index = 0; index < operands.Length; index++) + { + operands[index] = context.FPAbsNeg(operands[index], abs, neg); + } + + return operands; + } + + public static Operand[] GetHalfSources(EmitterContext context, Operand src, FPHalfSwizzle swizzle) + { + switch (swizzle) + { + case FPHalfSwizzle.FP16: + return new Operand[] + { + context.UnpackHalf2x16Low (src), + context.UnpackHalf2x16High(src) + }; + + case FPHalfSwizzle.FP32: return new Operand[] { src, src }; + + case FPHalfSwizzle.DupH0: + return new Operand[] + { + context.UnpackHalf2x16Low(src), + context.UnpackHalf2x16Low(src) + }; + + case FPHalfSwizzle.DupH1: + return new Operand[] + { + context.UnpackHalf2x16High(src), + context.UnpackHalf2x16High(src) + }; + } + + throw new ArgumentException($"Invalid swizzle \"{swizzle}\"."); + } + + public static Operand GetHalfPacked(EmitterContext context, Operand[] results) + { + OpCode op = context.CurrOp; + + FPHalfSwizzle swizzle = FPHalfSwizzle.FP16; + + if (!(op is OpCodeAluImm32)) + { + swizzle = (FPHalfSwizzle)context.CurrOp.RawOpCode.Extract(49, 2); + } + + switch (swizzle) + { + case FPHalfSwizzle.FP16: return context.PackHalf2x16(results[0], results[1]); + + case FPHalfSwizzle.FP32: return results[0]; + + case FPHalfSwizzle.DupH0: + { + Operand h1 = GetHalfDest(context, isHigh: true); + + return context.PackHalf2x16(results[0], h1); + } + + case FPHalfSwizzle.DupH1: + { + Operand h0 = GetHalfDest(context, isHigh: false); + + return context.PackHalf2x16(h0, results[1]); + } + } + + throw new ArgumentException($"Invalid swizzle \"{swizzle}\"."); + } + + public static Operand GetHalfDest(EmitterContext context, bool isHigh) + { + if (isHigh) + { + return context.UnpackHalf2x16High(GetDest(context)); + } + else + { + return context.UnpackHalf2x16Low(GetDest(context)); + } + } + + public static Operand GetPredicate39(EmitterContext context) + { + IOpCodeAlu op = (IOpCodeAlu)context.CurrOp; + + Operand local = Register(op.Predicate39); + + if (op.InvertP) + { + local = context.BitwiseNot(local); + } + + return local; + } + + public static Operand SignExtendTo32(EmitterContext context, Operand src, int srcBits) + { + return context.BitfieldExtractS32(src, Const(0), Const(srcBits)); + } + + public static Operand ZeroExtendTo32(EmitterContext context, Operand src, int srcBits) + { + int mask = (int)(0xffffffffu >> (32 - srcBits)); + + return context.BitwiseAnd(src, Const(mask)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitMemory.cs new file mode 100644 index 0000000000..d81e97a13f --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitMemory.cs @@ -0,0 +1,138 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; + +using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void Ald(EmitterContext context) + { + OpCodeAttribute op = (OpCodeAttribute)context.CurrOp; + + Operand[] elems = new Operand[op.Count]; + + for (int index = 0; index < op.Count; index++) + { + Operand src = Attribute(op.AttributeOffset + index * 4); + + context.Copy(elems[index] = Local(), src); + } + + for (int index = 0; index < op.Count; index++) + { + Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); + + if (rd.IsRZ) + { + break; + } + + context.Copy(Register(rd), elems[index]); + } + } + + public static void Ast(EmitterContext context) + { + OpCodeAttribute op = (OpCodeAttribute)context.CurrOp; + + for (int index = 0; index < op.Count; index++) + { + if (op.Rd.Index + index > RegisterConsts.RegisterZeroIndex) + { + break; + } + + Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); + + Operand dest = Attribute(op.AttributeOffset + index * 4); + + context.Copy(dest, Register(rd)); + } + } + + public static void Ipa(EmitterContext context) + { + OpCodeIpa op = (OpCodeIpa)context.CurrOp; + + Operand srcA = new Operand(OperandType.Attribute, op.AttributeOffset); + + Operand srcB = GetSrcB(context); + + context.Copy(GetDest(context), srcA); + } + + public static void Ldc(EmitterContext context) + { + OpCodeLdc op = (OpCodeLdc)context.CurrOp; + + if (op.Size > IntegerSize.B64) + { + //TODO: Warning. + } + + bool isSmallInt = op.Size < IntegerSize.B32; + + int count = op.Size == IntegerSize.B64 ? 2 : 1; + + Operand baseOffset = context.Copy(GetSrcA(context)); + + for (int index = 0; index < count; index++) + { + Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); + + if (rd.IsRZ) + { + break; + } + + Operand offset = context.IAdd(baseOffset, Const((op.Offset + index) * 4)); + + Operand value = context.LoadConstant(Const(op.Slot), offset); + + if (isSmallInt) + { + Operand shift = context.BitwiseAnd(baseOffset, Const(3)); + + value = context.ShiftRightU32(value, shift); + + switch (op.Size) + { + case IntegerSize.U8: value = ZeroExtendTo32(context, value, 8); break; + case IntegerSize.U16: value = ZeroExtendTo32(context, value, 16); break; + case IntegerSize.S8: value = SignExtendTo32(context, value, 8); break; + case IntegerSize.S16: value = SignExtendTo32(context, value, 16); break; + } + } + + context.Copy(Register(rd), value); + } + } + + public static void Out(EmitterContext context) + { + OpCode op = context.CurrOp; + + bool emit = op.RawOpCode.Extract(39); + bool cut = op.RawOpCode.Extract(40); + + if (!(emit || cut)) + { + //TODO: Warning. + } + + if (emit) + { + context.EmitVertex(); + } + + if (cut) + { + context.EndPrimitive(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitMove.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitMove.cs new file mode 100644 index 0000000000..b74dbfd721 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitMove.cs @@ -0,0 +1,32 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; + +using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void Mov(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + context.Copy(GetDest(context), GetSrcB(context)); + } + + public static void Sel(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + Operand pred = GetPredicate39(context); + + Operand srcA = GetSrcA(context); + Operand srcB = GetSrcB(context); + + Operand res = context.ConditionalSelect(pred, srcA, srcB); + + context.Copy(GetDest(context), res); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitTexture.cs new file mode 100644 index 0000000000..1b19d901cf --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitTexture.cs @@ -0,0 +1,776 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; +using System; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void Tex(EmitterContext context) + { + Tex(context, TextureFlags.None); + } + + public static void Tex_B(EmitterContext context) + { + Tex(context, TextureFlags.Bindless); + } + + public static void Tld(EmitterContext context) + { + Tex(context, TextureFlags.IntCoords); + } + + public static void Tld_B(EmitterContext context) + { + Tex(context, TextureFlags.IntCoords | TextureFlags.Bindless); + } + + public static void Texs(EmitterContext context) + { + OpCodeTextureScalar op = (OpCodeTextureScalar)context.CurrOp; + + if (op.Rd0.IsRZ && op.Rd1.IsRZ) + { + return; + } + + List sourcesList = new List(); + + int raIndex = op.Ra.Index; + int rbIndex = op.Rb.Index; + + Operand Ra() + { + if (raIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return context.Copy(Register(raIndex++, RegisterType.Gpr)); + } + + Operand Rb() + { + if (rbIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return context.Copy(Register(rbIndex++, RegisterType.Gpr)); + } + + void AddTextureOffset(int coordsCount, int stride, int size) + { + Operand packedOffs = Rb(); + + for (int index = 0; index < coordsCount; index++) + { + sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * stride), Const(size))); + } + } + + TextureType type; + TextureFlags flags; + + if (op is OpCodeTexs texsOp) + { + type = GetTextureType (texsOp.Type); + flags = GetTextureFlags(texsOp.Type); + + if ((type & TextureType.Array) != 0) + { + Operand arrayIndex = Ra(); + + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + + sourcesList.Add(arrayIndex); + + if ((type & TextureType.Shadow) != 0) + { + sourcesList.Add(Rb()); + } + + if ((flags & TextureFlags.LodLevel) != 0) + { + sourcesList.Add(ConstF(0)); + } + } + else + { + switch (texsOp.Type) + { + case TextureScalarType.Texture1DLodZero: + sourcesList.Add(Ra()); + break; + + case TextureScalarType.Texture2D: + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TextureScalarType.Texture2DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(ConstF(0)); + break; + + case TextureScalarType.Texture2DLodLevel: + case TextureScalarType.Texture2DDepthCompare: + case TextureScalarType.Texture3D: + case TextureScalarType.TextureCube: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TextureScalarType.Texture2DLodZeroDepthCompare: + case TextureScalarType.Texture3DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(ConstF(0)); + break; + + case TextureScalarType.Texture2DLodLevelDepthCompare: + case TextureScalarType.TextureCubeLodLevel: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(Rb()); + break; + } + } + } + else if (op is OpCodeTlds tldsOp) + { + type = GetTextureType (tldsOp.Type); + flags = GetTextureFlags(tldsOp.Type) | TextureFlags.IntCoords; + + switch (tldsOp.Type) + { + case TexelLoadScalarType.Texture1DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Const(0)); + break; + + case TexelLoadScalarType.Texture1DLodLevel: + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TexelLoadScalarType.Texture2DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(Const(0)); + break; + + case TexelLoadScalarType.Texture2DLodZeroOffset: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Const(0)); + break; + + case TexelLoadScalarType.Texture2DLodZeroMultisample: + case TexelLoadScalarType.Texture2DLodLevel: + case TexelLoadScalarType.Texture2DLodLevelOffset: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + break; + + case TexelLoadScalarType.Texture3DLodZero: + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + sourcesList.Add(Const(0)); + break; + + case TexelLoadScalarType.Texture2DArrayLodZero: + sourcesList.Add(Rb()); + sourcesList.Add(Rb()); + sourcesList.Add(Ra()); + sourcesList.Add(Const(0)); + break; + } + + if ((flags & TextureFlags.Offset) != 0) + { + AddTextureOffset(type.GetCoordsCount(), 4, 4); + } + } + else if (op is OpCodeTld4s tld4sOp) + { + if (!(tld4sOp.HasDepthCompare || tld4sOp.HasOffset)) + { + sourcesList.Add(Ra()); + sourcesList.Add(Rb()); + } + else + { + sourcesList.Add(Ra()); + sourcesList.Add(Ra()); + } + + type = TextureType.Texture2D; + flags = TextureFlags.Gather; + + if (tld4sOp.HasDepthCompare) + { + sourcesList.Add(Rb()); + + type |= TextureType.Shadow; + } + + if (tld4sOp.HasOffset) + { + AddTextureOffset(type.GetCoordsCount(), 8, 6); + + flags |= TextureFlags.Offset; + } + + sourcesList.Add(Const(tld4sOp.GatherCompIndex)); + } + else + { + throw new InvalidOperationException($"Invalid opcode type \"{op.GetType().Name}\"."); + } + + Operand[] sources = sourcesList.ToArray(); + + Operand[] rd0 = new Operand[2] { ConstF(0), ConstF(0) }; + Operand[] rd1 = new Operand[2] { ConstF(0), ConstF(0) }; + + int destIncrement = 0; + + Operand GetDest() + { + int high = destIncrement >> 1; + int low = destIncrement & 1; + + destIncrement++; + + if (op.IsFp16) + { + return high != 0 + ? (rd1[low] = Local()) + : (rd0[low] = Local()); + } + else + { + int rdIndex = high != 0 ? op.Rd1.Index : op.Rd0.Index; + + if (rdIndex < RegisterConsts.RegisterZeroIndex) + { + rdIndex += low; + } + + return Register(rdIndex, RegisterType.Gpr); + } + } + + int handle = op.Immediate; + + for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) + { + if ((compMask & 1) != 0) + { + Operand dest = GetDest(); + + TextureOperation operation = new TextureOperation( + Instruction.TextureSample, + type, + flags, + handle, + compIndex, + dest, + sources); + + context.Add(operation); + } + } + + if (op.IsFp16) + { + context.Copy(Register(op.Rd0), context.PackHalf2x16(rd0[0], rd0[1])); + context.Copy(Register(op.Rd1), context.PackHalf2x16(rd1[0], rd1[1])); + } + } + + public static void Tld4(EmitterContext context) + { + OpCodeTld4 op = (OpCodeTld4)context.CurrOp; + + if (op.Rd.IsRZ) + { + return; + } + + int raIndex = op.Ra.Index; + int rbIndex = op.Rb.Index; + + Operand Ra() + { + if (raIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return context.Copy(Register(raIndex++, RegisterType.Gpr)); + } + + Operand Rb() + { + if (rbIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return context.Copy(Register(rbIndex++, RegisterType.Gpr)); + } + + Operand arrayIndex = op.IsArray ? Ra() : null; + + List sourcesList = new List(); + + TextureType type = GetTextureType(op.Dimensions); + + TextureFlags flags = TextureFlags.Gather; + + int coordsCount = type.GetCoordsCount(); + + for (int index = 0; index < coordsCount; index++) + { + sourcesList.Add(Ra()); + } + + if (op.IsArray) + { + sourcesList.Add(arrayIndex); + + type |= TextureType.Array; + } + + Operand[] packedOffs = new Operand[2]; + + packedOffs[0] = op.Offset != TextureGatherOffset.None ? Rb() : null; + packedOffs[1] = op.Offset == TextureGatherOffset.Offsets ? Rb() : null; + + if (op.HasDepthCompare) + { + sourcesList.Add(Rb()); + + type |= TextureType.Shadow; + } + + if (op.Offset != TextureGatherOffset.None) + { + int offsetTexelsCount = op.Offset == TextureGatherOffset.Offsets ? 4 : 1; + + for (int index = 0; index < coordsCount * offsetTexelsCount; index++) + { + Operand packed = packedOffs[(index >> 2) & 1]; + + sourcesList.Add(context.BitfieldExtractS32(packed, Const((index & 3) * 8), Const(6))); + } + + flags |= op.Offset == TextureGatherOffset.Offsets + ? TextureFlags.Offsets + : TextureFlags.Offset; + } + + sourcesList.Add(Const(op.GatherCompIndex)); + + Operand[] sources = sourcesList.ToArray(); + + int rdIndex = op.Rd.Index; + + Operand GetDest() + { + if (rdIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return Register(rdIndex++, RegisterType.Gpr); + } + + int handle = op.Immediate; + + for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) + { + if ((compMask & 1) != 0) + { + Operand dest = GetDest(); + + TextureOperation operation = new TextureOperation( + Instruction.TextureSample, + type, + flags, + handle, + compIndex, + dest, + sources); + + context.Add(operation); + } + } + } + + public static void Txq(EmitterContext context) + { + Txq(context, bindless: false); + } + + public static void Txq_B(EmitterContext context) + { + Txq(context, bindless: true); + } + + private static void Txq(EmitterContext context, bool bindless) + { + OpCodeTex op = (OpCodeTex)context.CurrOp; + + if (op.Rd.IsRZ) + { + return; + } + + TextureProperty property = (TextureProperty)op.RawOpCode.Extract(22, 6); + + //TODO: Validate and use property. + Instruction inst = Instruction.TextureSize; + + TextureType type = TextureType.Texture2D; + + TextureFlags flags = bindless ? TextureFlags.Bindless : TextureFlags.None; + + int raIndex = op.Ra.Index; + + Operand Ra() + { + if (raIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return context.Copy(Register(raIndex++, RegisterType.Gpr)); + } + + List sourcesList = new List(); + + if (bindless) + { + sourcesList.Add(Ra()); + } + + sourcesList.Add(Ra()); + + Operand[] sources = sourcesList.ToArray(); + + int rdIndex = op.Rd.Index; + + Operand GetDest() + { + if (rdIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return Register(rdIndex++, RegisterType.Gpr); + } + + int handle = !bindless ? op.Immediate : 0; + + for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) + { + if ((compMask & 1) != 0) + { + Operand dest = GetDest(); + + TextureOperation operation = new TextureOperation( + inst, + type, + flags, + handle, + compIndex, + dest, + sources); + + context.Add(operation); + } + } + } + + private static void Tex(EmitterContext context, TextureFlags flags) + { + OpCodeTexture op = (OpCodeTexture)context.CurrOp; + + bool isBindless = (flags & TextureFlags.Bindless) != 0; + bool intCoords = (flags & TextureFlags.IntCoords) != 0; + + if (op.Rd.IsRZ) + { + return; + } + + int raIndex = op.Ra.Index; + int rbIndex = op.Rb.Index; + + Operand Ra() + { + if (raIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return context.Copy(Register(raIndex++, RegisterType.Gpr)); + } + + Operand Rb() + { + if (rbIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return context.Copy(Register(rbIndex++, RegisterType.Gpr)); + } + + Operand arrayIndex = op.IsArray ? Ra() : null; + + List sourcesList = new List(); + + if (isBindless) + { + sourcesList.Add(Rb()); + } + + TextureType type = GetTextureType(op.Dimensions); + + int coordsCount = type.GetCoordsCount(); + + for (int index = 0; index < coordsCount; index++) + { + sourcesList.Add(Ra()); + } + + if (op.IsArray) + { + sourcesList.Add(arrayIndex); + + type |= TextureType.Array; + } + + bool hasLod = op.LodMode > TextureLodMode.LodZero; + + Operand lodValue = hasLod ? Rb() : ConstF(0); + + Operand packedOffs = op.HasOffset ? Rb() : null; + + if (op.HasDepthCompare) + { + sourcesList.Add(Rb()); + + type |= TextureType.Shadow; + } + + if ((op.LodMode == TextureLodMode.LodZero || + op.LodMode == TextureLodMode.LodLevel || + op.LodMode == TextureLodMode.LodLevelA) && !op.IsMultisample) + { + sourcesList.Add(lodValue); + + flags |= TextureFlags.LodLevel; + } + + if (op.HasOffset) + { + for (int index = 0; index < coordsCount; index++) + { + sourcesList.Add(context.BitfieldExtractS32(packedOffs, Const(index * 4), Const(4))); + } + + flags |= TextureFlags.Offset; + } + + if (op.LodMode == TextureLodMode.LodBias || + op.LodMode == TextureLodMode.LodBiasA) + { + sourcesList.Add(lodValue); + + flags |= TextureFlags.LodBias; + } + + if (op.IsMultisample) + { + sourcesList.Add(Rb()); + + type |= TextureType.Multisample; + } + + Operand[] sources = sourcesList.ToArray(); + + int rdIndex = op.Rd.Index; + + Operand GetDest() + { + if (rdIndex > RegisterConsts.RegisterZeroIndex) + { + return Const(0); + } + + return Register(rdIndex++, RegisterType.Gpr); + } + + int handle = !isBindless ? op.Immediate : 0; + + for (int compMask = op.ComponentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++) + { + if ((compMask & 1) != 0) + { + Operand dest = GetDest(); + + TextureOperation operation = new TextureOperation( + Instruction.TextureSample, + type, + flags, + handle, + compIndex, + dest, + sources); + + context.Add(operation); + } + } + } + + private static TextureType GetTextureType(TextureDimensions dimensions) + { + switch (dimensions) + { + case TextureDimensions.Texture1D: return TextureType.Texture1D; + case TextureDimensions.Texture2D: return TextureType.Texture2D; + case TextureDimensions.Texture3D: return TextureType.Texture3D; + case TextureDimensions.TextureCube: return TextureType.TextureCube; + } + + throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\"."); + } + + private static TextureType GetTextureType(TextureScalarType type) + { + switch (type) + { + case TextureScalarType.Texture1DLodZero: + return TextureType.Texture1D; + + case TextureScalarType.Texture2D: + case TextureScalarType.Texture2DLodZero: + case TextureScalarType.Texture2DLodLevel: + return TextureType.Texture2D; + + case TextureScalarType.Texture2DDepthCompare: + case TextureScalarType.Texture2DLodLevelDepthCompare: + case TextureScalarType.Texture2DLodZeroDepthCompare: + return TextureType.Texture2D | TextureType.Shadow; + + case TextureScalarType.Texture2DArray: + case TextureScalarType.Texture2DArrayLodZero: + return TextureType.Texture2D | TextureType.Array; + + case TextureScalarType.Texture2DArrayLodZeroDepthCompare: + return TextureType.Texture2D | TextureType.Array | TextureType.Shadow; + + case TextureScalarType.Texture3D: + case TextureScalarType.Texture3DLodZero: + return TextureType.Texture3D; + + case TextureScalarType.TextureCube: + case TextureScalarType.TextureCubeLodLevel: + return TextureType.TextureCube; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } + + private static TextureType GetTextureType(TexelLoadScalarType type) + { + switch (type) + { + case TexelLoadScalarType.Texture1DLodZero: + case TexelLoadScalarType.Texture1DLodLevel: + return TextureType.Texture1D; + + case TexelLoadScalarType.Texture2DLodZero: + case TexelLoadScalarType.Texture2DLodZeroOffset: + case TexelLoadScalarType.Texture2DLodLevel: + case TexelLoadScalarType.Texture2DLodLevelOffset: + return TextureType.Texture2D; + + case TexelLoadScalarType.Texture2DLodZeroMultisample: + return TextureType.Texture2D | TextureType.Multisample; + + case TexelLoadScalarType.Texture3DLodZero: + return TextureType.Texture3D; + + case TexelLoadScalarType.Texture2DArrayLodZero: + return TextureType.Texture2D | TextureType.Array; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } + + private static TextureFlags GetTextureFlags(TextureScalarType type) + { + switch (type) + { + case TextureScalarType.Texture1DLodZero: + case TextureScalarType.Texture2DLodZero: + case TextureScalarType.Texture2DLodLevel: + case TextureScalarType.Texture2DLodLevelDepthCompare: + case TextureScalarType.Texture2DLodZeroDepthCompare: + case TextureScalarType.Texture2DArrayLodZero: + case TextureScalarType.Texture2DArrayLodZeroDepthCompare: + case TextureScalarType.Texture3DLodZero: + case TextureScalarType.TextureCubeLodLevel: + return TextureFlags.LodLevel; + + case TextureScalarType.Texture2D: + case TextureScalarType.Texture2DDepthCompare: + case TextureScalarType.Texture2DArray: + case TextureScalarType.Texture3D: + case TextureScalarType.TextureCube: + return TextureFlags.None; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } + + private static TextureFlags GetTextureFlags(TexelLoadScalarType type) + { + switch (type) + { + case TexelLoadScalarType.Texture1DLodZero: + case TexelLoadScalarType.Texture1DLodLevel: + case TexelLoadScalarType.Texture2DLodZero: + case TexelLoadScalarType.Texture2DLodLevel: + case TexelLoadScalarType.Texture2DLodZeroMultisample: + case TexelLoadScalarType.Texture3DLodZero: + case TexelLoadScalarType.Texture2DArrayLodZero: + return TextureFlags.LodLevel; + + case TexelLoadScalarType.Texture2DLodZeroOffset: + case TexelLoadScalarType.Texture2DLodLevelOffset: + return TextureFlags.LodLevel | TextureFlags.Offset; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitter.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitter.cs new file mode 100644 index 0000000000..91c740b684 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/InstEmitter.cs @@ -0,0 +1,6 @@ +using Ryujinx.Graphics.Shader.Translation; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + delegate void InstEmitter(EmitterContext context); +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/Lop3Expression.cs b/Ryujinx.Graphics/Shader/Instructions/Lop3Expression.cs new file mode 100644 index 0000000000..e55ed660a3 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Instructions/Lop3Expression.cs @@ -0,0 +1,149 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static class Lop3Expression + { + public static Operand GetFromTruthTable( + EmitterContext context, + Operand srcA, + Operand srcB, + Operand srcC, + int imm) + { + Operand expr = null; + + //Handle some simple cases, or cases where + //the KMap would yield poor results (like XORs). + if (imm == 0x96 || imm == 0x69) + { + //XOR (0x96) and XNOR (0x69). + if (imm == 0x69) + { + srcA = context.BitwiseNot(srcA); + } + + expr = context.BitwiseExclusiveOr(srcA, srcB); + expr = context.BitwiseExclusiveOr(expr, srcC); + + return expr; + } + else if (imm == 0) + { + //Always false. + return Const(IrConsts.False); + } + else if (imm == 0xff) + { + //Always true. + return Const(IrConsts.True); + } + + int map; + + //Encode into gray code. + map = ((imm >> 0) & 1) << 0; + map |= ((imm >> 1) & 1) << 4; + map |= ((imm >> 2) & 1) << 1; + map |= ((imm >> 3) & 1) << 5; + map |= ((imm >> 4) & 1) << 3; + map |= ((imm >> 5) & 1) << 7; + map |= ((imm >> 6) & 1) << 2; + map |= ((imm >> 7) & 1) << 6; + + //Solve KMap, get sum of products. + int visited = 0; + + for (int index = 0; index < 8 && visited != 0xff; index++) + { + if ((map & (1 << index)) == 0) + { + continue; + } + + int mask = 0; + + for (int mSize = 4; mSize != 0; mSize >>= 1) + { + mask = RotateLeft4((1 << mSize) - 1, index & 3) << (index & 4); + + if ((map & mask) == mask) + { + break; + } + } + + //The mask should wrap, if we are on the high row, shift to low etc. + int mask2 = (index & 4) != 0 ? mask >> 4 : mask << 4; + + if ((map & mask2) == mask2) + { + mask |= mask2; + } + + if ((mask & visited) == mask) + { + continue; + } + + bool notA = (mask & 0x33) != 0; + bool notB = (mask & 0x99) != 0; + bool notC = (mask & 0x0f) != 0; + + bool aChanges = (mask & 0xcc) != 0 && notA; + bool bChanges = (mask & 0x66) != 0 && notB; + bool cChanges = (mask & 0xf0) != 0 && notC; + + Operand localExpr = null; + + void And(Operand source) + { + if (localExpr != null) + { + localExpr = context.BitwiseAnd(localExpr, source); + } + else + { + localExpr = source; + } + } + + if (!aChanges) + { + And(context.BitwiseNot(srcA, notA)); + } + + if (!bChanges) + { + And(context.BitwiseNot(srcB, notB)); + } + + if (!cChanges) + { + And(context.BitwiseNot(srcC, notC)); + } + + if (expr != null) + { + expr = context.BitwiseOr(expr, localExpr); + } + else + { + expr = localExpr; + } + + visited |= mask; + } + + return expr; + } + + private static int RotateLeft4(int value, int shift) + { + return ((value << shift) | (value >> (4 - shift))) & 0xf; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/BasicBlock.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/BasicBlock.cs new file mode 100644 index 0000000000..949753377e --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/BasicBlock.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + class BasicBlock + { + public int Index { get; set; } + + public LinkedList Operations { get; } + + private BasicBlock _next; + private BasicBlock _branch; + + public BasicBlock Next + { + get => _next; + set => _next = AddSuccessor(_next, value); + } + + public BasicBlock Branch + { + get => _branch; + set => _branch = AddSuccessor(_branch, value); + } + + public bool HasBranch => _branch != null; + + public List Predecessors { get; } + + public HashSet DominanceFrontiers { get; } + + public BasicBlock ImmediateDominator { get; set; } + + public BasicBlock() + { + Operations = new LinkedList(); + + Predecessors = new List(); + + DominanceFrontiers = new HashSet(); + } + + public BasicBlock(int index) : this() + { + Index = index; + } + + private BasicBlock AddSuccessor(BasicBlock oldBlock, BasicBlock newBlock) + { + oldBlock?.Predecessors.Remove(this); + newBlock?.Predecessors.Add(this); + + return newBlock; + } + + public INode GetLastOp() + { + return Operations.Last?.Value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/INode.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/INode.cs new file mode 100644 index 0000000000..48dda24b1e --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/INode.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + interface INode + { + Operand Dest { get; set; } + + int SourcesCount { get; } + + Operand GetSource(int index); + + void SetSource(int index, Operand operand); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/Instruction.cs new file mode 100644 index 0000000000..ac0ebc2b08 --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/Instruction.cs @@ -0,0 +1,87 @@ +using System; + +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + [Flags] + enum Instruction + { + Absolute = 1, + Add, + BitfieldExtractS32, + BitfieldExtractU32, + BitfieldInsert, + BitfieldReverse, + BitwiseAnd, + BitwiseExclusiveOr, + BitwiseNot, + BitwiseOr, + Branch, + BranchIfFalse, + BranchIfTrue, + Ceiling, + Clamp, + ClampU32, + CompareEqual, + CompareGreater, + CompareGreaterOrEqual, + CompareGreaterOrEqualU32, + CompareGreaterU32, + CompareLess, + CompareLessOrEqual, + CompareLessOrEqualU32, + CompareLessU32, + CompareNotEqual, + ConditionalSelect, + ConvertFPToS32, + ConvertS32ToFP, + ConvertU32ToFP, + Copy, + Cosine, + Discard, + Divide, + EmitVertex, + EndPrimitive, + ExponentB2, + Floor, + FusedMultiplyAdd, + IsNan, + LoadConstant, + LoadGlobal, + LoadLocal, + LogarithmB2, + LogicalAnd, + LogicalExclusiveOr, + LogicalNot, + LogicalOr, + LoopBreak, + LoopContinue, + MarkLabel, + Maximum, + MaximumU32, + Minimum, + MinimumU32, + Multiply, + Negate, + PackDouble2x32, + PackHalf2x16, + ReciprocalSquareRoot, + Return, + ShiftLeft, + ShiftRightS32, + ShiftRightU32, + Sine, + SquareRoot, + StoreGlobal, + StoreLocal, + Subtract, + TextureSample, + TextureSize, + Truncate, + UnpackDouble2x32, + UnpackHalf2x16, + + Count, + FP = 1 << 16, + Mask = 0xffff + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/IrConsts.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/IrConsts.cs new file mode 100644 index 0000000000..c264e47d1a --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/IrConsts.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + static class IrConsts + { + public const int False = 0; + public const int True = -1; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operand.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operand.cs new file mode 100644 index 0000000000..1df88a3d9b --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operand.cs @@ -0,0 +1,79 @@ +using Ryujinx.Graphics.Shader.Decoders; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + class Operand + { + private const int CbufSlotBits = 5; + private const int CbufSlotLsb = 32 - CbufSlotBits; + private const int CbufSlotMask = (1 << CbufSlotBits) - 1; + + public OperandType Type { get; } + + public int Value { get; } + + public INode AsgOp { get; set; } + + public HashSet UseOps { get; } + + private Operand() + { + UseOps = new HashSet(); + } + + public Operand(OperandType type) : this() + { + Type = type; + } + + public Operand(OperandType type, int value) : this() + { + Type = type; + Value = value; + } + + public Operand(Register reg) : this() + { + Type = OperandType.Register; + Value = PackRegInfo(reg.Index, reg.Type); + } + + public Operand(int slot, int offset) : this() + { + Type = OperandType.ConstantBuffer; + Value = PackCbufInfo(slot, offset); + } + + private static int PackCbufInfo(int slot, int offset) + { + return (slot << CbufSlotLsb) | offset; + } + + private static int PackRegInfo(int index, RegisterType type) + { + return ((int)type << 24) | index; + } + + public int GetCbufSlot() + { + return (Value >> CbufSlotLsb) & CbufSlotMask; + } + + public int GetCbufOffset() + { + return Value & ~(CbufSlotMask << CbufSlotLsb); + } + + public Register GetRegister() + { + return new Register(Value & 0xffffff, (RegisterType)(Value >> 24)); + } + + public float AsFloat() + { + return BitConverter.Int32BitsToSingle(Value); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandHelper.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandHelper.cs new file mode 100644 index 0000000000..6765f8a44f --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandHelper.cs @@ -0,0 +1,62 @@ +using Ryujinx.Graphics.Shader.Decoders; +using System; + +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + static class OperandHelper + { + public static Operand Attribute(int value) + { + return new Operand(OperandType.Attribute, value); + } + + public static Operand Cbuf(int slot, int offset) + { + return new Operand(slot, offset); + } + + public static Operand Const(int value) + { + return new Operand(OperandType.Constant, value); + } + + public static Operand ConstF(float value) + { + return new Operand(OperandType.Constant, BitConverter.SingleToInt32Bits(value)); + } + + public static Operand Label() + { + return new Operand(OperandType.Label); + } + + public static Operand Local() + { + return new Operand(OperandType.LocalVariable); + } + + public static Operand Register(int index, RegisterType type) + { + return Register(new Register(index, type)); + } + + public static Operand Register(Register reg) + { + if (reg.IsRZ) + { + return Const(0); + } + else if (reg.IsPT) + { + return Const(IrConsts.True); + } + + return new Operand(reg); + } + + public static Operand Undef() + { + return new Operand(OperandType.Undefined); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandType.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandType.cs new file mode 100644 index 0000000000..e0e2a6675a --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandType.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + enum OperandType + { + Attribute, + Constant, + ConstantBuffer, + GlobalMemory, + Label, + LocalMemory, + LocalVariable, + Register, + Undefined + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operation.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operation.cs new file mode 100644 index 0000000000..f657995370 --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operation.cs @@ -0,0 +1,101 @@ +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + class Operation : INode + { + public Instruction Inst { get; private set; } + + private Operand _dest; + + public Operand Dest + { + get => _dest; + set => _dest = AssignDest(value); + } + + private Operand[] _sources; + + public int SourcesCount => _sources.Length; + + public int ComponentIndex { get; } + + public Operation(Instruction inst, Operand dest, params Operand[] sources) + { + Inst = inst; + Dest = dest; + + //The array may be modified externally, so we store a copy. + _sources = (Operand[])sources.Clone(); + + for (int index = 0; index < _sources.Length; index++) + { + Operand source = _sources[index]; + + if (source.Type == OperandType.LocalVariable) + { + source.UseOps.Add(this); + } + } + } + + public Operation( + Instruction inst, + int compIndex, + Operand dest, + params Operand[] sources) : this(inst, dest, sources) + { + ComponentIndex = compIndex; + } + + private Operand AssignDest(Operand dest) + { + if (dest != null && dest.Type == OperandType.LocalVariable) + { + dest.AsgOp = this; + } + + return dest; + } + + public Operand GetSource(int index) + { + return _sources[index]; + } + + public void SetSource(int index, Operand source) + { + Operand oldSrc = _sources[index]; + + if (oldSrc != null && oldSrc.Type == OperandType.LocalVariable) + { + oldSrc.UseOps.Remove(this); + } + + if (source.Type == OperandType.LocalVariable) + { + source.UseOps.Add(this); + } + + _sources[index] = source; + } + + public void TurnIntoCopy(Operand source) + { + Inst = Instruction.Copy; + + foreach (Operand oldSrc in _sources) + { + if (oldSrc.Type == OperandType.LocalVariable) + { + oldSrc.UseOps.Remove(this); + } + } + + if (source.Type == OperandType.LocalVariable) + { + source.UseOps.Add(this); + } + + _sources = new Operand[] { source }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/PhiNode.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/PhiNode.cs new file mode 100644 index 0000000000..13ff41bd14 --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/PhiNode.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + class PhiNode : INode + { + private Operand _dest; + + public Operand Dest + { + get => _dest; + set => _dest = AssignDest(value); + } + + private HashSet _blocks; + + private class PhiSource + { + public BasicBlock Block { get; } + public Operand Operand { get; set; } + + public PhiSource(BasicBlock block, Operand operand) + { + Block = block; + Operand = operand; + } + } + + private List _sources; + + public int SourcesCount => _sources.Count; + + public PhiNode(Operand dest) + { + _blocks = new HashSet(); + + _sources = new List(); + + dest.AsgOp = this; + + Dest = dest; + } + + private Operand AssignDest(Operand dest) + { + if (dest != null && dest.Type == OperandType.LocalVariable) + { + dest.AsgOp = this; + } + + return dest; + } + + public void AddSource(BasicBlock block, Operand operand) + { + if (_blocks.Add(block)) + { + if (operand.Type == OperandType.LocalVariable) + { + operand.UseOps.Add(this); + } + + _sources.Add(new PhiSource(block, operand)); + } + } + + public Operand GetSource(int index) + { + return _sources[index].Operand; + } + + public BasicBlock GetBlock(int index) + { + return _sources[index].Block; + } + + public void SetSource(int index, Operand source) + { + Operand oldSrc = _sources[index].Operand; + + if (oldSrc != null && oldSrc.Type == OperandType.LocalVariable) + { + oldSrc.UseOps.Remove(this); + } + + if (source.Type == OperandType.LocalVariable) + { + source.UseOps.Add(this); + } + + _sources[index].Operand = source; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureFlags.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureFlags.cs new file mode 100644 index 0000000000..5f0a84276c --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureFlags.cs @@ -0,0 +1,17 @@ +using System; + +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + [Flags] + enum TextureFlags + { + None = 0, + Bindless = 1 << 0, + Gather = 1 << 1, + IntCoords = 1 << 2, + LodBias = 1 << 3, + LodLevel = 1 << 4, + Offset = 1 << 5, + Offsets = 1 << 6 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureOperation.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureOperation.cs new file mode 100644 index 0000000000..f5f2cc5c60 --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureOperation.cs @@ -0,0 +1,24 @@ +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + class TextureOperation : Operation + { + public TextureType Type { get; } + public TextureFlags Flags { get; } + + public int Handle { get; } + + public TextureOperation( + Instruction inst, + TextureType type, + TextureFlags flags, + int handle, + int compIndex, + Operand dest, + params Operand[] sources) : base(inst, compIndex, dest, sources) + { + Type = type; + Flags = flags; + Handle = handle; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureType.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureType.cs new file mode 100644 index 0000000000..bf20700776 --- /dev/null +++ b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureType.cs @@ -0,0 +1,35 @@ +using System; + +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + [Flags] + enum TextureType + { + Texture1D, + Texture2D, + Texture3D, + TextureCube, + + Mask = 0xff, + + Array = 1 << 8, + Multisample = 1 << 9, + Shadow = 1 << 10 + } + + static class TextureTypeExtensions + { + public static int GetCoordsCount(this TextureType type) + { + switch (type & TextureType.Mask) + { + case TextureType.Texture1D: return 1; + case TextureType.Texture2D: return 2; + case TextureType.Texture3D: return 3; + case TextureType.TextureCube: return 3; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/ShaderConfig.cs b/Ryujinx.Graphics/Shader/ShaderConfig.cs new file mode 100644 index 0000000000..c2a94814e9 --- /dev/null +++ b/Ryujinx.Graphics/Shader/ShaderConfig.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.Gal; +using System; + +namespace Ryujinx.Graphics.Shader +{ + public struct ShaderConfig + { + public GalShaderType Type { get; } + + public int MaxCBufferSize; + + public ShaderConfig(GalShaderType type, int maxCBufferSize) + { + if (maxCBufferSize <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxCBufferSize)); + } + + Type = type; + MaxCBufferSize = maxCBufferSize; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/ShaderHeader.cs b/Ryujinx.Graphics/Shader/ShaderHeader.cs new file mode 100644 index 0000000000..53abdc56ed --- /dev/null +++ b/Ryujinx.Graphics/Shader/ShaderHeader.cs @@ -0,0 +1,166 @@ +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Shader.Decoders; +using System; + +namespace Ryujinx.Graphics.Shader +{ + struct OutputMapTarget + { + public bool Red { get; } + public bool Green { get; } + public bool Blue { get; } + public bool Alpha { get; } + + public bool Enabled => Red || Green || Blue || Alpha; + + public OutputMapTarget(bool red, bool green, bool blue, bool alpha) + { + Red = red; + Green = green; + Blue = blue; + Alpha = alpha; + } + + public bool ComponentEnabled(int component) + { + switch (component) + { + case 0: return Red; + case 1: return Green; + case 2: return Blue; + case 3: return Alpha; + } + + throw new ArgumentOutOfRangeException(nameof(component)); + } + } + + class ShaderHeader + { + public int SphType { get; } + + public int Version { get; } + + public int ShaderType { get; } + + public bool MrtEnable { get; } + + public bool KillsPixels { get; } + + public bool DoesGlobalStore { get; } + + public int SassVersion { get; } + + public bool DoesLoadOrStore { get; } + + public bool DoesFp64 { get; } + + public int StreamOutMask{ get; } + + public int ShaderLocalMemoryLowSize { get; } + + public int PerPatchAttributeCount { get; } + + public int ShaderLocalMemoryHighSize { get; } + + public int ThreadsPerInputPrimitive { get; } + + public int ShaderLocalMemoryCrsSize { get; } + + public int OutputTopology { get; } + + public int MaxOutputVertexCount { get; } + + public int StoreReqStart { get; } + public int StoreReqEnd { get; } + + public OutputMapTarget[] OmapTargets { get; } + public bool OmapSampleMask { get; } + public bool OmapDepth { get; } + + public ShaderHeader(IGalMemory memory, ulong address) + { + int commonWord0 = memory.ReadInt32((long)address + 0); + int commonWord1 = memory.ReadInt32((long)address + 4); + int commonWord2 = memory.ReadInt32((long)address + 8); + int commonWord3 = memory.ReadInt32((long)address + 12); + int commonWord4 = memory.ReadInt32((long)address + 16); + + SphType = commonWord0.Extract(0, 5); + + Version = commonWord0.Extract(5, 5); + + ShaderType = commonWord0.Extract(10, 4); + + MrtEnable = commonWord0.Extract(14); + + KillsPixels = commonWord0.Extract(15); + + DoesGlobalStore = commonWord0.Extract(16); + + SassVersion = commonWord0.Extract(17, 4); + + DoesLoadOrStore = commonWord0.Extract(26); + + DoesFp64 = commonWord0.Extract(27); + + StreamOutMask = commonWord0.Extract(28, 4); + + ShaderLocalMemoryLowSize = commonWord1.Extract(0, 24); + + PerPatchAttributeCount = commonWord1.Extract(24, 8); + + ShaderLocalMemoryHighSize = commonWord2.Extract(0, 24); + + ThreadsPerInputPrimitive = commonWord2.Extract(24, 8); + + ShaderLocalMemoryCrsSize = commonWord3.Extract(0, 24); + + OutputTopology = commonWord3.Extract(24, 4); + + MaxOutputVertexCount = commonWord4.Extract(0, 12); + + StoreReqStart = commonWord4.Extract(12, 8); + StoreReqEnd = commonWord4.Extract(24, 8); + + int type2OmapTarget = memory.ReadInt32((long)address + 72); + int type2Omap = memory.ReadInt32((long)address + 76); + + OmapTargets = new OutputMapTarget[8]; + + for (int offset = 0; offset < OmapTargets.Length * 4; offset += 4) + { + OmapTargets[offset >> 2] = new OutputMapTarget( + type2OmapTarget.Extract(offset + 0), + type2OmapTarget.Extract(offset + 1), + type2OmapTarget.Extract(offset + 2), + type2OmapTarget.Extract(offset + 3)); + } + + OmapSampleMask = type2Omap.Extract(0); + OmapDepth = type2Omap.Extract(1); + } + + public int DepthRegister + { + get + { + int count = 0; + + for (int index = 0; index < OmapTargets.Length; index++) + { + for (int component = 0; component < 4; component++) + { + if (OmapTargets[index].ComponentEnabled(component)) + { + count++; + } + } + } + + //Depth register is always two registers after the last color output. + return count + 1; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/ShaderProgram.cs b/Ryujinx.Graphics/Shader/ShaderProgram.cs new file mode 100644 index 0000000000..9257fd262d --- /dev/null +++ b/Ryujinx.Graphics/Shader/ShaderProgram.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Shader +{ + public class ShaderProgram + { + public ShaderProgramInfo Info { get; } + + public string Code { get; } + + internal ShaderProgram(ShaderProgramInfo info, string code) + { + Info = info; + Code = code; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/ShaderProgramInfo.cs b/Ryujinx.Graphics/Shader/ShaderProgramInfo.cs new file mode 100644 index 0000000000..c529a3536f --- /dev/null +++ b/Ryujinx.Graphics/Shader/ShaderProgramInfo.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.ObjectModel; + +namespace Ryujinx.Graphics.Shader +{ + public class ShaderProgramInfo + { + public ReadOnlyCollection CBuffers { get; } + public ReadOnlyCollection Textures { get; } + + internal ShaderProgramInfo(CBufferDescriptor[] cBuffers, TextureDescriptor[] textures) + { + CBuffers = Array.AsReadOnly(cBuffers); + Textures = Array.AsReadOnly(textures); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstAssignment.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstAssignment.cs new file mode 100644 index 0000000000..bb3fe7af4b --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstAssignment.cs @@ -0,0 +1,35 @@ +using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class AstAssignment : AstNode + { + public IAstNode Destination { get; } + + private IAstNode _source; + + public IAstNode Source + { + get + { + return _source; + } + set + { + RemoveUse(_source, this); + + AddUse(value, this); + + _source = value; + } + } + + public AstAssignment(IAstNode destination, IAstNode source) + { + Destination = destination; + Source = source; + + AddDef(destination, this); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstBlock.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstBlock.cs new file mode 100644 index 0000000000..fdef87de56 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstBlock.cs @@ -0,0 +1,116 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; +using System.Collections; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class AstBlock : AstNode, IEnumerable + { + public AstBlockType Type { get; private set; } + + private IAstNode _condition; + + public IAstNode Condition + { + get + { + return _condition; + } + set + { + RemoveUse(_condition, this); + + AddUse(value, this); + + _condition = value; + } + } + + private LinkedList _nodes; + + public IAstNode First => _nodes.First?.Value; + + public int Count => _nodes.Count; + + public AstBlock(AstBlockType type, IAstNode condition = null) + { + Type = type; + Condition = condition; + + _nodes = new LinkedList(); + } + + public void Add(IAstNode node) + { + Add(node, _nodes.AddLast(node)); + } + + public void AddFirst(IAstNode node) + { + Add(node, _nodes.AddFirst(node)); + } + + public void AddBefore(IAstNode next, IAstNode node) + { + Add(node, _nodes.AddBefore(next.LLNode, node)); + } + + public void AddAfter(IAstNode prev, IAstNode node) + { + Add(node, _nodes.AddAfter(prev.LLNode, node)); + } + + private void Add(IAstNode node, LinkedListNode newNode) + { + if (node.Parent != null) + { + throw new ArgumentException("Node already belongs to a block."); + } + + node.Parent = this; + node.LLNode = newNode; + } + + public void Remove(IAstNode node) + { + _nodes.Remove(node.LLNode); + + node.Parent = null; + node.LLNode = null; + } + + public void AndCondition(IAstNode cond) + { + Condition = new AstOperation(Instruction.LogicalAnd, Condition, cond); + } + + public void OrCondition(IAstNode cond) + { + Condition = new AstOperation(Instruction.LogicalOr, Condition, cond); + } + public void TurnIntoIf(IAstNode cond) + { + Condition = cond; + + Type = AstBlockType.If; + } + + public void TurnIntoElseIf() + { + Type = AstBlockType.ElseIf; + } + + public IEnumerator GetEnumerator() + { + return _nodes.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstBlockType.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstBlockType.cs new file mode 100644 index 0000000000..c12efda909 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstBlockType.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + enum AstBlockType + { + DoWhile, + If, + Else, + ElseIf, + Main, + While + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstBlockVisitor.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstBlockVisitor.cs new file mode 100644 index 0000000000..9397fdb913 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstBlockVisitor.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class AstBlockVisitor + { + public AstBlock Block { get; private set; } + + public class BlockVisitationEventArgs : EventArgs + { + public AstBlock Block { get; } + + public BlockVisitationEventArgs(AstBlock block) + { + Block = block; + } + } + + public event EventHandler BlockEntered; + public event EventHandler BlockLeft; + + public AstBlockVisitor(AstBlock mainBlock) + { + Block = mainBlock; + } + + public IEnumerable Visit() + { + IAstNode node = Block.First; + + while (node != null) + { + //We reached a child block, visit the nodes inside. + while (node is AstBlock childBlock) + { + Block = childBlock; + + node = childBlock.First; + + BlockEntered?.Invoke(this, new BlockVisitationEventArgs(Block)); + } + + //Node may be null, if the block is empty. + if (node != null) + { + IAstNode next = Next(node); + + yield return node; + + node = next; + } + + //We reached the end of the list, go up on tree to the parent blocks. + while (node == null && Block.Type != AstBlockType.Main) + { + BlockLeft?.Invoke(this, new BlockVisitationEventArgs(Block)); + + node = Next(Block); + + Block = Block.Parent; + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstHelper.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstHelper.cs new file mode 100644 index 0000000000..9d3148e1bb --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstHelper.cs @@ -0,0 +1,73 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + static class AstHelper + { + public static void AddUse(IAstNode node, IAstNode parent) + { + if (node is AstOperand operand && operand.Type == OperandType.LocalVariable) + { + operand.Uses.Add(parent); + } + } + + public static void AddDef(IAstNode node, IAstNode parent) + { + if (node is AstOperand operand && operand.Type == OperandType.LocalVariable) + { + operand.Defs.Add(parent); + } + } + + public static void RemoveUse(IAstNode node, IAstNode parent) + { + if (node is AstOperand operand && operand.Type == OperandType.LocalVariable) + { + operand.Uses.Remove(parent); + } + } + + public static void RemoveDef(IAstNode node, IAstNode parent) + { + if (node is AstOperand operand && operand.Type == OperandType.LocalVariable) + { + operand.Defs.Remove(parent); + } + } + + public static AstAssignment Assign(IAstNode destination, IAstNode source) + { + return new AstAssignment(destination, source); + } + + public static AstOperand Const(int value) + { + return new AstOperand(OperandType.Constant, value); + } + + public static AstOperand Local(VariableType type) + { + AstOperand local = new AstOperand(OperandType.LocalVariable); + + local.VarType = type; + + return local; + } + + public static IAstNode InverseCond(IAstNode cond) + { + return new AstOperation(Instruction.LogicalNot, cond); + } + + public static IAstNode Next(IAstNode node) + { + return node.LLNode.Next?.Value; + } + + public static IAstNode Previous(IAstNode node) + { + return node.LLNode.Previous?.Value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstNode.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstNode.cs new file mode 100644 index 0000000000..c667aac988 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstNode.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class AstNode : IAstNode + { + public AstBlock Parent { get; set; } + + public LinkedListNode LLNode { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstOperand.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstOperand.cs new file mode 100644 index 0000000000..97ff3ca97c --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstOperand.cs @@ -0,0 +1,49 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class AstOperand : AstNode + { + public HashSet Defs { get; } + public HashSet Uses { get; } + + public OperandType Type { get; } + + public VariableType VarType { get; set; } + + public int Value { get; } + + public int CbufSlot { get; } + public int CbufOffset { get; } + + private AstOperand() + { + Defs = new HashSet(); + Uses = new HashSet(); + + VarType = VariableType.S32; + } + + public AstOperand(Operand operand) : this() + { + Type = operand.Type; + + if (Type == OperandType.ConstantBuffer) + { + CbufSlot = operand.GetCbufSlot(); + CbufOffset = operand.GetCbufOffset(); + } + else + { + Value = operand.Value; + } + } + + public AstOperand(OperandType type, int value = 0) : this() + { + Type = type; + Value = value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstOperation.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstOperation.cs new file mode 100644 index 0000000000..1607ffecde --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstOperation.cs @@ -0,0 +1,49 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; + +using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class AstOperation : AstNode + { + public Instruction Inst { get; } + + public int ComponentMask { get; } + + private IAstNode[] _sources; + + public int SourcesCount => _sources.Length; + + public AstOperation(Instruction inst, params IAstNode[] sources) + { + Inst = inst; + _sources = sources; + + foreach (IAstNode source in sources) + { + AddUse(source, this); + } + + ComponentMask = 1; + } + + public AstOperation(Instruction inst, int compMask, params IAstNode[] sources) : this(inst, sources) + { + ComponentMask = compMask; + } + + public IAstNode GetSource(int index) + { + return _sources[index]; + } + + public void SetSource(int index, IAstNode source) + { + RemoveUse(_sources[index], this); + + AddUse(source, this); + + _sources[index] = source; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstOptimizer.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstOptimizer.cs new file mode 100644 index 0000000000..0f5392b7d6 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstOptimizer.cs @@ -0,0 +1,149 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; +using System.Linq; + +using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + static class AstOptimizer + { + public static void Optimize(StructuredProgramInfo info) + { + AstBlock mainBlock = info.MainBlock; + + AstBlockVisitor visitor = new AstBlockVisitor(mainBlock); + + foreach (IAstNode node in visitor.Visit()) + { + if (node is AstAssignment assignment && assignment.Destination is AstOperand propVar) + { + bool isWorthPropagating = propVar.Uses.Count == 1 || IsWorthPropagating(assignment.Source); + + if (propVar.Defs.Count == 1 && isWorthPropagating) + { + PropagateExpression(propVar, assignment.Source); + } + + if (propVar.Type == OperandType.LocalVariable && propVar.Uses.Count == 0) + { + visitor.Block.Remove(assignment); + + info.Locals.Remove(propVar); + } + } + } + + RemoveEmptyBlocks(mainBlock); + } + + private static bool IsWorthPropagating(IAstNode source) + { + if (!(source is AstOperation srcOp)) + { + return false; + } + + if (!InstructionInfo.IsUnary(srcOp.Inst)) + { + return false; + } + + return srcOp.GetSource(0) is AstOperand || srcOp.Inst == Instruction.Copy; + } + + private static void PropagateExpression(AstOperand propVar, IAstNode source) + { + IAstNode[] uses = propVar.Uses.ToArray(); + + foreach (IAstNode useNode in uses) + { + if (useNode is AstBlock useBlock) + { + useBlock.Condition = source; + } + else if (useNode is AstOperation useOperation) + { + for (int srcIndex = 0; srcIndex < useOperation.SourcesCount; srcIndex++) + { + if (useOperation.GetSource(srcIndex) == propVar) + { + useOperation.SetSource(srcIndex, source); + } + } + } + else if (useNode is AstAssignment useAssignment) + { + useAssignment.Source = source; + } + } + } + + private static void RemoveEmptyBlocks(AstBlock mainBlock) + { + Queue pending = new Queue(); + + pending.Enqueue(mainBlock); + + while (pending.TryDequeue(out AstBlock block)) + { + foreach (IAstNode node in block) + { + if (node is AstBlock childBlock) + { + pending.Enqueue(childBlock); + } + } + + AstBlock parent = block.Parent; + + if (parent == null) + { + continue; + } + + AstBlock nextBlock = Next(block) as AstBlock; + + bool hasElse = nextBlock != null && nextBlock.Type == AstBlockType.Else; + + bool isIf = block.Type == AstBlockType.If; + + if (block.Count == 0) + { + if (isIf) + { + if (hasElse) + { + nextBlock.TurnIntoIf(InverseCond(block.Condition)); + } + + parent.Remove(block); + } + else if (block.Type == AstBlockType.Else) + { + parent.Remove(block); + } + } + else if (isIf && parent.Type == AstBlockType.Else && parent.Count == (hasElse ? 2 : 1)) + { + AstBlock parentOfParent = parent.Parent; + + parent.Remove(block); + + parentOfParent.AddAfter(parent, block); + + if (hasElse) + { + parent.Remove(nextBlock); + + parentOfParent.AddAfter(block, nextBlock); + } + + parentOfParent.Remove(parent); + + block.TurnIntoElseIf(); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstTextureOperation.cs b/Ryujinx.Graphics/Shader/StructuredIr/AstTextureOperation.cs new file mode 100644 index 0000000000..e40f7b70eb --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/AstTextureOperation.cs @@ -0,0 +1,25 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class AstTextureOperation : AstOperation + { + public TextureType Type { get; } + public TextureFlags Flags { get; } + + public int Handle { get; } + + public AstTextureOperation( + Instruction inst, + TextureType type, + TextureFlags flags, + int handle, + int compMask, + params IAstNode[] sources) : base(inst, compMask, sources) + { + Type = type; + Flags = flags; + Handle = handle; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/GotoElimination.cs b/Ryujinx.Graphics/Shader/StructuredIr/GotoElimination.cs new file mode 100644 index 0000000000..dffc3142f1 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/GotoElimination.cs @@ -0,0 +1,459 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + static class GotoElimination + { + //This is a modified version of the algorithm presented on the paper + //"Taming Control Flow: A Structured Approach to Eliminating Goto Statements". + public static void Eliminate(GotoStatement[] gotos) + { + for (int index = gotos.Length - 1; index >= 0; index--) + { + GotoStatement stmt = gotos[index]; + + AstBlock gBlock = ParentBlock(stmt.Goto); + AstBlock lBlock = ParentBlock(stmt.Label); + + int gLevel = Level(gBlock); + int lLevel = Level(lBlock); + + if (IndirectlyRelated(gBlock, lBlock, gLevel, lLevel)) + { + AstBlock drBlock = gBlock; + + int drLevel = gLevel; + + do + { + drBlock = drBlock.Parent; + + drLevel--; + } + while (!DirectlyRelated(drBlock, lBlock, drLevel, lLevel)); + + MoveOutward(stmt, gLevel, drLevel); + + gBlock = drBlock; + gLevel = drLevel; + + if (Previous(stmt.Goto) is AstBlock elseBlock && elseBlock.Type == AstBlockType.Else) + { + //It's possible that the label was enclosed inside an else block, + //in this case we need to update the block and level. + //We also need to set the IsLoop for the case when the label is + //now before the goto, due to the newly introduced else block. + lBlock = ParentBlock(stmt.Label); + + lLevel = Level(lBlock); + + if (!IndirectlyRelated(elseBlock, lBlock, gLevel + 1, lLevel)) + { + stmt.IsLoop = true; + } + } + } + + if (DirectlyRelated(gBlock, lBlock, gLevel, lLevel)) + { + if (gLevel > lLevel) + { + MoveOutward(stmt, gLevel, lLevel); + } + else + { + if (stmt.IsLoop) + { + Lift(stmt); + } + + MoveInward(stmt); + } + } + + gBlock = ParentBlock(stmt.Goto); + + if (stmt.IsLoop) + { + EncloseDoWhile(stmt, gBlock, stmt.Label); + } + else + { + Enclose(gBlock, AstBlockType.If, stmt.Condition, Next(stmt.Goto), stmt.Label); + } + + gBlock.Remove(stmt.Goto); + } + } + + private static bool IndirectlyRelated(AstBlock lBlock, AstBlock rBlock, int lLevel, int rlevel) + { + return !(lBlock == rBlock || DirectlyRelated(lBlock, rBlock, lLevel, rlevel)); + } + + private static bool DirectlyRelated(AstBlock lBlock, AstBlock rBlock, int lLevel, int rLevel) + { + //If the levels are equal, they can be either siblings or indirectly related. + if (lLevel == rLevel) + { + return false; + } + + IAstNode block; + IAstNode other; + + int blockLvl, otherLvl; + + if (lLevel > rLevel) + { + block = lBlock; + blockLvl = lLevel; + other = rBlock; + otherLvl = rLevel; + } + else /* if (rLevel > lLevel) */ + { + block = rBlock; + blockLvl = rLevel; + other = lBlock; + otherLvl = lLevel; + } + + while (blockLvl >= otherLvl) + { + if (block == other) + { + return true; + } + + block = block.Parent; + + blockLvl--; + } + + return false; + } + + private static void Lift(GotoStatement stmt) + { + AstBlock block = ParentBlock(stmt.Goto); + + AstBlock[] path = BackwardsPath(block, ParentBlock(stmt.Label)); + + AstBlock loopFirstStmt = path[path.Length - 1]; + + if (loopFirstStmt.Type == AstBlockType.Else) + { + loopFirstStmt = Previous(loopFirstStmt) as AstBlock; + + if (loopFirstStmt == null || loopFirstStmt.Type != AstBlockType.If) + { + throw new InvalidOperationException("Found an else without a matching if."); + } + } + + AstBlock newBlock = EncloseDoWhile(stmt, block, loopFirstStmt); + + block.Remove(stmt.Goto); + + newBlock.AddFirst(stmt.Goto); + + stmt.IsLoop = false; + } + + private static void MoveOutward(GotoStatement stmt, int gLevel, int lLevel) + { + AstBlock origin = ParentBlock(stmt.Goto); + + AstBlock block = origin; + + //Check if a loop is enclosing the goto, and the block that is + //directly related to the label is above the loop block. + //In that case, we need to introduce a break to get out of the loop. + AstBlock loopBlock = origin; + + int loopLevel = gLevel; + + while (loopLevel > lLevel) + { + AstBlock child = loopBlock; + + loopBlock = loopBlock.Parent; + + loopLevel--; + + if (child.Type == AstBlockType.DoWhile) + { + EncloseSingleInst(stmt, Instruction.LoopBreak); + + block.Remove(stmt.Goto); + + loopBlock.AddAfter(child, stmt.Goto); + + block = loopBlock; + gLevel = loopLevel; + } + } + + //Insert ifs to skip the parts that shouldn't be executed due to the goto. + bool tryInsertElse = stmt.IsUnconditional && origin.Type == AstBlockType.If; + + while (gLevel > lLevel) + { + Enclose(block, AstBlockType.If, stmt.Condition, Next(stmt.Goto)); + + block.Remove(stmt.Goto); + + AstBlock child = block; + + //We can't move the goto in the middle of a if and a else block, in + //this case we need to move it after the else. + //IsLoop may need to be updated if the label is inside the else, as + //introducing a loop is the only way to ensure the else will be executed. + if (Next(child) is AstBlock elseBlock && elseBlock.Type == AstBlockType.Else) + { + child = elseBlock; + } + + block = block.Parent; + + block.AddAfter(child, stmt.Goto); + + gLevel--; + + if (tryInsertElse && child == origin) + { + AstBlock lBlock = ParentBlock(stmt.Label); + + IAstNode last = block == lBlock && !stmt.IsLoop ? stmt.Label : null; + + AstBlock newBlock = Enclose(block, AstBlockType.Else, null, Next(stmt.Goto), last); + + if (newBlock != null) + { + block.Remove(stmt.Goto); + + block.AddAfter(newBlock, stmt.Goto); + } + } + } + } + + private static void MoveInward(GotoStatement stmt) + { + AstBlock block = ParentBlock(stmt.Goto); + + AstBlock[] path = BackwardsPath(block, ParentBlock(stmt.Label)); + + for (int index = path.Length - 1; index >= 0; index--) + { + AstBlock child = path[index]; + AstBlock last = child; + + if (child.Type == AstBlockType.If) + { + //Modify the if condition to allow it to be entered by the goto. + if (!ContainsCondComb(child.Condition, Instruction.LogicalOr, stmt.Condition)) + { + child.OrCondition(stmt.Condition); + } + } + else if (child.Type == AstBlockType.Else) + { + //Modify the matching if condition to force the else to be entered by the goto. + if (!(Previous(child) is AstBlock ifBlock) || ifBlock.Type != AstBlockType.If) + { + throw new InvalidOperationException("Found an else without a matching if."); + } + + IAstNode cond = InverseCond(stmt.Condition); + + if (!ContainsCondComb(ifBlock.Condition, Instruction.LogicalAnd, cond)) + { + ifBlock.AndCondition(cond); + } + + last = ifBlock; + } + + Enclose(block, AstBlockType.If, stmt.Condition, Next(stmt.Goto), last); + + block.Remove(stmt.Goto); + + child.AddFirst(stmt.Goto); + + block = child; + } + } + + private static bool ContainsCondComb(IAstNode node, Instruction inst, IAstNode newCond) + { + while (node is AstOperation operation && operation.SourcesCount == 2) + { + if (operation.Inst == inst && IsSameCond(operation.GetSource(1), newCond)) + { + return true; + } + + node = operation.GetSource(0); + } + + return false; + } + + private static AstBlock EncloseDoWhile(GotoStatement stmt, AstBlock block, IAstNode first) + { + if (block.Type == AstBlockType.DoWhile && first == block.First) + { + //We only need to insert the continue if we're not at the end of the loop, + //or if our condition is different from the loop condition. + if (Next(stmt.Goto) != null || block.Condition != stmt.Condition) + { + EncloseSingleInst(stmt, Instruction.LoopContinue); + } + + //Modify the do-while condition to allow it to continue. + if (!ContainsCondComb(block.Condition, Instruction.LogicalOr, stmt.Condition)) + { + block.OrCondition(stmt.Condition); + } + + return block; + } + + return Enclose(block, AstBlockType.DoWhile, stmt.Condition, first, stmt.Goto); + } + + private static void EncloseSingleInst(GotoStatement stmt, Instruction inst) + { + AstBlock block = ParentBlock(stmt.Goto); + + AstBlock newBlock = new AstBlock(AstBlockType.If, stmt.Condition); + + block.AddAfter(stmt.Goto, newBlock); + + newBlock.AddFirst(new AstOperation(inst)); + } + + private static AstBlock Enclose( + AstBlock block, + AstBlockType type, + IAstNode cond, + IAstNode first, + IAstNode last = null) + { + if (first == last) + { + return null; + } + + if (type == AstBlockType.If) + { + cond = InverseCond(cond); + } + + //Do a quick check, if we are enclosing a single block, + //and the block type/condition matches the one we're going + //to create, then we don't need a new block, we can just + //return the old one. + bool hasSingleNode = Next(first) == last; + + if (hasSingleNode && BlockMatches(first, type, cond)) + { + return first as AstBlock; + } + + AstBlock newBlock = new AstBlock(type, cond); + + block.AddBefore(first, newBlock); + + while (first != last) + { + IAstNode next = Next(first); + + block.Remove(first); + + newBlock.Add(first); + + first = next; + } + + return newBlock; + } + + private static bool BlockMatches(IAstNode node, AstBlockType type, IAstNode cond) + { + if (!(node is AstBlock block)) + { + return false; + } + + return block.Type == type && IsSameCond(block.Condition, cond); + } + + private static bool IsSameCond(IAstNode lCond, IAstNode rCond) + { + if (lCond is AstOperation lCondOp && lCondOp.Inst == Instruction.LogicalNot) + { + if (!(rCond is AstOperation rCondOp) || rCondOp.Inst != lCondOp.Inst) + { + return false; + } + + lCond = lCondOp.GetSource(0); + rCond = rCondOp.GetSource(0); + } + + return lCond == rCond; + } + + private static AstBlock ParentBlock(IAstNode node) + { + if (node is AstBlock block) + { + return block.Parent; + } + + while (!(node is AstBlock)) + { + node = node.Parent; + } + + return node as AstBlock; + } + + private static AstBlock[] BackwardsPath(AstBlock top, AstBlock bottom) + { + AstBlock block = bottom; + + List path = new List(); + + while (block != top) + { + path.Add(block); + + block = block.Parent; + } + + return path.ToArray(); + } + + private static int Level(IAstNode node) + { + int level = 0; + + while (node != null) + { + level++; + + node = node.Parent; + } + + return level; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/GotoStatement.cs b/Ryujinx.Graphics/Shader/StructuredIr/GotoStatement.cs new file mode 100644 index 0000000000..25216e55fb --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/GotoStatement.cs @@ -0,0 +1,23 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class GotoStatement + { + public AstOperation Goto { get; } + public AstAssignment Label { get; } + + public IAstNode Condition => Label.Destination; + + public bool IsLoop { get; set; } + + public bool IsUnconditional => Goto.Inst == Instruction.Branch; + + public GotoStatement(AstOperation branch, AstAssignment label, bool isLoop) + { + Goto = branch; + Label = label; + IsLoop = isLoop; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/IAstNode.cs b/Ryujinx.Graphics/Shader/StructuredIr/IAstNode.cs new file mode 100644 index 0000000000..5ececbb5e4 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/IAstNode.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + interface IAstNode + { + AstBlock Parent { get; set; } + + LinkedListNode LLNode { get; set; } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/InstructionInfo.cs b/Ryujinx.Graphics/Shader/StructuredIr/InstructionInfo.cs new file mode 100644 index 0000000000..46a61553b8 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/InstructionInfo.cs @@ -0,0 +1,142 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + static class InstructionInfo + { + private struct InstInfo + { + public VariableType DestType { get; } + + public VariableType[] SrcTypes { get; } + + public InstInfo(VariableType destType, params VariableType[] srcTypes) + { + DestType = destType; + SrcTypes = srcTypes; + } + } + + private static InstInfo[] _infoTbl; + + static InstructionInfo() + { + _infoTbl = new InstInfo[(int)Instruction.Count]; + + Add(Instruction.Absolute, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.Add, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.BitfieldExtractS32, VariableType.S32, VariableType.S32, VariableType.S32, VariableType.S32); + Add(Instruction.BitfieldExtractU32, VariableType.U32, VariableType.U32, VariableType.S32, VariableType.S32); + Add(Instruction.BitfieldInsert, VariableType.Int, VariableType.Int, VariableType.Int, VariableType.S32, VariableType.S32); + Add(Instruction.BitfieldReverse, VariableType.Int, VariableType.Int); + Add(Instruction.BitwiseAnd, VariableType.Int, VariableType.Int, VariableType.Int); + Add(Instruction.BitwiseExclusiveOr, VariableType.Int, VariableType.Int, VariableType.Int); + Add(Instruction.BitwiseNot, VariableType.Int, VariableType.Int); + Add(Instruction.BitwiseOr, VariableType.Int, VariableType.Int, VariableType.Int); + Add(Instruction.BranchIfTrue, VariableType.None, VariableType.Bool); + Add(Instruction.BranchIfFalse, VariableType.None, VariableType.Bool); + Add(Instruction.Ceiling, VariableType.F32, VariableType.F32, VariableType.F32); + Add(Instruction.Clamp, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.ClampU32, VariableType.U32, VariableType.U32, VariableType.U32, VariableType.U32); + Add(Instruction.CompareEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.CompareGreater, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.CompareGreaterOrEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.CompareGreaterOrEqualU32, VariableType.Bool, VariableType.U32, VariableType.U32); + Add(Instruction.CompareGreaterU32, VariableType.Bool, VariableType.U32, VariableType.U32); + Add(Instruction.CompareLess, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.CompareLessOrEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.CompareLessOrEqualU32, VariableType.Bool, VariableType.U32, VariableType.U32); + Add(Instruction.CompareLessU32, VariableType.Bool, VariableType.U32, VariableType.U32); + Add(Instruction.CompareNotEqual, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.ConditionalSelect, VariableType.Scalar, VariableType.Bool, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.ConvertFPToS32, VariableType.S32, VariableType.F32); + Add(Instruction.ConvertS32ToFP, VariableType.F32, VariableType.S32); + Add(Instruction.ConvertU32ToFP, VariableType.F32, VariableType.U32); + Add(Instruction.Cosine, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.Divide, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.ExponentB2, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.Floor, VariableType.F32, VariableType.F32); + Add(Instruction.FusedMultiplyAdd, VariableType.F32, VariableType.F32, VariableType.F32, VariableType.F32); + Add(Instruction.IsNan, VariableType.Bool, VariableType.F32); + Add(Instruction.LoadConstant, VariableType.F32, VariableType.S32, VariableType.S32); + Add(Instruction.LogarithmB2, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.LogicalAnd, VariableType.Bool, VariableType.Bool, VariableType.Bool); + Add(Instruction.LogicalExclusiveOr, VariableType.Bool, VariableType.Bool, VariableType.Bool); + Add(Instruction.LogicalNot, VariableType.Bool, VariableType.Bool); + Add(Instruction.LogicalOr, VariableType.Bool, VariableType.Bool, VariableType.Bool); + Add(Instruction.ShiftLeft, VariableType.Int, VariableType.Int, VariableType.Int); + Add(Instruction.ShiftRightS32, VariableType.S32, VariableType.S32, VariableType.Int); + Add(Instruction.ShiftRightU32, VariableType.U32, VariableType.U32, VariableType.Int); + Add(Instruction.Maximum, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.MaximumU32, VariableType.U32, VariableType.U32, VariableType.U32); + Add(Instruction.Minimum, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.MinimumU32, VariableType.U32, VariableType.U32, VariableType.U32); + Add(Instruction.Multiply, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.Negate, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.PackHalf2x16, VariableType.U32, VariableType.F32, VariableType.F32); + Add(Instruction.ReciprocalSquareRoot, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.Subtract, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.TextureSample, VariableType.F32); + Add(Instruction.TextureSize, VariableType.S32, VariableType.S32, VariableType.S32); + Add(Instruction.Truncate, VariableType.F32, VariableType.F32); + Add(Instruction.UnpackHalf2x16, VariableType.F32, VariableType.U32); + } + + private static void Add(Instruction inst, VariableType destType, params VariableType[] srcTypes) + { + _infoTbl[(int)inst] = new InstInfo(destType, srcTypes); + } + + public static VariableType GetDestVarType(Instruction inst) + { + return GetFinalVarType(_infoTbl[(int)(inst & Instruction.Mask)].DestType, inst); + } + + public static VariableType GetSrcVarType(Instruction inst, int index) + { + if (inst == Instruction.TextureSample) + { + return VariableType.F32; + } + + return GetFinalVarType(_infoTbl[(int)(inst & Instruction.Mask)].SrcTypes[index], inst); + } + + private static VariableType GetFinalVarType(VariableType type, Instruction inst) + { + if (type == VariableType.Scalar) + { + return (inst & Instruction.FP) != 0 + ? VariableType.F32 + : VariableType.S32; + } + else if (type == VariableType.Int) + { + return VariableType.S32; + } + else if (type == VariableType.None) + { + throw new ArgumentException($"Invalid operand for instruction \"{inst}\"."); + } + + return type; + } + + public static bool IsUnary(Instruction inst) + { + if (inst == Instruction.Copy) + { + return true; + } + else if (inst == Instruction.TextureSample) + { + return false; + } + + return _infoTbl[(int)(inst & Instruction.Mask)].SrcTypes.Length == 1; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/OperandInfo.cs b/Ryujinx.Graphics/Shader/StructuredIr/OperandInfo.cs new file mode 100644 index 0000000000..a3a8d13839 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/OperandInfo.cs @@ -0,0 +1,34 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + static class OperandInfo + { + public static VariableType GetVarType(AstOperand operand) + { + if (operand.Type == OperandType.LocalVariable) + { + return operand.VarType; + } + else + { + return GetVarType(operand.Type); + } + } + + public static VariableType GetVarType(OperandType type) + { + switch (type) + { + case OperandType.Attribute: return VariableType.F32; + case OperandType.Constant: return VariableType.S32; + case OperandType.ConstantBuffer: return VariableType.F32; + case OperandType.GlobalMemory: return VariableType.F32; + case OperandType.Undefined: return VariableType.S32; + } + + throw new ArgumentException($"Invalid operand type \"{type}\"."); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/PhiFunctions.cs b/Ryujinx.Graphics/Shader/StructuredIr/PhiFunctions.cs new file mode 100644 index 0000000000..53391b6268 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/PhiFunctions.cs @@ -0,0 +1,74 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + static class PhiFunctions + { + public static void Remove(BasicBlock[] blocks) + { + for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) + { + BasicBlock block = blocks[blkIndex]; + + LinkedListNode node = block.Operations.First; + + while (node != null) + { + LinkedListNode nextNode = node.Next; + + if (!(node.Value is PhiNode phi)) + { + node = nextNode; + + continue; + } + + for (int index = 0; index < phi.SourcesCount; index++) + { + Operand src = phi.GetSource(index); + + BasicBlock srcBlock = phi.GetBlock(index); + + Operation copyOp = new Operation(Instruction.Copy, phi.Dest, src); + + AddBeforeBranch(srcBlock, copyOp); + } + + block.Operations.Remove(node); + + node = nextNode; + } + } + } + + private static void AddBeforeBranch(BasicBlock block, INode node) + { + INode lastOp = block.GetLastOp(); + + if (lastOp is Operation operation && IsControlFlowInst(operation.Inst)) + { + block.Operations.AddBefore(block.Operations.Last, node); + } + else + { + block.Operations.AddLast(node); + } + } + + private static bool IsControlFlowInst(Instruction inst) + { + switch (inst) + { + case Instruction.Branch: + case Instruction.BranchIfFalse: + case Instruction.BranchIfTrue: + case Instruction.Discard: + case Instruction.Return: + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgram.cs new file mode 100644 index 0000000000..f65631be7c --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgram.cs @@ -0,0 +1,254 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + static class StructuredProgram + { + public static StructuredProgramInfo MakeStructuredProgram(BasicBlock[] blocks) + { + PhiFunctions.Remove(blocks); + + StructuredProgramContext context = new StructuredProgramContext(blocks.Length); + + for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) + { + BasicBlock block = blocks[blkIndex]; + + context.EnterBlock(block); + + foreach (INode node in block.Operations) + { + Operation operation = (Operation)node; + + if (IsBranchInst(operation.Inst)) + { + context.LeaveBlock(block, operation); + } + else + { + AddOperation(context, operation); + } + } + } + + GotoElimination.Eliminate(context.GetGotos()); + + AstOptimizer.Optimize(context.Info); + + return context.Info; + } + + private static void AddOperation(StructuredProgramContext context, Operation operation) + { + Instruction inst = operation.Inst; + + IAstNode[] sources = new IAstNode[operation.SourcesCount]; + + for (int index = 0; index < sources.Length; index++) + { + sources[index] = context.GetOperandUse(operation.GetSource(index)); + } + + if (operation.Dest != null) + { + AstOperand dest = context.GetOperandDef(operation.Dest); + + if (inst == Instruction.LoadConstant) + { + Operand ldcSource = operation.GetSource(0); + + if (ldcSource.Type != OperandType.Constant) + { + throw new InvalidOperationException("Found LDC with non-constant constant buffer slot."); + } + + context.Info.CBuffers.Add(ldcSource.Value); + } + + AstAssignment assignment; + + //If all the sources are bool, it's better to use short-circuiting + //logical operations, rather than forcing a cast to int and doing + //a bitwise operation with the value, as it is likely to be used as + //a bool in the end. + if (IsBitwiseInst(inst) && AreAllSourceTypesEqual(sources, VariableType.Bool)) + { + inst = GetLogicalFromBitwiseInst(inst); + } + + bool isCondSel = inst == Instruction.ConditionalSelect; + bool isCopy = inst == Instruction.Copy; + + if (isCondSel || isCopy) + { + VariableType type = GetVarTypeFromUses(operation.Dest); + + if (isCondSel && type == VariableType.F32) + { + inst |= Instruction.FP; + } + + dest.VarType = type; + } + else + { + dest.VarType = InstructionInfo.GetDestVarType(inst); + } + + int componentMask = 1 << operation.ComponentIndex; + + IAstNode source; + + if (operation is TextureOperation texOp) + { + AstTextureOperation astTexOp = new AstTextureOperation( + inst, + texOp.Type, + texOp.Flags, + texOp.Handle, + componentMask, + sources); + + context.Info.Samplers.Add(astTexOp); + + source = astTexOp; + } + else if (!isCopy) + { + source = new AstOperation(inst, componentMask, sources); + } + else + { + source = sources[0]; + } + + assignment = new AstAssignment(dest, source); + + context.AddNode(assignment); + } + else + { + context.AddNode(new AstOperation(inst, sources)); + } + } + + private static VariableType GetVarTypeFromUses(Operand dest) + { + HashSet visited = new HashSet(); + + Queue pending = new Queue(); + + bool Enqueue(Operand operand) + { + if (visited.Add(operand)) + { + pending.Enqueue(operand); + + return true; + } + + return false; + } + + Enqueue(dest); + + while (pending.TryDequeue(out Operand operand)) + { + foreach (INode useNode in operand.UseOps) + { + if (!(useNode is Operation operation)) + { + continue; + } + + if (operation.Inst == Instruction.Copy) + { + if (operation.Dest.Type == OperandType.LocalVariable) + { + if (Enqueue(operation.Dest)) + { + break; + } + } + else + { + return OperandInfo.GetVarType(operation.Dest.Type); + } + } + else + { + for (int index = 0; index < operation.SourcesCount; index++) + { + if (operation.GetSource(index) == operand) + { + return InstructionInfo.GetSrcVarType(operation.Inst, index); + } + } + } + } + } + + return VariableType.S32; + } + + private static bool AreAllSourceTypesEqual(IAstNode[] sources, VariableType type) + { + foreach (IAstNode node in sources) + { + if (!(node is AstOperand operand)) + { + return false; + } + + if (operand.VarType != type) + { + return false; + } + } + + return true; + } + + private static bool IsBranchInst(Instruction inst) + { + switch (inst) + { + case Instruction.Branch: + case Instruction.BranchIfFalse: + case Instruction.BranchIfTrue: + return true; + } + + return false; + } + + private static bool IsBitwiseInst(Instruction inst) + { + switch (inst) + { + case Instruction.BitwiseAnd: + case Instruction.BitwiseExclusiveOr: + case Instruction.BitwiseNot: + case Instruction.BitwiseOr: + return true; + } + + return false; + } + + private static Instruction GetLogicalFromBitwiseInst(Instruction inst) + { + switch (inst) + { + case Instruction.BitwiseAnd: return Instruction.LogicalAnd; + case Instruction.BitwiseExclusiveOr: return Instruction.LogicalExclusiveOr; + case Instruction.BitwiseNot: return Instruction.LogicalNot; + case Instruction.BitwiseOr: return Instruction.LogicalOr; + } + + throw new ArgumentException($"Unexpected instruction \"{inst}\"."); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs new file mode 100644 index 0000000000..e1f0503a57 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs @@ -0,0 +1,292 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; +using System.Linq; + +using static Ryujinx.Graphics.Shader.StructuredIr.AstHelper; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class StructuredProgramContext + { + private HashSet _loopTails; + + private Stack<(AstBlock Block, int EndIndex)> _blockStack; + + private Dictionary _localsMap; + + private Dictionary _gotoTempAsgs; + + private List _gotos; + + private AstBlock _currBlock; + + private int _currEndIndex; + + public StructuredProgramInfo Info { get; } + + public StructuredProgramContext(int blocksCount) + { + _loopTails = new HashSet(); + + _blockStack = new Stack<(AstBlock, int)>(); + + _localsMap = new Dictionary(); + + _gotoTempAsgs = new Dictionary(); + + _gotos = new List(); + + _currBlock = new AstBlock(AstBlockType.Main); + + _currEndIndex = blocksCount; + + Info = new StructuredProgramInfo(_currBlock); + } + + public void EnterBlock(BasicBlock block) + { + while (_currEndIndex == block.Index) + { + (_currBlock, _currEndIndex) = _blockStack.Pop(); + } + + if (_gotoTempAsgs.TryGetValue(block.Index, out AstAssignment gotoTempAsg)) + { + AddGotoTempReset(block, gotoTempAsg); + } + + LookForDoWhileStatements(block); + } + + public void LeaveBlock(BasicBlock block, Operation branchOp) + { + LookForIfStatements(block, branchOp); + } + + private void LookForDoWhileStatements(BasicBlock block) + { + //Check if we have any predecessor whose index is greater than the + //current block, this indicates a loop. + bool done = false; + + foreach (BasicBlock predecessor in block.Predecessors.OrderByDescending(x => x.Index)) + { + if (predecessor.Index < block.Index) + { + break; + } + + if (predecessor.Index < _currEndIndex && !done) + { + Operation branchOp = (Operation)predecessor.GetLastOp(); + + NewBlock(AstBlockType.DoWhile, branchOp, predecessor.Index + 1); + + _loopTails.Add(predecessor); + + done = true; + } + else + { + AddGotoTempReset(block, GetGotoTempAsg(block.Index)); + + break; + } + } + } + + private void LookForIfStatements(BasicBlock block, Operation branchOp) + { + if (block.Branch == null) + { + return; + } + + bool isLoop = block.Branch.Index <= block.Index; + + if (block.Branch.Index <= _currEndIndex && !isLoop) + { + NewBlock(AstBlockType.If, branchOp, block.Branch.Index); + } + else if (!_loopTails.Contains(block)) + { + AstAssignment gotoTempAsg = GetGotoTempAsg(block.Branch.Index); + + IAstNode cond = GetBranchCond(AstBlockType.DoWhile, branchOp); + + AddNode(Assign(gotoTempAsg.Destination, cond)); + + AstOperation branch = new AstOperation(branchOp.Inst); + + AddNode(branch); + + GotoStatement gotoStmt = new GotoStatement(branch, gotoTempAsg, isLoop); + + _gotos.Add(gotoStmt); + } + } + + private AstAssignment GetGotoTempAsg(int index) + { + if (_gotoTempAsgs.TryGetValue(index, out AstAssignment gotoTempAsg)) + { + return gotoTempAsg; + } + + AstOperand gotoTemp = NewTemp(VariableType.Bool); + + gotoTempAsg = Assign(gotoTemp, Const(IrConsts.False)); + + _gotoTempAsgs.Add(index, gotoTempAsg); + + return gotoTempAsg; + } + + private void AddGotoTempReset(BasicBlock block, AstAssignment gotoTempAsg) + { + AddNode(gotoTempAsg); + + //For block 0, we don't need to add the extra "reset" at the beggining, + //because it is already the first node to be executed on the shader, + //so it is reset to false by the "local" assignment anyway. + if (block.Index != 0) + { + Info.MainBlock.AddFirst(Assign(gotoTempAsg.Destination, Const(IrConsts.False))); + } + } + + private void NewBlock(AstBlockType type, Operation branchOp, int endIndex) + { + NewBlock(type, GetBranchCond(type, branchOp), endIndex); + } + + private void NewBlock(AstBlockType type, IAstNode cond, int endIndex) + { + AstBlock childBlock = new AstBlock(type, cond); + + AddNode(childBlock); + + _blockStack.Push((_currBlock, _currEndIndex)); + + _currBlock = childBlock; + _currEndIndex = endIndex; + } + + private IAstNode GetBranchCond(AstBlockType type, Operation branchOp) + { + IAstNode cond; + + if (branchOp.Inst == Instruction.Branch) + { + cond = Const(type == AstBlockType.If ? IrConsts.False : IrConsts.True); + } + else + { + cond = GetOperandUse(branchOp.GetSource(0)); + + Instruction invInst = type == AstBlockType.If + ? Instruction.BranchIfTrue + : Instruction.BranchIfFalse; + + if (branchOp.Inst == invInst) + { + cond = new AstOperation(Instruction.LogicalNot, cond); + } + } + + return cond; + } + + public void AddNode(IAstNode node) + { + _currBlock.Add(node); + } + + public GotoStatement[] GetGotos() + { + return _gotos.ToArray(); + } + + private AstOperand NewTemp(VariableType type) + { + AstOperand newTemp = Local(type); + + Info.Locals.Add(newTemp); + + return newTemp; + } + + public AstOperand GetOperandDef(Operand operand) + { + if (TryGetUserAttributeIndex(operand, out int attrIndex)) + { + Info.OAttributes.Add(attrIndex); + } + + return GetOperand(operand); + } + + public AstOperand GetOperandUse(Operand operand) + { + if (TryGetUserAttributeIndex(operand, out int attrIndex)) + { + Info.IAttributes.Add(attrIndex); + } + else if (operand.Type == OperandType.ConstantBuffer) + { + Info.CBuffers.Add(operand.GetCbufSlot()); + } + + return GetOperand(operand); + } + + private AstOperand GetOperand(Operand operand) + { + if (operand == null) + { + return null; + } + + if (operand.Type != OperandType.LocalVariable) + { + return new AstOperand(operand); + } + + if (!_localsMap.TryGetValue(operand, out AstOperand astOperand)) + { + astOperand = new AstOperand(operand); + + _localsMap.Add(operand, astOperand); + + Info.Locals.Add(astOperand); + } + + return astOperand; + } + + private static bool TryGetUserAttributeIndex(Operand operand, out int attrIndex) + { + if (operand.Type == OperandType.Attribute) + { + if (operand.Value >= AttributeConsts.UserAttributeBase && + operand.Value < AttributeConsts.UserAttributeEnd) + { + attrIndex = (operand.Value - AttributeConsts.UserAttributeBase) >> 4; + + return true; + } + else if (operand.Value >= AttributeConsts.FragmentOutputColorBase && + operand.Value < AttributeConsts.FragmentOutputColorEnd) + { + attrIndex = (operand.Value - AttributeConsts.FragmentOutputColorBase) >> 4; + + return true; + } + } + + attrIndex = 0; + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramInfo.cs new file mode 100644 index 0000000000..d368ef0058 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramInfo.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class StructuredProgramInfo + { + public AstBlock MainBlock { get; } + + public HashSet Locals { get; } + + public HashSet CBuffers { get; } + + public HashSet IAttributes { get; } + public HashSet OAttributes { get; } + + public HashSet Samplers { get; } + + public StructuredProgramInfo(AstBlock mainBlock) + { + MainBlock = mainBlock; + + Locals = new HashSet(); + + CBuffers = new HashSet(); + + IAttributes = new HashSet(); + OAttributes = new HashSet(); + + Samplers = new HashSet(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/VariableType.cs b/Ryujinx.Graphics/Shader/StructuredIr/VariableType.cs new file mode 100644 index 0000000000..4c7f384978 --- /dev/null +++ b/Ryujinx.Graphics/Shader/StructuredIr/VariableType.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + enum VariableType + { + None, + Bool, + Scalar, + Int, + F32, + S32, + U32 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/TextureDescriptor.cs b/Ryujinx.Graphics/Shader/TextureDescriptor.cs new file mode 100644 index 0000000000..96f0f5b16d --- /dev/null +++ b/Ryujinx.Graphics/Shader/TextureDescriptor.cs @@ -0,0 +1,36 @@ +namespace Ryujinx.Graphics.Shader +{ + public struct TextureDescriptor + { + public string Name { get; } + + public int HandleIndex { get; } + + public bool IsBindless { get; } + + public int CbufSlot { get; } + public int CbufOffset { get; } + + public TextureDescriptor(string name, int hIndex) + { + Name = name; + HandleIndex = hIndex; + + IsBindless = false; + + CbufSlot = 0; + CbufOffset = 0; + } + + public TextureDescriptor(string name, int cbufSlot, int cbufOffset) + { + Name = name; + HandleIndex = 0; + + IsBindless = true; + + CbufSlot = cbufSlot; + CbufOffset = cbufOffset; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics/Shader/Translation/AttributeConsts.cs new file mode 100644 index 0000000000..ae3e361c72 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/AttributeConsts.cs @@ -0,0 +1,30 @@ +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + static class AttributeConsts + { + public const int Layer = 0x064; + public const int PointSize = 0x06c; + public const int PositionX = 0x070; + public const int PositionY = 0x074; + public const int PositionZ = 0x078; + public const int PositionW = 0x07c; + public const int PointCoordX = 0x2e0; + public const int PointCoordY = 0x2e4; + public const int TessCoordX = 0x2f0; + public const int TessCoordY = 0x2f4; + public const int InstanceId = 0x2f8; + public const int VertexId = 0x2fc; + public const int FrontFacing = 0x3fc; + + public const int UserAttributesCount = 32; + public const int UserAttributeBase = 0x80; + public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16; + + + //Note: Those attributes are used internally by the translator + //only, they don't exist on Maxwell. + public const int FragmentOutputDepth = 0x1000000; + public const int FragmentOutputColorBase = 0x1000010; + public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/ControlFlowGraph.cs b/Ryujinx.Graphics/Shader/Translation/ControlFlowGraph.cs new file mode 100644 index 0000000000..e2ca74a4de --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/ControlFlowGraph.cs @@ -0,0 +1,108 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Translation +{ + static class ControlFlowGraph + { + public static BasicBlock[] MakeCfg(Operation[] operations) + { + Dictionary labels = new Dictionary(); + + List blocks = new List(); + + BasicBlock currentBlock = null; + + void NextBlock(BasicBlock nextBlock) + { + if (currentBlock != null && !EndsWithUnconditionalInst(currentBlock.GetLastOp())) + { + currentBlock.Next = nextBlock; + } + + currentBlock = nextBlock; + } + + void NewNextBlock() + { + BasicBlock block = new BasicBlock(blocks.Count); + + blocks.Add(block); + + NextBlock(block); + } + + bool needsNewBlock = true; + + for (int index = 0; index < operations.Length; index++) + { + Operation operation = operations[index]; + + if (operation.Inst == Instruction.MarkLabel) + { + Operand label = operation.Dest; + + if (labels.TryGetValue(label, out BasicBlock nextBlock)) + { + nextBlock.Index = blocks.Count; + + blocks.Add(nextBlock); + + NextBlock(nextBlock); + } + else + { + NewNextBlock(); + + labels.Add(label, currentBlock); + } + } + else + { + if (needsNewBlock) + { + NewNextBlock(); + } + + currentBlock.Operations.AddLast(operation); + } + + needsNewBlock = operation.Inst == Instruction.Branch || + operation.Inst == Instruction.BranchIfTrue || + operation.Inst == Instruction.BranchIfFalse; + + if (needsNewBlock) + { + Operand label = operation.Dest; + + if (!labels.TryGetValue(label, out BasicBlock branchBlock)) + { + branchBlock = new BasicBlock(); + + labels.Add(label, branchBlock); + } + + currentBlock.Branch = branchBlock; + } + } + + return blocks.ToArray(); + } + + private static bool EndsWithUnconditionalInst(INode node) + { + if (node is Operation operation) + { + switch (operation.Inst) + { + case Instruction.Branch: + case Instruction.Discard: + case Instruction.Return: + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/Dominance.cs b/Ryujinx.Graphics/Shader/Translation/Dominance.cs new file mode 100644 index 0000000000..b4b80e3ef2 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/Dominance.cs @@ -0,0 +1,127 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Shader.Translation +{ + static class Dominance + { + //Those methods are an implementation of the algorithms on "A Simple, Fast Dominance Algorithm". + //https://www.cs.rice.edu/~keith/EMBED/dom.pdf + public static void FindDominators(BasicBlock entry, int blocksCount) + { + HashSet visited = new HashSet(); + + Stack blockStack = new Stack(); + + List postOrderBlocks = new List(blocksCount); + + int[] postOrderMap = new int[blocksCount]; + + visited.Add(entry); + + blockStack.Push(entry); + + while (blockStack.TryPop(out BasicBlock block)) + { + if (block.Next != null && visited.Add(block.Next)) + { + blockStack.Push(block); + blockStack.Push(block.Next); + } + else if (block.Branch != null && visited.Add(block.Branch)) + { + blockStack.Push(block); + blockStack.Push(block.Branch); + } + else + { + postOrderMap[block.Index] = postOrderBlocks.Count; + + postOrderBlocks.Add(block); + } + } + + BasicBlock Intersect(BasicBlock block1, BasicBlock block2) + { + while (block1 != block2) + { + while (postOrderMap[block1.Index] < postOrderMap[block2.Index]) + { + block1 = block1.ImmediateDominator; + } + + while (postOrderMap[block2.Index] < postOrderMap[block1.Index]) + { + block2 = block2.ImmediateDominator; + } + } + + return block1; + } + + entry.ImmediateDominator = entry; + + bool modified; + + do + { + modified = false; + + for (int blkIndex = postOrderBlocks.Count - 2; blkIndex >= 0; blkIndex--) + { + BasicBlock block = postOrderBlocks[blkIndex]; + + BasicBlock newIDom = null; + + foreach (BasicBlock predecessor in block.Predecessors) + { + if (predecessor.ImmediateDominator != null) + { + if (newIDom != null) + { + newIDom = Intersect(predecessor, newIDom); + } + else + { + newIDom = predecessor; + } + } + } + + if (block.ImmediateDominator != newIDom) + { + block.ImmediateDominator = newIDom; + + modified = true; + } + } + } + while (modified); + } + + public static void FindDominanceFrontiers(BasicBlock[] blocks) + { + for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) + { + BasicBlock block = blocks[blkIndex]; + + if (block.Predecessors.Count < 2) + { + continue; + } + + for (int pBlkIndex = 0; pBlkIndex < block.Predecessors.Count; pBlkIndex++) + { + BasicBlock current = block.Predecessors[pBlkIndex]; + + while (current != block.ImmediateDominator) + { + current.DominanceFrontiers.Add(block); + + current = current.ImmediateDominator; + } + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics/Shader/Translation/EmitterContext.cs new file mode 100644 index 0000000000..6c2bf6e478 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/EmitterContext.cs @@ -0,0 +1,105 @@ +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation +{ + class EmitterContext + { + public Block CurrBlock { get; set; } + public OpCode CurrOp { get; set; } + + private GalShaderType _shaderType; + + private ShaderHeader _header; + + private List _operations; + + private Dictionary _labels; + + public EmitterContext(GalShaderType shaderType, ShaderHeader header) + { + _shaderType = shaderType; + _header = header; + + _operations = new List(); + + _labels = new Dictionary(); + } + + public Operand Add(Instruction inst, Operand dest = null, params Operand[] sources) + { + Operation operation = new Operation(inst, dest, sources); + + Add(operation); + + return dest; + } + + public void Add(Operation operation) + { + _operations.Add(operation); + } + + public void MarkLabel(Operand label) + { + Add(Instruction.MarkLabel, label); + } + + public Operand GetLabel(ulong address) + { + if (!_labels.TryGetValue(address, out Operand label)) + { + label = Label(); + + _labels.Add(address, label); + } + + return label; + } + + public void PrepareForReturn() + { + if (_shaderType == GalShaderType.Fragment) + { + if (_header.OmapDepth) + { + Operand dest = Attribute(AttributeConsts.FragmentOutputDepth); + + Operand src = Register(_header.DepthRegister, RegisterType.Gpr); + + this.Copy(dest, src); + } + + int regIndex = 0; + + for (int attachment = 0; attachment < 8; attachment++) + { + OutputMapTarget target = _header.OmapTargets[attachment]; + + for (int component = 0; component < 4; component++) + { + if (target.ComponentEnabled(component)) + { + Operand dest = Attribute(AttributeConsts.FragmentOutputColorBase + regIndex * 4); + + Operand src = Register(regIndex, RegisterType.Gpr); + + this.Copy(dest, src); + + regIndex++; + } + } + } + } + } + + public Operation[] GetOperations() + { + return _operations.ToArray(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs new file mode 100644 index 0000000000..604aa67d34 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs @@ -0,0 +1,420 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation +{ + static class EmitterContextInsts + { + public static Operand BitfieldExtractS32(this EmitterContext context, Operand a, Operand b, Operand c) + { + return context.Add(Instruction.BitfieldExtractS32, Local(), a, b, c); + } + + public static Operand BitfieldExtractU32(this EmitterContext context, Operand a, Operand b, Operand c) + { + return context.Add(Instruction.BitfieldExtractU32, Local(), a, b, c); + } + + public static Operand BitfieldInsert(this EmitterContext context, Operand a, Operand b, Operand c, Operand d) + { + return context.Add(Instruction.BitfieldInsert, Local(), a, b, c, d); + } + + public static Operand BitfieldReverse(this EmitterContext context, Operand a) + { + return context.Add(Instruction.BitfieldReverse, Local(), a); + } + + public static Operand BitwiseAnd(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.BitwiseAnd, Local(), a, b); + } + + public static Operand BitwiseExclusiveOr(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.BitwiseExclusiveOr, Local(), a, b); + } + + public static Operand BitwiseNot(this EmitterContext context, Operand a, bool invert) + { + if (invert) + { + a = context.BitwiseNot(a); + } + + return a; + } + + public static Operand BitwiseNot(this EmitterContext context, Operand a) + { + return context.Add(Instruction.BitwiseNot, Local(), a); + } + + public static Operand BitwiseOr(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.BitwiseOr, Local(), a, b); + } + + public static Operand Branch(this EmitterContext context, Operand d) + { + return context.Add(Instruction.Branch, d); + } + + public static Operand BranchIfFalse(this EmitterContext context, Operand d, Operand a) + { + return context.Add(Instruction.BranchIfFalse, d, a); + } + + public static Operand BranchIfTrue(this EmitterContext context, Operand d, Operand a) + { + return context.Add(Instruction.BranchIfTrue, d, a); + } + + public static Operand ConditionalSelect(this EmitterContext context, Operand a, Operand b, Operand c) + { + return context.Add(Instruction.ConditionalSelect, Local(), a, b, c); + } + + public static Operand Copy(this EmitterContext context, Operand a) + { + return context.Add(Instruction.Copy, Local(), a); + } + + public static void Copy(this EmitterContext context, Operand d, Operand a) + { + if (d.Type == OperandType.Constant) + { + return; + } + + context.Add(Instruction.Copy, d, a); + } + + public static Operand Discard(this EmitterContext context) + { + return context.Add(Instruction.Discard); + } + + public static Operand EmitVertex(this EmitterContext context) + { + return context.Add(Instruction.EmitVertex); + } + + public static Operand EndPrimitive(this EmitterContext context) + { + return context.Add(Instruction.EndPrimitive); + } + + public static Operand FPAbsNeg(this EmitterContext context, Operand a, bool abs, bool neg) + { + return context.FPNegate(context.FPAbsolute(a, abs), neg); + } + + public static Operand FPAbsolute(this EmitterContext context, Operand a, bool abs) + { + if (abs) + { + a = context.FPAbsolute(a); + } + + return a; + } + + public static Operand FPAbsolute(this EmitterContext context, Operand a) + { + return context.Add(Instruction.FP | Instruction.Absolute, Local(), a); + } + + public static Operand FPAdd(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.FP | Instruction.Add, Local(), a, b); + } + + public static Operand FPCeiling(this EmitterContext context, Operand a) + { + return context.Add(Instruction.FP | Instruction.Ceiling, Local(), a); + } + + public static Operand FPCompareEqual(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.FP | Instruction.CompareEqual, Local(), a, b); + } + + public static Operand FPCompareLess(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.FP | Instruction.CompareLess, Local(), a, b); + } + + public static Operand FPConvertToS32(this EmitterContext context, Operand a) + { + return context.Add(Instruction.ConvertFPToS32, Local(), a); + } + + public static Operand FPCosine(this EmitterContext context, Operand a) + { + return context.Add(Instruction.FP | Instruction.Cosine, Local(), a); + } + + public static Operand FPDivide(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.FP | Instruction.Divide, Local(), a, b); + } + + public static Operand FPExponentB2(this EmitterContext context, Operand a) + { + return context.Add(Instruction.FP | Instruction.ExponentB2, Local(), a); + } + + public static Operand FPFloor(this EmitterContext context, Operand a) + { + return context.Add(Instruction.FP | Instruction.Floor, Local(), a); + } + + public static Operand FPLogarithmB2(this EmitterContext context, Operand a) + { + return context.Add(Instruction.FP | Instruction.LogarithmB2, Local(), a); + } + + public static Operand FPMaximum(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.FP | Instruction.Maximum, Local(), a, b); + } + + public static Operand FPMinimum(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.FP | Instruction.Minimum, Local(), a, b); + } + + public static Operand FPMultiply(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.FP | Instruction.Multiply, Local(), a, b); + } + + public static Operand FPFusedMultiplyAdd(this EmitterContext context, Operand a, Operand b, Operand c) + { + return context.Add(Instruction.FusedMultiplyAdd, Local(), a, b, c); + } + + public static Operand FPNegate(this EmitterContext context, Operand a, bool neg) + { + if (neg) + { + a = context.FPNegate(a); + } + + return a; + } + + public static Operand FPNegate(this EmitterContext context, Operand a) + { + return context.Add(Instruction.FP | Instruction.Negate, Local(), a); + } + + public static Operand FPReciprocal(this EmitterContext context, Operand a) + { + return context.FPDivide(ConstF(1), a); + } + + public static Operand FPReciprocalSquareRoot(this EmitterContext context, Operand a) + { + return context.Add(Instruction.FP | Instruction.ReciprocalSquareRoot, Local(), a); + } + + public static Operand FPSaturate(this EmitterContext context, Operand a, bool sat) + { + if (sat) + { + a = context.FPSaturate(a); + } + + return a; + } + + public static Operand FPSaturate(this EmitterContext context, Operand a) + { + return context.Add(Instruction.FP | Instruction.Clamp, Local(), a, ConstF(0), ConstF(1)); + } + + public static Operand FPSine(this EmitterContext context, Operand a) + { + return context.Add(Instruction.FP | Instruction.Sine, Local(), a); + } + + public static Operand FPSquareRoot(this EmitterContext context, Operand a) + { + return context.Add(Instruction.FP | Instruction.SquareRoot, Local(), a); + } + + public static Operand FPTruncate(this EmitterContext context, Operand a) + { + return context.Add(Instruction.Truncate, Local(), a); + } + + public static Operand IAbsNeg(this EmitterContext context, Operand a, bool abs, bool neg) + { + return context.INegate(context.IAbsolute(a, abs), neg); + } + + public static Operand IAbsolute(this EmitterContext context, Operand a, bool abs) + { + if (abs) + { + a = context.IAbsolute(a); + } + + return a; + } + + public static Operand IAbsolute(this EmitterContext context, Operand a) + { + return context.Add(Instruction.Absolute, Local(), a); + } + + public static Operand IAdd(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.Add, Local(), a, b); + } + + public static Operand IClampS32(this EmitterContext context, Operand a, Operand b, Operand c) + { + return context.Add(Instruction.Clamp, Local(), a, b, c); + } + + public static Operand IClampU32(this EmitterContext context, Operand a, Operand b, Operand c) + { + return context.Add(Instruction.ClampU32, Local(), a, b, c); + } + + public static Operand ICompareEqual(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.CompareEqual, Local(), a, b); + } + + public static Operand ICompareLess(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.CompareLess, Local(), a, b); + } + + public static Operand ICompareLessUnsigned(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.CompareLessU32, Local(), a, b); + } + + public static Operand ICompareNotEqual(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.CompareNotEqual, Local(), a, b); + } + + public static Operand IConvertS32ToFP(this EmitterContext context, Operand a) + { + return context.Add(Instruction.ConvertS32ToFP, Local(), a); + } + + public static Operand IConvertU32ToFP(this EmitterContext context, Operand a) + { + return context.Add(Instruction.ConvertU32ToFP, Local(), a); + } + + public static Operand IMaximumS32(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.Maximum, Local(), a, b); + } + + public static Operand IMaximumU32(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.MaximumU32, Local(), a, b); + } + + public static Operand IMinimumS32(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.Minimum, Local(), a, b); + } + + public static Operand IMinimumU32(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.MinimumU32, Local(), a, b); + } + + public static Operand IMultiply(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.Multiply, Local(), a, b); + } + + public static Operand INegate(this EmitterContext context, Operand a, bool neg) + { + if (neg) + { + a = context.INegate(a); + } + + return a; + } + + public static Operand INegate(this EmitterContext context, Operand a) + { + return context.Add(Instruction.Negate, Local(), a); + } + + public static Operand ISubtract(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.Subtract, Local(), a, b); + } + + public static Operand IsNan(this EmitterContext context, Operand a) + { + return context.Add(Instruction.IsNan, Local(), a); + } + + public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.LoadConstant, Local(), a, b); + } + + public static Operand PackHalf2x16(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.PackHalf2x16, Local(), a, b); + } + + public static Operand Return(this EmitterContext context) + { + context.PrepareForReturn(); + + return context.Add(Instruction.Return); + } + + public static Operand ShiftLeft(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.ShiftLeft, Local(), a, b); + } + + public static Operand ShiftRightS32(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.ShiftRightS32, Local(), a, b); + } + + public static Operand ShiftRightU32(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.ShiftRightU32, Local(), a, b); + } + + public static Operand UnpackHalf2x16High(this EmitterContext context, Operand a) + { + return UnpackHalf2x16(context, a, 1); + } + + public static Operand UnpackHalf2x16Low(this EmitterContext context, Operand a) + { + return UnpackHalf2x16(context, a, 0); + } + + private static Operand UnpackHalf2x16(this EmitterContext context, Operand a, int index) + { + Operand dest = Local(); + + context.Add(new Operation(Instruction.UnpackHalf2x16, index, dest, a)); + + return dest; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/Optimizations/BranchElimination.cs b/Ryujinx.Graphics/Shader/Translation/Optimizations/BranchElimination.cs new file mode 100644 index 0000000000..2b0f19052b --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/Optimizations/BranchElimination.cs @@ -0,0 +1,64 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; + +namespace Ryujinx.Graphics.Shader.Translation.Optimizations +{ + static class BranchElimination + { + public static bool Eliminate(BasicBlock block) + { + if (block.HasBranch && IsRedundantBranch((Operation)block.GetLastOp(), Next(block))) + { + block.Branch = null; + + return true; + } + + return false; + } + + private static bool IsRedundantBranch(Operation current, BasicBlock nextBlock) + { + //Here we check that: + //- The current block ends with a branch. + //- The next block only contains a branch. + //- The branch on the next block is unconditional. + //- Both branches are jumping to the same location. + //In this case, the branch on the current block can be removed, + //as the next block is going to jump to the same place anyway. + if (nextBlock == null) + { + return false; + } + + if (!(nextBlock.Operations.First?.Value is Operation next)) + { + return false; + } + + if (next.Inst != Instruction.Branch) + { + return false; + } + + return current.Dest == next.Dest; + } + + private static BasicBlock Next(BasicBlock block) + { + block = block.Next; + + while (block != null && block.Operations.Count == 0) + { + if (block.HasBranch) + { + throw new InvalidOperationException("Found a bogus empty block that \"ends with a branch\"."); + } + + block = block.Next; + } + + return block; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/Optimizations/ConstantFolding.cs b/Ryujinx.Graphics/Shader/Translation/Optimizations/ConstantFolding.cs new file mode 100644 index 0000000000..a2e05ef120 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/Optimizations/ConstantFolding.cs @@ -0,0 +1,323 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation.Optimizations +{ + static class ConstantFolding + { + public static void Fold(Operation operation) + { + if (!AreAllSourcesConstant(operation)) + { + return; + } + + switch (operation.Inst) + { + case Instruction.Add: + EvaluateBinary(operation, (x, y) => x + y); + break; + + case Instruction.BitwiseAnd: + EvaluateBinary(operation, (x, y) => x & y); + break; + + case Instruction.BitwiseExclusiveOr: + EvaluateBinary(operation, (x, y) => x ^ y); + break; + + case Instruction.BitwiseNot: + EvaluateUnary(operation, (x) => ~x); + break; + + case Instruction.BitwiseOr: + EvaluateBinary(operation, (x, y) => x | y); + break; + + case Instruction.BitfieldExtractS32: + BitfieldExtractS32(operation); + break; + + case Instruction.BitfieldExtractU32: + BitfieldExtractU32(operation); + break; + + case Instruction.Clamp: + EvaluateTernary(operation, (x, y, z) => Math.Clamp(x, y, z)); + break; + + case Instruction.ClampU32: + EvaluateTernary(operation, (x, y, z) => (int)Math.Clamp((uint)x, (uint)y, (uint)z)); + break; + + case Instruction.CompareEqual: + EvaluateBinary(operation, (x, y) => x == y); + break; + + case Instruction.CompareGreater: + EvaluateBinary(operation, (x, y) => x > y); + break; + + case Instruction.CompareGreaterOrEqual: + EvaluateBinary(operation, (x, y) => x >= y); + break; + + case Instruction.CompareGreaterOrEqualU32: + EvaluateBinary(operation, (x, y) => (uint)x >= (uint)y); + break; + + case Instruction.CompareGreaterU32: + EvaluateBinary(operation, (x, y) => (uint)x > (uint)y); + break; + + case Instruction.CompareLess: + EvaluateBinary(operation, (x, y) => x < y); + break; + + case Instruction.CompareLessOrEqual: + EvaluateBinary(operation, (x, y) => x <= y); + break; + + case Instruction.CompareLessOrEqualU32: + EvaluateBinary(operation, (x, y) => (uint)x <= (uint)y); + break; + + case Instruction.CompareLessU32: + EvaluateBinary(operation, (x, y) => (uint)x < (uint)y); + break; + + case Instruction.CompareNotEqual: + EvaluateBinary(operation, (x, y) => x != y); + break; + + case Instruction.Divide: + EvaluateBinary(operation, (x, y) => y != 0 ? x / y : 0); + break; + + case Instruction.FP | Instruction.Add: + EvaluateFPBinary(operation, (x, y) => x + y); + break; + + case Instruction.FP | Instruction.Clamp: + EvaluateFPTernary(operation, (x, y, z) => Math.Clamp(x, y, z)); + break; + + case Instruction.FP | Instruction.CompareEqual: + EvaluateFPBinary(operation, (x, y) => x == y); + break; + + case Instruction.FP | Instruction.CompareGreater: + EvaluateFPBinary(operation, (x, y) => x > y); + break; + + case Instruction.FP | Instruction.CompareGreaterOrEqual: + EvaluateFPBinary(operation, (x, y) => x >= y); + break; + + case Instruction.FP | Instruction.CompareLess: + EvaluateFPBinary(operation, (x, y) => x < y); + break; + + case Instruction.FP | Instruction.CompareLessOrEqual: + EvaluateFPBinary(operation, (x, y) => x <= y); + break; + + case Instruction.FP | Instruction.CompareNotEqual: + EvaluateFPBinary(operation, (x, y) => x != y); + break; + + case Instruction.FP | Instruction.Divide: + EvaluateFPBinary(operation, (x, y) => x / y); + break; + + case Instruction.FP | Instruction.Multiply: + EvaluateFPBinary(operation, (x, y) => x * y); + break; + + case Instruction.FP | Instruction.Negate: + EvaluateFPUnary(operation, (x) => -x); + break; + + case Instruction.FP | Instruction.Subtract: + EvaluateFPBinary(operation, (x, y) => x - y); + break; + + case Instruction.IsNan: + EvaluateFPUnary(operation, (x) => float.IsNaN(x)); + break; + + case Instruction.Maximum: + EvaluateBinary(operation, (x, y) => Math.Max(x, y)); + break; + + case Instruction.MaximumU32: + EvaluateBinary(operation, (x, y) => (int)Math.Max((uint)x, (uint)y)); + break; + + case Instruction.Minimum: + EvaluateBinary(operation, (x, y) => Math.Min(x, y)); + break; + + case Instruction.MinimumU32: + EvaluateBinary(operation, (x, y) => (int)Math.Min((uint)x, (uint)y)); + break; + + case Instruction.Multiply: + EvaluateBinary(operation, (x, y) => x * y); + break; + + case Instruction.Negate: + EvaluateUnary(operation, (x) => -x); + break; + + case Instruction.ShiftLeft: + EvaluateBinary(operation, (x, y) => x << y); + break; + + case Instruction.ShiftRightS32: + EvaluateBinary(operation, (x, y) => x >> y); + break; + + case Instruction.ShiftRightU32: + EvaluateBinary(operation, (x, y) => (int)((uint)x >> y)); + break; + + case Instruction.Subtract: + EvaluateBinary(operation, (x, y) => x - y); + break; + + case Instruction.UnpackHalf2x16: + UnpackHalf2x16(operation); + break; + } + } + + private static bool AreAllSourcesConstant(Operation operation) + { + for (int index = 0; index < operation.SourcesCount; index++) + { + if (operation.GetSource(index).Type != OperandType.Constant) + { + return false; + } + } + + return true; + } + + private static void BitfieldExtractS32(Operation operation) + { + int value = GetBitfieldExtractValue(operation); + + int shift = 32 - operation.GetSource(2).Value; + + value = (value << shift) >> shift; + + operation.TurnIntoCopy(Const(value)); + } + + private static void BitfieldExtractU32(Operation operation) + { + operation.TurnIntoCopy(Const(GetBitfieldExtractValue(operation))); + } + + private static int GetBitfieldExtractValue(Operation operation) + { + int value = operation.GetSource(0).Value; + int lsb = operation.GetSource(1).Value; + int length = operation.GetSource(2).Value; + + return value.Extract(lsb, length); + } + + private static void UnpackHalf2x16(Operation operation) + { + int value = operation.GetSource(0).Value; + + value = (value >> operation.ComponentIndex * 16) & 0xffff; + + operation.TurnIntoCopy(ConstF(HalfConversion.HalfToSingle(value))); + } + + private static void FPNegate(Operation operation) + { + float value = operation.GetSource(0).AsFloat(); + + operation.TurnIntoCopy(ConstF(-value)); + } + + private static void EvaluateUnary(Operation operation, Func op) + { + int x = operation.GetSource(0).Value; + + operation.TurnIntoCopy(Const(op(x))); + } + + private static void EvaluateFPUnary(Operation operation, Func op) + { + float x = operation.GetSource(0).AsFloat(); + + operation.TurnIntoCopy(ConstF(op(x))); + } + + private static void EvaluateFPUnary(Operation operation, Func op) + { + float x = operation.GetSource(0).AsFloat(); + + operation.TurnIntoCopy(Const(op(x) ? IrConsts.True : IrConsts.False)); + } + + private static void EvaluateBinary(Operation operation, Func op) + { + int x = operation.GetSource(0).Value; + int y = operation.GetSource(1).Value; + + operation.TurnIntoCopy(Const(op(x, y))); + } + + private static void EvaluateBinary(Operation operation, Func op) + { + int x = operation.GetSource(0).Value; + int y = operation.GetSource(1).Value; + + operation.TurnIntoCopy(Const(op(x, y) ? IrConsts.True : IrConsts.False)); + } + + private static void EvaluateFPBinary(Operation operation, Func op) + { + float x = operation.GetSource(0).AsFloat(); + float y = operation.GetSource(1).AsFloat(); + + operation.TurnIntoCopy(ConstF(op(x, y))); + } + + private static void EvaluateFPBinary(Operation operation, Func op) + { + float x = operation.GetSource(0).AsFloat(); + float y = operation.GetSource(1).AsFloat(); + + operation.TurnIntoCopy(Const(op(x, y) ? IrConsts.True : IrConsts.False)); + } + + private static void EvaluateTernary(Operation operation, Func op) + { + int x = operation.GetSource(0).Value; + int y = operation.GetSource(1).Value; + int z = operation.GetSource(2).Value; + + operation.TurnIntoCopy(Const(op(x, y, z))); + } + + private static void EvaluateFPTernary(Operation operation, Func op) + { + float x = operation.GetSource(0).AsFloat(); + float y = operation.GetSource(1).AsFloat(); + float z = operation.GetSource(2).AsFloat(); + + operation.TurnIntoCopy(ConstF(op(x, y, z))); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/Optimizations/HalfConversion.cs b/Ryujinx.Graphics/Shader/Translation/Optimizations/HalfConversion.cs new file mode 100644 index 0000000000..9ef35abc92 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/Optimizations/HalfConversion.cs @@ -0,0 +1,47 @@ +using System; + +namespace Ryujinx.Graphics.Shader.Translation.Optimizations +{ + static class HalfConversion + { + public static float HalfToSingle(int value) + { + int mantissa = (value >> 0) & 0x3ff; + int exponent = (value >> 10) & 0x1f; + int sign = (value >> 15) & 0x1; + + if (exponent == 0x1f) + { + //NaN or Infinity. + mantissa <<= 13; + exponent = 0xff; + } + else if (exponent != 0 || mantissa != 0 ) + { + if (exponent == 0) + { + //Denormal. + int e = -1; + int m = mantissa; + + do + { + e++; + m <<= 1; + } + while ((m & 0x400) == 0); + + mantissa = m & 0x3ff; + exponent = e; + } + + mantissa <<= 13; + exponent = 127 - 15 + exponent; + } + + int output = (sign << 31) | (exponent << 23) | mantissa; + + return BitConverter.Int32BitsToSingle(output); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs new file mode 100644 index 0000000000..88118e3a75 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs @@ -0,0 +1,172 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; +using System.Linq; + +namespace Ryujinx.Graphics.Shader.Translation.Optimizations +{ + static class Optimizer + { + public static void Optimize(BasicBlock[] blocks) + { + bool modified; + + do + { + modified = false; + + for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) + { + BasicBlock block = blocks[blkIndex]; + + LinkedListNode node = block.Operations.First; + + while (node != null) + { + LinkedListNode nextNode = node.Next; + + bool isUnused = IsUnused(node.Value); + + if (!(node.Value is Operation operation) || isUnused) + { + if (isUnused) + { + RemoveNode(block, node); + + modified = true; + } + + node = nextNode; + + continue; + } + + ConstantFolding.Fold(operation); + + Simplification.Simplify(operation); + + if (DestIsLocalVar(operation)) + { + if (operation.Inst == Instruction.Copy) + { + PropagateCopy(operation); + + RemoveNode(block, node); + + modified = true; + } + else if (operation.Inst == Instruction.PackHalf2x16 && PropagatePack(operation)) + { + if (operation.Dest.UseOps.Count == 0) + { + RemoveNode(block, node); + } + + modified = true; + } + } + + node = nextNode; + } + + if (BranchElimination.Eliminate(block)) + { + RemoveNode(block, block.Operations.Last); + + modified = true; + } + } + } + while (modified); + } + + private static void PropagateCopy(Operation copyOp) + { + //Propagate copy source operand to all uses of + //the destination operand. + Operand dest = copyOp.Dest; + Operand src = copyOp.GetSource(0); + + INode[] uses = dest.UseOps.ToArray(); + + foreach (INode useNode in uses) + { + for (int index = 0; index < useNode.SourcesCount; index++) + { + if (useNode.GetSource(index) == dest) + { + useNode.SetSource(index, src); + } + } + } + } + + private static bool PropagatePack(Operation packOp) + { + //Propagate pack source operands to uses by unpack + //instruction. The source depends on the unpack instruction. + bool modified = false; + + Operand dest = packOp.Dest; + Operand src0 = packOp.GetSource(0); + Operand src1 = packOp.GetSource(1); + + INode[] uses = dest.UseOps.ToArray(); + + foreach (INode useNode in uses) + { + if (!(useNode is Operation operation) || operation.Inst != Instruction.UnpackHalf2x16) + { + continue; + } + + if (operation.GetSource(0) == dest) + { + operation.TurnIntoCopy(operation.ComponentIndex == 1 ? src1 : src0); + + modified = true; + } + } + + return modified; + } + + private static void RemoveNode(BasicBlock block, LinkedListNode llNode) + { + //Remove a node from the nodes list, and also remove itself + //from all the use lists on the operands that this node uses. + block.Operations.Remove(llNode); + + Queue nodes = new Queue(); + + nodes.Enqueue(llNode.Value); + + while (nodes.TryDequeue(out INode node)) + { + for (int index = 0; index < node.SourcesCount; index++) + { + Operand src = node.GetSource(index); + + if (src.Type != OperandType.LocalVariable) + { + continue; + } + + if (src.UseOps.Remove(node) && src.UseOps.Count == 0) + { + nodes.Enqueue(src.AsgOp); + } + } + } + } + + private static bool IsUnused(INode node) + { + return DestIsLocalVar(node) && node.Dest.UseOps.Count == 0; + } + + private static bool DestIsLocalVar(INode node) + { + return node.Dest != null && node.Dest.Type == OperandType.LocalVariable; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/Optimizations/Simplification.cs b/Ryujinx.Graphics/Shader/Translation/Optimizations/Simplification.cs new file mode 100644 index 0000000000..56b1543f12 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/Optimizations/Simplification.cs @@ -0,0 +1,147 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation.Optimizations +{ + static class Simplification + { + private const int AllOnes = ~0; + + public static void Simplify(Operation operation) + { + switch (operation.Inst) + { + case Instruction.Add: + case Instruction.BitwiseExclusiveOr: + TryEliminateBinaryOpComutative(operation, 0); + break; + + case Instruction.BitwiseAnd: + TryEliminateBitwiseAnd(operation); + break; + + case Instruction.BitwiseOr: + TryEliminateBitwiseOr(operation); + break; + + case Instruction.ConditionalSelect: + TryEliminateConditionalSelect(operation); + break; + + case Instruction.Divide: + TryEliminateBinaryOpY(operation, 1); + break; + + case Instruction.Multiply: + TryEliminateBinaryOpComutative(operation, 1); + break; + + case Instruction.ShiftLeft: + case Instruction.ShiftRightS32: + case Instruction.ShiftRightU32: + case Instruction.Subtract: + TryEliminateBinaryOpY(operation, 0); + break; + } + } + + private static void TryEliminateBitwiseAnd(Operation operation) + { + //Try to recognize and optimize those 3 patterns (in order): + //x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y, + //x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000 + Operand x = operation.GetSource(0); + Operand y = operation.GetSource(1); + + if (IsConstEqual(x, AllOnes)) + { + operation.TurnIntoCopy(y); + } + else if (IsConstEqual(y, AllOnes)) + { + operation.TurnIntoCopy(x); + } + else if (IsConstEqual(x, 0) || IsConstEqual(y, 0)) + { + operation.TurnIntoCopy(Const(0)); + } + } + + private static void TryEliminateBitwiseOr(Operation operation) + { + //Try to recognize and optimize those 3 patterns (in order): + //x | 0x00000000 == x, 0x00000000 | y == y, + //x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF + Operand x = operation.GetSource(0); + Operand y = operation.GetSource(1); + + if (IsConstEqual(x, 0)) + { + operation.TurnIntoCopy(y); + } + else if (IsConstEqual(y, 0)) + { + operation.TurnIntoCopy(x); + } + else if (IsConstEqual(x, AllOnes) || IsConstEqual(y, AllOnes)) + { + operation.TurnIntoCopy(Const(AllOnes)); + } + } + + private static void TryEliminateBinaryOpY(Operation operation, int comparand) + { + Operand x = operation.GetSource(0); + Operand y = operation.GetSource(1); + + if (IsConstEqual(y, comparand)) + { + operation.TurnIntoCopy(x); + } + } + + private static void TryEliminateBinaryOpComutative(Operation operation, int comparand) + { + Operand x = operation.GetSource(0); + Operand y = operation.GetSource(1); + + if (IsConstEqual(x, comparand)) + { + operation.TurnIntoCopy(y); + } + else if (IsConstEqual(y, comparand)) + { + operation.TurnIntoCopy(x); + } + } + + private static void TryEliminateConditionalSelect(Operation operation) + { + Operand cond = operation.GetSource(0); + + if (cond.Type != OperandType.Constant) + { + return; + } + + //The condition is constant, we can turn it into a copy, and select + //the source based on the condition value. + int srcIndex = cond.Value != 0 ? 1 : 2; + + Operand source = operation.GetSource(srcIndex); + + operation.TurnIntoCopy(source); + } + + private static bool IsConstEqual(Operand operand, int comparand) + { + if (operand.Type != OperandType.Constant) + { + return false; + } + + return operand.Value == comparand; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/Ssa.cs b/Ryujinx.Graphics/Shader/Translation/Ssa.cs new file mode 100644 index 0000000000..b612649ca3 --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/Ssa.cs @@ -0,0 +1,330 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation +{ + static class Ssa + { + private const int GprsAndPredsCount = RegisterConsts.GprsCount + RegisterConsts.PredsCount; + + private class DefMap + { + private Dictionary _map; + + private long[] _phiMasks; + + public DefMap() + { + _map = new Dictionary(); + + _phiMasks = new long[(RegisterConsts.TotalCount + 63) / 64]; + } + + public bool TryAddOperand(Register reg, Operand operand) + { + return _map.TryAdd(reg, operand); + } + + public bool TryGetOperand(Register reg, out Operand operand) + { + return _map.TryGetValue(reg, out operand); + } + + public bool AddPhi(Register reg) + { + int key = GetKeyFromRegister(reg); + + int index = key / 64; + int bit = key & 63; + + long mask = 1L << bit; + + if ((_phiMasks[index] & mask) != 0) + { + return false; + } + + _phiMasks[index] |= mask; + + return true; + } + + public bool HasPhi(Register reg) + { + int key = GetKeyFromRegister(reg); + + int index = key / 64; + int bit = key & 63; + + return (_phiMasks[index] & (1L << bit)) != 0; + } + } + + private struct Definition + { + public BasicBlock Block { get; } + public Operand Local { get; } + + public Definition(BasicBlock block, Operand local) + { + Block = block; + Local = local; + } + } + + public static void Rename(BasicBlock[] blocks) + { + DefMap[] globalDefs = new DefMap[blocks.Length]; + + for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) + { + globalDefs[blkIndex] = new DefMap(); + } + + Queue dfPhiBlocks = new Queue(); + + //First pass, get all defs and locals uses. + for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) + { + Operand[] localDefs = new Operand[RegisterConsts.TotalCount]; + + Operand RenameLocal(Operand operand) + { + if (operand != null && operand.Type == OperandType.Register) + { + Operand local = localDefs[GetKeyFromRegister(operand.GetRegister())]; + + operand = local ?? operand; + } + + return operand; + } + + BasicBlock block = blocks[blkIndex]; + + LinkedListNode node = block.Operations.First; + + while (node != null) + { + if (node.Value is Operation operation) + { + for (int index = 0; index < operation.SourcesCount; index++) + { + operation.SetSource(index, RenameLocal(operation.GetSource(index))); + } + + if (operation.Dest != null && operation.Dest.Type == OperandType.Register) + { + Operand local = Local(); + + localDefs[GetKeyFromRegister(operation.Dest.GetRegister())] = local; + + operation.Dest = local; + } + } + + node = node.Next; + } + + for (int index = 0; index < RegisterConsts.TotalCount; index++) + { + Operand local = localDefs[index]; + + if (local == null) + { + continue; + } + + Register reg = GetRegisterFromKey(index); + + globalDefs[block.Index].TryAddOperand(reg, local); + + dfPhiBlocks.Enqueue(block); + + while (dfPhiBlocks.TryDequeue(out BasicBlock dfPhiBlock)) + { + foreach (BasicBlock domFrontier in dfPhiBlock.DominanceFrontiers) + { + if (globalDefs[domFrontier.Index].AddPhi(reg)) + { + dfPhiBlocks.Enqueue(domFrontier); + } + } + } + } + } + + //Second pass, rename variables with definitions on different blocks. + for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) + { + Operand[] localDefs = new Operand[RegisterConsts.TotalCount]; + + BasicBlock block = blocks[blkIndex]; + + Operand RenameGlobal(Operand operand) + { + if (operand != null && operand.Type == OperandType.Register) + { + int key = GetKeyFromRegister(operand.GetRegister()); + + Operand local = localDefs[key]; + + if (local != null) + { + return local; + } + + operand = FindDefinitionForCurr(globalDefs, block, operand.GetRegister()); + + localDefs[key] = operand; + } + + return operand; + } + + LinkedListNode node = block.Operations.First; + + while (node != null) + { + if (node.Value is Operation operation) + { + for (int index = 0; index < operation.SourcesCount; index++) + { + operation.SetSource(index, RenameGlobal(operation.GetSource(index))); + } + } + + node = node.Next; + } + } + } + + private static Operand FindDefinitionForCurr(DefMap[] globalDefs, BasicBlock current, Register reg) + { + if (globalDefs[current.Index].HasPhi(reg)) + { + return InsertPhi(globalDefs, current, reg); + } + + if (current != current.ImmediateDominator) + { + return FindDefinition(globalDefs, current.ImmediateDominator, reg).Local; + } + + return Undef(); + } + + private static Definition FindDefinition(DefMap[] globalDefs, BasicBlock current, Register reg) + { + foreach (BasicBlock block in SelfAndImmediateDominators(current)) + { + DefMap defMap = globalDefs[block.Index]; + + if (defMap.TryGetOperand(reg, out Operand lastDef)) + { + return new Definition(block, lastDef); + } + + if (defMap.HasPhi(reg)) + { + return new Definition(block, InsertPhi(globalDefs, block, reg)); + } + } + + return new Definition(current, Undef()); + } + + private static IEnumerable SelfAndImmediateDominators(BasicBlock block) + { + while (block != block.ImmediateDominator) + { + yield return block; + + block = block.ImmediateDominator; + } + + yield return block; + } + + private static Operand InsertPhi(DefMap[] globalDefs, BasicBlock block, Register reg) + { + //This block has a Phi that has not been materialized yet, but that + //would define a new version of the variable we're looking for. We need + //to materialize the Phi, add all the block/operand pairs into the Phi, and + //then use the definition from that Phi. + Operand local = Local(); + + PhiNode phi = new PhiNode(local); + + AddPhi(block, phi); + + globalDefs[block.Index].TryAddOperand(reg, local); + + foreach (BasicBlock predecessor in block.Predecessors) + { + Definition def = FindDefinition(globalDefs, predecessor, reg); + + phi.AddSource(def.Block, def.Local); + } + + return local; + } + + private static void AddPhi(BasicBlock block, PhiNode phi) + { + LinkedListNode node = block.Operations.First; + + if (node != null) + { + while (node.Next?.Value is PhiNode) + { + node = node.Next; + } + } + + if (node?.Value is PhiNode) + { + block.Operations.AddAfter(node, phi); + } + else + { + block.Operations.AddFirst(phi); + } + } + + private static int GetKeyFromRegister(Register reg) + { + if (reg.Type == RegisterType.Gpr) + { + return reg.Index; + } + else if (reg.Type == RegisterType.Predicate) + { + return RegisterConsts.GprsCount + reg.Index; + } + else /* if (reg.Type == RegisterType.Flag) */ + { + return GprsAndPredsCount + reg.Index; + } + } + + private static Register GetRegisterFromKey(int key) + { + if (key < RegisterConsts.GprsCount) + { + return new Register(key, RegisterType.Gpr); + } + else if (key < GprsAndPredsCount) + { + return new Register(key - RegisterConsts.GprsCount, RegisterType.Predicate); + } + else /* if (key < RegisterConsts.TotalCount) */ + { + return new Register(key - GprsAndPredsCount, RegisterType.Flag); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/Translator.cs b/Ryujinx.Graphics/Shader/Translation/Translator.cs new file mode 100644 index 0000000000..706f3cfa4e --- /dev/null +++ b/Ryujinx.Graphics/Shader/Translation/Translator.cs @@ -0,0 +1,219 @@ +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Shader.CodeGen.Glsl; +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.Instructions; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation.Optimizations; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation +{ + public static class Translator + { + public static ShaderProgram Translate(IGalMemory memory, ulong address, ShaderConfig config) + { + return Translate(memory, address, 0, config); + } + + public static ShaderProgram Translate( + IGalMemory memory, + ulong address, + ulong addressB, + ShaderConfig config) + { + Operation[] shaderOps = DecodeShader(memory, address, config.Type); + + if (addressB != 0) + { + //Dual vertex shader. + Operation[] shaderOpsB = DecodeShader(memory, addressB, config.Type); + + shaderOps = Combine(shaderOps, shaderOpsB); + } + + BasicBlock[] irBlocks = ControlFlowGraph.MakeCfg(shaderOps); + + Dominance.FindDominators(irBlocks[0], irBlocks.Length); + + Dominance.FindDominanceFrontiers(irBlocks); + + Ssa.Rename(irBlocks); + + Optimizer.Optimize(irBlocks); + + StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(irBlocks); + + GlslProgram program = GlslGenerator.Generate(sInfo, config); + + ShaderProgramInfo spInfo = new ShaderProgramInfo( + program.CBufferDescriptors, + program.TextureDescriptors); + + return new ShaderProgram(spInfo, program.Code); + } + + private static Operation[] DecodeShader(IGalMemory memory, ulong address, GalShaderType shaderType) + { + ShaderHeader header = new ShaderHeader(memory, address); + + Block[] cfg = Decoder.Decode(memory, address); + + EmitterContext context = new EmitterContext(shaderType, header); + + for (int blkIndex = 0; blkIndex < cfg.Length; blkIndex++) + { + Block block = cfg[blkIndex]; + + context.CurrBlock = block; + + context.MarkLabel(context.GetLabel(block.Address)); + + for (int opIndex = 0; opIndex < block.OpCodes.Count; opIndex++) + { + OpCode op = block.OpCodes[opIndex]; + + if (op.NeverExecute) + { + continue; + } + + Operand predSkipLbl = null; + + bool skipPredicateCheck = op.Emitter == InstEmit.Bra; + + if (op is OpCodeSync opSync) + { + //If the instruction is a SYNC instruction with only one + //possible target address, then the instruction is basically + //just a simple branch, we can generate code similar to branch + //instructions, with the condition check on the branch itself. + skipPredicateCheck |= opSync.Targets.Count < 2; + } + + if (!(op.Predicate.IsPT || skipPredicateCheck)) + { + Operand label; + + if (opIndex == block.OpCodes.Count - 1 && block.Next != null) + { + label = context.GetLabel(block.Next.Address); + } + else + { + label = Label(); + + predSkipLbl = label; + } + + Operand pred = Register(op.Predicate); + + if (op.InvertPredicate) + { + context.BranchIfTrue(label, pred); + } + else + { + context.BranchIfFalse(label, pred); + } + } + + context.CurrOp = op; + + op.Emitter(context); + + if (predSkipLbl != null) + { + context.MarkLabel(predSkipLbl); + } + } + } + + return context.GetOperations(); + } + + private static Operation[] Combine(Operation[] a, Operation[] b) + { + //Here we combine two shaders. + //For shader A: + //- All user attribute stores on shader A are turned into copies to a + //temporary variable. It's assumed that shader B will consume them. + //- All return instructions are turned into branch instructions, the + //branch target being the start of the shader B code. + //For shader B: + //- All user attribute loads on shader B are turned into copies from a + //temporary variable, as long that attribute is written by shader A. + List output = new List(a.Length + b.Length); + + Operand[] temps = new Operand[AttributeConsts.UserAttributesCount * 4]; + + Operand lblB = Label(); + + for (int index = 0; index < a.Length; index++) + { + Operation operation = a[index]; + + if (IsUserAttribute(operation.Dest)) + { + int tIndex = (operation.Dest.Value - AttributeConsts.UserAttributeBase) / 4; + + Operand temp = temps[tIndex]; + + if (temp == null) + { + temp = Local(); + + temps[tIndex] = temp; + } + + operation.Dest = temp; + } + + if (operation.Inst == Instruction.Return) + { + output.Add(new Operation(Instruction.Branch, lblB)); + } + else + { + output.Add(operation); + } + } + + output.Add(new Operation(Instruction.MarkLabel, lblB)); + + for (int index = 0; index < b.Length; index++) + { + Operation operation = b[index]; + + for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++) + { + Operand src = operation.GetSource(srcIndex); + + if (IsUserAttribute(src)) + { + Operand temp = temps[(src.Value - AttributeConsts.UserAttributeBase) / 4]; + + if (temp != null) + { + operation.SetSource(srcIndex, temp); + } + } + } + + output.Add(operation); + } + + return output.ToArray(); + } + + private static bool IsUserAttribute(Operand operand) + { + return operand != null && + operand.Type == OperandType.Attribute && + operand.Value >= AttributeConsts.UserAttributeBase && + operand.Value < AttributeConsts.UserAttributeEnd; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/BitStreamWriter.cs b/Ryujinx.Graphics/VDec/BitStreamWriter.cs index 44d07906af..db2d39e593 100644 --- a/Ryujinx.Graphics/VDec/BitStreamWriter.cs +++ b/Ryujinx.Graphics/VDec/BitStreamWriter.cs @@ -6,69 +6,69 @@ namespace Ryujinx.Graphics.VDec { private const int BufferSize = 8; - private Stream BaseStream; + private Stream _baseStream; - private int Buffer; - private int BufferPos; + private int _buffer; + private int _bufferPos; - public BitStreamWriter(Stream BaseStream) + public BitStreamWriter(Stream baseStream) { - this.BaseStream = BaseStream; + _baseStream = baseStream; } - public void WriteBit(bool Value) + public void WriteBit(bool value) { - WriteBits(Value ? 1 : 0, 1); + WriteBits(value ? 1 : 0, 1); } - public void WriteBits(int Value, int ValueSize) + public void WriteBits(int value, int valueSize) { - int ValuePos = 0; + int valuePos = 0; - int Remaining = ValueSize; + int remaining = valueSize; - while (Remaining > 0) + while (remaining > 0) { - int CopySize = Remaining; + int copySize = remaining; - int Free = GetFreeBufferBits(); + int free = GetFreeBufferBits(); - if (CopySize > Free) + if (copySize > free) { - CopySize = Free; + copySize = free; } - int Mask = (1 << CopySize) - 1; + int mask = (1 << copySize) - 1; - int SrcShift = (ValueSize - ValuePos) - CopySize; - int DstShift = (BufferSize - BufferPos) - CopySize; + int srcShift = (valueSize - valuePos) - copySize; + int dstShift = (BufferSize - _bufferPos) - copySize; - Buffer |= ((Value >> SrcShift) & Mask) << DstShift; + _buffer |= ((value >> srcShift) & mask) << dstShift; - ValuePos += CopySize; - BufferPos += CopySize; - Remaining -= CopySize; + valuePos += copySize; + _bufferPos += copySize; + remaining -= copySize; } } private int GetFreeBufferBits() { - if (BufferPos == BufferSize) + if (_bufferPos == BufferSize) { Flush(); } - return BufferSize - BufferPos; + return BufferSize - _bufferPos; } public void Flush() { - if (BufferPos != 0) + if (_bufferPos != 0) { - BaseStream.WriteByte((byte)Buffer); + _baseStream.WriteByte((byte)_buffer); - Buffer = 0; - BufferPos = 0; + _buffer = 0; + _bufferPos = 0; } } } diff --git a/Ryujinx.Graphics/VDec/DecoderHelper.cs b/Ryujinx.Graphics/VDec/DecoderHelper.cs index 485bb42b63..4f17d8d109 100644 --- a/Ryujinx.Graphics/VDec/DecoderHelper.cs +++ b/Ryujinx.Graphics/VDec/DecoderHelper.cs @@ -4,14 +4,14 @@ namespace Ryujinx.Graphics.VDec { static class DecoderHelper { - public static byte[] Combine(byte[] Arr0, byte[] Arr1) + public static byte[] Combine(byte[] arr0, byte[] arr1) { - byte[] Output = new byte[Arr0.Length + Arr1.Length]; + byte[] output = new byte[arr0.Length + arr1.Length]; - Buffer.BlockCopy(Arr0, 0, Output, 0, Arr0.Length); - Buffer.BlockCopy(Arr1, 0, Output, Arr0.Length, Arr1.Length); + Buffer.BlockCopy(arr0, 0, output, 0, arr0.Length); + Buffer.BlockCopy(arr1, 0, output, arr0.Length, arr1.Length); - return Output; + return output; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/FFmpeg.cs b/Ryujinx.Graphics/VDec/FFmpeg.cs index 183d077922..ccd01f0d3d 100644 --- a/Ryujinx.Graphics/VDec/FFmpeg.cs +++ b/Ryujinx.Graphics/VDec/FFmpeg.cs @@ -4,15 +4,15 @@ using System.Runtime.InteropServices; namespace Ryujinx.Graphics.VDec { - unsafe static class FFmpegWrapper + static unsafe class FFmpegWrapper { - private static AVCodec* Codec; - private static AVCodecContext* Context; - private static AVFrame* Frame; - private static SwsContext* ScalerCtx; + private static AVCodec* _codec; + private static AVCodecContext* _context; + private static AVFrame* _frame; + private static SwsContext* _scalerCtx; - private static int ScalerWidth; - private static int ScalerHeight; + private static int _scalerWidth; + private static int _scalerHeight; public static bool IsInitialized { get; private set; } @@ -26,42 +26,42 @@ namespace Ryujinx.Graphics.VDec EnsureCodecInitialized(AVCodecID.AV_CODEC_ID_VP9); } - private static void EnsureCodecInitialized(AVCodecID CodecId) + private static void EnsureCodecInitialized(AVCodecID codecId) { if (IsInitialized) { Uninitialize(); } - Codec = ffmpeg.avcodec_find_decoder(CodecId); - Context = ffmpeg.avcodec_alloc_context3(Codec); - Frame = ffmpeg.av_frame_alloc(); + _codec = ffmpeg.avcodec_find_decoder(codecId); + _context = ffmpeg.avcodec_alloc_context3(_codec); + _frame = ffmpeg.av_frame_alloc(); - ffmpeg.avcodec_open2(Context, Codec, null); + ffmpeg.avcodec_open2(_context, _codec, null); IsInitialized = true; } - public static int DecodeFrame(byte[] Data) + public static int DecodeFrame(byte[] data) { if (!IsInitialized) { throw new InvalidOperationException("Tried to use uninitialized codec!"); } - AVPacket Packet; + AVPacket packet; - ffmpeg.av_init_packet(&Packet); + ffmpeg.av_init_packet(&packet); - fixed (byte* Ptr = Data) + fixed (byte* ptr = data) { - Packet.data = Ptr; - Packet.size = Data.Length; + packet.data = ptr; + packet.size = data.Length; - ffmpeg.avcodec_send_packet(Context, &Packet); + ffmpeg.avcodec_send_packet(_context, &packet); } - return ffmpeg.avcodec_receive_frame(Context, Frame); + return ffmpeg.avcodec_receive_frame(_context, _frame); } public static FFmpegFrame GetFrame() @@ -71,18 +71,18 @@ namespace Ryujinx.Graphics.VDec throw new InvalidOperationException("Tried to use uninitialized codec!"); } - AVFrame ManagedFrame = Marshal.PtrToStructure((IntPtr)Frame); + AVFrame managedFrame = Marshal.PtrToStructure((IntPtr)_frame); - byte*[] Data = ManagedFrame.data.ToArray(); + byte*[] data = managedFrame.data.ToArray(); return new FFmpegFrame() { - Width = ManagedFrame.width, - Height = ManagedFrame.height, + Width = managedFrame.width, + Height = managedFrame.height, - LumaPtr = Data[0], - ChromaBPtr = Data[1], - ChromaRPtr = Data[2] + LumaPtr = data[0], + ChromaBPtr = data[1], + ChromaRPtr = data[2] }; } @@ -93,51 +93,51 @@ namespace Ryujinx.Graphics.VDec throw new InvalidOperationException("Tried to use uninitialized codec!"); } - AVFrame ManagedFrame = Marshal.PtrToStructure((IntPtr)Frame); + AVFrame managedFrame = Marshal.PtrToStructure((IntPtr)_frame); - EnsureScalerSetup(ManagedFrame.width, ManagedFrame.height); + EnsureScalerSetup(managedFrame.width, managedFrame.height); - byte*[] Data = ManagedFrame.data.ToArray(); + byte*[] data = managedFrame.data.ToArray(); - int[] LineSizes = ManagedFrame.linesize.ToArray(); + int[] lineSizes = managedFrame.linesize.ToArray(); - byte[] Dst = new byte[ManagedFrame.width * ManagedFrame.height * 4]; + byte[] dst = new byte[managedFrame.width * managedFrame.height * 4]; - fixed (byte* Ptr = Dst) + fixed (byte* ptr = dst) { - byte*[] DstData = new byte*[] { Ptr }; + byte*[] dstData = new byte*[] { ptr }; - int[] DstLineSizes = new int[] { ManagedFrame.width * 4 }; + int[] dstLineSizes = new int[] { managedFrame.width * 4 }; - ffmpeg.sws_scale(ScalerCtx, Data, LineSizes, 0, ManagedFrame.height, DstData, DstLineSizes); + ffmpeg.sws_scale(_scalerCtx, data, lineSizes, 0, managedFrame.height, dstData, dstLineSizes); } return new FFmpegFrame() { - Width = ManagedFrame.width, - Height = ManagedFrame.height, + Width = managedFrame.width, + Height = managedFrame.height, - Data = Dst + Data = dst }; } - private static void EnsureScalerSetup(int Width, int Height) + private static void EnsureScalerSetup(int width, int height) { - if (Width == 0 || Height == 0) + if (width == 0 || height == 0) { return; } - if (ScalerCtx == null || ScalerWidth != Width || ScalerHeight != Height) + if (_scalerCtx == null || _scalerWidth != width || _scalerHeight != height) { FreeScaler(); - ScalerCtx = ffmpeg.sws_getContext( - Width, Height, AVPixelFormat.AV_PIX_FMT_YUV420P, - Width, Height, AVPixelFormat.AV_PIX_FMT_RGBA, 0, null, null, null); + _scalerCtx = ffmpeg.sws_getContext( + width, height, AVPixelFormat.AV_PIX_FMT_YUV420P, + width, height, AVPixelFormat.AV_PIX_FMT_RGBA, 0, null, null, null); - ScalerWidth = Width; - ScalerHeight = Height; + _scalerWidth = width; + _scalerHeight = height; } } @@ -145,9 +145,9 @@ namespace Ryujinx.Graphics.VDec { if (IsInitialized) { - ffmpeg.av_frame_unref(Frame); - ffmpeg.av_free(Frame); - ffmpeg.avcodec_close(Context); + ffmpeg.av_frame_unref(_frame); + ffmpeg.av_free(_frame); + ffmpeg.avcodec_close(_context); FreeScaler(); @@ -157,11 +157,11 @@ namespace Ryujinx.Graphics.VDec private static void FreeScaler() { - if (ScalerCtx != null) + if (_scalerCtx != null) { - ffmpeg.sws_freeContext(ScalerCtx); + ffmpeg.sws_freeContext(_scalerCtx); - ScalerCtx = null; + _scalerCtx = null; } } } diff --git a/Ryujinx.Graphics/VDec/H264BitStreamWriter.cs b/Ryujinx.Graphics/VDec/H264BitStreamWriter.cs index b388a2aafb..b4fad59be3 100644 --- a/Ryujinx.Graphics/VDec/H264BitStreamWriter.cs +++ b/Ryujinx.Graphics/VDec/H264BitStreamWriter.cs @@ -4,21 +4,21 @@ namespace Ryujinx.Graphics.VDec { class H264BitStreamWriter : BitStreamWriter { - public H264BitStreamWriter(Stream BaseStream) : base(BaseStream) { } + public H264BitStreamWriter(Stream baseStream) : base(baseStream) { } - public void WriteU(int Value, int ValueSize) + public void WriteU(int value, int valueSize) { - WriteBits(Value, ValueSize); + WriteBits(value, valueSize); } - public void WriteSe(int Value) + public void WriteSe(int value) { - WriteExpGolombCodedInt(Value); + WriteExpGolombCodedInt(value); } - public void WriteUe(int Value) + public void WriteUe(int value) { - WriteExpGolombCodedUInt((uint)Value); + WriteExpGolombCodedUInt((uint)value); } public void End() @@ -28,52 +28,52 @@ namespace Ryujinx.Graphics.VDec Flush(); } - private void WriteExpGolombCodedInt(int Value) + private void WriteExpGolombCodedInt(int value) { - int Sign = Value <= 0 ? 0 : 1; + int sign = value <= 0 ? 0 : 1; - if (Value < 0) + if (value < 0) { - Value = -Value; + value = -value; } - Value = (Value << 1) - Sign; + value = (value << 1) - sign; - WriteExpGolombCodedUInt((uint)Value); + WriteExpGolombCodedUInt((uint)value); } - private void WriteExpGolombCodedUInt(uint Value) + private void WriteExpGolombCodedUInt(uint value) { - int Size = 32 - CountLeadingZeros((int)Value + 1); + int size = 32 - CountLeadingZeros((int)value + 1); - WriteBits(1, Size); + WriteBits(1, size); - Value -= (1u << (Size - 1)) - 1; + value -= (1u << (size - 1)) - 1; - WriteBits((int)Value, Size - 1); + WriteBits((int)value, size - 1); } private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; - private static int CountLeadingZeros(int Value) + private static int CountLeadingZeros(int value) { - if (Value == 0) + if (value == 0) { return 32; } - int NibbleIdx = 32; - int PreCount, Count = 0; + int nibbleIdx = 32; + int preCount, count = 0; do { - NibbleIdx -= 4; - PreCount = ClzNibbleTbl[(Value >> NibbleIdx) & 0b1111]; - Count += PreCount; + nibbleIdx -= 4; + preCount = ClzNibbleTbl[(value >> nibbleIdx) & 0b1111]; + count += preCount; } - while (PreCount == 4); + while (preCount == 4); - return Count; + return count; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/H264Decoder.cs b/Ryujinx.Graphics/VDec/H264Decoder.cs index d5d4671348..01085a733f 100644 --- a/Ryujinx.Graphics/VDec/H264Decoder.cs +++ b/Ryujinx.Graphics/VDec/H264Decoder.cs @@ -4,195 +4,195 @@ namespace Ryujinx.Graphics.VDec { class H264Decoder { - private int Log2MaxPicOrderCntLsbMinus4; - private bool DeltaPicOrderAlwaysZeroFlag; - private bool FrameMbsOnlyFlag; - private int PicWidthInMbs; - private int PicHeightInMapUnits; - private bool EntropyCodingModeFlag; - private bool BottomFieldPicOrderInFramePresentFlag; - private int NumRefIdxL0DefaultActiveMinus1; - private int NumRefIdxL1DefaultActiveMinus1; - private bool DeblockingFilterControlPresentFlag; - private bool RedundantPicCntPresentFlag; - private bool Transform8x8ModeFlag; - private bool MbAdaptiveFrameFieldFlag; - private bool Direct8x8InferenceFlag; - private bool WeightedPredFlag; - private bool ConstrainedIntraPredFlag; - private bool FieldPicFlag; - private bool BottomFieldFlag; - private int Log2MaxFrameNumMinus4; - private int ChromaFormatIdc; - private int PicOrderCntType; - private int PicInitQpMinus26; - private int ChromaQpIndexOffset; - private int ChromaQpIndexOffset2; - private int WeightedBipredIdc; - private int FrameNumber; - private byte[] ScalingMatrix4; - private byte[] ScalingMatrix8; + private int _log2MaxPicOrderCntLsbMinus4; + private bool _deltaPicOrderAlwaysZeroFlag; + private bool _frameMbsOnlyFlag; + private int _picWidthInMbs; + private int _picHeightInMapUnits; + private bool _entropyCodingModeFlag; + private bool _bottomFieldPicOrderInFramePresentFlag; + private int _numRefIdxL0DefaultActiveMinus1; + private int _numRefIdxL1DefaultActiveMinus1; + private bool _deblockingFilterControlPresentFlag; + private bool _redundantPicCntPresentFlag; + private bool _transform8x8ModeFlag; + private bool _mbAdaptiveFrameFieldFlag; + private bool _direct8x8InferenceFlag; + private bool _weightedPredFlag; + private bool _constrainedIntraPredFlag; + private bool _fieldPicFlag; + private bool _bottomFieldFlag; + private int _log2MaxFrameNumMinus4; + private int _chromaFormatIdc; + private int _picOrderCntType; + private int _picInitQpMinus26; + private int _chromaQpIndexOffset; + private int _chromaQpIndexOffset2; + private int _weightedBipredIdc; + private int _frameNumber; + private byte[] _scalingMatrix4; + private byte[] _scalingMatrix8; - public void Decode(H264ParameterSets Params, H264Matrices Matrices, byte[] FrameData) + public void Decode(H264ParameterSets Params, H264Matrices matrices, byte[] frameData) { - Log2MaxPicOrderCntLsbMinus4 = Params.Log2MaxPicOrderCntLsbMinus4; - DeltaPicOrderAlwaysZeroFlag = Params.DeltaPicOrderAlwaysZeroFlag; - FrameMbsOnlyFlag = Params.FrameMbsOnlyFlag; - PicWidthInMbs = Params.PicWidthInMbs; - PicHeightInMapUnits = Params.PicHeightInMapUnits; - EntropyCodingModeFlag = Params.EntropyCodingModeFlag; - BottomFieldPicOrderInFramePresentFlag = Params.BottomFieldPicOrderInFramePresentFlag; - NumRefIdxL0DefaultActiveMinus1 = Params.NumRefIdxL0DefaultActiveMinus1; - NumRefIdxL1DefaultActiveMinus1 = Params.NumRefIdxL1DefaultActiveMinus1; - DeblockingFilterControlPresentFlag = Params.DeblockingFilterControlPresentFlag; - RedundantPicCntPresentFlag = Params.RedundantPicCntPresentFlag; - Transform8x8ModeFlag = Params.Transform8x8ModeFlag; + _log2MaxPicOrderCntLsbMinus4 = Params.Log2MaxPicOrderCntLsbMinus4; + _deltaPicOrderAlwaysZeroFlag = Params.DeltaPicOrderAlwaysZeroFlag; + _frameMbsOnlyFlag = Params.FrameMbsOnlyFlag; + _picWidthInMbs = Params.PicWidthInMbs; + _picHeightInMapUnits = Params.PicHeightInMapUnits; + _entropyCodingModeFlag = Params.EntropyCodingModeFlag; + _bottomFieldPicOrderInFramePresentFlag = Params.BottomFieldPicOrderInFramePresentFlag; + _numRefIdxL0DefaultActiveMinus1 = Params.NumRefIdxL0DefaultActiveMinus1; + _numRefIdxL1DefaultActiveMinus1 = Params.NumRefIdxL1DefaultActiveMinus1; + _deblockingFilterControlPresentFlag = Params.DeblockingFilterControlPresentFlag; + _redundantPicCntPresentFlag = Params.RedundantPicCntPresentFlag; + _transform8x8ModeFlag = Params.Transform8x8ModeFlag; - MbAdaptiveFrameFieldFlag = ((Params.Flags >> 0) & 1) != 0; - Direct8x8InferenceFlag = ((Params.Flags >> 1) & 1) != 0; - WeightedPredFlag = ((Params.Flags >> 2) & 1) != 0; - ConstrainedIntraPredFlag = ((Params.Flags >> 3) & 1) != 0; - FieldPicFlag = ((Params.Flags >> 5) & 1) != 0; - BottomFieldFlag = ((Params.Flags >> 6) & 1) != 0; + _mbAdaptiveFrameFieldFlag = ((Params.Flags >> 0) & 1) != 0; + _direct8x8InferenceFlag = ((Params.Flags >> 1) & 1) != 0; + _weightedPredFlag = ((Params.Flags >> 2) & 1) != 0; + _constrainedIntraPredFlag = ((Params.Flags >> 3) & 1) != 0; + _fieldPicFlag = ((Params.Flags >> 5) & 1) != 0; + _bottomFieldFlag = ((Params.Flags >> 6) & 1) != 0; - Log2MaxFrameNumMinus4 = (int)(Params.Flags >> 8) & 0xf; - ChromaFormatIdc = (int)(Params.Flags >> 12) & 0x3; - PicOrderCntType = (int)(Params.Flags >> 14) & 0x3; - PicInitQpMinus26 = (int)(Params.Flags >> 16) & 0x3f; - ChromaQpIndexOffset = (int)(Params.Flags >> 22) & 0x1f; - ChromaQpIndexOffset2 = (int)(Params.Flags >> 27) & 0x1f; - WeightedBipredIdc = (int)(Params.Flags >> 32) & 0x3; - FrameNumber = (int)(Params.Flags >> 46) & 0x1ffff; + _log2MaxFrameNumMinus4 = (int)(Params.Flags >> 8) & 0xf; + _chromaFormatIdc = (int)(Params.Flags >> 12) & 0x3; + _picOrderCntType = (int)(Params.Flags >> 14) & 0x3; + _picInitQpMinus26 = (int)(Params.Flags >> 16) & 0x3f; + _chromaQpIndexOffset = (int)(Params.Flags >> 22) & 0x1f; + _chromaQpIndexOffset2 = (int)(Params.Flags >> 27) & 0x1f; + _weightedBipredIdc = (int)(Params.Flags >> 32) & 0x3; + _frameNumber = (int)(Params.Flags >> 46) & 0x1ffff; - PicInitQpMinus26 = (PicInitQpMinus26 << 26) >> 26; - ChromaQpIndexOffset = (ChromaQpIndexOffset << 27) >> 27; - ChromaQpIndexOffset2 = (ChromaQpIndexOffset2 << 27) >> 27; + _picInitQpMinus26 = (_picInitQpMinus26 << 26) >> 26; + _chromaQpIndexOffset = (_chromaQpIndexOffset << 27) >> 27; + _chromaQpIndexOffset2 = (_chromaQpIndexOffset2 << 27) >> 27; - ScalingMatrix4 = Matrices.ScalingMatrix4; - ScalingMatrix8 = Matrices.ScalingMatrix8; + _scalingMatrix4 = matrices.ScalingMatrix4; + _scalingMatrix8 = matrices.ScalingMatrix8; if (FFmpegWrapper.IsInitialized) { - FFmpegWrapper.DecodeFrame(FrameData); + FFmpegWrapper.DecodeFrame(frameData); } else { FFmpegWrapper.H264Initialize(); - FFmpegWrapper.DecodeFrame(DecoderHelper.Combine(EncodeHeader(), FrameData)); + FFmpegWrapper.DecodeFrame(DecoderHelper.Combine(EncodeHeader(), frameData)); } } private byte[] EncodeHeader() { - using (MemoryStream Data = new MemoryStream()) + using (MemoryStream data = new MemoryStream()) { - H264BitStreamWriter Writer = new H264BitStreamWriter(Data); + H264BitStreamWriter writer = new H264BitStreamWriter(data); //Sequence Parameter Set. - Writer.WriteU(1, 24); - Writer.WriteU(0, 1); - Writer.WriteU(3, 2); - Writer.WriteU(7, 5); - Writer.WriteU(100, 8); - Writer.WriteU(0, 8); - Writer.WriteU(31, 8); - Writer.WriteUe(0); - Writer.WriteUe(ChromaFormatIdc); + writer.WriteU(1, 24); + writer.WriteU(0, 1); + writer.WriteU(3, 2); + writer.WriteU(7, 5); + writer.WriteU(100, 8); + writer.WriteU(0, 8); + writer.WriteU(31, 8); + writer.WriteUe(0); + writer.WriteUe(_chromaFormatIdc); - if (ChromaFormatIdc == 3) + if (_chromaFormatIdc == 3) { - Writer.WriteBit(false); + writer.WriteBit(false); } - Writer.WriteUe(0); - Writer.WriteUe(0); - Writer.WriteBit(false); - Writer.WriteBit(false); //Scaling matrix present flag + writer.WriteUe(0); + writer.WriteUe(0); + writer.WriteBit(false); + writer.WriteBit(false); //Scaling matrix present flag - Writer.WriteUe(Log2MaxFrameNumMinus4); - Writer.WriteUe(PicOrderCntType); + writer.WriteUe(_log2MaxFrameNumMinus4); + writer.WriteUe(_picOrderCntType); - if (PicOrderCntType == 0) + if (_picOrderCntType == 0) { - Writer.WriteUe(Log2MaxPicOrderCntLsbMinus4); + writer.WriteUe(_log2MaxPicOrderCntLsbMinus4); } - else if (PicOrderCntType == 1) + else if (_picOrderCntType == 1) { - Writer.WriteBit(DeltaPicOrderAlwaysZeroFlag); + writer.WriteBit(_deltaPicOrderAlwaysZeroFlag); - Writer.WriteSe(0); - Writer.WriteSe(0); - Writer.WriteUe(0); + writer.WriteSe(0); + writer.WriteSe(0); + writer.WriteUe(0); } - int PicHeightInMbs = PicHeightInMapUnits / (FrameMbsOnlyFlag ? 1 : 2); + int picHeightInMbs = _picHeightInMapUnits / (_frameMbsOnlyFlag ? 1 : 2); - Writer.WriteUe(16); - Writer.WriteBit(false); - Writer.WriteUe(PicWidthInMbs - 1); - Writer.WriteUe(PicHeightInMbs - 1); - Writer.WriteBit(FrameMbsOnlyFlag); + writer.WriteUe(16); + writer.WriteBit(false); + writer.WriteUe(_picWidthInMbs - 1); + writer.WriteUe(picHeightInMbs - 1); + writer.WriteBit(_frameMbsOnlyFlag); - if (!FrameMbsOnlyFlag) + if (!_frameMbsOnlyFlag) { - Writer.WriteBit(MbAdaptiveFrameFieldFlag); + writer.WriteBit(_mbAdaptiveFrameFieldFlag); } - Writer.WriteBit(Direct8x8InferenceFlag); - Writer.WriteBit(false); //Frame cropping flag - Writer.WriteBit(false); //VUI parameter present flag + writer.WriteBit(_direct8x8InferenceFlag); + writer.WriteBit(false); //Frame cropping flag + writer.WriteBit(false); //VUI parameter present flag - Writer.End(); + writer.End(); //Picture Parameter Set. - Writer.WriteU(1, 24); - Writer.WriteU(0, 1); - Writer.WriteU(3, 2); - Writer.WriteU(8, 5); + writer.WriteU(1, 24); + writer.WriteU(0, 1); + writer.WriteU(3, 2); + writer.WriteU(8, 5); - Writer.WriteUe(0); - Writer.WriteUe(0); + writer.WriteUe(0); + writer.WriteUe(0); - Writer.WriteBit(EntropyCodingModeFlag); - Writer.WriteBit(false); - Writer.WriteUe(0); - Writer.WriteUe(NumRefIdxL0DefaultActiveMinus1); - Writer.WriteUe(NumRefIdxL1DefaultActiveMinus1); - Writer.WriteBit(WeightedPredFlag); - Writer.WriteU(WeightedBipredIdc, 2); - Writer.WriteSe(PicInitQpMinus26); - Writer.WriteSe(0); - Writer.WriteSe(ChromaQpIndexOffset); - Writer.WriteBit(DeblockingFilterControlPresentFlag); - Writer.WriteBit(ConstrainedIntraPredFlag); - Writer.WriteBit(RedundantPicCntPresentFlag); - Writer.WriteBit(Transform8x8ModeFlag); + writer.WriteBit(_entropyCodingModeFlag); + writer.WriteBit(false); + writer.WriteUe(0); + writer.WriteUe(_numRefIdxL0DefaultActiveMinus1); + writer.WriteUe(_numRefIdxL1DefaultActiveMinus1); + writer.WriteBit(_weightedPredFlag); + writer.WriteU(_weightedBipredIdc, 2); + writer.WriteSe(_picInitQpMinus26); + writer.WriteSe(0); + writer.WriteSe(_chromaQpIndexOffset); + writer.WriteBit(_deblockingFilterControlPresentFlag); + writer.WriteBit(_constrainedIntraPredFlag); + writer.WriteBit(_redundantPicCntPresentFlag); + writer.WriteBit(_transform8x8ModeFlag); - Writer.WriteBit(true); + writer.WriteBit(true); - for (int Index = 0; Index < 6; Index++) + for (int index = 0; index < 6; index++) { - Writer.WriteBit(true); + writer.WriteBit(true); - WriteScalingList(Writer, ScalingMatrix4, Index * 16, 16); + WriteScalingList(writer, _scalingMatrix4, index * 16, 16); } - if (Transform8x8ModeFlag) + if (_transform8x8ModeFlag) { - for (int Index = 0; Index < 2; Index++) + for (int index = 0; index < 2; index++) { - Writer.WriteBit(true); + writer.WriteBit(true); - WriteScalingList(Writer, ScalingMatrix8, Index * 64, 64); + WriteScalingList(writer, _scalingMatrix8, index * 64, 64); } } - Writer.WriteSe(ChromaQpIndexOffset2); + writer.WriteSe(_chromaQpIndexOffset2); - Writer.End(); + writer.End(); - return Data.ToArray(); + return data.ToArray(); } } @@ -217,21 +217,21 @@ namespace Ryujinx.Graphics.VDec 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4 }; - private static void WriteScalingList(H264BitStreamWriter Writer, byte[] List, int Start, int Count) + private static void WriteScalingList(H264BitStreamWriter writer, byte[] list, int start, int count) { - byte[] Scan = Count == 16 ? ZigZagScan : ZigZagDirect; + byte[] scan = count == 16 ? ZigZagScan : ZigZagDirect; - int LastScale = 8; + int lastScale = 8; - for (int Index = 0; Index < Count; Index++) + for (int index = 0; index < count; index++) { - byte Value = List[Start + Scan[Index]]; + byte value = list[start + scan[index]]; - int DeltaScale = Value - LastScale; + int deltaScale = value - lastScale; - Writer.WriteSe(DeltaScale); + writer.WriteSe(deltaScale); - LastScale = Value; + lastScale = value; } } } diff --git a/Ryujinx.Graphics/VDec/VideoDecoder.cs b/Ryujinx.Graphics/VDec/VideoDecoder.cs index 847392b0d9..2be47a30b7 100644 --- a/Ryujinx.Graphics/VDec/VideoDecoder.cs +++ b/Ryujinx.Graphics/VDec/VideoDecoder.cs @@ -9,124 +9,124 @@ namespace Ryujinx.Graphics.VDec { unsafe class VideoDecoder { - private NvGpu Gpu; + private NvGpu _gpu; - private H264Decoder H264Decoder; - private Vp9Decoder Vp9Decoder; + private H264Decoder _h264Decoder; + private Vp9Decoder _vp9Decoder; - private VideoCodec CurrentVideoCodec; + private VideoCodec _currentVideoCodec; - private long DecoderContextAddress; - private long FrameDataAddress; - private long VpxCurrLumaAddress; - private long VpxRef0LumaAddress; - private long VpxRef1LumaAddress; - private long VpxRef2LumaAddress; - private long VpxCurrChromaAddress; - private long VpxRef0ChromaAddress; - private long VpxRef1ChromaAddress; - private long VpxRef2ChromaAddress; - private long VpxProbTablesAddress; + private long _decoderContextAddress; + private long _frameDataAddress; + private long _vpxCurrLumaAddress; + private long _vpxRef0LumaAddress; + private long _vpxRef1LumaAddress; + private long _vpxRef2LumaAddress; + private long _vpxCurrChromaAddress; + private long _vpxRef0ChromaAddress; + private long _vpxRef1ChromaAddress; + private long _vpxRef2ChromaAddress; + private long _vpxProbTablesAddress; - public VideoDecoder(NvGpu Gpu) + public VideoDecoder(NvGpu gpu) { - this.Gpu = Gpu; + _gpu = gpu; - H264Decoder = new H264Decoder(); - Vp9Decoder = new Vp9Decoder(); + _h264Decoder = new H264Decoder(); + _vp9Decoder = new Vp9Decoder(); } - public void Process(NvGpuVmm Vmm, int MethodOffset, int[] Arguments) + public void Process(NvGpuVmm vmm, int methodOffset, int[] arguments) { - VideoDecoderMeth Method = (VideoDecoderMeth)MethodOffset; + VideoDecoderMeth method = (VideoDecoderMeth)methodOffset; - switch (Method) + switch (method) { - case VideoDecoderMeth.SetVideoCodec: SetVideoCodec (Vmm, Arguments); break; - case VideoDecoderMeth.Execute: Execute (Vmm, Arguments); break; - case VideoDecoderMeth.SetDecoderCtxAddr: SetDecoderCtxAddr (Vmm, Arguments); break; - case VideoDecoderMeth.SetFrameDataAddr: SetFrameDataAddr (Vmm, Arguments); break; - case VideoDecoderMeth.SetVpxCurrLumaAddr: SetVpxCurrLumaAddr (Vmm, Arguments); break; - case VideoDecoderMeth.SetVpxRef0LumaAddr: SetVpxRef0LumaAddr (Vmm, Arguments); break; - case VideoDecoderMeth.SetVpxRef1LumaAddr: SetVpxRef1LumaAddr (Vmm, Arguments); break; - case VideoDecoderMeth.SetVpxRef2LumaAddr: SetVpxRef2LumaAddr (Vmm, Arguments); break; - case VideoDecoderMeth.SetVpxCurrChromaAddr: SetVpxCurrChromaAddr(Vmm, Arguments); break; - case VideoDecoderMeth.SetVpxRef0ChromaAddr: SetVpxRef0ChromaAddr(Vmm, Arguments); break; - case VideoDecoderMeth.SetVpxRef1ChromaAddr: SetVpxRef1ChromaAddr(Vmm, Arguments); break; - case VideoDecoderMeth.SetVpxRef2ChromaAddr: SetVpxRef2ChromaAddr(Vmm, Arguments); break; - case VideoDecoderMeth.SetVpxProbTablesAddr: SetVpxProbTablesAddr(Vmm, Arguments); break; + case VideoDecoderMeth.SetVideoCodec: SetVideoCodec (vmm, arguments); break; + case VideoDecoderMeth.Execute: Execute (vmm, arguments); break; + case VideoDecoderMeth.SetDecoderCtxAddr: SetDecoderCtxAddr (vmm, arguments); break; + case VideoDecoderMeth.SetFrameDataAddr: SetFrameDataAddr (vmm, arguments); break; + case VideoDecoderMeth.SetVpxCurrLumaAddr: SetVpxCurrLumaAddr (vmm, arguments); break; + case VideoDecoderMeth.SetVpxRef0LumaAddr: SetVpxRef0LumaAddr (vmm, arguments); break; + case VideoDecoderMeth.SetVpxRef1LumaAddr: SetVpxRef1LumaAddr (vmm, arguments); break; + case VideoDecoderMeth.SetVpxRef2LumaAddr: SetVpxRef2LumaAddr (vmm, arguments); break; + case VideoDecoderMeth.SetVpxCurrChromaAddr: SetVpxCurrChromaAddr(vmm, arguments); break; + case VideoDecoderMeth.SetVpxRef0ChromaAddr: SetVpxRef0ChromaAddr(vmm, arguments); break; + case VideoDecoderMeth.SetVpxRef1ChromaAddr: SetVpxRef1ChromaAddr(vmm, arguments); break; + case VideoDecoderMeth.SetVpxRef2ChromaAddr: SetVpxRef2ChromaAddr(vmm, arguments); break; + case VideoDecoderMeth.SetVpxProbTablesAddr: SetVpxProbTablesAddr(vmm, arguments); break; } } - private void SetVideoCodec(NvGpuVmm Vmm, int[] Arguments) + private void SetVideoCodec(NvGpuVmm vmm, int[] arguments) { - CurrentVideoCodec = (VideoCodec)Arguments[0]; + _currentVideoCodec = (VideoCodec)arguments[0]; } - private void Execute(NvGpuVmm Vmm, int[] Arguments) + private void Execute(NvGpuVmm vmm, int[] arguments) { - if (CurrentVideoCodec == VideoCodec.H264) + if (_currentVideoCodec == VideoCodec.H264) { - int FrameDataSize = Vmm.ReadInt32(DecoderContextAddress + 0x48); + int frameDataSize = vmm.ReadInt32(_decoderContextAddress + 0x48); - H264ParameterSets Params = MemoryHelper.Read(Vmm.Memory, Vmm.GetPhysicalAddress(DecoderContextAddress + 0x58)); + H264ParameterSets Params = MemoryHelper.Read(vmm.Memory, vmm.GetPhysicalAddress(_decoderContextAddress + 0x58)); - H264Matrices Matrices = new H264Matrices() + H264Matrices matrices = new H264Matrices() { - ScalingMatrix4 = Vmm.ReadBytes(DecoderContextAddress + 0x1c0, 6 * 16), - ScalingMatrix8 = Vmm.ReadBytes(DecoderContextAddress + 0x220, 2 * 64) + ScalingMatrix4 = vmm.ReadBytes(_decoderContextAddress + 0x1c0, 6 * 16), + ScalingMatrix8 = vmm.ReadBytes(_decoderContextAddress + 0x220, 2 * 64) }; - byte[] FrameData = Vmm.ReadBytes(FrameDataAddress, FrameDataSize); + byte[] frameData = vmm.ReadBytes(_frameDataAddress, frameDataSize); - H264Decoder.Decode(Params, Matrices, FrameData); + _h264Decoder.Decode(Params, matrices, frameData); } - else if (CurrentVideoCodec == VideoCodec.Vp9) + else if (_currentVideoCodec == VideoCodec.Vp9) { - int FrameDataSize = Vmm.ReadInt32(DecoderContextAddress + 0x30); + int frameDataSize = vmm.ReadInt32(_decoderContextAddress + 0x30); - Vp9FrameKeys Keys = new Vp9FrameKeys() + Vp9FrameKeys keys = new Vp9FrameKeys() { - CurrKey = Vmm.GetPhysicalAddress(VpxCurrLumaAddress), - Ref0Key = Vmm.GetPhysicalAddress(VpxRef0LumaAddress), - Ref1Key = Vmm.GetPhysicalAddress(VpxRef1LumaAddress), - Ref2Key = Vmm.GetPhysicalAddress(VpxRef2LumaAddress) + CurrKey = vmm.GetPhysicalAddress(_vpxCurrLumaAddress), + Ref0Key = vmm.GetPhysicalAddress(_vpxRef0LumaAddress), + Ref1Key = vmm.GetPhysicalAddress(_vpxRef1LumaAddress), + Ref2Key = vmm.GetPhysicalAddress(_vpxRef2LumaAddress) }; - Vp9FrameHeader Header = MemoryHelper.Read(Vmm.Memory, Vmm.GetPhysicalAddress(DecoderContextAddress + 0x48)); + Vp9FrameHeader header = MemoryHelper.Read(vmm.Memory, vmm.GetPhysicalAddress(_decoderContextAddress + 0x48)); - Vp9ProbabilityTables Probs = new Vp9ProbabilityTables() + Vp9ProbabilityTables probs = new Vp9ProbabilityTables() { - SegmentationTreeProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x387, 0x7), - SegmentationPredProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x38e, 0x3), - Tx8x8Probs = Vmm.ReadBytes(VpxProbTablesAddress + 0x470, 0x2), - Tx16x16Probs = Vmm.ReadBytes(VpxProbTablesAddress + 0x472, 0x4), - Tx32x32Probs = Vmm.ReadBytes(VpxProbTablesAddress + 0x476, 0x6), - CoefProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x5a0, 0x900), - SkipProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x537, 0x3), - InterModeProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x400, 0x1c), - InterpFilterProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x52a, 0x8), - IsInterProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x41c, 0x4), - CompModeProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x532, 0x5), - SingleRefProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x580, 0xa), - CompRefProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x58a, 0x5), - YModeProbs0 = Vmm.ReadBytes(VpxProbTablesAddress + 0x480, 0x20), - YModeProbs1 = Vmm.ReadBytes(VpxProbTablesAddress + 0x47c, 0x4), - PartitionProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x4e0, 0x40), - MvJointProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x53b, 0x3), - MvSignProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x53e, 0x3), - MvClassProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x54c, 0x14), - MvClass0BitProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x540, 0x3), - MvBitsProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x56c, 0x14), - MvClass0FrProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x560, 0xc), - MvFrProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x542, 0x6), - MvClass0HpProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x548, 0x2), - MvHpProbs = Vmm.ReadBytes(VpxProbTablesAddress + 0x54a, 0x2) + SegmentationTreeProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x387, 0x7), + SegmentationPredProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x38e, 0x3), + Tx8x8Probs = vmm.ReadBytes(_vpxProbTablesAddress + 0x470, 0x2), + Tx16x16Probs = vmm.ReadBytes(_vpxProbTablesAddress + 0x472, 0x4), + Tx32x32Probs = vmm.ReadBytes(_vpxProbTablesAddress + 0x476, 0x6), + CoefProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x5a0, 0x900), + SkipProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x537, 0x3), + InterModeProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x400, 0x1c), + InterpFilterProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x52a, 0x8), + IsInterProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x41c, 0x4), + CompModeProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x532, 0x5), + SingleRefProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x580, 0xa), + CompRefProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x58a, 0x5), + YModeProbs0 = vmm.ReadBytes(_vpxProbTablesAddress + 0x480, 0x20), + YModeProbs1 = vmm.ReadBytes(_vpxProbTablesAddress + 0x47c, 0x4), + PartitionProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x4e0, 0x40), + MvJointProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x53b, 0x3), + MvSignProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x53e, 0x3), + MvClassProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x54c, 0x14), + MvClass0BitProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x540, 0x3), + MvBitsProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x56c, 0x14), + MvClass0FrProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x560, 0xc), + MvFrProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x542, 0x6), + MvClass0HpProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x548, 0x2), + MvHpProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x54a, 0x2) }; - byte[] FrameData = Vmm.ReadBytes(FrameDataAddress, FrameDataSize); + byte[] frameData = vmm.ReadBytes(_frameDataAddress, frameDataSize); - Vp9Decoder.Decode(Keys, Header, Probs, FrameData); + _vp9Decoder.Decode(keys, header, probs, frameData); } else { @@ -134,147 +134,148 @@ namespace Ryujinx.Graphics.VDec } } - private void SetDecoderCtxAddr(NvGpuVmm Vmm, int[] Arguments) + private void SetDecoderCtxAddr(NvGpuVmm vmm, int[] arguments) { - DecoderContextAddress = GetAddress(Arguments); + _decoderContextAddress = GetAddress(arguments); } - private void SetFrameDataAddr(NvGpuVmm Vmm, int[] Arguments) + private void SetFrameDataAddr(NvGpuVmm vmm, int[] arguments) { - FrameDataAddress = GetAddress(Arguments); + _frameDataAddress = GetAddress(arguments); } - private void SetVpxCurrLumaAddr(NvGpuVmm Vmm, int[] Arguments) + private void SetVpxCurrLumaAddr(NvGpuVmm vmm, int[] arguments) { - VpxCurrLumaAddress = GetAddress(Arguments); + _vpxCurrLumaAddress = GetAddress(arguments); } - private void SetVpxRef0LumaAddr(NvGpuVmm Vmm, int[] Arguments) + private void SetVpxRef0LumaAddr(NvGpuVmm vmm, int[] arguments) { - VpxRef0LumaAddress = GetAddress(Arguments); + _vpxRef0LumaAddress = GetAddress(arguments); } - private void SetVpxRef1LumaAddr(NvGpuVmm Vmm, int[] Arguments) + private void SetVpxRef1LumaAddr(NvGpuVmm vmm, int[] arguments) { - VpxRef1LumaAddress = GetAddress(Arguments); + _vpxRef1LumaAddress = GetAddress(arguments); } - private void SetVpxRef2LumaAddr(NvGpuVmm Vmm, int[] Arguments) + private void SetVpxRef2LumaAddr(NvGpuVmm vmm, int[] arguments) { - VpxRef2LumaAddress = GetAddress(Arguments); + _vpxRef2LumaAddress = GetAddress(arguments); } - private void SetVpxCurrChromaAddr(NvGpuVmm Vmm, int[] Arguments) + private void SetVpxCurrChromaAddr(NvGpuVmm vmm, int[] arguments) { - VpxCurrChromaAddress = GetAddress(Arguments); + _vpxCurrChromaAddress = GetAddress(arguments); } - private void SetVpxRef0ChromaAddr(NvGpuVmm Vmm, int[] Arguments) + private void SetVpxRef0ChromaAddr(NvGpuVmm vmm, int[] arguments) { - VpxRef0ChromaAddress = GetAddress(Arguments); + _vpxRef0ChromaAddress = GetAddress(arguments); } - private void SetVpxRef1ChromaAddr(NvGpuVmm Vmm, int[] Arguments) + private void SetVpxRef1ChromaAddr(NvGpuVmm vmm, int[] arguments) { - VpxRef1ChromaAddress = GetAddress(Arguments); + _vpxRef1ChromaAddress = GetAddress(arguments); } - private void SetVpxRef2ChromaAddr(NvGpuVmm Vmm, int[] Arguments) + private void SetVpxRef2ChromaAddr(NvGpuVmm vmm, int[] arguments) { - VpxRef2ChromaAddress = GetAddress(Arguments); + _vpxRef2ChromaAddress = GetAddress(arguments); } - private void SetVpxProbTablesAddr(NvGpuVmm Vmm, int[] Arguments) + private void SetVpxProbTablesAddr(NvGpuVmm vmm, int[] arguments) { - VpxProbTablesAddress = GetAddress(Arguments); + _vpxProbTablesAddress = GetAddress(arguments); } - private static long GetAddress(int[] Arguments) + private static long GetAddress(int[] arguments) { - return (long)(uint)Arguments[0] << 8; + return (long)(uint)arguments[0] << 8; } - internal void CopyPlanes(NvGpuVmm Vmm, SurfaceOutputConfig OutputConfig) + internal void CopyPlanes(NvGpuVmm vmm, SurfaceOutputConfig outputConfig) { - switch (OutputConfig.PixelFormat) + switch (outputConfig.PixelFormat) { - case SurfacePixelFormat.RGBA8: CopyPlanesRgba8 (Vmm, OutputConfig); break; - case SurfacePixelFormat.YUV420P: CopyPlanesYuv420p(Vmm, OutputConfig); break; + case SurfacePixelFormat.Rgba8: CopyPlanesRgba8 (vmm, outputConfig); break; + case SurfacePixelFormat.Yuv420P: CopyPlanesYuv420P(vmm, outputConfig); break; - default: ThrowUnimplementedPixelFormat(OutputConfig.PixelFormat); break; + default: ThrowUnimplementedPixelFormat(outputConfig.PixelFormat); break; } } - private void CopyPlanesRgba8(NvGpuVmm Vmm, SurfaceOutputConfig OutputConfig) + private void CopyPlanesRgba8(NvGpuVmm vmm, SurfaceOutputConfig outputConfig) { - FFmpegFrame Frame = FFmpegWrapper.GetFrameRgba(); + FFmpegFrame frame = FFmpegWrapper.GetFrameRgba(); - if ((Frame.Width | Frame.Height) == 0) + if ((frame.Width | frame.Height) == 0) { return; } - GalImage Image = new GalImage( - OutputConfig.SurfaceWidth, - OutputConfig.SurfaceHeight, 1, - OutputConfig.GobBlockHeight, + GalImage image = new GalImage( + outputConfig.SurfaceWidth, + outputConfig.SurfaceHeight, 1, 1, 1, + outputConfig.GobBlockHeight, 1, GalMemoryLayout.BlockLinear, - GalImageFormat.RGBA8 | GalImageFormat.Unorm); + GalImageFormat.Rgba8 | GalImageFormat.Unorm, + GalTextureTarget.TwoD); - ImageUtils.WriteTexture(Vmm, Image, Vmm.GetPhysicalAddress(OutputConfig.SurfaceLumaAddress), Frame.Data); + ImageUtils.WriteTexture(vmm, image, vmm.GetPhysicalAddress(outputConfig.SurfaceLumaAddress), frame.Data); } - private void CopyPlanesYuv420p(NvGpuVmm Vmm, SurfaceOutputConfig OutputConfig) + private void CopyPlanesYuv420P(NvGpuVmm vmm, SurfaceOutputConfig outputConfig) { - FFmpegFrame Frame = FFmpegWrapper.GetFrame(); + FFmpegFrame frame = FFmpegWrapper.GetFrame(); - if ((Frame.Width | Frame.Height) == 0) + if ((frame.Width | frame.Height) == 0) { return; } - int HalfSrcWidth = Frame.Width / 2; + int halfSrcWidth = frame.Width / 2; - int HalfWidth = Frame.Width / 2; - int HalfHeight = Frame.Height / 2; + int halfWidth = frame.Width / 2; + int halfHeight = frame.Height / 2; - int AlignedWidth = (OutputConfig.SurfaceWidth + 0xff) & ~0xff; + int alignedWidth = (outputConfig.SurfaceWidth + 0xff) & ~0xff; - for (int Y = 0; Y < Frame.Height; Y++) + for (int y = 0; y < frame.Height; y++) { - int Src = Y * Frame.Width; - int Dst = Y * AlignedWidth; + int src = y * frame.Width; + int dst = y * alignedWidth; - int Size = Frame.Width; + int size = frame.Width; - for (int Offset = 0; Offset < Size; Offset++) + for (int offset = 0; offset < size; offset++) { - Vmm.WriteByte(OutputConfig.SurfaceLumaAddress + Dst + Offset, *(Frame.LumaPtr + Src + Offset)); + vmm.WriteByte(outputConfig.SurfaceLumaAddress + dst + offset, *(frame.LumaPtr + src + offset)); } } //Copy chroma data from both channels with interleaving. - for (int Y = 0; Y < HalfHeight; Y++) + for (int y = 0; y < halfHeight; y++) { - int Src = Y * HalfSrcWidth; - int Dst = Y * AlignedWidth; + int src = y * halfSrcWidth; + int dst = y * alignedWidth; - for (int X = 0; X < HalfWidth; X++) + for (int x = 0; x < halfWidth; x++) { - Vmm.WriteByte(OutputConfig.SurfaceChromaUAddress + Dst + X * 2 + 0, *(Frame.ChromaBPtr + Src + X)); - Vmm.WriteByte(OutputConfig.SurfaceChromaUAddress + Dst + X * 2 + 1, *(Frame.ChromaRPtr + Src + X)); + vmm.WriteByte(outputConfig.SurfaceChromaUAddress + dst + x * 2 + 0, *(frame.ChromaBPtr + src + x)); + vmm.WriteByte(outputConfig.SurfaceChromaUAddress + dst + x * 2 + 1, *(frame.ChromaRPtr + src + x)); } } } private void ThrowUnimplementedCodec() { - throw new NotImplementedException("Codec \"" + CurrentVideoCodec + "\" is not supported!"); + throw new NotImplementedException("Codec \"" + _currentVideoCodec + "\" is not supported!"); } - private void ThrowUnimplementedPixelFormat(SurfacePixelFormat PixelFormat) + private void ThrowUnimplementedPixelFormat(SurfacePixelFormat pixelFormat) { - throw new NotImplementedException("Pixel format \"" + PixelFormat + "\" is not supported!"); + throw new NotImplementedException("Pixel format \"" + pixelFormat + "\" is not supported!"); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/Vp9Decoder.cs b/Ryujinx.Graphics/VDec/Vp9Decoder.cs index 6e3fc1f295..d77bc6c4a5 100644 --- a/Ryujinx.Graphics/VDec/Vp9Decoder.cs +++ b/Ryujinx.Graphics/VDec/Vp9Decoder.cs @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.VDec private byte[] DefaultTx16x16Probs = new byte[] { 20, 152, 15, 101 }; private byte[] DefaultTx32x32Probs = new byte[] { 3, 136, 37, 5, 52, 13 }; - private byte[] DefaultCoefProbs = new byte[] + private byte[] _defaultCoefProbs = new byte[] { 195, 29, 183, 0, 84, 49, 136, 0, 8, 42, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 107, 169, 0, 35, 99, 159, 0, @@ -181,39 +181,39 @@ namespace Ryujinx.Graphics.VDec 1, 115, 166, 0, 1, 84, 121, 0, 1, 51, 67, 0, 1, 16, 6, 0 }; - private byte[] DefaultSkipProbs = new byte[] { 192, 128, 64 }; + private byte[] _defaultSkipProbs = new byte[] { 192, 128, 64 }; - private byte[] DefaultInterModeProbs = new byte[] + private byte[] _defaultInterModeProbs = new byte[] { 2, 173, 34, 0, 7, 145, 85, 0, 7, 166, 63, 0, 7, 94, 66, 0, 8, 64, 46, 0, 17, 81, 31, 0, 25, 29, 30, 0 }; - private byte[] DefaultInterpFilterProbs = new byte[] + private byte[] _defaultInterpFilterProbs = new byte[] { 235, 162, 36, 255, 34, 3, 149, 144 }; - private byte[] DefaultIsInterProbs = new byte[] { 9, 102, 187, 225 }; + private byte[] _defaultIsInterProbs = new byte[] { 9, 102, 187, 225 }; - private byte[] DefaultCompModeProbs = new byte[] { 239, 183, 119, 96, 41 }; + private byte[] _defaultCompModeProbs = new byte[] { 239, 183, 119, 96, 41 }; - private byte[] DefaultSingleRefProbs = new byte[] + private byte[] _defaultSingleRefProbs = new byte[] { 33, 16, 77, 74, 142, 142, 172, 170, 238, 247 }; - private byte[] DefaultCompRefProbs = new byte[] { 50, 126, 123, 221, 226 }; + private byte[] _defaultCompRefProbs = new byte[] { 50, 126, 123, 221, 226 }; - private byte[] DefaultYModeProbs0 = new byte[] + private byte[] _defaultYModeProbs0 = new byte[] { 65, 32, 18, 144, 162, 194, 41, 51, 132, 68, 18, 165, 217, 196, 45, 40, 173, 80, 19, 176, 240, 193, 64, 35, 221, 135, 38, 194, 248, 121, 96, 85 }; - private byte[] DefaultYModeProbs1 = new byte[] { 98, 78, 46, 29 }; + private byte[] _defaultYModeProbs1 = new byte[] { 98, 78, 46, 29 }; - private byte[] DefaultPartitionProbs = new byte[] + private byte[] _defaultPartitionProbs = new byte[] { 199, 122, 141, 0, 147, 63, 159, 0, 148, 133, 118, 0, 121, 104, 114, 0, 174, 73, 87, 0, 92, 41, 83, 0, 82, 99, 50, 0, 53, 39, 39, 0, @@ -221,184 +221,184 @@ namespace Ryujinx.Graphics.VDec 222, 34, 30, 0, 72, 16, 44, 0, 58, 32, 12, 0, 10, 7, 6, 0 }; - private byte[] DefaultMvJointProbs = new byte[] { 32, 64, 96 }; + private byte[] _defaultMvJointProbs = new byte[] { 32, 64, 96 }; - private byte[] DefaultMvSignProbs = new byte[] { 128, 128 }; + private byte[] _defaultMvSignProbs = new byte[] { 128, 128 }; - private byte[] DefaultMvClassProbs = new byte[] + private byte[] _defaultMvClassProbs = new byte[] { 224, 144, 192, 168, 192, 176, 192, 198, 198, 245, 216, 128, 176, 160, 176, 176, 192, 198, 198, 208 }; - private byte[] DefaultMvClass0BitProbs = new byte[] { 216, 208 }; + private byte[] _defaultMvClass0BitProbs = new byte[] { 216, 208 }; - private byte[] DefaultMvBitsProbs = new byte[] + private byte[] _defaultMvBitsProbs = new byte[] { 136, 140, 148, 160, 176, 192, 224, 234, 234, 240, 136, 140, 148, 160, 176, 192, 224, 234, 234, 240 }; - private byte[] DefaultMvClass0FrProbs = new byte[] + private byte[] _defaultMvClass0FrProbs = new byte[] { 128, 128, 64, 96, 112, 64, 128, 128, 64, 96, 112, 64 }; - private byte[] DefaultMvFrProbs = new byte[] { 64, 96, 64, 64, 96, 64 }; + private byte[] _defaultMvFrProbs = new byte[] { 64, 96, 64, 64, 96, 64 }; - private byte[] DefaultMvClass0HpProbs = new byte[] { 160, 160 }; + private byte[] _defaultMvClass0HpProbs = new byte[] { 160, 160 }; - private byte[] DefaultMvHpProbs = new byte[] { 128, 128 }; + private byte[] _defaultMvHpProbs = new byte[] { 128, 128 }; - private sbyte[] LoopFilterRefDeltas; - private sbyte[] LoopFilterModeDeltas; + private sbyte[] _loopFilterRefDeltas; + private sbyte[] _loopFilterModeDeltas; - private LinkedList FrameSlotByLastUse; + private LinkedList _frameSlotByLastUse; - private Dictionary> CachedRefFrames; + private Dictionary> _cachedRefFrames; public Vp9Decoder() { - LoopFilterRefDeltas = new sbyte[4]; - LoopFilterModeDeltas = new sbyte[2]; + _loopFilterRefDeltas = new sbyte[4]; + _loopFilterModeDeltas = new sbyte[2]; - FrameSlotByLastUse = new LinkedList(); + _frameSlotByLastUse = new LinkedList(); - for (int Slot = 0; Slot < 8; Slot++) + for (int slot = 0; slot < 8; slot++) { - FrameSlotByLastUse.AddFirst(Slot); + _frameSlotByLastUse.AddFirst(slot); } - CachedRefFrames = new Dictionary>(); + _cachedRefFrames = new Dictionary>(); } public void Decode( - Vp9FrameKeys Keys, - Vp9FrameHeader Header, - Vp9ProbabilityTables Probs, - byte[] FrameData) + Vp9FrameKeys keys, + Vp9FrameHeader header, + Vp9ProbabilityTables probs, + byte[] frameData) { - bool IsKeyFrame = ((Header.Flags >> 0) & 1) != 0; - bool LastIsKeyFrame = ((Header.Flags >> 1) & 1) != 0; - bool FrameSizeChanged = ((Header.Flags >> 2) & 1) != 0; - bool ErrorResilientMode = ((Header.Flags >> 3) & 1) != 0; - bool LastShowFrame = ((Header.Flags >> 4) & 1) != 0; - bool IsFrameIntra = ((Header.Flags >> 5) & 1) != 0; + bool isKeyFrame = ((header.Flags >> 0) & 1) != 0; + bool lastIsKeyFrame = ((header.Flags >> 1) & 1) != 0; + bool frameSizeChanged = ((header.Flags >> 2) & 1) != 0; + bool errorResilientMode = ((header.Flags >> 3) & 1) != 0; + bool lastShowFrame = ((header.Flags >> 4) & 1) != 0; + bool isFrameIntra = ((header.Flags >> 5) & 1) != 0; - bool ShowFrame = !IsFrameIntra; + bool showFrame = !isFrameIntra; //Write compressed header. - byte[] CompressedHeaderData; + byte[] compressedHeaderData; - using (MemoryStream CompressedHeader = new MemoryStream()) + using (MemoryStream compressedHeader = new MemoryStream()) { - VpxRangeEncoder Writer = new VpxRangeEncoder(CompressedHeader); + VpxRangeEncoder writer = new VpxRangeEncoder(compressedHeader); - if (!Header.Lossless) + if (!header.Lossless) { - if ((uint)Header.TxMode >= 3) + if ((uint)header.TxMode >= 3) { - Writer.Write(3, 2); - Writer.Write(Header.TxMode == 4); + writer.Write(3, 2); + writer.Write(header.TxMode == 4); } else { - Writer.Write(Header.TxMode, 2); + writer.Write(header.TxMode, 2); } } - if (Header.TxMode == 4) + if (header.TxMode == 4) { - WriteProbabilityUpdate(Writer, Probs.Tx8x8Probs, DefaultTx8x8Probs); - WriteProbabilityUpdate(Writer, Probs.Tx16x16Probs, DefaultTx16x16Probs); - WriteProbabilityUpdate(Writer, Probs.Tx32x32Probs, DefaultTx32x32Probs); + WriteProbabilityUpdate(writer, probs.Tx8x8Probs, DefaultTx8x8Probs); + WriteProbabilityUpdate(writer, probs.Tx16x16Probs, DefaultTx16x16Probs); + WriteProbabilityUpdate(writer, probs.Tx32x32Probs, DefaultTx32x32Probs); } - WriteCoefProbabilityUpdate(Writer, Header.TxMode, Probs.CoefProbs, DefaultCoefProbs); + WriteCoefProbabilityUpdate(writer, header.TxMode, probs.CoefProbs, _defaultCoefProbs); - WriteProbabilityUpdate(Writer, Probs.SkipProbs, DefaultSkipProbs); + WriteProbabilityUpdate(writer, probs.SkipProbs, _defaultSkipProbs); - if (!IsFrameIntra) + if (!isFrameIntra) { - WriteProbabilityUpdateAligned4(Writer, Probs.InterModeProbs, DefaultInterModeProbs); + WriteProbabilityUpdateAligned4(writer, probs.InterModeProbs, _defaultInterModeProbs); - if (Header.RawInterpolationFilter == 4) + if (header.RawInterpolationFilter == 4) { - WriteProbabilityUpdate(Writer, Probs.InterpFilterProbs, DefaultInterpFilterProbs); + WriteProbabilityUpdate(writer, probs.InterpFilterProbs, _defaultInterpFilterProbs); } - WriteProbabilityUpdate(Writer, Probs.IsInterProbs, DefaultIsInterProbs); + WriteProbabilityUpdate(writer, probs.IsInterProbs, _defaultIsInterProbs); - if ((Header.RefFrameSignBias[1] & 1) != (Header.RefFrameSignBias[2] & 1) || - (Header.RefFrameSignBias[1] & 1) != (Header.RefFrameSignBias[3] & 1)) + if ((header.RefFrameSignBias[1] & 1) != (header.RefFrameSignBias[2] & 1) || + (header.RefFrameSignBias[1] & 1) != (header.RefFrameSignBias[3] & 1)) { - if ((uint)Header.CompPredMode >= 1) + if ((uint)header.CompPredMode >= 1) { - Writer.Write(1, 1); - Writer.Write(Header.CompPredMode == 2); + writer.Write(1, 1); + writer.Write(header.CompPredMode == 2); } else { - Writer.Write(0, 1); + writer.Write(0, 1); } } - if (Header.CompPredMode == 2) + if (header.CompPredMode == 2) { - WriteProbabilityUpdate(Writer, Probs.CompModeProbs, DefaultCompModeProbs); + WriteProbabilityUpdate(writer, probs.CompModeProbs, _defaultCompModeProbs); } - if (Header.CompPredMode != 1) + if (header.CompPredMode != 1) { - WriteProbabilityUpdate(Writer, Probs.SingleRefProbs, DefaultSingleRefProbs); + WriteProbabilityUpdate(writer, probs.SingleRefProbs, _defaultSingleRefProbs); } - if (Header.CompPredMode != 0) + if (header.CompPredMode != 0) { - WriteProbabilityUpdate(Writer, Probs.CompRefProbs, DefaultCompRefProbs); + WriteProbabilityUpdate(writer, probs.CompRefProbs, _defaultCompRefProbs); } - for (int Index = 0; Index < 4; Index++) + for (int index = 0; index < 4; index++) { - int i = Index * 8; - int j = Index; + int i = index * 8; + int j = index; - WriteProbabilityUpdate(Writer, Probs.YModeProbs0[i + 0], DefaultYModeProbs0[i + 0]); - WriteProbabilityUpdate(Writer, Probs.YModeProbs0[i + 1], DefaultYModeProbs0[i + 1]); - WriteProbabilityUpdate(Writer, Probs.YModeProbs0[i + 2], DefaultYModeProbs0[i + 2]); - WriteProbabilityUpdate(Writer, Probs.YModeProbs0[i + 3], DefaultYModeProbs0[i + 3]); - WriteProbabilityUpdate(Writer, Probs.YModeProbs0[i + 4], DefaultYModeProbs0[i + 4]); - WriteProbabilityUpdate(Writer, Probs.YModeProbs0[i + 5], DefaultYModeProbs0[i + 5]); - WriteProbabilityUpdate(Writer, Probs.YModeProbs0[i + 6], DefaultYModeProbs0[i + 6]); - WriteProbabilityUpdate(Writer, Probs.YModeProbs0[i + 7], DefaultYModeProbs0[i + 7]); - WriteProbabilityUpdate(Writer, Probs.YModeProbs1[j + 0], DefaultYModeProbs1[j + 0]); + WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 0], _defaultYModeProbs0[i + 0]); + WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 1], _defaultYModeProbs0[i + 1]); + WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 2], _defaultYModeProbs0[i + 2]); + WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 3], _defaultYModeProbs0[i + 3]); + WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 4], _defaultYModeProbs0[i + 4]); + WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 5], _defaultYModeProbs0[i + 5]); + WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 6], _defaultYModeProbs0[i + 6]); + WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 7], _defaultYModeProbs0[i + 7]); + WriteProbabilityUpdate(writer, probs.YModeProbs1[j + 0], _defaultYModeProbs1[j + 0]); } - WriteProbabilityUpdateAligned4(Writer, Probs.PartitionProbs, DefaultPartitionProbs); + WriteProbabilityUpdateAligned4(writer, probs.PartitionProbs, _defaultPartitionProbs); for (int i = 0; i < 3; i++) { - WriteMvProbabilityUpdate(Writer, Probs.MvJointProbs[i], DefaultMvJointProbs[i]); + WriteMvProbabilityUpdate(writer, probs.MvJointProbs[i], _defaultMvJointProbs[i]); } for (int i = 0; i < 2; i++) { - WriteMvProbabilityUpdate(Writer, Probs.MvSignProbs[i], DefaultMvSignProbs[i]); + WriteMvProbabilityUpdate(writer, probs.MvSignProbs[i], _defaultMvSignProbs[i]); for (int j = 0; j < 10; j++) { - int Index = i * 10 + j; + int index = i * 10 + j; - WriteMvProbabilityUpdate(Writer, Probs.MvClassProbs[Index], DefaultMvClassProbs[Index]); + WriteMvProbabilityUpdate(writer, probs.MvClassProbs[index], _defaultMvClassProbs[index]); } - WriteMvProbabilityUpdate(Writer, Probs.MvClass0BitProbs[i], DefaultMvClass0BitProbs[i]); + WriteMvProbabilityUpdate(writer, probs.MvClass0BitProbs[i], _defaultMvClass0BitProbs[i]); for (int j = 0; j < 10; j++) { - int Index = i * 10 + j; + int index = i * 10 + j; - WriteMvProbabilityUpdate(Writer, Probs.MvBitsProbs[Index], DefaultMvBitsProbs[Index]); + WriteMvProbabilityUpdate(writer, probs.MvBitsProbs[index], _defaultMvBitsProbs[index]); } } @@ -408,249 +408,249 @@ namespace Ryujinx.Graphics.VDec { for (int k = 0; k < 3; k++) { - int Index = i * 2 * 3 + j * 3 + k; + int index = i * 2 * 3 + j * 3 + k; - WriteMvProbabilityUpdate(Writer, Probs.MvClass0FrProbs[Index], DefaultMvClass0FrProbs[Index]); + WriteMvProbabilityUpdate(writer, probs.MvClass0FrProbs[index], _defaultMvClass0FrProbs[index]); } } for (int j = 0; j < 3; j++) { - int Index = i * 3 + j; + int index = i * 3 + j; - WriteMvProbabilityUpdate(Writer, Probs.MvFrProbs[Index], DefaultMvFrProbs[Index]); + WriteMvProbabilityUpdate(writer, probs.MvFrProbs[index], _defaultMvFrProbs[index]); } } - if (Header.AllowHighPrecisionMv) + if (header.AllowHighPrecisionMv) { - for (int Index = 0; Index < 2; Index++) + for (int index = 0; index < 2; index++) { - WriteMvProbabilityUpdate(Writer, Probs.MvClass0HpProbs[Index], DefaultMvClass0HpProbs[Index]); - WriteMvProbabilityUpdate(Writer, Probs.MvHpProbs[Index], DefaultMvHpProbs[Index]); + WriteMvProbabilityUpdate(writer, probs.MvClass0HpProbs[index], _defaultMvClass0HpProbs[index]); + WriteMvProbabilityUpdate(writer, probs.MvHpProbs[index], _defaultMvHpProbs[index]); } } } - Writer.End(); + writer.End(); - CompressedHeaderData = CompressedHeader.ToArray(); + compressedHeaderData = compressedHeader.ToArray(); } //Write uncompressed header. - using (MemoryStream EncodedHeader = new MemoryStream()) + using (MemoryStream encodedHeader = new MemoryStream()) { - VpxBitStreamWriter Writer = new VpxBitStreamWriter(EncodedHeader); + VpxBitStreamWriter writer = new VpxBitStreamWriter(encodedHeader); - Writer.WriteU(2, 2); //Frame marker. - Writer.WriteU(0, 2); //Profile. - Writer.WriteBit(false); //Show existing frame. - Writer.WriteBit(!IsKeyFrame); - Writer.WriteBit(ShowFrame); - Writer.WriteBit(ErrorResilientMode); + writer.WriteU(2, 2); //Frame marker. + writer.WriteU(0, 2); //Profile. + writer.WriteBit(false); //Show existing frame. + writer.WriteBit(!isKeyFrame); + writer.WriteBit(showFrame); + writer.WriteBit(errorResilientMode); - if (IsKeyFrame) + if (isKeyFrame) { - Writer.WriteU(FrameSyncCode, 24); - Writer.WriteU(0, 3); //Color space. - Writer.WriteU(0, 1); //Color range. - Writer.WriteU(Header.CurrentFrame.Width - 1, 16); - Writer.WriteU(Header.CurrentFrame.Height - 1, 16); - Writer.WriteBit(false); //Render and frame size different. + writer.WriteU(FrameSyncCode, 24); + writer.WriteU(0, 3); //Color space. + writer.WriteU(0, 1); //Color range. + writer.WriteU(header.CurrentFrame.Width - 1, 16); + writer.WriteU(header.CurrentFrame.Height - 1, 16); + writer.WriteBit(false); //Render and frame size different. - CachedRefFrames.Clear(); + _cachedRefFrames.Clear(); //On key frames, all frame slots are set to the current frame, //so the value of the selected slot doesn't really matter. - GetNewFrameSlot(Keys.CurrKey); + GetNewFrameSlot(keys.CurrKey); } else { - if (!ShowFrame) + if (!showFrame) { - Writer.WriteBit(IsFrameIntra); + writer.WriteBit(isFrameIntra); } - if (!ErrorResilientMode) + if (!errorResilientMode) { - Writer.WriteU(0, 2); //Reset frame context. + writer.WriteU(0, 2); //Reset frame context. } - int RefreshFrameFlags = 1 << GetNewFrameSlot(Keys.CurrKey); + int refreshFrameFlags = 1 << GetNewFrameSlot(keys.CurrKey); - if (IsFrameIntra) + if (isFrameIntra) { - Writer.WriteU(FrameSyncCode, 24); - Writer.WriteU(RefreshFrameFlags, 8); - Writer.WriteU(Header.CurrentFrame.Width - 1, 16); - Writer.WriteU(Header.CurrentFrame.Height - 1, 16); - Writer.WriteBit(false); //Render and frame size different. + writer.WriteU(FrameSyncCode, 24); + writer.WriteU(refreshFrameFlags, 8); + writer.WriteU(header.CurrentFrame.Width - 1, 16); + writer.WriteU(header.CurrentFrame.Height - 1, 16); + writer.WriteBit(false); //Render and frame size different. } else { - Writer.WriteU(RefreshFrameFlags, 8); + writer.WriteU(refreshFrameFlags, 8); - int[] RefFrameIndex = new int[] + int[] refFrameIndex = new int[] { - GetFrameSlot(Keys.Ref0Key), - GetFrameSlot(Keys.Ref1Key), - GetFrameSlot(Keys.Ref2Key) + GetFrameSlot(keys.Ref0Key), + GetFrameSlot(keys.Ref1Key), + GetFrameSlot(keys.Ref2Key) }; - byte[] RefFrameSignBias = Header.RefFrameSignBias; + byte[] refFrameSignBias = header.RefFrameSignBias; - for (int Index = 1; Index < 4; Index++) + for (int index = 1; index < 4; index++) { - Writer.WriteU(RefFrameIndex[Index - 1], 3); - Writer.WriteU(RefFrameSignBias[Index], 1); + writer.WriteU(refFrameIndex[index - 1], 3); + writer.WriteU(refFrameSignBias[index], 1); } - Writer.WriteBit(true); //Frame size with refs. - Writer.WriteBit(false); //Render and frame size different. - Writer.WriteBit(Header.AllowHighPrecisionMv); - Writer.WriteBit(Header.RawInterpolationFilter == 4); + writer.WriteBit(true); //Frame size with refs. + writer.WriteBit(false); //Render and frame size different. + writer.WriteBit(header.AllowHighPrecisionMv); + writer.WriteBit(header.RawInterpolationFilter == 4); - if (Header.RawInterpolationFilter != 4) + if (header.RawInterpolationFilter != 4) { - Writer.WriteU(Header.RawInterpolationFilter, 2); + writer.WriteU(header.RawInterpolationFilter, 2); } } } - if (!ErrorResilientMode) + if (!errorResilientMode) { - Writer.WriteBit(false); //Refresh frame context. - Writer.WriteBit(true); //Frame parallel decoding mode. + writer.WriteBit(false); //Refresh frame context. + writer.WriteBit(true); //Frame parallel decoding mode. } - Writer.WriteU(0, 2); //Frame context index. + writer.WriteU(0, 2); //Frame context index. - Writer.WriteU(Header.LoopFilterLevel, 6); - Writer.WriteU(Header.LoopFilterSharpness, 3); - Writer.WriteBit(Header.LoopFilterDeltaEnabled); + writer.WriteU(header.LoopFilterLevel, 6); + writer.WriteU(header.LoopFilterSharpness, 3); + writer.WriteBit(header.LoopFilterDeltaEnabled); - if (Header.LoopFilterDeltaEnabled) + if (header.LoopFilterDeltaEnabled) { - bool[] UpdateLoopFilterRefDeltas = new bool[4]; - bool[] UpdateLoopFilterModeDeltas = new bool[2]; + bool[] updateLoopFilterRefDeltas = new bool[4]; + bool[] updateLoopFilterModeDeltas = new bool[2]; - bool LoopFilterDeltaUpdate = false; + bool loopFilterDeltaUpdate = false; - for (int Index = 0; Index < Header.LoopFilterRefDeltas.Length; Index++) + for (int index = 0; index < header.LoopFilterRefDeltas.Length; index++) { - sbyte Old = LoopFilterRefDeltas[Index]; - sbyte New = Header.LoopFilterRefDeltas[Index]; + sbyte old = _loopFilterRefDeltas[index]; + sbyte New = header.LoopFilterRefDeltas[index]; - LoopFilterDeltaUpdate |= (UpdateLoopFilterRefDeltas[Index] = Old != New); + loopFilterDeltaUpdate |= (updateLoopFilterRefDeltas[index] = old != New); } - for (int Index = 0; Index < Header.LoopFilterModeDeltas.Length; Index++) + for (int index = 0; index < header.LoopFilterModeDeltas.Length; index++) { - sbyte Old = LoopFilterModeDeltas[Index]; - sbyte New = Header.LoopFilterModeDeltas[Index]; + sbyte old = _loopFilterModeDeltas[index]; + sbyte New = header.LoopFilterModeDeltas[index]; - LoopFilterDeltaUpdate |= (UpdateLoopFilterModeDeltas[Index] = Old != New); + loopFilterDeltaUpdate |= (updateLoopFilterModeDeltas[index] = old != New); } - Writer.WriteBit(LoopFilterDeltaUpdate); + writer.WriteBit(loopFilterDeltaUpdate); - if (LoopFilterDeltaUpdate) + if (loopFilterDeltaUpdate) { - for (int Index = 0; Index < Header.LoopFilterRefDeltas.Length; Index++) + for (int index = 0; index < header.LoopFilterRefDeltas.Length; index++) { - Writer.WriteBit(UpdateLoopFilterRefDeltas[Index]); + writer.WriteBit(updateLoopFilterRefDeltas[index]); - if (UpdateLoopFilterRefDeltas[Index]) + if (updateLoopFilterRefDeltas[index]) { - Writer.WriteS(Header.LoopFilterRefDeltas[Index], 6); + writer.WriteS(header.LoopFilterRefDeltas[index], 6); } } - for (int Index = 0; Index < Header.LoopFilterModeDeltas.Length; Index++) + for (int index = 0; index < header.LoopFilterModeDeltas.Length; index++) { - Writer.WriteBit(UpdateLoopFilterModeDeltas[Index]); + writer.WriteBit(updateLoopFilterModeDeltas[index]); - if (UpdateLoopFilterModeDeltas[Index]) + if (updateLoopFilterModeDeltas[index]) { - Writer.WriteS(Header.LoopFilterModeDeltas[Index], 6); + writer.WriteS(header.LoopFilterModeDeltas[index], 6); } } } } - Writer.WriteU(Header.BaseQIndex, 8); + writer.WriteU(header.BaseQIndex, 8); - Writer.WriteDeltaQ(Header.DeltaQYDc); - Writer.WriteDeltaQ(Header.DeltaQUvDc); - Writer.WriteDeltaQ(Header.DeltaQUvAc); + writer.WriteDeltaQ(header.DeltaQYDc); + writer.WriteDeltaQ(header.DeltaQUvDc); + writer.WriteDeltaQ(header.DeltaQUvAc); - Writer.WriteBit(false); //Segmentation enabled (TODO). + writer.WriteBit(false); //Segmentation enabled (TODO). - int MinTileColsLog2 = CalcMinLog2TileCols(Header.CurrentFrame.Width); - int MaxTileColsLog2 = CalcMaxLog2TileCols(Header.CurrentFrame.Width); + int minTileColsLog2 = CalcMinLog2TileCols(header.CurrentFrame.Width); + int maxTileColsLog2 = CalcMaxLog2TileCols(header.CurrentFrame.Width); - int TileColsLog2Diff = Header.TileColsLog2 - MinTileColsLog2; + int tileColsLog2Diff = header.TileColsLog2 - minTileColsLog2; - int TileColsLog2IncMask = (1 << TileColsLog2Diff) - 1; + int tileColsLog2IncMask = (1 << tileColsLog2Diff) - 1; //If it's less than the maximum, we need to add an extra 0 on the bitstream //to indicate that it should stop reading. - if (Header.TileColsLog2 < MaxTileColsLog2) + if (header.TileColsLog2 < maxTileColsLog2) { - Writer.WriteU(TileColsLog2IncMask << 1, TileColsLog2Diff + 1); + writer.WriteU(tileColsLog2IncMask << 1, tileColsLog2Diff + 1); } else { - Writer.WriteU(TileColsLog2IncMask, TileColsLog2Diff); + writer.WriteU(tileColsLog2IncMask, tileColsLog2Diff); } - bool TileRowsLog2IsNonZero = Header.TileRowsLog2 != 0; + bool tileRowsLog2IsNonZero = header.TileRowsLog2 != 0; - Writer.WriteBit(TileRowsLog2IsNonZero); + writer.WriteBit(tileRowsLog2IsNonZero); - if (TileRowsLog2IsNonZero) + if (tileRowsLog2IsNonZero) { - Writer.WriteBit(Header.TileRowsLog2 > 1); + writer.WriteBit(header.TileRowsLog2 > 1); } - Writer.WriteU(CompressedHeaderData.Length, 16); + writer.WriteU(compressedHeaderData.Length, 16); - Writer.Flush(); + writer.Flush(); - EncodedHeader.Write(CompressedHeaderData, 0, CompressedHeaderData.Length); + encodedHeader.Write(compressedHeaderData, 0, compressedHeaderData.Length); if (!FFmpegWrapper.IsInitialized) { FFmpegWrapper.Vp9Initialize(); } - FFmpegWrapper.DecodeFrame(DecoderHelper.Combine(EncodedHeader.ToArray(), FrameData)); + FFmpegWrapper.DecodeFrame(DecoderHelper.Combine(encodedHeader.ToArray(), frameData)); } - LoopFilterRefDeltas = Header.LoopFilterRefDeltas; - LoopFilterModeDeltas = Header.LoopFilterModeDeltas; + _loopFilterRefDeltas = header.LoopFilterRefDeltas; + _loopFilterModeDeltas = header.LoopFilterModeDeltas; } - private int GetNewFrameSlot(long Key) + private int GetNewFrameSlot(long key) { - LinkedListNode Node = FrameSlotByLastUse.Last; + LinkedListNode node = _frameSlotByLastUse.Last; - FrameSlotByLastUse.RemoveLast(); - FrameSlotByLastUse.AddFirst(Node); + _frameSlotByLastUse.RemoveLast(); + _frameSlotByLastUse.AddFirst(node); - CachedRefFrames[Key] = Node; + _cachedRefFrames[key] = node; - return Node.Value; + return node.Value; } - private int GetFrameSlot(long Key) + private int GetFrameSlot(long key) { - if (CachedRefFrames.TryGetValue(Key, out LinkedListNode Node)) + if (_cachedRefFrames.TryGetValue(key, out LinkedListNode node)) { - FrameSlotByLastUse.Remove(Node); - FrameSlotByLastUse.AddFirst(Node); + _frameSlotByLastUse.Remove(node); + _frameSlotByLastUse.AddFirst(node); - return Node.Value; + return node.Value; } //Reference frame was lost. @@ -658,53 +658,53 @@ namespace Ryujinx.Graphics.VDec return 0; } - private void WriteProbabilityUpdate(VpxRangeEncoder Writer, byte[] New, byte[] Old) + private void WriteProbabilityUpdate(VpxRangeEncoder writer, byte[] New, byte[] old) { - for (int Offset = 0; Offset < New.Length; Offset++) + for (int offset = 0; offset < New.Length; offset++) { - WriteProbabilityUpdate(Writer, New[Offset], Old[Offset]); + WriteProbabilityUpdate(writer, New[offset], old[offset]); } } - private void WriteCoefProbabilityUpdate(VpxRangeEncoder Writer, int TxMode, byte[] New, byte[] Old) + private void WriteCoefProbabilityUpdate(VpxRangeEncoder writer, int txMode, byte[] New, byte[] old) { //Note: There's 1 byte added on each packet for alignment, //this byte is ignored when doing updates. - const int BlockBytes = 2 * 2 * 6 * 6 * 4; + const int blockBytes = 2 * 2 * 6 * 6 * 4; - bool NeedsUpdate(int BaseIndex) + bool NeedsUpdate(int baseIndex) { - int Index = BaseIndex; + int index = baseIndex; for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) for (int k = 0; k < 6; k++) for (int l = 0; l < 6; l++) { - if (New[Index + 0] != Old[Index + 0] || - New[Index + 1] != Old[Index + 1] || - New[Index + 2] != Old[Index + 2]) + if (New[index + 0] != old[index + 0] || + New[index + 1] != old[index + 1] || + New[index + 2] != old[index + 2]) { return true; } - Index += 4; + index += 4; } return false; } - for (int BlockIndex = 0; BlockIndex < 4; BlockIndex++) + for (int blockIndex = 0; blockIndex < 4; blockIndex++) { - int BaseIndex = BlockIndex * BlockBytes; + int baseIndex = blockIndex * blockBytes; - bool Update = NeedsUpdate(BaseIndex); + bool update = NeedsUpdate(baseIndex); - Writer.Write(Update); + writer.Write(update); - if (Update) + if (update) { - int Index = BaseIndex; + int index = baseIndex; for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) @@ -713,167 +713,167 @@ namespace Ryujinx.Graphics.VDec { if (k != 0 || l < 3) { - WriteProbabilityUpdate(Writer, New[Index + 0], Old[Index + 0]); - WriteProbabilityUpdate(Writer, New[Index + 1], Old[Index + 1]); - WriteProbabilityUpdate(Writer, New[Index + 2], Old[Index + 2]); + WriteProbabilityUpdate(writer, New[index + 0], old[index + 0]); + WriteProbabilityUpdate(writer, New[index + 1], old[index + 1]); + WriteProbabilityUpdate(writer, New[index + 2], old[index + 2]); } - Index += 4; + index += 4; } } - if (BlockIndex == TxMode) + if (blockIndex == txMode) { break; } } } - private void WriteProbabilityUpdateAligned4(VpxRangeEncoder Writer, byte[] New, byte[] Old) + private void WriteProbabilityUpdateAligned4(VpxRangeEncoder writer, byte[] New, byte[] old) { - for (int Offset = 0; Offset < New.Length; Offset += 4) + for (int offset = 0; offset < New.Length; offset += 4) { - WriteProbabilityUpdate(Writer, New[Offset + 0], Old[Offset + 0]); - WriteProbabilityUpdate(Writer, New[Offset + 1], Old[Offset + 1]); - WriteProbabilityUpdate(Writer, New[Offset + 2], Old[Offset + 2]); + WriteProbabilityUpdate(writer, New[offset + 0], old[offset + 0]); + WriteProbabilityUpdate(writer, New[offset + 1], old[offset + 1]); + WriteProbabilityUpdate(writer, New[offset + 2], old[offset + 2]); } } - private void WriteProbabilityUpdate(VpxRangeEncoder Writer, byte New, byte Old) + private void WriteProbabilityUpdate(VpxRangeEncoder writer, byte New, byte old) { - bool Update = New != Old; + bool update = New != old; - Writer.Write(Update, DiffUpdateProbability); + writer.Write(update, DiffUpdateProbability); - if (Update) + if (update) { - WriteProbabilityDelta(Writer, New, Old); + WriteProbabilityDelta(writer, New, old); } } - private void WriteProbabilityDelta(VpxRangeEncoder Writer, int New, int Old) + private void WriteProbabilityDelta(VpxRangeEncoder writer, int New, int old) { - int Delta = RemapProbability(New, Old); + int delta = RemapProbability(New, old); - EncodeTermSubExp(Writer, Delta); + EncodeTermSubExp(writer, delta); } - private int RemapProbability(int New, int Old) + private int RemapProbability(int New, int old) { New--; - Old--; + old--; - int Index; + int index; - if (Old * 2 <= 0xff) + if (old * 2 <= 0xff) { - Index = RecenterNonNeg(New, Old) - 1; + index = RecenterNonNeg(New, old) - 1; } else { - Index = RecenterNonNeg(0xff - 1 - New, 0xff - 1 - Old) - 1; + index = RecenterNonNeg(0xff - 1 - New, 0xff - 1 - old) - 1; } - return MapLut[Index]; + return MapLut[index]; } - private int RecenterNonNeg(int New, int Old) + private int RecenterNonNeg(int New, int old) { - if (New > Old * 2) + if (New > old * 2) { return New; } - else if (New >= Old) + else if (New >= old) { - return (New - Old) * 2; + return (New - old) * 2; } else /* if (New < Old) */ { - return (Old - New) * 2 - 1; + return (old - New) * 2 - 1; } } - private void EncodeTermSubExp(VpxRangeEncoder Writer, int Value) + private void EncodeTermSubExp(VpxRangeEncoder writer, int value) { - if (WriteLessThan(Writer, Value, 16)) + if (WriteLessThan(writer, value, 16)) { - Writer.Write(Value, 4); + writer.Write(value, 4); } - else if (WriteLessThan(Writer, Value, 32)) + else if (WriteLessThan(writer, value, 32)) { - Writer.Write(Value - 16, 4); + writer.Write(value - 16, 4); } - else if (WriteLessThan(Writer, Value, 64)) + else if (WriteLessThan(writer, value, 64)) { - Writer.Write(Value - 32, 5); + writer.Write(value - 32, 5); } else { - Value -= 64; + value -= 64; - const int Size = 8; + const int size = 8; - int Mask = (1 << Size) - 191; + int mask = (1 << size) - 191; - int Delta = Value - Mask; + int delta = value - mask; - if (Delta < 0) + if (delta < 0) { - Writer.Write(Value, Size - 1); + writer.Write(value, size - 1); } else { - Writer.Write(Delta / 2 + Mask, Size - 1); - Writer.Write(Delta & 1, 1); + writer.Write(delta / 2 + mask, size - 1); + writer.Write(delta & 1, 1); } } } - private bool WriteLessThan(VpxRangeEncoder Writer, int Value, int Test) + private bool WriteLessThan(VpxRangeEncoder writer, int value, int test) { - bool IsLessThan = Value < Test; + bool isLessThan = value < test; - Writer.Write(!IsLessThan); + writer.Write(!isLessThan); - return IsLessThan; + return isLessThan; } - private void WriteMvProbabilityUpdate(VpxRangeEncoder Writer, byte New, byte Old) + private void WriteMvProbabilityUpdate(VpxRangeEncoder writer, byte New, byte old) { - bool Update = New != Old; + bool update = New != old; - Writer.Write(Update, DiffUpdateProbability); + writer.Write(update, DiffUpdateProbability); - if (Update) + if (update) { - Writer.Write(New >> 1, 7); + writer.Write(New >> 1, 7); } } - private static int CalcMinLog2TileCols(int FrameWidth) + private static int CalcMinLog2TileCols(int frameWidth) { - int Sb64Cols = (FrameWidth + 63) / 64; - int MinLog2 = 0; + int sb64Cols = (frameWidth + 63) / 64; + int minLog2 = 0; - while ((64 << MinLog2) < Sb64Cols) + while ((64 << minLog2) < sb64Cols) { - MinLog2++; + minLog2++; } - return MinLog2; + return minLog2; } - private static int CalcMaxLog2TileCols(int FrameWidth) + private static int CalcMaxLog2TileCols(int frameWidth) { - int Sb64Cols = (FrameWidth + 63) / 64; - int MaxLog2 = 1; + int sb64Cols = (frameWidth + 63) / 64; + int maxLog2 = 1; - while ((Sb64Cols >> MaxLog2) >= 4) + while ((sb64Cols >> maxLog2) >= 4) { - MaxLog2++; + maxLog2++; } - return MaxLog2 - 1; + return maxLog2 - 1; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/VpxBitStreamWriter.cs b/Ryujinx.Graphics/VDec/VpxBitStreamWriter.cs index 0c712d2c4d..97ada333e7 100644 --- a/Ryujinx.Graphics/VDec/VpxBitStreamWriter.cs +++ b/Ryujinx.Graphics/VDec/VpxBitStreamWriter.cs @@ -4,34 +4,34 @@ namespace Ryujinx.Graphics.VDec { class VpxBitStreamWriter : BitStreamWriter { - public VpxBitStreamWriter(Stream BaseStream) : base(BaseStream) { } + public VpxBitStreamWriter(Stream baseStream) : base(baseStream) { } - public void WriteU(int Value, int ValueSize) + public void WriteU(int value, int valueSize) { - WriteBits(Value, ValueSize); + WriteBits(value, valueSize); } - public void WriteS(int Value, int ValueSize) + public void WriteS(int value, int valueSize) { - bool Sign = Value < 0; + bool sign = value < 0; - if (Sign) + if (sign) { - Value = -Value; + value = -value; } - WriteBits((Value << 1) | (Sign ? 1 : 0), ValueSize + 1); + WriteBits((value << 1) | (sign ? 1 : 0), valueSize + 1); } - public void WriteDeltaQ(int Value) + public void WriteDeltaQ(int value) { - bool DeltaCoded = Value != 0; + bool deltaCoded = value != 0; - WriteBit(DeltaCoded); + WriteBit(deltaCoded); - if (DeltaCoded) + if (deltaCoded) { - WriteBits(Value, 4); + WriteBits(value, 4); } } } diff --git a/Ryujinx.Graphics/VDec/VpxRangeEncoder.cs b/Ryujinx.Graphics/VDec/VpxRangeEncoder.cs index 3e381d2c16..c854c9d9de 100644 --- a/Ryujinx.Graphics/VDec/VpxRangeEncoder.cs +++ b/Ryujinx.Graphics/VDec/VpxRangeEncoder.cs @@ -26,106 +26,106 @@ namespace Ryujinx.Graphics.VDec 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - private Stream BaseStream; + private Stream _baseStream; - private uint LowValue; - private uint Range; - private int Count; + private uint _lowValue; + private uint _range; + private int _count; - public VpxRangeEncoder(Stream BaseStream) + public VpxRangeEncoder(Stream baseStream) { - this.BaseStream = BaseStream; + _baseStream = baseStream; - Range = 0xff; - Count = -24; + _range = 0xff; + _count = -24; Write(false); } - public void WriteByte(byte Value) + public void WriteByte(byte value) { - Write(Value, 8); + Write(value, 8); } - public void Write(int Value, int ValueSize) + public void Write(int value, int valueSize) { - for (int Bit = ValueSize - 1; Bit >= 0; Bit--) + for (int bit = valueSize - 1; bit >= 0; bit--) { - Write(((Value >> Bit) & 1) != 0); + Write(((value >> bit) & 1) != 0); } } - public void Write(bool Bit) + public void Write(bool bit) { - Write(Bit, HalfProbability); + Write(bit, HalfProbability); } - public void Write(bool Bit, int Probability) + public void Write(bool bit, int probability) { - uint Range = this.Range; + uint range = _range; - uint Split = 1 + (((Range - 1) * (uint)Probability) >> 8); + uint split = 1 + (((range - 1) * (uint)probability) >> 8); - Range = Split; + range = split; - if (Bit) + if (bit) { - LowValue += Split; - Range = this.Range - Split; + _lowValue += split; + range = _range - split; } - int Shift = NormLut[Range]; + int shift = NormLut[range]; - Range <<= Shift; - Count += Shift; + range <<= shift; + _count += shift; - if (Count >= 0) + if (_count >= 0) { - int Offset = Shift - Count; + int offset = shift - _count; - if (((LowValue << (Offset - 1)) >> 31) != 0) + if (((_lowValue << (offset - 1)) >> 31) != 0) { - long CurrentPos = BaseStream.Position; + long currentPos = _baseStream.Position; - BaseStream.Seek(-1, SeekOrigin.Current); + _baseStream.Seek(-1, SeekOrigin.Current); - while (BaseStream.Position >= 0 && PeekByte() == 0xff) + while (_baseStream.Position >= 0 && PeekByte() == 0xff) { - BaseStream.WriteByte(0); + _baseStream.WriteByte(0); - BaseStream.Seek(-2, SeekOrigin.Current); + _baseStream.Seek(-2, SeekOrigin.Current); } - BaseStream.WriteByte((byte)(PeekByte() + 1)); + _baseStream.WriteByte((byte)(PeekByte() + 1)); - BaseStream.Seek(CurrentPos, SeekOrigin.Begin); + _baseStream.Seek(currentPos, SeekOrigin.Begin); } - BaseStream.WriteByte((byte)(LowValue >> (24 - Offset))); + _baseStream.WriteByte((byte)(_lowValue >> (24 - offset))); - LowValue <<= Offset; - Shift = Count; - LowValue &= 0xffffff; - Count -= 8; + _lowValue <<= offset; + shift = _count; + _lowValue &= 0xffffff; + _count -= 8; } - LowValue <<= Shift; + _lowValue <<= shift; - this.Range = Range; + _range = range; } private byte PeekByte() { - byte Value = (byte)BaseStream.ReadByte(); + byte value = (byte)_baseStream.ReadByte(); - BaseStream.Seek(-1, SeekOrigin.Current); + _baseStream.Seek(-1, SeekOrigin.Current); - return Value; + return value; } public void End() { - for (int Index = 0; Index < 32; Index++) + for (int index = 0; index < 32; index++) { Write(false); } diff --git a/Ryujinx.Graphics/Vic/StructUnpacker.cs b/Ryujinx.Graphics/Vic/StructUnpacker.cs index 62777aadd2..6b6b9795c7 100644 --- a/Ryujinx.Graphics/Vic/StructUnpacker.cs +++ b/Ryujinx.Graphics/Vic/StructUnpacker.cs @@ -5,64 +5,64 @@ namespace Ryujinx.Graphics.Vic { class StructUnpacker { - private NvGpuVmm Vmm; + private NvGpuVmm _vmm; - private long Position; + private long _position; - private ulong Buffer; - private int BuffPos; + private ulong _buffer; + private int _buffPos; - public StructUnpacker(NvGpuVmm Vmm, long Position) + public StructUnpacker(NvGpuVmm vmm, long position) { - this.Vmm = Vmm; - this.Position = Position; + _vmm = vmm; + _position = position; - BuffPos = 64; + _buffPos = 64; } - public int Read(int Bits) + public int Read(int bits) { - if ((uint)Bits > 32) + if ((uint)bits > 32) { - throw new ArgumentOutOfRangeException(nameof(Bits)); + throw new ArgumentOutOfRangeException(nameof(bits)); } - int Value = 0; + int value = 0; - while (Bits > 0) + while (bits > 0) { RefillBufferIfNeeded(); - int ReadBits = Bits; + int readBits = bits; - int MaxReadBits = 64 - BuffPos; + int maxReadBits = 64 - _buffPos; - if (ReadBits > MaxReadBits) + if (readBits > maxReadBits) { - ReadBits = MaxReadBits; + readBits = maxReadBits; } - Value <<= ReadBits; + value <<= readBits; - Value |= (int)(Buffer >> BuffPos) & (int)(0xffffffff >> (32 - ReadBits)); + value |= (int)(_buffer >> _buffPos) & (int)(0xffffffff >> (32 - readBits)); - BuffPos += ReadBits; + _buffPos += readBits; - Bits -= ReadBits; + bits -= readBits; } - return Value; + return value; } private void RefillBufferIfNeeded() { - if (BuffPos >= 64) + if (_buffPos >= 64) { - Buffer = Vmm.ReadUInt64(Position); + _buffer = _vmm.ReadUInt64(_position); - Position += 8; + _position += 8; - BuffPos = 0; + _buffPos = 0; } } } diff --git a/Ryujinx.Graphics/Vic/SurfaceOutputConfig.cs b/Ryujinx.Graphics/Vic/SurfaceOutputConfig.cs index 0a232744c3..bdd55fc7c8 100644 --- a/Ryujinx.Graphics/Vic/SurfaceOutputConfig.cs +++ b/Ryujinx.Graphics/Vic/SurfaceOutputConfig.cs @@ -13,21 +13,21 @@ namespace Ryujinx.Graphics.Vic public long SurfaceChromaVAddress; public SurfaceOutputConfig( - SurfacePixelFormat PixelFormat, - int SurfaceWidth, - int SurfaceHeight, - int GobBlockHeight, - long OutputSurfaceLumaAddress, - long OutputSurfaceChromaUAddress, - long OutputSurfaceChromaVAddress) + SurfacePixelFormat pixelFormat, + int surfaceWidth, + int surfaceHeight, + int gobBlockHeight, + long outputSurfaceLumaAddress, + long outputSurfaceChromaUAddress, + long outputSurfaceChromaVAddress) { - this.PixelFormat = PixelFormat; - this.SurfaceWidth = SurfaceWidth; - this.SurfaceHeight = SurfaceHeight; - this.GobBlockHeight = GobBlockHeight; - this.SurfaceLumaAddress = OutputSurfaceLumaAddress; - this.SurfaceChromaUAddress = OutputSurfaceChromaUAddress; - this.SurfaceChromaVAddress = OutputSurfaceChromaVAddress; + PixelFormat = pixelFormat; + SurfaceWidth = surfaceWidth; + SurfaceHeight = surfaceHeight; + GobBlockHeight = gobBlockHeight; + SurfaceLumaAddress = outputSurfaceLumaAddress; + SurfaceChromaUAddress = outputSurfaceChromaUAddress; + SurfaceChromaVAddress = outputSurfaceChromaVAddress; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Vic/SurfacePixelFormat.cs b/Ryujinx.Graphics/Vic/SurfacePixelFormat.cs index ee56ac0535..8dabd09423 100644 --- a/Ryujinx.Graphics/Vic/SurfacePixelFormat.cs +++ b/Ryujinx.Graphics/Vic/SurfacePixelFormat.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Graphics.Vic { enum SurfacePixelFormat { - RGBA8 = 0x1f, - YUV420P = 0x44 + Rgba8 = 0x1f, + Yuv420P = 0x44 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Vic/VideoImageComposer.cs b/Ryujinx.Graphics/Vic/VideoImageComposer.cs index 758382fa88..e05bcfdb65 100644 --- a/Ryujinx.Graphics/Vic/VideoImageComposer.cs +++ b/Ryujinx.Graphics/Vic/VideoImageComposer.cs @@ -4,104 +4,104 @@ namespace Ryujinx.Graphics.Vic { class VideoImageComposer { - private NvGpu Gpu; + private NvGpu _gpu; - private long ConfigStructAddress; - private long OutputSurfaceLumaAddress; - private long OutputSurfaceChromaUAddress; - private long OutputSurfaceChromaVAddress; + private long _configStructAddress; + private long _outputSurfaceLumaAddress; + private long _outputSurfaceChromaUAddress; + private long _outputSurfaceChromaVAddress; - public VideoImageComposer(NvGpu Gpu) + public VideoImageComposer(NvGpu gpu) { - this.Gpu = Gpu; + _gpu = gpu; } - public void Process(NvGpuVmm Vmm, int MethodOffset, int[] Arguments) + public void Process(NvGpuVmm vmm, int methodOffset, int[] arguments) { - VideoImageComposerMeth Method = (VideoImageComposerMeth)MethodOffset; + VideoImageComposerMeth method = (VideoImageComposerMeth)methodOffset; - switch (Method) + switch (method) { case VideoImageComposerMeth.Execute: - Execute(Vmm, Arguments); + Execute(vmm, arguments); break; case VideoImageComposerMeth.SetConfigStructOffset: - SetConfigStructOffset(Vmm, Arguments); + SetConfigStructOffset(vmm, arguments); break; case VideoImageComposerMeth.SetOutputSurfaceLumaOffset: - SetOutputSurfaceLumaOffset(Vmm, Arguments); + SetOutputSurfaceLumaOffset(vmm, arguments); break; case VideoImageComposerMeth.SetOutputSurfaceChromaUOffset: - SetOutputSurfaceChromaUOffset(Vmm, Arguments); + SetOutputSurfaceChromaUOffset(vmm, arguments); break; case VideoImageComposerMeth.SetOutputSurfaceChromaVOffset: - SetOutputSurfaceChromaVOffset(Vmm, Arguments); + SetOutputSurfaceChromaVOffset(vmm, arguments); break; } } - private void Execute(NvGpuVmm Vmm, int[] Arguments) + private void Execute(NvGpuVmm vmm, int[] arguments) { - StructUnpacker Unpacker = new StructUnpacker(Vmm, ConfigStructAddress + 0x20); + StructUnpacker unpacker = new StructUnpacker(vmm, _configStructAddress + 0x20); - SurfacePixelFormat PixelFormat = (SurfacePixelFormat)Unpacker.Read(7); + SurfacePixelFormat pixelFormat = (SurfacePixelFormat)unpacker.Read(7); - int ChromaLocHoriz = Unpacker.Read(2); - int ChromaLocVert = Unpacker.Read(2); + int chromaLocHoriz = unpacker.Read(2); + int chromaLocVert = unpacker.Read(2); - int BlockLinearKind = Unpacker.Read(4); - int BlockLinearHeightLog2 = Unpacker.Read(4); + int blockLinearKind = unpacker.Read(4); + int blockLinearHeightLog2 = unpacker.Read(4); - int Reserved0 = Unpacker.Read(3); - int Reserved1 = Unpacker.Read(10); + int reserved0 = unpacker.Read(3); + int reserved1 = unpacker.Read(10); - int SurfaceWidthMinus1 = Unpacker.Read(14); - int SurfaceHeightMinus1 = Unpacker.Read(14); + int surfaceWidthMinus1 = unpacker.Read(14); + int surfaceHeightMinus1 = unpacker.Read(14); - int GobBlockHeight = 1 << BlockLinearHeightLog2; + int gobBlockHeight = 1 << blockLinearHeightLog2; - int SurfaceWidth = SurfaceWidthMinus1 + 1; - int SurfaceHeight = SurfaceHeightMinus1 + 1; + int surfaceWidth = surfaceWidthMinus1 + 1; + int surfaceHeight = surfaceHeightMinus1 + 1; - SurfaceOutputConfig OutputConfig = new SurfaceOutputConfig( - PixelFormat, - SurfaceWidth, - SurfaceHeight, - GobBlockHeight, - OutputSurfaceLumaAddress, - OutputSurfaceChromaUAddress, - OutputSurfaceChromaVAddress); + SurfaceOutputConfig outputConfig = new SurfaceOutputConfig( + pixelFormat, + surfaceWidth, + surfaceHeight, + gobBlockHeight, + _outputSurfaceLumaAddress, + _outputSurfaceChromaUAddress, + _outputSurfaceChromaVAddress); - Gpu.VideoDecoder.CopyPlanes(Vmm, OutputConfig); + _gpu.VideoDecoder.CopyPlanes(vmm, outputConfig); } - private void SetConfigStructOffset(NvGpuVmm Vmm, int[] Arguments) + private void SetConfigStructOffset(NvGpuVmm vmm, int[] arguments) { - ConfigStructAddress = GetAddress(Arguments); + _configStructAddress = GetAddress(arguments); } - private void SetOutputSurfaceLumaOffset(NvGpuVmm Vmm, int[] Arguments) + private void SetOutputSurfaceLumaOffset(NvGpuVmm vmm, int[] arguments) { - OutputSurfaceLumaAddress = GetAddress(Arguments); + _outputSurfaceLumaAddress = GetAddress(arguments); } - private void SetOutputSurfaceChromaUOffset(NvGpuVmm Vmm, int[] Arguments) + private void SetOutputSurfaceChromaUOffset(NvGpuVmm vmm, int[] arguments) { - OutputSurfaceChromaUAddress = GetAddress(Arguments); + _outputSurfaceChromaUAddress = GetAddress(arguments); } - private void SetOutputSurfaceChromaVOffset(NvGpuVmm Vmm, int[] Arguments) + private void SetOutputSurfaceChromaVOffset(NvGpuVmm vmm, int[] arguments) { - OutputSurfaceChromaVAddress = GetAddress(Arguments); + _outputSurfaceChromaVAddress = GetAddress(arguments); } - private static long GetAddress(int[] Arguments) + private static long GetAddress(int[] arguments) { - return (long)(uint)Arguments[0] << 8; + return (long)(uint)arguments[0] << 8; } } } \ No newline at end of file diff --git a/Ryujinx.HLE/DeviceMemory.cs b/Ryujinx.HLE/DeviceMemory.cs index 310942b872..524adb8466 100644 --- a/Ryujinx.HLE/DeviceMemory.cs +++ b/Ryujinx.HLE/DeviceMemory.cs @@ -1,5 +1,5 @@ +using ChocolArm64.Memory; using System; -using System.Runtime.InteropServices; namespace Ryujinx.HLE { @@ -7,13 +7,13 @@ namespace Ryujinx.HLE { public const long RamSize = 4L * 1024 * 1024 * 1024; - public IntPtr RamPointer { get; private set; } + public IntPtr RamPointer { get; } private unsafe byte* _ramPtr; public unsafe DeviceMemory() { - RamPointer = Marshal.AllocHGlobal(new IntPtr(RamSize)); + RamPointer = MemoryManagement.AllocateWriteTracked(RamSize); _ramPtr = (byte*)RamPointer; } @@ -177,7 +177,7 @@ namespace Ryujinx.HLE protected virtual void Dispose(bool disposing) { - Marshal.FreeHGlobal(RamPointer); + MemoryManagement.Free(RamPointer); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs b/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs index d5ea3bbfa1..893ce26e7b 100644 --- a/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs +++ b/Ryujinx.HLE/Exceptions/ServiceNotImplementedException.cs @@ -76,60 +76,70 @@ namespace Ryujinx.HLE.Exceptions } } + sb.AppendLine("Guest Stack Trace:"); + sb.AppendLine(Context.Thread.GetGuestStackTrace()); + // Print buffer information - sb.AppendLine("Buffer Information"); - - if (Request.PtrBuff.Count > 0) + if (Request.PtrBuff.Count > 0 || + Request.SendBuff.Count > 0 || + Request.ReceiveBuff.Count > 0 || + Request.ExchangeBuff.Count > 0 || + Request.RecvListBuff.Count > 0) { - sb.AppendLine("\tPtrBuff:"); + sb.AppendLine("Buffer Information:"); - foreach (var buff in Request.PtrBuff) + if (Request.PtrBuff.Count > 0) { - sb.AppendLine($"\t[{buff.Index}] Position: 0x{buff.Position:x16} Size: 0x{buff.Size:x16}"); + sb.AppendLine("\tPtrBuff:"); + + foreach (var buff in Request.PtrBuff) + { + sb.AppendLine($"\t[{buff.Index}] Position: 0x{buff.Position:x16} Size: 0x{buff.Size:x16}"); + } } - } - if (Request.SendBuff.Count > 0) - { - sb.AppendLine("\tSendBuff:"); - - foreach (var buff in Request.SendBuff) + if (Request.SendBuff.Count > 0) { - sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}"); + sb.AppendLine("\tSendBuff:"); + + foreach (var buff in Request.SendBuff) + { + sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}"); + } } - } - if (Request.ReceiveBuff.Count > 0) - { - sb.AppendLine("\tReceiveBuff:"); - - foreach (var buff in Request.ReceiveBuff) + if (Request.ReceiveBuff.Count > 0) { - sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}"); + sb.AppendLine("\tReceiveBuff:"); + + foreach (var buff in Request.ReceiveBuff) + { + sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}"); + } } - } - if (Request.ExchangeBuff.Count > 0) - { - sb.AppendLine("\tExchangeBuff:"); - - foreach (var buff in Request.ExchangeBuff) + if (Request.ExchangeBuff.Count > 0) { - sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}"); + sb.AppendLine("\tExchangeBuff:"); + + foreach (var buff in Request.ExchangeBuff) + { + sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16} Flags: {buff.Flags}"); + } } - } - if (Request.RecvListBuff.Count > 0) - { - sb.AppendLine("\tRecvListBuff:"); - - foreach (var buff in Request.RecvListBuff) + if (Request.RecvListBuff.Count > 0) { - sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16}"); - } - } + sb.AppendLine("\tRecvListBuff:"); - sb.AppendLine(); + foreach (var buff in Request.RecvListBuff) + { + sb.AppendLine($"\tPosition: 0x{buff.Position:x16} Size: 0x{buff.Size:x16}"); + } + } + + sb.AppendLine(); + } sb.AppendLine("Raw Request Data:"); sb.Append(HexUtils.HexTable(Request.RawData)); diff --git a/Ryujinx.HLE/FileSystem/Content/LocationHelper.cs b/Ryujinx.HLE/FileSystem/Content/LocationHelper.cs index df3f5ad655..c522b053ba 100644 --- a/Ryujinx.HLE/FileSystem/Content/LocationHelper.cs +++ b/Ryujinx.HLE/FileSystem/Content/LocationHelper.cs @@ -14,9 +14,9 @@ namespace Ryujinx.HLE.FileSystem.Content switch (switchContentPath) { case ContentPath.SystemContent: - return Path.Combine(fileSystem.GetBasePath(), SystemNandPath, "Contents"); + return Path.Combine(basePath, SystemNandPath, "Contents"); case ContentPath.UserContent: - return Path.Combine(fileSystem.GetBasePath(), UserNandPath, "Contents"); + return Path.Combine(basePath, UserNandPath, "Contents"); case ContentPath.SdCardContent: return Path.Combine(fileSystem.GetSdCardPath(), "Nintendo", "Contents"); case ContentPath.System: diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index b5ce555a16..e152846e42 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -30,6 +30,7 @@ namespace Ryujinx.HLE.HOS internal const int HidSize = 0x40000; internal const int FontSize = 0x1100000; + internal const int IirsSize = 0x8000; private const int MemoryBlockAllocatorSize = 0x2710; @@ -81,6 +82,7 @@ namespace Ryujinx.HLE.HOS internal KSharedMemory HidSharedMem { get; private set; } internal KSharedMemory FontSharedMem { get; private set; } + internal KSharedMemory IirsSharedMem { get; private set; } internal SharedFontManager Font { get; private set; } @@ -151,17 +153,21 @@ namespace Ryujinx.HLE.HOS ulong hidPa = region.Address; ulong fontPa = region.Address + HidSize; + ulong iirsPa = region.Address + HidSize + FontSize; HidBaseAddress = (long)(hidPa - DramMemoryMap.DramBase); KPageList hidPageList = new KPageList(); KPageList fontPageList = new KPageList(); + KPageList iirsPageList = new KPageList(); hidPageList .AddRange(hidPa, HidSize / KMemoryManager.PageSize); fontPageList.AddRange(fontPa, FontSize / KMemoryManager.PageSize); + iirsPageList.AddRange(iirsPa, IirsSize / KMemoryManager.PageSize); HidSharedMem = new KSharedMemory(this, hidPageList, 0, 0, MemoryPermission.Read); FontSharedMem = new KSharedMemory(this, fontPageList, 0, 0, MemoryPermission.Read); + IirsSharedMem = new KSharedMemory(this, iirsPageList, 0, 0, MemoryPermission.Read); AppletState = new AppletStateMgr(this); diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs index 1eba4b4124..9f087a1a04 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs @@ -2,6 +2,7 @@ using ChocolArm64.Memory; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Ipc; using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.HLE.HOS.Kernel.Threading; using System; using System.IO; @@ -13,6 +14,7 @@ namespace Ryujinx.HLE.HOS.Ipc Switch device, KProcess process, MemoryManager memory, + KThread thread, KClientSession session, IpcMessage request, long cmdPtr) @@ -36,6 +38,7 @@ namespace Ryujinx.HLE.HOS.Ipc device, process, memory, + thread, session, request, response, diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs b/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs index 2b7591406f..0fcb31483a 100644 --- a/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs +++ b/Ryujinx.HLE/HOS/Kernel/Common/KernelTransfer.cs @@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common if (currentProcess.CpuMemory.IsMapped((long)address) && currentProcess.CpuMemory.IsMapped((long)address + 3)) { - currentProcess.CpuMemory.WriteInt32ToSharedAddr((long)address, value); + currentProcess.CpuMemory.WriteInt32((long)address, value); return true; } diff --git a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs index 0268de7d07..d31f95b43e 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs @@ -4,6 +4,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Diagnostics.Demangler; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.Loaders.Elf; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -41,14 +42,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _images = new List(); } - public void PrintGuestStackTrace(CpuThreadState threadState) + public string GetGuestStackTrace(CpuThreadState threadState) { EnsureLoaded(); StringBuilder trace = new StringBuilder(); - trace.AppendLine("Guest stack trace:"); - void AppendTrace(long address) { Image image = GetImage(address, out int imageIndex); @@ -68,22 +67,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Process string imageName = GetGuessedNsoNameFromIndex(imageIndex); - string imageNameAndOffset = $"[{_owner.Name}] {imageName}:0x{offset:x8}"; - - trace.AppendLine($" {imageNameAndOffset} {subName}"); + trace.AppendLine($" {imageName}:0x{offset:x8} {subName}"); } else { - trace.AppendLine($" [{_owner.Name}] ??? {subName}"); + trace.AppendLine($" ??? {subName}"); } } //TODO: ARM32. long framePointer = (long)threadState.X29; + trace.AppendLine($"Process: {_owner.Name}, PID: {_owner.Pid}"); + while (framePointer != 0) { - if ((framePointer & 7) != 0 || + if ((framePointer & 7) != 0 || !_owner.CpuMemory.IsMapped(framePointer) || !_owner.CpuMemory.IsMapped(framePointer + 8)) { @@ -97,7 +96,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process framePointer = _owner.CpuMemory.ReadInt64(framePointer); } - Logger.PrintInfo(LogClass.Cpu, trace.ToString()); + return trace.ToString(); } private bool TryGetSubName(Image image, long address, out string name) diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 338e5543d7..909f6333a9 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -80,12 +80,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public bool IsPaused { get; private set; } - public Translator Translator { get; private set; } - public MemoryManager CpuMemory { get; private set; } + public Translator Translator { get; private set; } + private SvcHandler _svcHandler; + private Horizon _system; + public HleProcessDebugger Debugger { get; private set; } public KProcess(Horizon system) : base(system) @@ -93,14 +95,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _processLock = new object(); _threadingLock = new object(); - CpuMemory = new MemoryManager(system.Device.Memory.RamPointer); - - CpuMemory.InvalidAccess += InvalidAccessHandler; + _system = system; AddressArbiter = new KAddressArbiter(system); - MemoryManager = new KMemoryManager(system, CpuMemory); - _fullTlsPages = new SortedDictionary(); _freeTlsPages = new SortedDictionary(); @@ -110,10 +108,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process _threads = new LinkedList(); - Translator = new Translator(CpuMemory); - - Translator.CpuTrace += CpuTraceHandler; - _svcHandler = new SvcHandler(system.Device, this); Debugger = new HleProcessDebugger(this); @@ -131,6 +125,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process AddressSpaceType addrSpaceType = (AddressSpaceType)((creationInfo.MmuFlags >> 1) & 7); + InitializeMemoryManager(addrSpaceType, memRegion); + bool aslrEnabled = ((creationInfo.MmuFlags >> 5) & 1) != 0; ulong codeAddress = creationInfo.CodeAddress; @@ -238,6 +234,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process AddressSpaceType addrSpaceType = (AddressSpaceType)((creationInfo.MmuFlags >> 1) & 7); + InitializeMemoryManager(addrSpaceType, memRegion); + bool aslrEnabled = ((creationInfo.MmuFlags >> 5) & 1) != 0; ulong codeAddress = creationInfo.CodeAddress; @@ -405,7 +403,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process case AddressSpaceType.Addr36Bits: case AddressSpaceType.Addr39Bits: _memoryUsageCapacity = MemoryManager.HeapRegionEnd - - MemoryManager.HeapRegionStart; + MemoryManager.HeapRegionStart; break; case AddressSpaceType.Addr32BitsNoMap: @@ -1010,9 +1008,29 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } } - private void InvalidAccessHandler(object sender, MemoryAccessEventArgs e) + private void InitializeMemoryManager(AddressSpaceType addrSpaceType, MemoryRegion memRegion) { - PrintCurrentThreadStackTrace(); + int addrSpaceBits; + + switch (addrSpaceType) + { + case AddressSpaceType.Addr32Bits: addrSpaceBits = 32; break; + case AddressSpaceType.Addr36Bits: addrSpaceBits = 36; break; + case AddressSpaceType.Addr32BitsNoMap: addrSpaceBits = 32; break; + case AddressSpaceType.Addr39Bits: addrSpaceBits = 39; break; + + default: throw new ArgumentException(nameof(addrSpaceType)); + } + + bool useFlatPageTable = memRegion == MemoryRegion.Application; + + CpuMemory = new MemoryManager(_system.Device.Memory.RamPointer, addrSpaceBits, useFlatPageTable); + + MemoryManager = new KMemoryManager(_system, CpuMemory); + + Translator = new Translator(CpuMemory); + + Translator.CpuTrace += CpuTraceHandler; } public void PrintCurrentThreadStackTrace() diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs index 071b3c2019..cf881a7932 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcHandler.cs @@ -1,5 +1,4 @@ using ChocolArm64.Events; -using ChocolArm64.Memory; using ChocolArm64.State; using Ryujinx.HLE.HOS.Kernel.Process; using System; @@ -11,14 +10,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall private Switch _device; private KProcess _process; private Horizon _system; - private MemoryManager _memory; public SvcHandler(Switch device, KProcess process) { _device = device; _process = process; _system = device.System; - _memory = process.CpuMemory; } public void SvcCall(object sender, InstExceptionEventArgs e) diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs index 5493941894..eb7595c0a2 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcIpc.cs @@ -93,7 +93,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall private KernelResult SendSyncRequest(ulong messagePtr, ulong size, int handle) { - byte[] messageData = _memory.ReadBytes((long)messagePtr, (long)size); + byte[] messageData = _process.CpuMemory.ReadBytes((long)messagePtr, (long)size); KClientSession clientSession = _process.HandleTable.GetObject(handle); @@ -142,7 +142,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall ipcMessage.Thread.ObjSyncResult = IpcHandler.IpcCall( _device, _process, - _memory, + _process.CpuMemory, + ipcMessage.Thread, ipcMessage.Session, ipcMessage.Message, ipcMessage.MessagePtr); diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs index 6f8180c507..f794d13073 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcMemory.cs @@ -62,11 +62,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall attributeMask, attributeValue); - if (result == KernelResult.Success) - { - _memory.StopObservingRegion((long)position, (long)size); - } - return result; } @@ -157,14 +152,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { KMemoryInfo blkInfo = _process.MemoryManager.QueryMemory(position); - _memory.WriteUInt64((long)infoPtr + 0x00, blkInfo.Address); - _memory.WriteUInt64((long)infoPtr + 0x08, blkInfo.Size); - _memory.WriteInt32 ((long)infoPtr + 0x10, (int)blkInfo.State & 0xff); - _memory.WriteInt32 ((long)infoPtr + 0x14, (int)blkInfo.Attribute); - _memory.WriteInt32 ((long)infoPtr + 0x18, (int)blkInfo.Permission); - _memory.WriteInt32 ((long)infoPtr + 0x1c, blkInfo.IpcRefCount); - _memory.WriteInt32 ((long)infoPtr + 0x20, blkInfo.DeviceRefCount); - _memory.WriteInt32 ((long)infoPtr + 0x24, 0); + _process.CpuMemory.WriteUInt64((long)infoPtr + 0x00, blkInfo.Address); + _process.CpuMemory.WriteUInt64((long)infoPtr + 0x08, blkInfo.Size); + _process.CpuMemory.WriteInt32 ((long)infoPtr + 0x10, (int)blkInfo.State & 0xff); + _process.CpuMemory.WriteInt32 ((long)infoPtr + 0x14, (int)blkInfo.Attribute); + _process.CpuMemory.WriteInt32 ((long)infoPtr + 0x18, (int)blkInfo.Permission); + _process.CpuMemory.WriteInt32 ((long)infoPtr + 0x1c, blkInfo.IpcRefCount); + _process.CpuMemory.WriteInt32 ((long)infoPtr + 0x20, blkInfo.DeviceRefCount); + _process.CpuMemory.WriteInt32 ((long)infoPtr + 0x24, 0); return KernelResult.Success; } diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs index be136ff0a5..efc10512ab 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcSystem.cs @@ -201,7 +201,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall private void OutputDebugString(ulong strPtr, ulong size) { - string str = MemoryHelper.ReadAsciiString(_memory, (long)strPtr, (long)size); + string str = MemoryHelper.ReadAsciiString(_process.CpuMemory, (long)strPtr, (long)size); Logger.PrintWarning(LogClass.KernelSvc, str); } diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs index 64268ff23d..fa0b3a6c88 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThread.cs @@ -1,3 +1,4 @@ +using ChocolArm64.Memory; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; @@ -346,79 +347,81 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidThread; } - _memory.WriteUInt64((long)address + 0x0, thread.Context.ThreadState.X0); - _memory.WriteUInt64((long)address + 0x8, thread.Context.ThreadState.X1); - _memory.WriteUInt64((long)address + 0x10, thread.Context.ThreadState.X2); - _memory.WriteUInt64((long)address + 0x18, thread.Context.ThreadState.X3); - _memory.WriteUInt64((long)address + 0x20, thread.Context.ThreadState.X4); - _memory.WriteUInt64((long)address + 0x28, thread.Context.ThreadState.X5); - _memory.WriteUInt64((long)address + 0x30, thread.Context.ThreadState.X6); - _memory.WriteUInt64((long)address + 0x38, thread.Context.ThreadState.X7); - _memory.WriteUInt64((long)address + 0x40, thread.Context.ThreadState.X8); - _memory.WriteUInt64((long)address + 0x48, thread.Context.ThreadState.X9); - _memory.WriteUInt64((long)address + 0x50, thread.Context.ThreadState.X10); - _memory.WriteUInt64((long)address + 0x58, thread.Context.ThreadState.X11); - _memory.WriteUInt64((long)address + 0x60, thread.Context.ThreadState.X12); - _memory.WriteUInt64((long)address + 0x68, thread.Context.ThreadState.X13); - _memory.WriteUInt64((long)address + 0x70, thread.Context.ThreadState.X14); - _memory.WriteUInt64((long)address + 0x78, thread.Context.ThreadState.X15); - _memory.WriteUInt64((long)address + 0x80, thread.Context.ThreadState.X16); - _memory.WriteUInt64((long)address + 0x88, thread.Context.ThreadState.X17); - _memory.WriteUInt64((long)address + 0x90, thread.Context.ThreadState.X18); - _memory.WriteUInt64((long)address + 0x98, thread.Context.ThreadState.X19); - _memory.WriteUInt64((long)address + 0xa0, thread.Context.ThreadState.X20); - _memory.WriteUInt64((long)address + 0xa8, thread.Context.ThreadState.X21); - _memory.WriteUInt64((long)address + 0xb0, thread.Context.ThreadState.X22); - _memory.WriteUInt64((long)address + 0xb8, thread.Context.ThreadState.X23); - _memory.WriteUInt64((long)address + 0xc0, thread.Context.ThreadState.X24); - _memory.WriteUInt64((long)address + 0xc8, thread.Context.ThreadState.X25); - _memory.WriteUInt64((long)address + 0xd0, thread.Context.ThreadState.X26); - _memory.WriteUInt64((long)address + 0xd8, thread.Context.ThreadState.X27); - _memory.WriteUInt64((long)address + 0xe0, thread.Context.ThreadState.X28); - _memory.WriteUInt64((long)address + 0xe8, thread.Context.ThreadState.X29); - _memory.WriteUInt64((long)address + 0xf0, thread.Context.ThreadState.X30); - _memory.WriteUInt64((long)address + 0xf8, thread.Context.ThreadState.X31); + MemoryManager memory = currentProcess.CpuMemory; - _memory.WriteInt64((long)address + 0x100, thread.LastPc); + memory.WriteUInt64((long)address + 0x0, thread.Context.ThreadState.X0); + memory.WriteUInt64((long)address + 0x8, thread.Context.ThreadState.X1); + memory.WriteUInt64((long)address + 0x10, thread.Context.ThreadState.X2); + memory.WriteUInt64((long)address + 0x18, thread.Context.ThreadState.X3); + memory.WriteUInt64((long)address + 0x20, thread.Context.ThreadState.X4); + memory.WriteUInt64((long)address + 0x28, thread.Context.ThreadState.X5); + memory.WriteUInt64((long)address + 0x30, thread.Context.ThreadState.X6); + memory.WriteUInt64((long)address + 0x38, thread.Context.ThreadState.X7); + memory.WriteUInt64((long)address + 0x40, thread.Context.ThreadState.X8); + memory.WriteUInt64((long)address + 0x48, thread.Context.ThreadState.X9); + memory.WriteUInt64((long)address + 0x50, thread.Context.ThreadState.X10); + memory.WriteUInt64((long)address + 0x58, thread.Context.ThreadState.X11); + memory.WriteUInt64((long)address + 0x60, thread.Context.ThreadState.X12); + memory.WriteUInt64((long)address + 0x68, thread.Context.ThreadState.X13); + memory.WriteUInt64((long)address + 0x70, thread.Context.ThreadState.X14); + memory.WriteUInt64((long)address + 0x78, thread.Context.ThreadState.X15); + memory.WriteUInt64((long)address + 0x80, thread.Context.ThreadState.X16); + memory.WriteUInt64((long)address + 0x88, thread.Context.ThreadState.X17); + memory.WriteUInt64((long)address + 0x90, thread.Context.ThreadState.X18); + memory.WriteUInt64((long)address + 0x98, thread.Context.ThreadState.X19); + memory.WriteUInt64((long)address + 0xa0, thread.Context.ThreadState.X20); + memory.WriteUInt64((long)address + 0xa8, thread.Context.ThreadState.X21); + memory.WriteUInt64((long)address + 0xb0, thread.Context.ThreadState.X22); + memory.WriteUInt64((long)address + 0xb8, thread.Context.ThreadState.X23); + memory.WriteUInt64((long)address + 0xc0, thread.Context.ThreadState.X24); + memory.WriteUInt64((long)address + 0xc8, thread.Context.ThreadState.X25); + memory.WriteUInt64((long)address + 0xd0, thread.Context.ThreadState.X26); + memory.WriteUInt64((long)address + 0xd8, thread.Context.ThreadState.X27); + memory.WriteUInt64((long)address + 0xe0, thread.Context.ThreadState.X28); + memory.WriteUInt64((long)address + 0xe8, thread.Context.ThreadState.X29); + memory.WriteUInt64((long)address + 0xf0, thread.Context.ThreadState.X30); + memory.WriteUInt64((long)address + 0xf8, thread.Context.ThreadState.X31); - _memory.WriteUInt64((long)address + 0x108, (ulong)thread.Context.ThreadState.Psr); + memory.WriteInt64((long)address + 0x100, thread.LastPc); - _memory.WriteVector128((long)address + 0x110, thread.Context.ThreadState.V0); - _memory.WriteVector128((long)address + 0x120, thread.Context.ThreadState.V1); - _memory.WriteVector128((long)address + 0x130, thread.Context.ThreadState.V2); - _memory.WriteVector128((long)address + 0x140, thread.Context.ThreadState.V3); - _memory.WriteVector128((long)address + 0x150, thread.Context.ThreadState.V4); - _memory.WriteVector128((long)address + 0x160, thread.Context.ThreadState.V5); - _memory.WriteVector128((long)address + 0x170, thread.Context.ThreadState.V6); - _memory.WriteVector128((long)address + 0x180, thread.Context.ThreadState.V7); - _memory.WriteVector128((long)address + 0x190, thread.Context.ThreadState.V8); - _memory.WriteVector128((long)address + 0x1a0, thread.Context.ThreadState.V9); - _memory.WriteVector128((long)address + 0x1b0, thread.Context.ThreadState.V10); - _memory.WriteVector128((long)address + 0x1c0, thread.Context.ThreadState.V11); - _memory.WriteVector128((long)address + 0x1d0, thread.Context.ThreadState.V12); - _memory.WriteVector128((long)address + 0x1e0, thread.Context.ThreadState.V13); - _memory.WriteVector128((long)address + 0x1f0, thread.Context.ThreadState.V14); - _memory.WriteVector128((long)address + 0x200, thread.Context.ThreadState.V15); - _memory.WriteVector128((long)address + 0x210, thread.Context.ThreadState.V16); - _memory.WriteVector128((long)address + 0x220, thread.Context.ThreadState.V17); - _memory.WriteVector128((long)address + 0x230, thread.Context.ThreadState.V18); - _memory.WriteVector128((long)address + 0x240, thread.Context.ThreadState.V19); - _memory.WriteVector128((long)address + 0x250, thread.Context.ThreadState.V20); - _memory.WriteVector128((long)address + 0x260, thread.Context.ThreadState.V21); - _memory.WriteVector128((long)address + 0x270, thread.Context.ThreadState.V22); - _memory.WriteVector128((long)address + 0x280, thread.Context.ThreadState.V23); - _memory.WriteVector128((long)address + 0x290, thread.Context.ThreadState.V24); - _memory.WriteVector128((long)address + 0x2a0, thread.Context.ThreadState.V25); - _memory.WriteVector128((long)address + 0x2b0, thread.Context.ThreadState.V26); - _memory.WriteVector128((long)address + 0x2c0, thread.Context.ThreadState.V27); - _memory.WriteVector128((long)address + 0x2d0, thread.Context.ThreadState.V28); - _memory.WriteVector128((long)address + 0x2e0, thread.Context.ThreadState.V29); - _memory.WriteVector128((long)address + 0x2f0, thread.Context.ThreadState.V30); - _memory.WriteVector128((long)address + 0x300, thread.Context.ThreadState.V31); + memory.WriteUInt64((long)address + 0x108, (ulong)thread.Context.ThreadState.Psr); - _memory.WriteInt32((long)address + 0x310, thread.Context.ThreadState.Fpcr); - _memory.WriteInt32((long)address + 0x314, thread.Context.ThreadState.Fpsr); - _memory.WriteInt64((long)address + 0x318, thread.Context.ThreadState.Tpidr); + memory.WriteVector128((long)address + 0x110, thread.Context.ThreadState.V0); + memory.WriteVector128((long)address + 0x120, thread.Context.ThreadState.V1); + memory.WriteVector128((long)address + 0x130, thread.Context.ThreadState.V2); + memory.WriteVector128((long)address + 0x140, thread.Context.ThreadState.V3); + memory.WriteVector128((long)address + 0x150, thread.Context.ThreadState.V4); + memory.WriteVector128((long)address + 0x160, thread.Context.ThreadState.V5); + memory.WriteVector128((long)address + 0x170, thread.Context.ThreadState.V6); + memory.WriteVector128((long)address + 0x180, thread.Context.ThreadState.V7); + memory.WriteVector128((long)address + 0x190, thread.Context.ThreadState.V8); + memory.WriteVector128((long)address + 0x1a0, thread.Context.ThreadState.V9); + memory.WriteVector128((long)address + 0x1b0, thread.Context.ThreadState.V10); + memory.WriteVector128((long)address + 0x1c0, thread.Context.ThreadState.V11); + memory.WriteVector128((long)address + 0x1d0, thread.Context.ThreadState.V12); + memory.WriteVector128((long)address + 0x1e0, thread.Context.ThreadState.V13); + memory.WriteVector128((long)address + 0x1f0, thread.Context.ThreadState.V14); + memory.WriteVector128((long)address + 0x200, thread.Context.ThreadState.V15); + memory.WriteVector128((long)address + 0x210, thread.Context.ThreadState.V16); + memory.WriteVector128((long)address + 0x220, thread.Context.ThreadState.V17); + memory.WriteVector128((long)address + 0x230, thread.Context.ThreadState.V18); + memory.WriteVector128((long)address + 0x240, thread.Context.ThreadState.V19); + memory.WriteVector128((long)address + 0x250, thread.Context.ThreadState.V20); + memory.WriteVector128((long)address + 0x260, thread.Context.ThreadState.V21); + memory.WriteVector128((long)address + 0x270, thread.Context.ThreadState.V22); + memory.WriteVector128((long)address + 0x280, thread.Context.ThreadState.V23); + memory.WriteVector128((long)address + 0x290, thread.Context.ThreadState.V24); + memory.WriteVector128((long)address + 0x2a0, thread.Context.ThreadState.V25); + memory.WriteVector128((long)address + 0x2b0, thread.Context.ThreadState.V26); + memory.WriteVector128((long)address + 0x2c0, thread.Context.ThreadState.V27); + memory.WriteVector128((long)address + 0x2d0, thread.Context.ThreadState.V28); + memory.WriteVector128((long)address + 0x2e0, thread.Context.ThreadState.V29); + memory.WriteVector128((long)address + 0x2f0, thread.Context.ThreadState.V30); + memory.WriteVector128((long)address + 0x300, thread.Context.ThreadState.V31); + + memory.WriteInt32((long)address + 0x310, thread.Context.ThreadState.Fpcr); + memory.WriteInt32((long)address + 0x314, thread.Context.ThreadState.Fpsr); + memory.WriteInt64((long)address + 0x318, thread.Context.ThreadState.Tpidr); return KernelResult.Success; } diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs index ecda9e2d00..6e5b478251 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcThreadSync.cs @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall for (int index = 0; index < handlesCount; index++) { - int handle = _memory.ReadInt32((long)handlesPtr + index * 4); + int handle = _process.CpuMemory.ReadInt32((long)handlesPtr + index * 4); KSynchronizationObject syncObj = _process.HandleTable.GetObject(handle); diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/HleScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/HleScheduler.cs index 835c2a2f83..d5dbb4d8e4 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/HleScheduler.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/HleScheduler.cs @@ -92,8 +92,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (coreContext.CurrentThread != null) { - coreContext.CurrentThread.ClearExclusive(); - CoreManager.Set(coreContext.CurrentThread.Context.Work); coreContext.CurrentThread.Context.Execute(); diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs index faeea5c54b..b11df61ed9 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs @@ -228,43 +228,31 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KProcess currentProcess = _system.Scheduler.GetCurrentProcess(); - currentProcess.CpuMemory.SetExclusive(0, (long)address); + int mutexValue, newMutexValue; - if (!KernelTransfer.UserToKernelInt32(_system, address, out int mutexValue)) + do { - //Invalid address. - currentProcess.CpuMemory.ClearExclusive(0); - - requester.SignaledObj = null; - requester.ObjSyncResult = KernelResult.InvalidMemState; - - return null; - } - - while (true) - { - if (currentProcess.CpuMemory.TestExclusive(0, (long)address)) + if (!KernelTransfer.UserToKernelInt32(_system, address, out mutexValue)) { - if (mutexValue != 0) - { - //Update value to indicate there is a mutex waiter now. - currentProcess.CpuMemory.WriteInt32((long)address, mutexValue | HasListenersMask); - } - else - { - //No thread owning the mutex, assign to requesting thread. - currentProcess.CpuMemory.WriteInt32((long)address, requester.ThreadHandleForUserMutex); - } + //Invalid address. + requester.SignaledObj = null; + requester.ObjSyncResult = KernelResult.InvalidMemState; - currentProcess.CpuMemory.ClearExclusiveForStore(0); - - break; + return null; } - currentProcess.CpuMemory.SetExclusive(0, (long)address); - - mutexValue = currentProcess.CpuMemory.ReadInt32((long)address); + if (mutexValue != 0) + { + //Update value to indicate there is a mutex waiter now. + newMutexValue = mutexValue | HasListenersMask; + } + else + { + //No thread owning the mutex, assign to requesting thread. + newMutexValue = requester.ThreadHandleForUserMutex; + } } + while (!currentProcess.CpuMemory.AtomicCompareExchangeInt32((long)address, mutexValue, newMutexValue)); if (mutexValue == 0) { @@ -392,9 +380,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KProcess currentProcess = _system.Scheduler.GetCurrentProcess(); - //If ShouldDecrement is true, do atomic decrement of the value at Address. - currentProcess.CpuMemory.SetExclusive(0, (long)address); - if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue)) { _system.CriticalSection.Leave(); @@ -404,25 +389,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading if (shouldDecrement) { - while (currentValue < value) - { - if (currentProcess.CpuMemory.TestExclusive(0, (long)address)) - { - currentProcess.CpuMemory.WriteInt32((long)address, currentValue - 1); - - currentProcess.CpuMemory.ClearExclusiveForStore(0); - - break; - } - - currentProcess.CpuMemory.SetExclusive(0, (long)address); - - currentValue = currentProcess.CpuMemory.ReadInt32((long)address); - } + currentValue = currentProcess.CpuMemory.AtomicDecrementInt32((long)address) + 1; } - currentProcess.CpuMemory.ClearExclusive(0); - if (currentValue < value) { if (timeout == 0) @@ -511,39 +480,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KProcess currentProcess = _system.Scheduler.GetCurrentProcess(); - currentProcess.CpuMemory.SetExclusive(0, (long)address); + int currentValue; - if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue)) + do { - _system.CriticalSection.Leave(); - - return KernelResult.InvalidMemState; - } - - while (currentValue == value) - { - if (currentProcess.CpuMemory.TestExclusive(0, (long)address)) + if (!KernelTransfer.UserToKernelInt32(_system, address, out currentValue)) { - currentProcess.CpuMemory.WriteInt32((long)address, currentValue + 1); + _system.CriticalSection.Leave(); - currentProcess.CpuMemory.ClearExclusiveForStore(0); - - break; + return KernelResult.InvalidMemState; } - currentProcess.CpuMemory.SetExclusive(0, (long)address); + if (currentValue != value) + { + _system.CriticalSection.Leave(); - currentValue = currentProcess.CpuMemory.ReadInt32((long)address); - } - - currentProcess.CpuMemory.ClearExclusive(0); - - if (currentValue != value) - { - _system.CriticalSection.Leave(); - - return KernelResult.InvalidState; + return KernelResult.InvalidState; + } } + while (!currentProcess.CpuMemory.AtomicCompareExchangeInt32((long)address, currentValue, currentValue + 1)); WakeArbiterThreads(address, count); @@ -582,39 +537,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading KProcess currentProcess = _system.Scheduler.GetCurrentProcess(); - currentProcess.CpuMemory.SetExclusive(0, (long)address); + int currentValue; - if (!KernelTransfer.UserToKernelInt32(_system, address, out int currentValue)) + do { - _system.CriticalSection.Leave(); - - return KernelResult.InvalidMemState; - } - - while (currentValue == value) - { - if (currentProcess.CpuMemory.TestExclusive(0, (long)address)) + if (!KernelTransfer.UserToKernelInt32(_system, address, out currentValue)) { - currentProcess.CpuMemory.WriteInt32((long)address, currentValue + offset); + _system.CriticalSection.Leave(); - currentProcess.CpuMemory.ClearExclusiveForStore(0); - - break; + return KernelResult.InvalidMemState; } - currentProcess.CpuMemory.SetExclusive(0, (long)address); + if (currentValue != value) + { + _system.CriticalSection.Leave(); - currentValue = currentProcess.CpuMemory.ReadInt32((long)address); - } - - currentProcess.CpuMemory.ClearExclusive(0); - - if (currentValue != value) - { - _system.CriticalSection.Leave(); - - return KernelResult.InvalidState; + return KernelResult.InvalidState; + } } + while (!currentProcess.CpuMemory.AtomicCompareExchangeInt32((long)address, currentValue, currentValue + offset)); WakeArbiterThreads(address, count); diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KCoreContext.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KCoreContext.cs index 81cd88834f..9790717729 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KCoreContext.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KCoreContext.cs @@ -70,8 +70,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading CurrentThread.TotalTimeRunning += currentTime - CurrentThread.LastScheduledTime; CurrentThread.LastScheduledTime = currentTime; - CurrentThread.ClearExclusive(); - _coreManager.Set(CurrentThread.Context.Work); CurrentThread.Context.Execute(); diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 7eb27efc12..ebde34baf1 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -1,10 +1,12 @@ using ChocolArm64; using ChocolArm64.Memory; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; using System; using System.Collections.Generic; using System.Linq; +using System.Text; namespace Ryujinx.HLE.HOS.Kernel.Threading { @@ -1004,19 +1006,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading Context.ThreadState.X1 = (ulong)threadHandle; } - public void ClearExclusive() - { - Owner.CpuMemory.ClearExclusive(CurrentCore); - } - public void TimeUp() { ReleaseAndResume(); } + public string GetGuestStackTrace() + { + return Owner.Debugger.GetGuestStackTrace(Context.ThreadState); + } + public void PrintGuestStackTrace() { - Owner.Debugger.PrintGuestStackTrace(Context.ThreadState); + StringBuilder trace = new StringBuilder(); + + trace.AppendLine("Guest stack trace:"); + trace.AppendLine(GetGuestStackTrace()); + + Logger.PrintInfo(LogClass.Cpu, trace.ToString()); } private void ThreadFinishedHandler(object sender, EventArgs e) diff --git a/Ryujinx.HLE/HOS/ServiceCtx.cs b/Ryujinx.HLE/HOS/ServiceCtx.cs index af42d41762..99b2d5afe6 100644 --- a/Ryujinx.HLE/HOS/ServiceCtx.cs +++ b/Ryujinx.HLE/HOS/ServiceCtx.cs @@ -2,6 +2,7 @@ using ChocolArm64.Memory; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Ipc; using Ryujinx.HLE.HOS.Kernel.Process; +using Ryujinx.HLE.HOS.Kernel.Threading; using System.IO; namespace Ryujinx.HLE.HOS @@ -11,6 +12,7 @@ namespace Ryujinx.HLE.HOS public Switch Device { get; } public KProcess Process { get; } public MemoryManager Memory { get; } + public KThread Thread { get; } public KClientSession Session { get; } public IpcMessage Request { get; } public IpcMessage Response { get; } @@ -21,6 +23,7 @@ namespace Ryujinx.HLE.HOS Switch device, KProcess process, MemoryManager memory, + KThread thread, KClientSession session, IpcMessage request, IpcMessage response, @@ -30,6 +33,7 @@ namespace Ryujinx.HLE.HOS Device = device; Process = process; Memory = memory; + Thread = thread; Session = session; Request = request; Response = response; diff --git a/Ryujinx.HLE/HOS/Services/DummyService.cs b/Ryujinx.HLE/HOS/Services/DummyService.cs new file mode 100644 index 0000000000..28087bdf8b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/DummyService.cs @@ -0,0 +1,20 @@ +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services +{ + class DummyService : IpcService + { + private Dictionary _commands; + + public override IReadOnlyDictionary Commands => _commands; + + public string ServiceName { get; set; } + + public DummyService(string serviceName) + { + _commands = new Dictionary(); + ServiceName = serviceName; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs index d000635de3..85ab2cf65e 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IStorage.cs @@ -16,7 +16,8 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv { _commands = new Dictionary { - { 0, Read } + { 0, Read }, + { 4, GetSize } }; _baseStream = baseStream; @@ -51,5 +52,13 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return 0; } + + // GetSize() -> u64 size + public long GetSize(ServiceCtx context) + { + context.ResponseData.Write(_baseStream.Length); + + return 0; + } } } diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs index 7ae5934b0c..b93c842299 100644 --- a/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -90,19 +90,35 @@ namespace Ryujinx.HLE.HOS.Services long sfciMagic = context.RequestData.ReadInt64(); int commandId = (int)context.RequestData.ReadInt64(); - if (service.Commands.TryGetValue(commandId, out ServiceProcessRequest processRequest)) + bool serviceExists = service.Commands.TryGetValue(commandId, out ServiceProcessRequest processRequest); + + if (ServiceConfiguration.IgnoreMissingServices || serviceExists) { + long result = 0; + context.ResponseData.BaseStream.Seek(_isDomain ? 0x20 : 0x10, SeekOrigin.Begin); - Logger.PrintDebug(LogClass.KernelIpc, $"{service.GetType().Name}: {processRequest.Method.Name}"); + if (serviceExists) + { + Logger.PrintDebug(LogClass.KernelIpc, $"{service.GetType().Name}: {processRequest.Method.Name}"); - ProfileConfig profile = Profiles.ServiceCall; - profile.SessionGroup = service.GetType().Name; - profile.SessionItem = processRequest.Method.Name; + ProfileConfig profile = Profiles.ServiceCall; + profile.SessionGroup = service.GetType().Name; + profile.SessionItem = processRequest.Method.Name; - Profile.Begin(profile); - long result = processRequest(context); - Profile.End(profile); + Profile.Begin(profile); + result = processRequest(context); + Profile.End(profile); + } + else + { + string serviceName; + DummyService dummyService = service as DummyService; + + serviceName = (dummyService == null) ? service.GetType().FullName : dummyService.ServiceName; + + Logger.PrintWarning(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored"); + } if (_isDomain) { @@ -194,4 +210,4 @@ namespace Ryujinx.HLE.HOS.Services return _domainObjects.GetData(id); } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs b/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs index e6521c041e..500d5f1079 100644 --- a/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs +++ b/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs @@ -1,5 +1,9 @@ using Ryujinx.Common.Logging; +using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Memory; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Irs @@ -10,15 +14,19 @@ namespace Ryujinx.HLE.HOS.Services.Irs public override IReadOnlyDictionary Commands => _commands; - private bool _activated; + private KSharedMemory _irsSharedMem; - public IIrSensorServer() + public IIrSensorServer(KSharedMemory irsSharedMem) { _commands = new Dictionary { - { 302, ActivateIrsensor }, - { 303, DeactivateIrsensor } + { 302, ActivateIrsensor }, + { 303, DeactivateIrsensor }, + { 304, GetIrsensorSharedMemoryHandle }, + { 311, GetNpadIrCameraHandle } }; + + _irsSharedMem = irsSharedMem; } // ActivateIrsensor(nn::applet::AppletResourceUserId, pid) @@ -40,5 +48,60 @@ namespace Ryujinx.HLE.HOS.Services.Irs return 0; } + + // GetIrsensorSharedMemoryHandle(nn::applet::AppletResourceUserId, pid) -> handle + public long GetIrsensorSharedMemoryHandle(ServiceCtx context) + { + var handleTable = context.Process.HandleTable; + + if (handleTable.GenerateHandle(_irsSharedMem, out int handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + return 0; + } + + // GetNpadIrCameraHandle(u32) -> nn::irsensor::IrCameraHandle + public long GetNpadIrCameraHandle(ServiceCtx context) + { + uint npadId = context.RequestData.ReadUInt32(); + + if (npadId >= 8 && npadId != 16 && npadId != 32) + { + return ErrorCode.MakeError(ErrorModule.Hid, 0x2c5); + } + + if (((1 << (int)npadId) & 0x1000100FF) == 0) + { + return ErrorCode.MakeError(ErrorModule.Hid, 0x2c5); + } + + int npadTypeId = GetNpadTypeId(npadId); + + context.ResponseData.Write(npadTypeId); + + return 0; + } + + private int GetNpadTypeId(uint npadId) + { + switch(npadId) + { + case 0: return 0; + case 1: return 1; + case 2: return 2; + case 3: return 3; + case 4: return 4; + case 5: return 5; + case 6: return 6; + case 7: return 7; + case 32: return 8; + case 16: return 9; + default: throw new ArgumentOutOfRangeException(nameof(npadId)); + } + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs index 83a217a5d3..3bde7b8779 100644 --- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs +++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Acc; using Ryujinx.HLE.HOS.Services.Am; using Ryujinx.HLE.HOS.Services.Apm; @@ -30,6 +31,11 @@ using System; namespace Ryujinx.HLE.HOS.Services { + public static class ServiceConfiguration + { + public static bool IgnoreMissingServices { get; set; } + } + static class ServiceFactory { public static IpcService MakeService(Horizon system, string name) @@ -106,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Services return new IHidServer(system); case "irs": - return new IIrSensorServer(); + return new IIrSensorServer(system.IirsSharedMem); case "ldr:ro": return new IRoInterface(); @@ -209,6 +215,12 @@ namespace Ryujinx.HLE.HOS.Services return new IApplicationRootService(); } + if (ServiceConfiguration.IgnoreMissingServices) + { + Logger.PrintWarning(LogClass.Service, $"Missing service {name} ignored"); + return new DummyService(name); + } + throw new NotImplementedException(name); } } diff --git a/Ryujinx.HLE/HOS/Services/Vi/ColorFormat.cs b/Ryujinx.HLE/HOS/Services/Vi/ColorFormat.cs index d4fc6adc10..67848b4347 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/ColorFormat.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/ColorFormat.cs @@ -4,7 +4,6 @@ { public const int Swizzle = 16; public const int DataType = 14; - // FIXME: check this one public const int Space = 32; public const int Component = 8; } @@ -102,265 +101,265 @@ enum ColorSpace : ulong { - NonColor = 0x0L << ColorShift.Space, - LinearRGBA = 0x1L << ColorShift.Space, - SRGB = 0x2L << ColorShift.Space, + NonColor = 0x0L << ColorShift.Space, + LinearRGBA = 0x1L << ColorShift.Space, + SRGB = 0x2L << ColorShift.Space, - Unknown3 = 0x3L << ColorShift.Space, - Unknown4 = 0x4L << ColorShift.Space, - Unknown5 = 0x5L << ColorShift.Space, - Unknown6 = 0x6L << ColorShift.Space, - Unknown7 = 0x7L << ColorShift.Space, - Unknown8 = 0x8L << ColorShift.Space, + RGB709 = 0x3L << ColorShift.Space, + LinearRGB709 = 0x4L << ColorShift.Space, - ColorIndex = 0x9L << ColorShift.Space, - YCbCr601 = 0xAL << ColorShift.Space, - YCbCr601_RR = 0xBL << ColorShift.Space, + LinearScRGB = 0x5L << ColorShift.Space, - UnknownC = 0xCL << ColorShift.Space, + RGB2020 = 0x6L << ColorShift.Space, + LinearRGB2020 = 0x7L << ColorShift.Space, + RGB2020_PQ = 0x8L << ColorShift.Space, - YCbCr709 = 0xDL << ColorShift.Space, + ColorIndex = 0x9L << ColorShift.Space, - UnknownE = 0xEL << ColorShift.Space, + YCbCr601 = 0xAL << ColorShift.Space, + YCbCr601_RR = 0xBL << ColorShift.Space, + YCbCr601_ER = 0xCL << ColorShift.Space, + YCbCr709 = 0xDL << ColorShift.Space, + YCbCr709_ER = 0xEL << ColorShift.Space, - BayerRGGB = 0x10L << ColorShift.Space, - BayerBGGR = 0x11L << ColorShift.Space, - BayerGRBG = 0x12L << ColorShift.Space, - BayerGBRG = 0x13L << ColorShift.Space, + BayerRGGB = 0x10L << ColorShift.Space, + BayerBGGR = 0x11L << ColorShift.Space, + BayerGRBG = 0x12L << ColorShift.Space, + BayerGBRG = 0x13L << ColorShift.Space, - Unknown14 = 0x14L << ColorShift.Space, + XYZ = 0x14L << ColorShift.Space, } enum ColorFormat : ulong { - NonColor8 = ColorSpace.NonColor | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, - NonColor16 = ColorSpace.NonColor | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Integer, - NonColor24 = ColorSpace.NonColor | ColorSwizzle.X000 | ColorComponent.X24 | ColorDataType.Integer, - NonColor32 = ColorSpace.NonColor | ColorSwizzle.X000 | ColorComponent.X32 | ColorDataType.Integer, - X4C4 = ColorSpace.NonColor | ColorSwizzle.Y000 | ColorComponent.Y4X4 | ColorDataType.Integer, - A4L4 = ColorSpace.LinearRGBA | ColorSwizzle.YYYX | ColorComponent.Y4X4 | ColorDataType.Integer, - A8L8 = ColorSpace.LinearRGBA | ColorSwizzle.YYYX | ColorComponent.Y8X8 | ColorDataType.Integer, - Float_A16L16 = ColorSpace.LinearRGBA | ColorSwizzle.YYYX | ColorComponent.X16Y16 | ColorDataType.Float, - A1B5G5R5 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X1Y5Z5W5 | ColorDataType.Integer, - A4B4G4R4 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X4Y4Z4W4 | ColorDataType.Integer, - A5B5G5R1 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X5Y5Z5W1 | ColorDataType.Integer, - A2B10G10R10 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - A8B8G8R8 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - A16B16G16R16 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - Float_A16B16G16R16 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Float, - A1R5G5B5 = ColorSpace.LinearRGBA | ColorSwizzle.YZWX | ColorComponent.X1Y5Z5W5 | ColorDataType.Integer, - A4R4G4B4 = ColorSpace.LinearRGBA | ColorSwizzle.YZWX | ColorComponent.X4Y4Z4W4 | ColorDataType.Integer, - A5R1G5B5 = ColorSpace.LinearRGBA | ColorSwizzle.YZWX | ColorComponent.X5Y1Z5W5 | ColorDataType.Integer, - A2R10G10B10 = ColorSpace.LinearRGBA | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - A8R8G8B8 = ColorSpace.LinearRGBA | ColorSwizzle.YZWX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - A1 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X1 | ColorDataType.Integer, - A2 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X2 | ColorDataType.Integer, - A4 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X4 | ColorDataType.Integer, - A8 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X8 | ColorDataType.Integer, - A16 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X16 | ColorDataType.Integer, - A32 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X32 | ColorDataType.Integer, - Float_A16 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X16 | ColorDataType.Float, - L4A4 = ColorSpace.LinearRGBA | ColorSwizzle.XXXY | ColorComponent.Y4X4 | ColorDataType.Integer, - L8A8 = ColorSpace.LinearRGBA | ColorSwizzle.XXXY | ColorComponent.Y8X8 | ColorDataType.Integer, - B4G4R4A4 = ColorSpace.LinearRGBA | ColorSwizzle.ZYXW | ColorComponent.X4Y4Z4W4 | ColorDataType.Integer, - B5G5R1A5 = ColorSpace.LinearRGBA | ColorSwizzle.ZYXW | ColorComponent.X5Y5Z1W5 | ColorDataType.Integer, - B5G5R5A1 = ColorSpace.LinearRGBA | ColorSwizzle.ZYXW | ColorComponent.X5Y5Z5W1 | ColorDataType.Integer, - B8G8R8A8 = ColorSpace.LinearRGBA | ColorSwizzle.ZYXW | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - B10G10R10A2 = ColorSpace.LinearRGBA | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - R1G5B5A5 = ColorSpace.LinearRGBA | ColorSwizzle.XYZW | ColorComponent.X1Y5Z5W5 | ColorDataType.Integer, - R4G4B4A4 = ColorSpace.LinearRGBA | ColorSwizzle.XYZW | ColorComponent.X4Y4Z4W4 | ColorDataType.Integer, - R5G5B5A1 = ColorSpace.LinearRGBA | ColorSwizzle.XYZW | ColorComponent.X5Y5Z5W1 | ColorDataType.Integer, - R8G8B8A8 = ColorSpace.LinearRGBA | ColorSwizzle.XYZW | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - R10G10B10A2 = ColorSpace.LinearRGBA | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - L1 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X1 | ColorDataType.Integer, - L2 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X2 | ColorDataType.Integer, - L4 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X4 | ColorDataType.Integer, - L8 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X8 | ColorDataType.Integer, - L16 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X16 | ColorDataType.Integer, - L32 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X32 | ColorDataType.Integer, - Float_L16 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X16 | ColorDataType.Float, - B5G6R5 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X5Y6Z5 | ColorDataType.Integer, - B6G5R5 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X6Y5Z5 | ColorDataType.Integer, - B5G5R5X1 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X5Y5Z5W1 | ColorDataType.Integer, - B8_G8_R8 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X8Y8Z8 | ColorDataType.Integer, - B8G8R8X8 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - Float_B10G11R11 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X11Y11Z10 | ColorDataType.Float, - X1B5G5R5 = ColorSpace.LinearRGBA | ColorSwizzle.WZY1 | ColorComponent.X1Y5Z5W5 | ColorDataType.Integer, - X8B8G8R8 = ColorSpace.LinearRGBA | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - X16B16G16R16 = ColorSpace.LinearRGBA | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - Float_X16B16G16R16 = ColorSpace.LinearRGBA | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Float, - R3G3B2 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.X3Y3Z2 | ColorDataType.Integer, - R5G5B6 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.Z5Y5X6 | ColorDataType.Integer, - R5G6B5 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.X5Y6Z5 | ColorDataType.Integer, - R5G5B5X1 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.X5Y5Z5W1 | ColorDataType.Integer, - R8_G8_B8 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.X8Y8Z8 | ColorDataType.Integer, - R8G8B8X8 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - X1R5G5B5 = ColorSpace.LinearRGBA | ColorSwizzle.YZW1 | ColorComponent.X1Y5Z5W5 | ColorDataType.Integer, - X8R8G8B8 = ColorSpace.LinearRGBA | ColorSwizzle.YZW1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - RG8 = ColorSpace.LinearRGBA | ColorSwizzle.XY01 | ColorComponent.Y8X8 | ColorDataType.Integer, - R16G16 = ColorSpace.LinearRGBA | ColorSwizzle.XY01 | ColorComponent.X16Y16 | ColorDataType.Integer, - Float_R16G16 = ColorSpace.LinearRGBA | ColorSwizzle.XY01 | ColorComponent.X16Y16 | ColorDataType.Float, - R8 = ColorSpace.LinearRGBA | ColorSwizzle.X001 | ColorComponent.X8 | ColorDataType.Integer, - R16 = ColorSpace.LinearRGBA | ColorSwizzle.X001 | ColorComponent.X16 | ColorDataType.Integer, - Float_R16 = ColorSpace.LinearRGBA | ColorSwizzle.X001 | ColorComponent.X16 | ColorDataType.Float, - A2B10G10R10_sRGB = ColorSpace.SRGB | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - A8B8G8R8_sRGB = ColorSpace.SRGB | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - A16B16G16R16_sRGB = ColorSpace.SRGB | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - A2R10G10B10_sRGB = ColorSpace.SRGB | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - B10G10R10A2_sRGB = ColorSpace.SRGB | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - R10G10B10A2_sRGB = ColorSpace.SRGB | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - X8B8G8R8_sRGB = ColorSpace.SRGB | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - X16B16G16R16_sRGB = ColorSpace.SRGB | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - A2B10G10R10_709 = ColorSpace.Unknown3 | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - A8B8G8R8_709 = ColorSpace.Unknown3 | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - A16B16G16R16_709 = ColorSpace.Unknown3 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - A2R10G10B10_709 = ColorSpace.Unknown3 | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - B10G10R10A2_709 = ColorSpace.Unknown3 | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - R10G10B10A2_709 = ColorSpace.Unknown3 | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - X8B8G8R8_709 = ColorSpace.Unknown3 | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - X16B16G16R16_709 = ColorSpace.Unknown3 | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - A2B10G10R10_709_Linear = ColorSpace.Unknown4 | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - A8B8G8R8_709_Linear = ColorSpace.Unknown4 | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - A16B16G16R16_709_Linear = ColorSpace.Unknown4 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - A2R10G10B10_709_Linear = ColorSpace.Unknown4 | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - B10G10R10A2_709_Linear = ColorSpace.Unknown4 | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - R10G10B10A2_709_Linear = ColorSpace.Unknown4 | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - X8B8G8R8_709_Linear = ColorSpace.Unknown4 | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - X16B16G16R16_709_Linear = ColorSpace.Unknown4 | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - Float_A16B16G16R16_scRGB_Linear = ColorSpace.Unknown5 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Float, - A2B10G10R10_2020 = ColorSpace.Unknown6 | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - A8B8G8R8_2020 = ColorSpace.Unknown6 | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - A16B16G16R16_2020 = ColorSpace.Unknown6 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - A2R10G10B10_2020 = ColorSpace.Unknown6 | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - B10G10R10A2_2020 = ColorSpace.Unknown6 | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - R10G10B10A2_2020 = ColorSpace.Unknown6 | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - X8B8G8R8_2020 = ColorSpace.Unknown6 | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - X16B16G16R16_2020 = ColorSpace.Unknown6 | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - A2B10G10R10_2020_Linear = ColorSpace.Unknown7 | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - A8B8G8R8_2020_Linear = ColorSpace.Unknown7 | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - A16B16G16R16_2020_Linear = ColorSpace.Unknown7 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - Float_A16B16G16R16_2020_Linear = ColorSpace.Unknown7 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Float, - A2R10G10B10_2020_Linear = ColorSpace.Unknown7 | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, - B10G10R10A2_2020_Linear = ColorSpace.Unknown7 | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - R10G10B10A2_2020_Linear = ColorSpace.Unknown7 | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, - X8B8G8R8_2020_Linear = ColorSpace.Unknown7 | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - X16B16G16R16_2020_Linear = ColorSpace.Unknown7 | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - Float_A16B16G16R16_2020_PQ = ColorSpace.Unknown8 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Float, - A4I4 = ColorSpace.ColorIndex | ColorSwizzle.X00X | ColorComponent.Y4X4 | ColorDataType.Integer, - A8I8 = ColorSpace.ColorIndex | ColorSwizzle.X00X | ColorComponent.Y8X8 | ColorDataType.Integer, - I4A4 = ColorSpace.ColorIndex | ColorSwizzle.X00Y | ColorComponent.Y4X4 | ColorDataType.Integer, - I8A8 = ColorSpace.ColorIndex | ColorSwizzle.X00Y | ColorComponent.Y8X8 | ColorDataType.Integer, - I1 = ColorSpace.ColorIndex | ColorSwizzle.X000 | ColorComponent.X1 | ColorDataType.Integer, - I2 = ColorSpace.ColorIndex | ColorSwizzle.X000 | ColorComponent.X2 | ColorDataType.Integer, - I4 = ColorSpace.ColorIndex | ColorSwizzle.X000 | ColorComponent.X4 | ColorDataType.Integer, - I8 = ColorSpace.ColorIndex | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, - A8Y8U8V8 = ColorSpace.YCbCr601 | ColorSwizzle.YZWX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - A16Y16U16V16 = ColorSpace.YCbCr601 | ColorSwizzle.YZWX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, - Y8U8V8A8 = ColorSpace.YCbCr601 | ColorSwizzle.XYZW | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, - V8_U8 = ColorSpace.YCbCr601 | ColorSwizzle._0YX0 | ColorComponent.X8Y8 | ColorDataType.Integer, - V8U8 = ColorSpace.YCbCr601 | ColorSwizzle._0YX0 | ColorComponent.Y8X8 | ColorDataType.Integer, - V10U10 = ColorSpace.YCbCr601 | ColorSwizzle._0ZY0 | ColorComponent.Y10X10 | ColorDataType.Integer, - V12U12 = ColorSpace.YCbCr601 | ColorSwizzle._0ZY0 | ColorComponent.Y12X12 | ColorDataType.Integer, - V8 = ColorSpace.YCbCr601 | ColorSwizzle._00X0 | ColorComponent.X8 | ColorDataType.Integer, - V10 = ColorSpace.YCbCr601 | ColorSwizzle._00X0 | ColorComponent.X10 | ColorDataType.Integer, - V12 = ColorSpace.YCbCr601 | ColorSwizzle._00X0 | ColorComponent.X12 | ColorDataType.Integer, - U8_V8 = ColorSpace.YCbCr601 | ColorSwizzle._0XY0 | ColorComponent.X8Y8 | ColorDataType.Integer, - U8V8 = ColorSpace.YCbCr601 | ColorSwizzle._0XY0 | ColorComponent.Y8X8 | ColorDataType.Integer, - U10V10 = ColorSpace.YCbCr601 | ColorSwizzle._0XZ0 | ColorComponent.Y10X10 | ColorDataType.Integer, - U12V12 = ColorSpace.YCbCr601 | ColorSwizzle._0XZ0 | ColorComponent.Y12X12 | ColorDataType.Integer, - U8 = ColorSpace.YCbCr601 | ColorSwizzle._0X00 | ColorComponent.X8 | ColorDataType.Integer, - U10 = ColorSpace.YCbCr601 | ColorSwizzle._0X00 | ColorComponent.X10 | ColorDataType.Integer, - U12 = ColorSpace.YCbCr601 | ColorSwizzle._0X00 | ColorComponent.X12 | ColorDataType.Integer, - Y8 = ColorSpace.YCbCr601 | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, - Y10 = ColorSpace.YCbCr601 | ColorSwizzle.X000 | ColorComponent.X10 | ColorDataType.Integer, - Y12 = ColorSpace.YCbCr601 | ColorSwizzle.X000 | ColorComponent.X12 | ColorDataType.Integer, - YVYU = ColorSpace.YCbCr601 | ColorSwizzle.XZY1 | ColorComponent.X8Y8X8Z8 | ColorDataType.Integer, - VYUY = ColorSpace.YCbCr601 | ColorSwizzle.XZY1 | ColorComponent.Y8X8Z8X8 | ColorDataType.Integer, - UYVY = ColorSpace.YCbCr601 | ColorSwizzle.XYZ1 | ColorComponent.Y8X8Z8X8 | ColorDataType.Integer, - YUYV = ColorSpace.YCbCr601 | ColorSwizzle.XYZ1 | ColorComponent.X8Y8X8Z8 | ColorDataType.Integer, - Y8_U8_V8 = ColorSpace.YCbCr601 | ColorSwizzle.XYZ1 | ColorComponent.X8Y8Z8 | ColorDataType.Integer, - V8_U8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._0YX0 | ColorComponent.X8Y8 | ColorDataType.Integer, - V8U8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._0YX0 | ColorComponent.Y8X8 | ColorDataType.Integer, - V8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._00X0 | ColorComponent.X8 | ColorDataType.Integer, - U8_V8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._0XY0 | ColorComponent.X8Y8 | ColorDataType.Integer, - U8V8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._0XY0 | ColorComponent.Y8X8 | ColorDataType.Integer, - U8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._0X00 | ColorComponent.X8 | ColorDataType.Integer, - Y8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, - V8_U8_ER = ColorSpace.UnknownC | ColorSwizzle._0YX0 | ColorComponent.X8Y8 | ColorDataType.Integer, - V8U8_ER = ColorSpace.UnknownC | ColorSwizzle._0YX0 | ColorComponent.Y8X8 | ColorDataType.Integer, - V8_ER = ColorSpace.UnknownC | ColorSwizzle._00X0 | ColorComponent.X8 | ColorDataType.Integer, - U8_V8_ER = ColorSpace.UnknownC | ColorSwizzle._0XY0 | ColorComponent.X8Y8 | ColorDataType.Integer, - U8V8_ER = ColorSpace.UnknownC | ColorSwizzle._0XY0 | ColorComponent.Y8X8 | ColorDataType.Integer, - U8_ER = ColorSpace.UnknownC | ColorSwizzle._0X00 | ColorComponent.X8 | ColorDataType.Integer, - Y8_ER = ColorSpace.UnknownC | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, - V8_U8_709 = ColorSpace.YCbCr709 | ColorSwizzle._0YX0 | ColorComponent.X8Y8 | ColorDataType.Integer, - V8U8_709 = ColorSpace.YCbCr709 | ColorSwizzle._0YX0 | ColorComponent.Y8X8 | ColorDataType.Integer, - V10U10_709 = ColorSpace.YCbCr709 | ColorSwizzle._0ZY0 | ColorComponent.Y10X10 | ColorDataType.Integer, - V12U12_709 = ColorSpace.YCbCr709 | ColorSwizzle._0ZY0 | ColorComponent.Y12X12 | ColorDataType.Integer, - V8_709 = ColorSpace.YCbCr709 | ColorSwizzle._00X0 | ColorComponent.X8 | ColorDataType.Integer, - V10_709 = ColorSpace.YCbCr709 | ColorSwizzle._00X0 | ColorComponent.X10 | ColorDataType.Integer, - V12_709 = ColorSpace.YCbCr709 | ColorSwizzle._00X0 | ColorComponent.X12 | ColorDataType.Integer, - U8_V8_709 = ColorSpace.YCbCr709 | ColorSwizzle._0XY0 | ColorComponent.X8Y8 | ColorDataType.Integer, - U8V8_709 = ColorSpace.YCbCr709 | ColorSwizzle._0XY0 | ColorComponent.Y8X8 | ColorDataType.Integer, - U10V10_709 = ColorSpace.YCbCr709 | ColorSwizzle._0XZ0 | ColorComponent.Y10X10 | ColorDataType.Integer, - U12V12_709 = ColorSpace.YCbCr709 | ColorSwizzle._0XZ0 | ColorComponent.Y12X12 | ColorDataType.Integer, - U8_709 = ColorSpace.YCbCr709 | ColorSwizzle._0X00 | ColorComponent.X8 | ColorDataType.Integer, - U10_709 = ColorSpace.YCbCr709 | ColorSwizzle._0X00 | ColorComponent.X10 | ColorDataType.Integer, - U12_709 = ColorSpace.YCbCr709 | ColorSwizzle._0X00 | ColorComponent.X12 | ColorDataType.Integer, - Y8_709 = ColorSpace.YCbCr709 | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, - Y10_709 = ColorSpace.YCbCr709 | ColorSwizzle.X000 | ColorComponent.X10 | ColorDataType.Integer, - Y12_709 = ColorSpace.YCbCr709 | ColorSwizzle.X000 | ColorComponent.X12 | ColorDataType.Integer, - V8_U8_709_ER = ColorSpace.UnknownE | ColorSwizzle._0YX0 | ColorComponent.X8Y8 | ColorDataType.Integer, - V8U8_709_ER = ColorSpace.UnknownE | ColorSwizzle._0YX0 | ColorComponent.Y8X8 | ColorDataType.Integer, - V10U10_709_ER = ColorSpace.UnknownE | ColorSwizzle._0ZY0 | ColorComponent.Y10X10 | ColorDataType.Integer, - V12U12_709_ER = ColorSpace.UnknownE | ColorSwizzle._0ZY0 | ColorComponent.Y12X12 | ColorDataType.Integer, - V8_709_ER = ColorSpace.UnknownE | ColorSwizzle._00X0 | ColorComponent.X8 | ColorDataType.Integer, - V10_709_ER = ColorSpace.UnknownE | ColorSwizzle._00X0 | ColorComponent.X10 | ColorDataType.Integer, - V12_709_ER = ColorSpace.UnknownE | ColorSwizzle._00X0 | ColorComponent.X12 | ColorDataType.Integer, - U8_V8_709_ER = ColorSpace.UnknownE | ColorSwizzle._0XY0 | ColorComponent.X8Y8 | ColorDataType.Integer, - U8V8_709_ER = ColorSpace.UnknownE | ColorSwizzle._0XY0 | ColorComponent.Y8X8 | ColorDataType.Integer, - U10V10_709_ER = ColorSpace.UnknownE | ColorSwizzle._0XZ0 | ColorComponent.Y10X10 | ColorDataType.Integer, - U12V12_709_ER = ColorSpace.UnknownE | ColorSwizzle._0XZ0 | ColorComponent.Y12X12 | ColorDataType.Integer, - U8_709_ER = ColorSpace.UnknownE | ColorSwizzle._0X00 | ColorComponent.X8 | ColorDataType.Integer, - U10_709_ER = ColorSpace.UnknownE | ColorSwizzle._0X00 | ColorComponent.X10 | ColorDataType.Integer, - U12_709_ER = ColorSpace.UnknownE | ColorSwizzle._0X00 | ColorComponent.X12 | ColorDataType.Integer, - Y8_709_ER = ColorSpace.UnknownE | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, - Y10_709_ER = ColorSpace.UnknownE | ColorSwizzle.X000 | ColorComponent.X10 | ColorDataType.Integer, - Y12_709_ER = ColorSpace.UnknownE | ColorSwizzle.X000 | ColorComponent.X12 | ColorDataType.Integer, - V10U10_2020 = ColorSpace.UnknownE | ColorSwizzle._0ZY0 | ColorComponent.Y10X10 | ColorDataType.Integer, - V12U12_2020 = ColorSpace.UnknownE | ColorSwizzle._0ZY0 | ColorComponent.Y12X12 | ColorDataType.Integer, - V10_2020 = ColorSpace.UnknownE | ColorSwizzle._00X0 | ColorComponent.X10 | ColorDataType.Integer, - V12_2020 = ColorSpace.UnknownE | ColorSwizzle._00X0 | ColorComponent.X12 | ColorDataType.Integer, - U10V10_2020 = ColorSpace.UnknownE | ColorSwizzle._0XZ0 | ColorComponent.Y10X10 | ColorDataType.Integer, - U12V12_2020 = ColorSpace.UnknownE | ColorSwizzle._0XZ0 | ColorComponent.Y12X12 | ColorDataType.Integer, - U10_2020 = ColorSpace.UnknownE | ColorSwizzle._0X00 | ColorComponent.X10 | ColorDataType.Integer, - U12_2020 = ColorSpace.UnknownE | ColorSwizzle._0X00 | ColorComponent.X12 | ColorDataType.Integer, - Y10_2020 = ColorSpace.UnknownE | ColorSwizzle.X000 | ColorComponent.X10 | ColorDataType.Integer, - Y12_2020 = ColorSpace.UnknownE | ColorSwizzle.X000 | ColorComponent.X12 | ColorDataType.Integer, - Bayer8RGGB = ColorSpace.BayerRGGB | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, - Bayer16RGGB = ColorSpace.BayerRGGB | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Integer, - BayerS16RGGB = ColorSpace.BayerRGGB | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Stencil, - X2Bayer14RGGB = ColorSpace.BayerRGGB | ColorSwizzle.Y000 | ColorComponent.Y2X14 | ColorDataType.Integer, - X4Bayer12RGGB = ColorSpace.BayerRGGB | ColorSwizzle.Y000 | ColorComponent.Y4X12 | ColorDataType.Integer, - X6Bayer10RGGB = ColorSpace.BayerRGGB | ColorSwizzle.Y000 | ColorComponent.Y6X10 | ColorDataType.Integer, - Bayer8BGGR = ColorSpace.BayerBGGR | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, - Bayer16BGGR = ColorSpace.BayerBGGR | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Integer, - BayerS16BGGR = ColorSpace.BayerBGGR | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Stencil, - X2Bayer14BGGR = ColorSpace.BayerBGGR | ColorSwizzle.Y000 | ColorComponent.Y2X14 | ColorDataType.Integer, - X4Bayer12BGGR = ColorSpace.BayerBGGR | ColorSwizzle.Y000 | ColorComponent.Y4X12 | ColorDataType.Integer, - X6Bayer10BGGR = ColorSpace.BayerBGGR | ColorSwizzle.Y000 | ColorComponent.Y6X10 | ColorDataType.Integer, - Bayer8GRBG = ColorSpace.BayerGRBG | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, - Bayer16GRBG = ColorSpace.BayerGRBG | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Integer, - BayerS16GRBG = ColorSpace.BayerGRBG | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Stencil, - X2Bayer14GRBG = ColorSpace.BayerGRBG | ColorSwizzle.Y000 | ColorComponent.Y2X14 | ColorDataType.Integer, - X4Bayer12GRBG = ColorSpace.BayerGRBG | ColorSwizzle.Y000 | ColorComponent.Y4X12 | ColorDataType.Integer, - X6Bayer10GRBG = ColorSpace.BayerGRBG | ColorSwizzle.Y000 | ColorComponent.Y6X10 | ColorDataType.Integer, - Bayer8GBRG = ColorSpace.BayerGBRG | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, - Bayer16GBRG = ColorSpace.BayerGBRG | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Integer, - BayerS16GBRG = ColorSpace.BayerGBRG | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Stencil, - X2Bayer14GBRG = ColorSpace.BayerGBRG | ColorSwizzle.Y000 | ColorComponent.Y2X14 | ColorDataType.Integer, - X4Bayer12GBRG = ColorSpace.BayerGBRG | ColorSwizzle.Y000 | ColorComponent.Y4X12 | ColorDataType.Integer, - X6Bayer10GBRG = ColorSpace.BayerGBRG | ColorSwizzle.Y000 | ColorComponent.Y6X10 | ColorDataType.Integer, - XYZ = ColorSpace.Unknown14 | ColorSwizzle.XYZ1 | ColorComponent.X20Y20Z20 | ColorDataType.Float, + NonColor8 = ColorSpace.NonColor | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, + NonColor16 = ColorSpace.NonColor | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Integer, + NonColor24 = ColorSpace.NonColor | ColorSwizzle.X000 | ColorComponent.X24 | ColorDataType.Integer, + NonColor32 = ColorSpace.NonColor | ColorSwizzle.X000 | ColorComponent.X32 | ColorDataType.Integer, + X4C4 = ColorSpace.NonColor | ColorSwizzle.Y000 | ColorComponent.Y4X4 | ColorDataType.Integer, + A4L4 = ColorSpace.LinearRGBA | ColorSwizzle.YYYX | ColorComponent.Y4X4 | ColorDataType.Integer, + A8L8 = ColorSpace.LinearRGBA | ColorSwizzle.YYYX | ColorComponent.Y8X8 | ColorDataType.Integer, + Float_A16L16 = ColorSpace.LinearRGBA | ColorSwizzle.YYYX | ColorComponent.X16Y16 | ColorDataType.Float, + A1B5G5R5 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X1Y5Z5W5 | ColorDataType.Integer, + A4B4G4R4 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X4Y4Z4W4 | ColorDataType.Integer, + A5B5G5R1 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X5Y5Z5W1 | ColorDataType.Integer, + A2B10G10R10 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + A8B8G8R8 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + A16B16G16R16 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + Float_A16B16G16R16 = ColorSpace.LinearRGBA | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Float, + A1R5G5B5 = ColorSpace.LinearRGBA | ColorSwizzle.YZWX | ColorComponent.X1Y5Z5W5 | ColorDataType.Integer, + A4R4G4B4 = ColorSpace.LinearRGBA | ColorSwizzle.YZWX | ColorComponent.X4Y4Z4W4 | ColorDataType.Integer, + A5R1G5B5 = ColorSpace.LinearRGBA | ColorSwizzle.YZWX | ColorComponent.X5Y1Z5W5 | ColorDataType.Integer, + A2R10G10B10 = ColorSpace.LinearRGBA | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + A8R8G8B8 = ColorSpace.LinearRGBA | ColorSwizzle.YZWX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + A1 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X1 | ColorDataType.Integer, + A2 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X2 | ColorDataType.Integer, + A4 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X4 | ColorDataType.Integer, + A8 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X8 | ColorDataType.Integer, + A16 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X16 | ColorDataType.Integer, + A32 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X32 | ColorDataType.Integer, + Float_A16 = ColorSpace.LinearRGBA | ColorSwizzle._000X | ColorComponent.X16 | ColorDataType.Float, + L4A4 = ColorSpace.LinearRGBA | ColorSwizzle.XXXY | ColorComponent.Y4X4 | ColorDataType.Integer, + L8A8 = ColorSpace.LinearRGBA | ColorSwizzle.XXXY | ColorComponent.Y8X8 | ColorDataType.Integer, + B4G4R4A4 = ColorSpace.LinearRGBA | ColorSwizzle.ZYXW | ColorComponent.X4Y4Z4W4 | ColorDataType.Integer, + B5G5R1A5 = ColorSpace.LinearRGBA | ColorSwizzle.ZYXW | ColorComponent.X5Y5Z1W5 | ColorDataType.Integer, + B5G5R5A1 = ColorSpace.LinearRGBA | ColorSwizzle.ZYXW | ColorComponent.X5Y5Z5W1 | ColorDataType.Integer, + B8G8R8A8 = ColorSpace.LinearRGBA | ColorSwizzle.ZYXW | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + B10G10R10A2 = ColorSpace.LinearRGBA | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + R1G5B5A5 = ColorSpace.LinearRGBA | ColorSwizzle.XYZW | ColorComponent.X1Y5Z5W5 | ColorDataType.Integer, + R4G4B4A4 = ColorSpace.LinearRGBA | ColorSwizzle.XYZW | ColorComponent.X4Y4Z4W4 | ColorDataType.Integer, + R5G5B5A1 = ColorSpace.LinearRGBA | ColorSwizzle.XYZW | ColorComponent.X5Y5Z5W1 | ColorDataType.Integer, + R8G8B8A8 = ColorSpace.LinearRGBA | ColorSwizzle.XYZW | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + R10G10B10A2 = ColorSpace.LinearRGBA | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + L1 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X1 | ColorDataType.Integer, + L2 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X2 | ColorDataType.Integer, + L4 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X4 | ColorDataType.Integer, + L8 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X8 | ColorDataType.Integer, + L16 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X16 | ColorDataType.Integer, + L32 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X32 | ColorDataType.Integer, + Float_L16 = ColorSpace.LinearRGBA | ColorSwizzle.XXX1 | ColorComponent.X16 | ColorDataType.Float, + B5G6R5 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X5Y6Z5 | ColorDataType.Integer, + B6G5R5 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X6Y5Z5 | ColorDataType.Integer, + B5G5R5X1 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X5Y5Z5W1 | ColorDataType.Integer, + B8_G8_R8 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X8Y8Z8 | ColorDataType.Integer, + B8G8R8X8 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + Float_B10G11R11 = ColorSpace.LinearRGBA | ColorSwizzle.ZYX1 | ColorComponent.X11Y11Z10 | ColorDataType.Float, + X1B5G5R5 = ColorSpace.LinearRGBA | ColorSwizzle.WZY1 | ColorComponent.X1Y5Z5W5 | ColorDataType.Integer, + X8B8G8R8 = ColorSpace.LinearRGBA | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + X16B16G16R16 = ColorSpace.LinearRGBA | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + Float_X16B16G16R16 = ColorSpace.LinearRGBA | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Float, + R3G3B2 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.X3Y3Z2 | ColorDataType.Integer, + R5G5B6 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.Z5Y5X6 | ColorDataType.Integer, + R5G6B5 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.X5Y6Z5 | ColorDataType.Integer, + R5G5B5X1 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.X5Y5Z5W1 | ColorDataType.Integer, + R8_G8_B8 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.X8Y8Z8 | ColorDataType.Integer, + R8G8B8X8 = ColorSpace.LinearRGBA | ColorSwizzle.XYZ1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + X1R5G5B5 = ColorSpace.LinearRGBA | ColorSwizzle.YZW1 | ColorComponent.X1Y5Z5W5 | ColorDataType.Integer, + X8R8G8B8 = ColorSpace.LinearRGBA | ColorSwizzle.YZW1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + RG8 = ColorSpace.LinearRGBA | ColorSwizzle.XY01 | ColorComponent.Y8X8 | ColorDataType.Integer, + R16G16 = ColorSpace.LinearRGBA | ColorSwizzle.XY01 | ColorComponent.X16Y16 | ColorDataType.Integer, + Float_R16G16 = ColorSpace.LinearRGBA | ColorSwizzle.XY01 | ColorComponent.X16Y16 | ColorDataType.Float, + R8 = ColorSpace.LinearRGBA | ColorSwizzle.X001 | ColorComponent.X8 | ColorDataType.Integer, + R16 = ColorSpace.LinearRGBA | ColorSwizzle.X001 | ColorComponent.X16 | ColorDataType.Integer, + Float_R16 = ColorSpace.LinearRGBA | ColorSwizzle.X001 | ColorComponent.X16 | ColorDataType.Float, + A2B10G10R10_sRGB = ColorSpace.SRGB | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + A8B8G8R8_sRGB = ColorSpace.SRGB | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + A16B16G16R16_sRGB = ColorSpace.SRGB | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + A2R10G10B10_sRGB = ColorSpace.SRGB | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + B10G10R10A2_sRGB = ColorSpace.SRGB | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + R10G10B10A2_sRGB = ColorSpace.SRGB | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + X8B8G8R8_sRGB = ColorSpace.SRGB | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + X16B16G16R16_sRGB = ColorSpace.SRGB | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + A2B10G10R10_709 = ColorSpace.RGB709 | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + A8B8G8R8_709 = ColorSpace.RGB709 | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + A16B16G16R16_709 = ColorSpace.RGB709 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + A2R10G10B10_709 = ColorSpace.RGB709 | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + B10G10R10A2_709 = ColorSpace.RGB709 | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + R10G10B10A2_709 = ColorSpace.RGB709 | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + X8B8G8R8_709 = ColorSpace.RGB709 | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + X16B16G16R16_709 = ColorSpace.RGB709 | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + A2B10G10R10_709_Linear = ColorSpace.LinearRGB709 | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + A8B8G8R8_709_Linear = ColorSpace.LinearRGB709 | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + A16B16G16R16_709_Linear = ColorSpace.LinearRGB709 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + A2R10G10B10_709_Linear = ColorSpace.LinearRGB709 | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + B10G10R10A2_709_Linear = ColorSpace.LinearRGB709 | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + R10G10B10A2_709_Linear = ColorSpace.LinearRGB709 | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + X8B8G8R8_709_Linear = ColorSpace.LinearRGB709 | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + X16B16G16R16_709_Linear = ColorSpace.LinearRGB709 | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + Float_A16B16G16R16_scRGB_Linear = ColorSpace.LinearScRGB | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Float, + A2B10G10R10_2020 = ColorSpace.RGB2020 | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + A8B8G8R8_2020 = ColorSpace.RGB2020 | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + A16B16G16R16_2020 = ColorSpace.RGB2020 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + A2R10G10B10_2020 = ColorSpace.RGB2020 | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + B10G10R10A2_2020 = ColorSpace.RGB2020 | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + R10G10B10A2_2020 = ColorSpace.RGB2020 | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + X8B8G8R8_2020 = ColorSpace.RGB2020 | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + X16B16G16R16_2020 = ColorSpace.RGB2020 | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + A2B10G10R10_2020_Linear = ColorSpace.LinearRGB2020 | ColorSwizzle.WZYX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + A8B8G8R8_2020_Linear = ColorSpace.LinearRGB2020 | ColorSwizzle.WZYX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + A16B16G16R16_2020_Linear = ColorSpace.LinearRGB2020 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + Float_A16B16G16R16_2020_Linear = ColorSpace.LinearRGB2020 | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Float, + A2R10G10B10_2020_Linear = ColorSpace.LinearRGB2020 | ColorSwizzle.YZWX | ColorComponent.X2Y10Z10W10 | ColorDataType.Integer, + B10G10R10A2_2020_Linear = ColorSpace.LinearRGB2020 | ColorSwizzle.ZYXW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + R10G10B10A2_2020_Linear = ColorSpace.LinearRGB2020 | ColorSwizzle.XYZW | ColorComponent.X10Y10Z10W2 | ColorDataType.Integer, + X8B8G8R8_2020_Linear = ColorSpace.LinearRGB2020 | ColorSwizzle.WZY1 | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + X16B16G16R16_2020_Linear = ColorSpace.LinearRGB2020 | ColorSwizzle.WZY1 | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + Float_A16B16G16R16_2020_PQ = ColorSpace.RGB2020_PQ | ColorSwizzle.WZYX | ColorComponent.X16Y16Z16W16 | ColorDataType.Float, + A4I4 = ColorSpace.ColorIndex | ColorSwizzle.X00X | ColorComponent.Y4X4 | ColorDataType.Integer, + A8I8 = ColorSpace.ColorIndex | ColorSwizzle.X00X | ColorComponent.Y8X8 | ColorDataType.Integer, + I4A4 = ColorSpace.ColorIndex | ColorSwizzle.X00Y | ColorComponent.Y4X4 | ColorDataType.Integer, + I8A8 = ColorSpace.ColorIndex | ColorSwizzle.X00Y | ColorComponent.Y8X8 | ColorDataType.Integer, + I1 = ColorSpace.ColorIndex | ColorSwizzle.X000 | ColorComponent.X1 | ColorDataType.Integer, + I2 = ColorSpace.ColorIndex | ColorSwizzle.X000 | ColorComponent.X2 | ColorDataType.Integer, + I4 = ColorSpace.ColorIndex | ColorSwizzle.X000 | ColorComponent.X4 | ColorDataType.Integer, + I8 = ColorSpace.ColorIndex | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, + A8Y8U8V8 = ColorSpace.YCbCr601 | ColorSwizzle.YZWX | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + A16Y16U16V16 = ColorSpace.YCbCr601 | ColorSwizzle.YZWX | ColorComponent.X16Y16Z16W16 | ColorDataType.Integer, + Y8U8V8A8 = ColorSpace.YCbCr601 | ColorSwizzle.XYZW | ColorComponent.X8Y8Z8W8 | ColorDataType.Integer, + V8_U8 = ColorSpace.YCbCr601 | ColorSwizzle._0YX0 | ColorComponent.X8Y8 | ColorDataType.Integer, + V8U8 = ColorSpace.YCbCr601 | ColorSwizzle._0YX0 | ColorComponent.Y8X8 | ColorDataType.Integer, + V10U10 = ColorSpace.YCbCr601 | ColorSwizzle._0ZY0 | ColorComponent.Y10X10 | ColorDataType.Integer, + V12U12 = ColorSpace.YCbCr601 | ColorSwizzle._0ZY0 | ColorComponent.Y12X12 | ColorDataType.Integer, + V8 = ColorSpace.YCbCr601 | ColorSwizzle._00X0 | ColorComponent.X8 | ColorDataType.Integer, + V10 = ColorSpace.YCbCr601 | ColorSwizzle._00X0 | ColorComponent.X10 | ColorDataType.Integer, + V12 = ColorSpace.YCbCr601 | ColorSwizzle._00X0 | ColorComponent.X12 | ColorDataType.Integer, + U8_V8 = ColorSpace.YCbCr601 | ColorSwizzle._0XY0 | ColorComponent.X8Y8 | ColorDataType.Integer, + U8V8 = ColorSpace.YCbCr601 | ColorSwizzle._0XY0 | ColorComponent.Y8X8 | ColorDataType.Integer, + U10V10 = ColorSpace.YCbCr601 | ColorSwizzle._0XZ0 | ColorComponent.Y10X10 | ColorDataType.Integer, + U12V12 = ColorSpace.YCbCr601 | ColorSwizzle._0XZ0 | ColorComponent.Y12X12 | ColorDataType.Integer, + U8 = ColorSpace.YCbCr601 | ColorSwizzle._0X00 | ColorComponent.X8 | ColorDataType.Integer, + U10 = ColorSpace.YCbCr601 | ColorSwizzle._0X00 | ColorComponent.X10 | ColorDataType.Integer, + U12 = ColorSpace.YCbCr601 | ColorSwizzle._0X00 | ColorComponent.X12 | ColorDataType.Integer, + Y8 = ColorSpace.YCbCr601 | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, + Y10 = ColorSpace.YCbCr601 | ColorSwizzle.X000 | ColorComponent.X10 | ColorDataType.Integer, + Y12 = ColorSpace.YCbCr601 | ColorSwizzle.X000 | ColorComponent.X12 | ColorDataType.Integer, + YVYU = ColorSpace.YCbCr601 | ColorSwizzle.XZY1 | ColorComponent.X8Y8X8Z8 | ColorDataType.Integer, + VYUY = ColorSpace.YCbCr601 | ColorSwizzle.XZY1 | ColorComponent.Y8X8Z8X8 | ColorDataType.Integer, + UYVY = ColorSpace.YCbCr601 | ColorSwizzle.XYZ1 | ColorComponent.Y8X8Z8X8 | ColorDataType.Integer, + YUYV = ColorSpace.YCbCr601 | ColorSwizzle.XYZ1 | ColorComponent.X8Y8X8Z8 | ColorDataType.Integer, + Y8_U8_V8 = ColorSpace.YCbCr601 | ColorSwizzle.XYZ1 | ColorComponent.X8Y8Z8 | ColorDataType.Integer, + V8_U8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._0YX0 | ColorComponent.X8Y8 | ColorDataType.Integer, + V8U8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._0YX0 | ColorComponent.Y8X8 | ColorDataType.Integer, + V8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._00X0 | ColorComponent.X8 | ColorDataType.Integer, + U8_V8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._0XY0 | ColorComponent.X8Y8 | ColorDataType.Integer, + U8V8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._0XY0 | ColorComponent.Y8X8 | ColorDataType.Integer, + U8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle._0X00 | ColorComponent.X8 | ColorDataType.Integer, + Y8_RR = ColorSpace.YCbCr601_RR | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, + V8_U8_ER = ColorSpace.YCbCr601_ER | ColorSwizzle._0YX0 | ColorComponent.X8Y8 | ColorDataType.Integer, + V8U8_ER = ColorSpace.YCbCr601_ER | ColorSwizzle._0YX0 | ColorComponent.Y8X8 | ColorDataType.Integer, + V8_ER = ColorSpace.YCbCr601_ER | ColorSwizzle._00X0 | ColorComponent.X8 | ColorDataType.Integer, + U8_V8_ER = ColorSpace.YCbCr601_ER | ColorSwizzle._0XY0 | ColorComponent.X8Y8 | ColorDataType.Integer, + U8V8_ER = ColorSpace.YCbCr601_ER | ColorSwizzle._0XY0 | ColorComponent.Y8X8 | ColorDataType.Integer, + U8_ER = ColorSpace.YCbCr601_ER | ColorSwizzle._0X00 | ColorComponent.X8 | ColorDataType.Integer, + Y8_ER = ColorSpace.YCbCr601_ER | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, + V8_U8_709 = ColorSpace.YCbCr709 | ColorSwizzle._0YX0 | ColorComponent.X8Y8 | ColorDataType.Integer, + V8U8_709 = ColorSpace.YCbCr709 | ColorSwizzle._0YX0 | ColorComponent.Y8X8 | ColorDataType.Integer, + V10U10_709 = ColorSpace.YCbCr709 | ColorSwizzle._0ZY0 | ColorComponent.Y10X10 | ColorDataType.Integer, + V12U12_709 = ColorSpace.YCbCr709 | ColorSwizzle._0ZY0 | ColorComponent.Y12X12 | ColorDataType.Integer, + V8_709 = ColorSpace.YCbCr709 | ColorSwizzle._00X0 | ColorComponent.X8 | ColorDataType.Integer, + V10_709 = ColorSpace.YCbCr709 | ColorSwizzle._00X0 | ColorComponent.X10 | ColorDataType.Integer, + V12_709 = ColorSpace.YCbCr709 | ColorSwizzle._00X0 | ColorComponent.X12 | ColorDataType.Integer, + U8_V8_709 = ColorSpace.YCbCr709 | ColorSwizzle._0XY0 | ColorComponent.X8Y8 | ColorDataType.Integer, + U8V8_709 = ColorSpace.YCbCr709 | ColorSwizzle._0XY0 | ColorComponent.Y8X8 | ColorDataType.Integer, + U10V10_709 = ColorSpace.YCbCr709 | ColorSwizzle._0XZ0 | ColorComponent.Y10X10 | ColorDataType.Integer, + U12V12_709 = ColorSpace.YCbCr709 | ColorSwizzle._0XZ0 | ColorComponent.Y12X12 | ColorDataType.Integer, + U8_709 = ColorSpace.YCbCr709 | ColorSwizzle._0X00 | ColorComponent.X8 | ColorDataType.Integer, + U10_709 = ColorSpace.YCbCr709 | ColorSwizzle._0X00 | ColorComponent.X10 | ColorDataType.Integer, + U12_709 = ColorSpace.YCbCr709 | ColorSwizzle._0X00 | ColorComponent.X12 | ColorDataType.Integer, + Y8_709 = ColorSpace.YCbCr709 | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, + Y10_709 = ColorSpace.YCbCr709 | ColorSwizzle.X000 | ColorComponent.X10 | ColorDataType.Integer, + Y12_709 = ColorSpace.YCbCr709 | ColorSwizzle.X000 | ColorComponent.X12 | ColorDataType.Integer, + V8_U8_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._0YX0 | ColorComponent.X8Y8 | ColorDataType.Integer, + V8U8_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._0YX0 | ColorComponent.Y8X8 | ColorDataType.Integer, + V10U10_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._0ZY0 | ColorComponent.Y10X10 | ColorDataType.Integer, + V12U12_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._0ZY0 | ColorComponent.Y12X12 | ColorDataType.Integer, + V8_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._00X0 | ColorComponent.X8 | ColorDataType.Integer, + V10_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._00X0 | ColorComponent.X10 | ColorDataType.Integer, + V12_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._00X0 | ColorComponent.X12 | ColorDataType.Integer, + U8_V8_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._0XY0 | ColorComponent.X8Y8 | ColorDataType.Integer, + U8V8_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._0XY0 | ColorComponent.Y8X8 | ColorDataType.Integer, + U10V10_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._0XZ0 | ColorComponent.Y10X10 | ColorDataType.Integer, + U12V12_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._0XZ0 | ColorComponent.Y12X12 | ColorDataType.Integer, + U8_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._0X00 | ColorComponent.X8 | ColorDataType.Integer, + U10_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._0X00 | ColorComponent.X10 | ColorDataType.Integer, + U12_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle._0X00 | ColorComponent.X12 | ColorDataType.Integer, + Y8_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, + Y10_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle.X000 | ColorComponent.X10 | ColorDataType.Integer, + Y12_709_ER = ColorSpace.YCbCr709_ER | ColorSwizzle.X000 | ColorComponent.X12 | ColorDataType.Integer, + V10U10_2020 = ColorSpace.YCbCr709_ER | ColorSwizzle._0ZY0 | ColorComponent.Y10X10 | ColorDataType.Integer, + V12U12_2020 = ColorSpace.YCbCr709_ER | ColorSwizzle._0ZY0 | ColorComponent.Y12X12 | ColorDataType.Integer, + V10_2020 = ColorSpace.YCbCr709_ER | ColorSwizzle._00X0 | ColorComponent.X10 | ColorDataType.Integer, + V12_2020 = ColorSpace.YCbCr709_ER | ColorSwizzle._00X0 | ColorComponent.X12 | ColorDataType.Integer, + U10V10_2020 = ColorSpace.YCbCr709_ER | ColorSwizzle._0XZ0 | ColorComponent.Y10X10 | ColorDataType.Integer, + U12V12_2020 = ColorSpace.YCbCr709_ER | ColorSwizzle._0XZ0 | ColorComponent.Y12X12 | ColorDataType.Integer, + U10_2020 = ColorSpace.YCbCr709_ER | ColorSwizzle._0X00 | ColorComponent.X10 | ColorDataType.Integer, + U12_2020 = ColorSpace.YCbCr709_ER | ColorSwizzle._0X00 | ColorComponent.X12 | ColorDataType.Integer, + Y10_2020 = ColorSpace.YCbCr709_ER | ColorSwizzle.X000 | ColorComponent.X10 | ColorDataType.Integer, + Y12_2020 = ColorSpace.YCbCr709_ER | ColorSwizzle.X000 | ColorComponent.X12 | ColorDataType.Integer, + Bayer8RGGB = ColorSpace.BayerRGGB | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, + Bayer16RGGB = ColorSpace.BayerRGGB | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Integer, + BayerS16RGGB = ColorSpace.BayerRGGB | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Stencil, + X2Bayer14RGGB = ColorSpace.BayerRGGB | ColorSwizzle.Y000 | ColorComponent.Y2X14 | ColorDataType.Integer, + X4Bayer12RGGB = ColorSpace.BayerRGGB | ColorSwizzle.Y000 | ColorComponent.Y4X12 | ColorDataType.Integer, + X6Bayer10RGGB = ColorSpace.BayerRGGB | ColorSwizzle.Y000 | ColorComponent.Y6X10 | ColorDataType.Integer, + Bayer8BGGR = ColorSpace.BayerBGGR | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, + Bayer16BGGR = ColorSpace.BayerBGGR | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Integer, + BayerS16BGGR = ColorSpace.BayerBGGR | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Stencil, + X2Bayer14BGGR = ColorSpace.BayerBGGR | ColorSwizzle.Y000 | ColorComponent.Y2X14 | ColorDataType.Integer, + X4Bayer12BGGR = ColorSpace.BayerBGGR | ColorSwizzle.Y000 | ColorComponent.Y4X12 | ColorDataType.Integer, + X6Bayer10BGGR = ColorSpace.BayerBGGR | ColorSwizzle.Y000 | ColorComponent.Y6X10 | ColorDataType.Integer, + Bayer8GRBG = ColorSpace.BayerGRBG | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, + Bayer16GRBG = ColorSpace.BayerGRBG | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Integer, + BayerS16GRBG = ColorSpace.BayerGRBG | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Stencil, + X2Bayer14GRBG = ColorSpace.BayerGRBG | ColorSwizzle.Y000 | ColorComponent.Y2X14 | ColorDataType.Integer, + X4Bayer12GRBG = ColorSpace.BayerGRBG | ColorSwizzle.Y000 | ColorComponent.Y4X12 | ColorDataType.Integer, + X6Bayer10GRBG = ColorSpace.BayerGRBG | ColorSwizzle.Y000 | ColorComponent.Y6X10 | ColorDataType.Integer, + Bayer8GBRG = ColorSpace.BayerGBRG | ColorSwizzle.X000 | ColorComponent.X8 | ColorDataType.Integer, + Bayer16GBRG = ColorSpace.BayerGBRG | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Integer, + BayerS16GBRG = ColorSpace.BayerGBRG | ColorSwizzle.X000 | ColorComponent.X16 | ColorDataType.Stencil, + X2Bayer14GBRG = ColorSpace.BayerGBRG | ColorSwizzle.Y000 | ColorComponent.Y2X14 | ColorDataType.Integer, + X4Bayer12GBRG = ColorSpace.BayerGBRG | ColorSwizzle.Y000 | ColorComponent.Y4X12 | ColorDataType.Integer, + X6Bayer10GBRG = ColorSpace.BayerGBRG | ColorSwizzle.Y000 | ColorComponent.Y6X10 | ColorDataType.Integer, + XYZ = ColorSpace.XYZ | ColorSwizzle.XYZ1 | ColorComponent.X20Y20Z20 | ColorDataType.Float, } } diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs index b272e0788d..48cf328806 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs @@ -180,22 +180,31 @@ namespace Ryujinx.HLE.HOS.Services.Vi public long ConvertScalingMode(ServiceCtx context) { - SrcScalingMode scalingMode = (SrcScalingMode)context.RequestData.ReadInt32(); - DstScalingMode? destScalingMode = ConvetScalingModeImpl(scalingMode); + SrcScalingMode scalingMode = (SrcScalingMode)context.RequestData.ReadInt32(); - if (!destScalingMode.HasValue) + DstScalingMode? convertedScalingMode = ConvertScalingMode(scalingMode); + + if (!convertedScalingMode.HasValue) { + //Scaling mode out of the range of valid values. return MakeError(ErrorModule.Vi, 1); } - context.ResponseData.Write((ulong)destScalingMode); + if (scalingMode != SrcScalingMode.ScaleToWindow && + scalingMode != SrcScalingMode.PreserveAspectRatio) + { + //Invalid scaling mode specified. + return MakeError(ErrorModule.Vi, 6); + } + + context.ResponseData.Write((ulong)convertedScalingMode); return 0; } - private DstScalingMode? ConvetScalingModeImpl(SrcScalingMode srcScalingMode) + private DstScalingMode? ConvertScalingMode(SrcScalingMode source) { - switch (srcScalingMode) + switch (source) { case SrcScalingMode.None: return DstScalingMode.None; case SrcScalingMode.Freeze: return DstScalingMode.Freeze; diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs index db04f47cd0..66c3327916 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; +using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS; using Ryujinx.HLE.HOS.Services.Nv.NvMap; @@ -356,15 +357,15 @@ namespace Ryujinx.HLE.HOS.Services.Android switch (colorFormat) { case ColorFormat.A8B8G8R8: - return GalImageFormat.RGBA8 | GalImageFormat.Unorm; + return GalImageFormat.Rgba8 | GalImageFormat.Unorm; case ColorFormat.X8B8G8R8: - return GalImageFormat.RGBX8 | GalImageFormat.Unorm; + return GalImageFormat.Rgbx8 | GalImageFormat.Unorm; case ColorFormat.R5G6B5: - return GalImageFormat.BGR565 | GalImageFormat.Unorm; + return GalImageFormat.Bgr565 | GalImageFormat.Unorm; case ColorFormat.A8R8G8B8: - return GalImageFormat.BGRA8 | GalImageFormat.Unorm; + return GalImageFormat.Bgra8 | GalImageFormat.Unorm; case ColorFormat.A4B4G4R4: - return GalImageFormat.RGBA4 | GalImageFormat.Unorm; + return GalImageFormat.Rgba4 | GalImageFormat.Unorm; default: throw new NotImplementedException($"Color Format \"{colorFormat}\" not implemented!"); } @@ -415,9 +416,10 @@ namespace Ryujinx.HLE.HOS.Services.Android { image = new GalImage( fbWidth, - fbHeight, 1, BlockHeight, + fbHeight, 1, 1, 1, BlockHeight, 1, GalMemoryLayout.BlockLinear, - imageFormat); + imageFormat, + GalTextureTarget.TwoD); } context.Device.Gpu.ResourceManager.ClearPbCache(); diff --git a/Ryujinx.HLE/HOS/Services/Vi/ScalingMode.cs b/Ryujinx.HLE/HOS/Services/Vi/ScalingMode.cs index 824a27b70a..7b555b5999 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/ScalingMode.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/ScalingMode.cs @@ -1,24 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Ryujinx.HLE.HOS.Services.Vi +namespace Ryujinx.HLE.HOS.Services.Vi { enum SrcScalingMode { - Freeze = 0, - ScaleToWindow = 1, - ScaleAndCrop = 2, - None = 3, + None = 0, + Freeze = 1, + ScaleToWindow = 2, + ScaleAndCrop = 3, PreserveAspectRatio = 4 } enum DstScalingMode { - None = 0, - Freeze = 1, - ScaleToWindow = 2, - ScaleAndCrop = 3, + Freeze = 0, + ScaleToWindow = 1, + ScaleAndCrop = 2, + None = 3, PreserveAspectRatio = 4 } } diff --git a/Ryujinx.ShaderTools/Memory.cs b/Ryujinx.ShaderTools/Memory.cs index f801ab39a9..c99224b5ed 100644 --- a/Ryujinx.ShaderTools/Memory.cs +++ b/Ryujinx.ShaderTools/Memory.cs @@ -23,4 +23,4 @@ namespace Ryujinx.ShaderTools return Reader.ReadInt32(); } } -} +} \ No newline at end of file diff --git a/Ryujinx.ShaderTools/Program.cs b/Ryujinx.ShaderTools/Program.cs index 30fa71aea2..e763e2c1c1 100644 --- a/Ryujinx.ShaderTools/Program.cs +++ b/Ryujinx.ShaderTools/Program.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Gal.Shader; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; using System; using System.IO; @@ -7,32 +8,30 @@ namespace Ryujinx.ShaderTools { class Program { - private static readonly int MaxUboSize = 65536; - static void Main(string[] args) { if (args.Length == 2) { - GlslDecompiler Decompiler = new GlslDecompiler(MaxUboSize); - - GalShaderType ShaderType = GalShaderType.Vertex; + GalShaderType type = GalShaderType.Vertex; switch (args[0].ToLower()) { - case "v": ShaderType = GalShaderType.Vertex; break; - case "tc": ShaderType = GalShaderType.TessControl; break; - case "te": ShaderType = GalShaderType.TessEvaluation; break; - case "g": ShaderType = GalShaderType.Geometry; break; - case "f": ShaderType = GalShaderType.Fragment; break; + case "v": type = GalShaderType.Vertex; break; + case "tc": type = GalShaderType.TessControl; break; + case "te": type = GalShaderType.TessEvaluation; break; + case "g": type = GalShaderType.Geometry; break; + case "f": type = GalShaderType.Fragment; break; } - using (FileStream FS = new FileStream(args[1], FileMode.Open, FileAccess.Read)) + using (FileStream fs = new FileStream(args[1], FileMode.Open, FileAccess.Read)) { - Memory Mem = new Memory(FS); + Memory mem = new Memory(fs); - GlslProgram Program = Decompiler.Decompile(Mem, 0, ShaderType); + ShaderConfig config = new ShaderConfig(type, 65536); - Console.WriteLine(Program.Code); + string code = Translator.Translate(mem, 0, config).Code; + + Console.WriteLine(code); } } else @@ -41,4 +40,4 @@ namespace Ryujinx.ShaderTools } } } -} +} \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 47feb573eb..b147cf4468 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -93,10 +93,14 @@ namespace Ryujinx.Tests.Cpu } protected void SetThreadState(ulong x0 = 0, ulong x1 = 0, ulong x2 = 0, ulong x3 = 0, ulong x31 = 0, - Vector128 v0 = default(Vector128), - Vector128 v1 = default(Vector128), - Vector128 v2 = default(Vector128), - Vector128 v3 = default(Vector128), + Vector128 v0 = default(Vector128), + Vector128 v1 = default(Vector128), + Vector128 v2 = default(Vector128), + Vector128 v3 = default(Vector128), + Vector128 v4 = default(Vector128), + Vector128 v5 = default(Vector128), + Vector128 v30 = default(Vector128), + Vector128 v31 = default(Vector128), bool overflow = false, bool carry = false, bool zero = false, bool negative = false, int fpcr = 0x0, int fpsr = 0x0) { @@ -107,10 +111,14 @@ namespace Ryujinx.Tests.Cpu _thread.ThreadState.X31 = x31; - _thread.ThreadState.V0 = v0; - _thread.ThreadState.V1 = v1; - _thread.ThreadState.V2 = v2; - _thread.ThreadState.V3 = v3; + _thread.ThreadState.V0 = v0; + _thread.ThreadState.V1 = v1; + _thread.ThreadState.V2 = v2; + _thread.ThreadState.V3 = v3; + _thread.ThreadState.V4 = v4; + _thread.ThreadState.V5 = v5; + _thread.ThreadState.V30 = v30; + _thread.ThreadState.V31 = v31; _thread.ThreadState.Overflow = overflow; _thread.ThreadState.Carry = carry; @@ -129,10 +137,14 @@ namespace Ryujinx.Tests.Cpu _unicornEmu.SP = x31; - _unicornEmu.Q[0] = v0; - _unicornEmu.Q[1] = v1; - _unicornEmu.Q[2] = v2; - _unicornEmu.Q[3] = v3; + _unicornEmu.Q[0] = v0; + _unicornEmu.Q[1] = v1; + _unicornEmu.Q[2] = v2; + _unicornEmu.Q[3] = v3; + _unicornEmu.Q[4] = v4; + _unicornEmu.Q[5] = v5; + _unicornEmu.Q[30] = v30; + _unicornEmu.Q[31] = v31; _unicornEmu.OverflowFlag = overflow; _unicornEmu.CarryFlag = carry; @@ -165,17 +177,21 @@ namespace Ryujinx.Tests.Cpu protected CpuThreadState SingleOpcode(uint opcode, ulong x0 = 0, ulong x1 = 0, ulong x2 = 0, ulong x3 = 0, ulong x31 = 0, - Vector128 v0 = default(Vector128), - Vector128 v1 = default(Vector128), - Vector128 v2 = default(Vector128), - Vector128 v3 = default(Vector128), + Vector128 v0 = default(Vector128), + Vector128 v1 = default(Vector128), + Vector128 v2 = default(Vector128), + Vector128 v3 = default(Vector128), + Vector128 v4 = default(Vector128), + Vector128 v5 = default(Vector128), + Vector128 v30 = default(Vector128), + Vector128 v31 = default(Vector128), bool overflow = false, bool carry = false, bool zero = false, bool negative = false, int fpcr = 0x0, int fpsr = 0x0) { Opcode(opcode); Opcode(0xD4200000); // BRK #0 Opcode(0xD65F03C0); // RET - SetThreadState(x0, x1, x2, x3, x31, v0, v1, v2, v3, overflow, carry, zero, negative, fpcr, fpsr); + SetThreadState(x0, x1, x2, x3, x31, v0, v1, v2, v3, v4, v5, v30, v31, overflow, carry, zero, negative, fpcr, fpsr); ExecuteOpcodes(); return GetThreadState(); diff --git a/Ryujinx.Tests/Cpu/CpuTestMisc.cs b/Ryujinx.Tests/Cpu/CpuTestMisc.cs index 4d6783f71e..e976c2c00a 100644 --- a/Ryujinx.Tests/Cpu/CpuTestMisc.cs +++ b/Ryujinx.Tests/Cpu/CpuTestMisc.cs @@ -1,3 +1,5 @@ +#define Misc + using ChocolArm64.State; using NUnit.Framework; @@ -6,9 +8,120 @@ using System.Runtime.Intrinsics.X86; namespace Ryujinx.Tests.Cpu { - [Category("Misc"), Explicit] + [Category("Misc")] public sealed class CpuTestMisc : CpuTest { +#if Misc + private const int RndCnt = 2; + private const int RndCntImm = 2; + +#region "AluImm & Csel" + [Test, Pairwise] + public void Adds_Csinc_64bit([Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + [Values(0b00u, 0b01u)] uint shift, // + [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // + { + uint opCmn = 0xB100001F; // ADDS X31, X0, #0, LSL #0 -> CMN X0, #0, LSL #0 + uint opCset = 0x9A9F07E0; // CSINC X0, X31, X31, EQ -> CSET X0, NE + + opCmn |= ((shift & 3) << 22) | ((imm & 4095) << 10); + opCset |= ((cond & 15) << 12); + + SetThreadState(x0: xn); + Opcode(opCmn); + Opcode(opCset); + Opcode(0xD4200000); // BRK #0 + Opcode(0xD65F03C0); // RET + ExecuteOpcodes(); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void Adds_Csinc_32bit([Values(0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + [Values(0b00u, 0b01u)] uint shift, // + [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // + { + uint opCmn = 0x3100001F; // ADDS W31, W0, #0, LSL #0 -> CMN W0, #0, LSL #0 + uint opCset = 0x1A9F07E0; // CSINC W0, W31, W31, EQ -> CSET W0, NE + + opCmn |= ((shift & 3) << 22) | ((imm & 4095) << 10); + opCset |= ((cond & 15) << 12); + + SetThreadState(x0: wn); + Opcode(opCmn); + Opcode(opCset); + Opcode(0xD4200000); // BRK #0 + Opcode(0xD65F03C0); // RET + ExecuteOpcodes(); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void Subs_Csinc_64bit([Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(RndCnt)] ulong xn, + [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + [Values(0b00u, 0b01u)] uint shift, // + [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // + { + uint opCmp = 0xF100001F; // SUBS X31, X0, #0, LSL #0 -> CMP X0, #0, LSL #0 + uint opCset = 0x9A9F07E0; // CSINC X0, X31, X31, EQ -> CSET X0, NE + + opCmp |= ((shift & 3) << 22) | ((imm & 4095) << 10); + opCset |= ((cond & 15) << 12); + + SetThreadState(x0: xn); + Opcode(opCmp); + Opcode(opCset); + Opcode(0xD4200000); // BRK #0 + Opcode(0xD65F03C0); // RET + ExecuteOpcodes(); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void Subs_Csinc_32bit([Values(0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu)] [Random(RndCnt)] uint wn, + [Values(0u, 4095u)] [Random(0u, 4095u, RndCntImm)] uint imm, + [Values(0b00u, 0b01u)] uint shift, // + [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u, // + { + uint opCmp = 0x7100001F; // SUBS W31, W0, #0, LSL #0 -> CMP W0, #0, LSL #0 + uint opCset = 0x1A9F07E0; // CSINC W0, W31, W31, EQ -> CSET W0, NE + + opCmp |= ((shift & 3) << 22) | ((imm & 4095) << 10); + opCset |= ((cond & 15) << 12); + + SetThreadState(x0: wn); + Opcode(opCmp); + Opcode(opCset); + Opcode(0xD4200000); // BRK #0 + Opcode(0xD65F03C0); // RET + ExecuteOpcodes(); + + CompareAgainstUnicorn(); + } +#endregion + + [Explicit] [TestCase(0xFFFFFFFDu)] // Roots. [TestCase(0x00000005u)] public void Misc1(uint a) @@ -42,6 +155,7 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetThreadState().X0, Is.Zero); } + [Explicit] [TestCase(-20f, -5f)] // 18 integer solutions. [TestCase(-12f, -6f)] [TestCase(-12f, 3f)] @@ -91,6 +205,7 @@ namespace Ryujinx.Tests.Cpu Assert.That(Sse41.Extract(GetThreadState().V0, (byte)0), Is.EqualTo(16f)); } + [Explicit] [TestCase(-20d, -5d)] // 18 integer solutions. [TestCase(-12d, -6d)] [TestCase(-12d, 3d)] @@ -140,7 +255,7 @@ namespace Ryujinx.Tests.Cpu Assert.That(VectorExtractDouble(GetThreadState().V0, (byte)0), Is.EqualTo(16d)); } - [Test] + [Test, Ignore("The Tester supports only one return point.")] public void MiscF([Range(0u, 92u, 1u)] uint a) { ulong Fn(uint n) @@ -213,6 +328,7 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetThreadState().X0, Is.EqualTo(Fn(a))); } + [Explicit] [Test] public void MiscR() { @@ -255,6 +371,7 @@ namespace Ryujinx.Tests.Cpu Assert.That(GetThreadState().X0, Is.EqualTo(result)); } + [Explicit] [TestCase( 0ul)] [TestCase( 1ul)] [TestCase( 2ul)] @@ -266,5 +383,6 @@ namespace Ryujinx.Tests.Cpu Assert.That(threadState.X0, Is.EqualTo(a)); } +#endif } } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index 565b6613e0..df23f2eff8 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -2,6 +2,7 @@ using NUnit.Framework; +using System; using System.Collections.Generic; using System.Runtime.Intrinsics; @@ -172,6 +173,70 @@ namespace Ryujinx.Tests.Cpu } } + private static IEnumerable _1S_F_W_() + { + // int + yield return 0x00000000CF000001ul; // -2.1474839E9f (-2147483904) + yield return 0x00000000CF000000ul; // -2.14748365E9f (-2147483648) + yield return 0x00000000CEFFFFFFul; // -2.14748352E9f (-2147483520) + yield return 0x000000004F000001ul; // 2.1474839E9f (2147483904) + yield return 0x000000004F000000ul; // 2.14748365E9f (2147483648) + yield return 0x000000004EFFFFFFul; // 2.14748352E9f (2147483520) + + // uint + yield return 0x000000004F800001ul; // 4.2949678E9f (4294967808) + yield return 0x000000004F800000ul; // 4.2949673E9f (4294967296) + yield return 0x000000004F7FFFFFul; // 4.29496704E9f (4294967040) + + yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x0000000080800000ul; // -Min Normal + yield return 0x00000000807FFFFFul; // -Max Subnormal + yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0000000000800000ul; // +Min Normal + yield return 0x00000000007FFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x0000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0x00000000FF800000ul; // -Infinity + yield return 0x000000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) + yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong grbg = TestContext.CurrentContext.Random.NextUInt(); + + ulong rnd1 = (uint)BitConverter.SingleToInt32Bits( + (float)((int)TestContext.CurrentContext.Random.NextUInt())); + ulong rnd2 = (uint)BitConverter.SingleToInt32Bits( + (float)((uint)TestContext.CurrentContext.Random.NextUInt())); + + ulong rnd3 = GenNormalS(); + ulong rnd4 = GenSubnormalS(); + + yield return (grbg << 32) | rnd1; + yield return (grbg << 32) | rnd2; + + yield return (grbg << 32) | rnd3; + yield return (grbg << 32) | rnd4; + } + } + private static IEnumerable _2S_F_() { yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) @@ -213,6 +278,68 @@ namespace Ryujinx.Tests.Cpu } } + private static IEnumerable _2S_F_W_() + { + // int + yield return 0xCF000001CF000001ul; // -2.1474839E9f (-2147483904) + yield return 0xCF000000CF000000ul; // -2.14748365E9f (-2147483648) + yield return 0xCEFFFFFFCEFFFFFFul; // -2.14748352E9f (-2147483520) + yield return 0x4F0000014F000001ul; // 2.1474839E9f (2147483904) + yield return 0x4F0000004F000000ul; // 2.14748365E9f (2147483648) + yield return 0x4EFFFFFF4EFFFFFFul; // 2.14748352E9f (2147483520) + + // uint + yield return 0x4F8000014F800001ul; // 4.2949678E9f (4294967808) + yield return 0x4F8000004F800000ul; // 4.2949673E9f (4294967296) + yield return 0x4F7FFFFF4F7FFFFFul; // 4.29496704E9f (4294967040) + + yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x8080000080800000ul; // -Min Normal + yield return 0x807FFFFF807FFFFFul; // -Max Subnormal + yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0080000000800000ul; // +Min Normal + yield return 0x007FFFFF007FFFFFul; // +Max Subnormal + yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFF800000FF800000ul; // -Infinity + yield return 0x7F8000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) + yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = (uint)BitConverter.SingleToInt32Bits( + (float)((int)TestContext.CurrentContext.Random.NextUInt())); + ulong rnd2 = (uint)BitConverter.SingleToInt32Bits( + (float)((uint)TestContext.CurrentContext.Random.NextUInt())); + + ulong rnd3 = GenNormalS(); + ulong rnd4 = GenSubnormalS(); + + yield return (rnd1 << 32) | rnd1; + yield return (rnd2 << 32) | rnd2; + + yield return (rnd3 << 32) | rnd3; + yield return (rnd4 << 32) | rnd4; + } + } + private static IEnumerable _1D_F_() { yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) @@ -253,6 +380,68 @@ namespace Ryujinx.Tests.Cpu yield return rnd2; } } + + private static IEnumerable _1D_F_X_() + { + // long + yield return 0xC3E0000000000001ul; // -9.2233720368547780E18d (-9223372036854778000) + yield return 0xC3E0000000000000ul; // -9.2233720368547760E18d (-9223372036854776000) + yield return 0xC3DFFFFFFFFFFFFFul; // -9.2233720368547750E18d (-9223372036854775000) + yield return 0x43E0000000000001ul; // 9.2233720368547780E18d (9223372036854778000) + yield return 0x43E0000000000000ul; // 9.2233720368547760E18d (9223372036854776000) + yield return 0x43DFFFFFFFFFFFFFul; // 9.2233720368547750E18d (9223372036854775000) + + // ulong + yield return 0x43F0000000000001ul; // 1.8446744073709556e19d (18446744073709556000) + yield return 0x43F0000000000000ul; // 1.8446744073709552E19d (18446744073709552000) + yield return 0x43EFFFFFFFFFFFFFul; // 1.8446744073709550e19d (18446744073709550000) + + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0x8010000000000000ul; // -Min Normal + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x0010000000000000ul; // +Min Normal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000000000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFFF0000000000000ul; // -Infinity + yield return 0x7FF0000000000000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = (ulong)BitConverter.DoubleToInt64Bits( + (double)((long)TestContext.CurrentContext.Random.NextULong())); + ulong rnd2 = (ulong)BitConverter.DoubleToInt64Bits( + (double)((ulong)TestContext.CurrentContext.Random.NextULong())); + + ulong rnd3 = GenNormalD(); + ulong rnd4 = GenSubnormalD(); + + yield return rnd1; + yield return rnd2; + + yield return rnd3; + yield return rnd4; + } + } #endregion #region "ValueSource (Opcodes)" @@ -1319,7 +1508,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] [Explicit] public void F_Cvt_NZ_SU_S_S([ValueSource("_F_Cvt_NZ_SU_S_S_")] uint opcodes, - [ValueSource("_1S_F_")] ulong a) + [ValueSource("_1S_F_W_")] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); Vector128 v0 = MakeVectorE0E1(z, z); @@ -1332,7 +1521,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise] [Explicit] public void F_Cvt_NZ_SU_S_D([ValueSource("_F_Cvt_NZ_SU_S_D_")] uint opcodes, - [ValueSource("_1D_F_")] ulong a) + [ValueSource("_1D_F_X_")] ulong a) { ulong z = TestContext.CurrentContext.Random.NextULong(); Vector128 v0 = MakeVectorE1(z); @@ -1347,8 +1536,8 @@ namespace Ryujinx.Tests.Cpu public void F_Cvt_NZ_SU_V_2S_4S([ValueSource("_F_Cvt_NZ_SU_V_2S_4S_")] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_2S_F_")] ulong z, - [ValueSource("_2S_F_")] ulong a, + [ValueSource("_2S_F_W_")] ulong z, + [ValueSource("_2S_F_W_")] ulong a, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1366,8 +1555,8 @@ namespace Ryujinx.Tests.Cpu public void F_Cvt_NZ_SU_V_2D([ValueSource("_F_Cvt_NZ_SU_V_2D_")] uint opcodes, [Values(0u)] uint rd, [Values(1u, 0u)] uint rn, - [ValueSource("_1D_F_")] ulong z, - [ValueSource("_1D_F_")] ulong a) + [ValueSource("_1D_F_X_")] ulong z, + [ValueSource("_1D_F_X_")] ulong a) { opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); @@ -1893,7 +2082,7 @@ namespace Ryujinx.Tests.Cpu SingleOpcode(opcodes, v0: v0, v1: v1); - CompareAgainstUnicorn(); + CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsD); // unsigned } [Test, Pairwise] [Explicit] @@ -1929,7 +2118,7 @@ namespace Ryujinx.Tests.Cpu SingleOpcode(opcodes, v0: v0, v1: v1); - CompareAgainstUnicorn(); + CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsD); // unsigned } [Test, Pairwise] diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs index 2f8604ebac..63e0bda83c 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs @@ -8,26 +8,6 @@ namespace Ryujinx.Tests.Cpu { public class CpuTestSimdArithmetic : CpuTest { - [TestCase(0x00000000u, 0x7F800000u)] - [TestCase(0x80000000u, 0xFF800000u)] - [TestCase(0x00FFF000u, 0x7E000000u)] - [TestCase(0x41200000u, 0x3DCC8000u)] - [TestCase(0xC1200000u, 0xBDCC8000u)] - [TestCase(0x001FFFFFu, 0x7F800000u)] - [TestCase(0x007FF000u, 0x7E800000u)] - public void Frecpe_S(uint a, uint result) - { - uint opcode = 0x5EA1D820; // FRECPE S0, S1 - - Vector128 v1 = MakeVectorE0(a); - - CpuThreadState threadState = SingleOpcode(opcode, v1: v1); - - Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result)); - - CompareAgainstUnicorn(); - } - [TestCase(0x3FE66666u, false, 0x40000000u)] [TestCase(0x3F99999Au, false, 0x3F800000u)] [TestCase(0x404CCCCDu, false, 0x40400000u)] @@ -601,19 +581,5 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - - [TestCase(0x41200000u, 0x3EA18000u)] - public void Frsqrte_S(uint a, uint result) - { - uint opcode = 0x7EA1D820; // FRSQRTE S0, S1 - - Vector128 v1 = MakeVectorE0(a); - - CpuThreadState threadState = SingleOpcode(opcode, v1: v1); - - Assert.That(GetVectorE0(threadState.V0), Is.EqualTo(result)); - - CompareAgainstUnicorn(); - } } } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs b/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs new file mode 100644 index 0000000000..ff8e802708 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs @@ -0,0 +1,672 @@ +#define SimdCvt + +using NUnit.Framework; + +using System; +using System.Collections.Generic; +using System.Runtime.Intrinsics; + +namespace Ryujinx.Tests.Cpu +{ + [Category("SimdCvt")] + public sealed class CpuTestSimdCvt : CpuTest + { +#if SimdCvt + +#region "ValueSource (Types)" + private static IEnumerable _1S_F_WX_() + { + // int + yield return 0x00000000CF000001ul; // -2.1474839E9f (-2147483904) + yield return 0x00000000CF000000ul; // -2.14748365E9f (-2147483648) + yield return 0x00000000CEFFFFFFul; // -2.14748352E9f (-2147483520) + yield return 0x000000004F000001ul; // 2.1474839E9f (2147483904) + yield return 0x000000004F000000ul; // 2.14748365E9f (2147483648) + yield return 0x000000004EFFFFFFul; // 2.14748352E9f (2147483520) + + // long + yield return 0x00000000DF000001ul; // -9.223373E18f (-9223373136366403584) + yield return 0x00000000DF000000ul; // -9.223372E18f (-9223372036854775808) + yield return 0x00000000DEFFFFFFul; // -9.2233715E18f (-9223371487098961920) + yield return 0x000000005F000001ul; // 9.223373E18f (9223373136366403584) + yield return 0x000000005F000000ul; // 9.223372E18f (9223372036854775808) + yield return 0x000000005EFFFFFFul; // 9.2233715E18f (9223371487098961920) + + // uint + yield return 0x000000004F800001ul; // 4.2949678E9f (4294967808) + yield return 0x000000004F800000ul; // 4.2949673E9f (4294967296) + yield return 0x000000004F7FFFFFul; // 4.29496704E9f (4294967040) + + // ulong + yield return 0x000000005F800001ul; // 1.8446746E19f (18446746272732807168) + yield return 0x000000005F800000ul; // 1.8446744E19f (18446744073709551616) + yield return 0x000000005F7FFFFFul; // 1.8446743E19f (18446742974197923840) + + yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x0000000080800000ul; // -Min Normal + yield return 0x00000000807FFFFFul; // -Max Subnormal + yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0000000000800000ul; // +Min Normal + yield return 0x00000000007FFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x0000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0x00000000FF800000ul; // -Infinity + yield return 0x000000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) + yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong grbg = TestContext.CurrentContext.Random.NextUInt(); + + ulong rnd1 = (uint)BitConverter.SingleToInt32Bits( + (float)((int)TestContext.CurrentContext.Random.NextUInt())); + ulong rnd2 = (uint)BitConverter.SingleToInt32Bits( + (float)((long)TestContext.CurrentContext.Random.NextULong())); + ulong rnd3 = (uint)BitConverter.SingleToInt32Bits( + (float)((uint)TestContext.CurrentContext.Random.NextUInt())); + ulong rnd4 = (uint)BitConverter.SingleToInt32Bits( + (float)((ulong)TestContext.CurrentContext.Random.NextULong())); + + ulong rnd5 = GenNormalS(); + ulong rnd6 = GenSubnormalS(); + + yield return (grbg << 32) | rnd1; + yield return (grbg << 32) | rnd2; + yield return (grbg << 32) | rnd3; + yield return (grbg << 32) | rnd4; + + yield return (grbg << 32) | rnd5; + yield return (grbg << 32) | rnd6; + } + } + + private static IEnumerable _1D_F_WX_() + { + // int + yield return 0xC1E0000000200000ul; // -2147483649.0000000d (-2147483649) + yield return 0xC1E0000000000000ul; // -2147483648.0000000d (-2147483648) + yield return 0xC1DFFFFFFFC00000ul; // -2147483647.0000000d (-2147483647) + yield return 0x41E0000000200000ul; // 2147483649.0000000d (2147483649) + yield return 0x41E0000000000000ul; // 2147483648.0000000d (2147483648) + yield return 0x41DFFFFFFFC00000ul; // 2147483647.0000000d (2147483647) + + // long + yield return 0xC3E0000000000001ul; // -9.2233720368547780E18d (-9223372036854778000) + yield return 0xC3E0000000000000ul; // -9.2233720368547760E18d (-9223372036854776000) + yield return 0xC3DFFFFFFFFFFFFFul; // -9.2233720368547750E18d (-9223372036854775000) + yield return 0x43E0000000000001ul; // 9.2233720368547780E18d (9223372036854778000) + yield return 0x43E0000000000000ul; // 9.2233720368547760E18d (9223372036854776000) + yield return 0x43DFFFFFFFFFFFFFul; // 9.2233720368547750E18d (9223372036854775000) + + // uint + yield return 0x41F0000000100000ul; // 4294967297.0000000d (4294967297) + yield return 0x41F0000000000000ul; // 4294967296.0000000d (4294967296) + yield return 0x41EFFFFFFFE00000ul; // 4294967295.0000000d (4294967295) + + // ulong + yield return 0x43F0000000000001ul; // 1.8446744073709556e19d (18446744073709556000) + yield return 0x43F0000000000000ul; // 1.8446744073709552E19d (18446744073709552000) + yield return 0x43EFFFFFFFFFFFFFul; // 1.8446744073709550e19d (18446744073709550000) + + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0x8010000000000000ul; // -Min Normal + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x0010000000000000ul; // +Min Normal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000000000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFFF0000000000000ul; // -Infinity + yield return 0x7FF0000000000000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = (ulong)BitConverter.DoubleToInt64Bits( + (double)((int)TestContext.CurrentContext.Random.NextUInt())); + ulong rnd2 = (ulong)BitConverter.DoubleToInt64Bits( + (double)((long)TestContext.CurrentContext.Random.NextULong())); + ulong rnd3 = (ulong)BitConverter.DoubleToInt64Bits( + (double)((uint)TestContext.CurrentContext.Random.NextUInt())); + ulong rnd4 = (ulong)BitConverter.DoubleToInt64Bits( + (double)((ulong)TestContext.CurrentContext.Random.NextULong())); + + ulong rnd5 = GenNormalD(); + ulong rnd6 = GenSubnormalD(); + + yield return rnd1; + yield return rnd2; + yield return rnd3; + yield return rnd4; + + yield return rnd5; + yield return rnd6; + } + } + + private static uint[] _W_() + { + return new uint[] { 0x00000000u, 0x7FFFFFFFu, + 0x80000000u, 0xFFFFFFFFu }; + } + + private static ulong[] _X_() + { + return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + } +#endregion + +#region "ValueSource (Opcodes)" + private static uint[] _F_Cvt_AMPZ_SU_Gp_SW_() + { + return new uint[] + { + 0x1E240000u, // FCVTAS W0, S0 + 0x1E250000u, // FCVTAU W0, S0 + 0x1E300000u, // FCVTMS W0, S0 + 0x1E310000u, // FCVTMU W0, S0 + 0x1E280000u, // FCVTPS W0, S0 + 0x1E290000u, // FCVTPU W0, S0 + 0x1E380000u, // FCVTZS W0, S0 + 0x1E390000u // FCVTZU W0, S0 + }; + } + + private static uint[] _F_Cvt_AMPZ_SU_Gp_SX_() + { + return new uint[] + { + 0x9E240000u, // FCVTAS X0, S0 + 0x9E250000u, // FCVTAU X0, S0 + 0x9E300000u, // FCVTMS X0, S0 + 0x9E310000u, // FCVTMU X0, S0 + 0x9E280000u, // FCVTPS X0, S0 + 0x9E290000u, // FCVTPU X0, S0 + 0x9E380000u, // FCVTZS X0, S0 + 0x9E390000u // FCVTZU X0, S0 + }; + } + + private static uint[] _F_Cvt_AMPZ_SU_Gp_DW_() + { + return new uint[] + { + 0x1E640000u, // FCVTAS W0, D0 + 0x1E650000u, // FCVTAU W0, D0 + 0x1E700000u, // FCVTMS W0, D0 + 0x1E710000u, // FCVTMU W0, D0 + 0x1E680000u, // FCVTPS W0, D0 + 0x1E690000u, // FCVTPU W0, D0 + 0x1E780000u, // FCVTZS W0, D0 + 0x1E790000u // FCVTZU W0, D0 + }; + } + + private static uint[] _F_Cvt_AMPZ_SU_Gp_DX_() + { + return new uint[] + { + 0x9E640000u, // FCVTAS X0, D0 + 0x9E650000u, // FCVTAU X0, D0 + 0x9E700000u, // FCVTMS X0, D0 + 0x9E710000u, // FCVTMU X0, D0 + 0x9E680000u, // FCVTPS X0, D0 + 0x9E690000u, // FCVTPU X0, D0 + 0x9E780000u, // FCVTZS X0, D0 + 0x9E790000u // FCVTZU X0, D0 + }; + } + + private static uint[] _F_Cvt_Z_SU_Gp_Fixed_SW_() + { + return new uint[] + { + 0x1E188000u, // FCVTZS W0, S0, #32 + 0x1E198000u // FCVTZU W0, S0, #32 + }; + } + + private static uint[] _F_Cvt_Z_SU_Gp_Fixed_SX_() + { + return new uint[] + { + 0x9E180000u, // FCVTZS X0, S0, #64 + 0x9E190000u // FCVTZU X0, S0, #64 + }; + } + + private static uint[] _F_Cvt_Z_SU_Gp_Fixed_DW_() + { + return new uint[] + { + 0x1E588000u, // FCVTZS W0, D0, #32 + 0x1E598000u // FCVTZU W0, D0, #32 + }; + } + + private static uint[] _F_Cvt_Z_SU_Gp_Fixed_DX_() + { + return new uint[] + { + 0x9E580000u, // FCVTZS X0, D0, #64 + 0x9E590000u // FCVTZU X0, D0, #64 + }; + } + + private static uint[] _SU_Cvt_F_Gp_WS_() + { + return new uint[] + { + 0x1E220000u, // SCVTF S0, W0 + 0x1E230000u // UCVTF S0, W0 + }; + } + + private static uint[] _SU_Cvt_F_Gp_WD_() + { + return new uint[] + { + 0x1E620000u, // SCVTF D0, W0 + 0x1E630000u // UCVTF D0, W0 + }; + } + + private static uint[] _SU_Cvt_F_Gp_XS_() + { + return new uint[] + { + 0x9E220000u, // SCVTF S0, X0 + 0x9E230000u // UCVTF S0, X0 + }; + } + + private static uint[] _SU_Cvt_F_Gp_XD_() + { + return new uint[] + { + 0x9E620000u, // SCVTF D0, X0 + 0x9E630000u // UCVTF D0, X0 + }; + } + + private static uint[] _SU_Cvt_F_Gp_Fixed_WS_() + { + return new uint[] + { + 0x1E028000u, // SCVTF S0, W0, #32 + 0x1E038000u // UCVTF S0, W0, #32 + }; + } + + private static uint[] _SU_Cvt_F_Gp_Fixed_WD_() + { + return new uint[] + { + 0x1E428000u, // SCVTF D0, W0, #32 + 0x1E438000u // UCVTF D0, W0, #32 + }; + } + + private static uint[] _SU_Cvt_F_Gp_Fixed_XS_() + { + return new uint[] + { + 0x9E020000u, // SCVTF S0, X0, #64 + 0x9E030000u // UCVTF S0, X0, #64 + }; + } + + private static uint[] _SU_Cvt_F_Gp_Fixed_XD_() + { + return new uint[] + { + 0x9E420000u, // SCVTF D0, X0, #64 + 0x9E430000u // UCVTF D0, X0, #64 + }; + } +#endregion + + private const int RndCnt = 2; + private const int RndCntFBits = 2; + + private static readonly bool NoZeros = false; + private static readonly bool NoInfs = false; + private static readonly bool NoNaNs = false; + + [Test, Pairwise] [Explicit] + public void F_Cvt_AMPZ_SU_Gp_SW([ValueSource("_F_Cvt_AMPZ_SU_Gp_SW_")] uint opcodes, + [Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_1S_F_WX_")] ulong a) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + ulong x0 = (ulong)TestContext.CurrentContext.Random.NextUInt() << 32; + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, x0: x0, x31: w31, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void F_Cvt_AMPZ_SU_Gp_SX([ValueSource("_F_Cvt_AMPZ_SU_Gp_SX_")] uint opcodes, + [Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_1S_F_WX_")] ulong a) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + ulong x31 = TestContext.CurrentContext.Random.NextULong(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, x31: x31, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void F_Cvt_AMPZ_SU_Gp_DW([ValueSource("_F_Cvt_AMPZ_SU_Gp_DW_")] uint opcodes, + [Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_1D_F_WX_")] ulong a) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + ulong x0 = (ulong)TestContext.CurrentContext.Random.NextUInt() << 32; + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, x0: x0, x31: w31, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void F_Cvt_AMPZ_SU_Gp_DX([ValueSource("_F_Cvt_AMPZ_SU_Gp_DX_")] uint opcodes, + [Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_1D_F_WX_")] ulong a) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + ulong x31 = TestContext.CurrentContext.Random.NextULong(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, x31: x31, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void F_Cvt_Z_SU_Gp_Fixed_SW([ValueSource("_F_Cvt_Z_SU_Gp_Fixed_SW_")] uint opcodes, + [Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_1S_F_WX_")] ulong a, + [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits) + { + uint scale = (64u - fBits) & 0x3Fu; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (scale << 10); + + ulong x0 = (ulong)TestContext.CurrentContext.Random.NextUInt() << 32; + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, x0: x0, x31: w31, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void F_Cvt_Z_SU_Gp_Fixed_SX([ValueSource("_F_Cvt_Z_SU_Gp_Fixed_SX_")] uint opcodes, + [Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_1S_F_WX_")] ulong a, + [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + { + uint scale = (64u - fBits) & 0x3Fu; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (scale << 10); + + ulong x31 = TestContext.CurrentContext.Random.NextULong(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, x31: x31, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void F_Cvt_Z_SU_Gp_Fixed_DW([ValueSource("_F_Cvt_Z_SU_Gp_Fixed_DW_")] uint opcodes, + [Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_1D_F_WX_")] ulong a, + [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits) + { + uint scale = (64u - fBits) & 0x3Fu; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (scale << 10); + + ulong x0 = (ulong)TestContext.CurrentContext.Random.NextUInt() << 32; + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, x0: x0, x31: w31, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void F_Cvt_Z_SU_Gp_Fixed_DX([ValueSource("_F_Cvt_Z_SU_Gp_Fixed_DX_")] uint opcodes, + [Values(0u, 31u)] uint rd, + [Values(1u)] uint rn, + [ValueSource("_1D_F_WX_")] ulong a, + [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + { + uint scale = (64u - fBits) & 0x3Fu; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (scale << 10); + + ulong x31 = TestContext.CurrentContext.Random.NextULong(); + Vector128 v1 = MakeVectorE0(a); + + SingleOpcode(opcodes, x31: x31, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_Gp_WS([ValueSource("_SU_Cvt_F_Gp_WS_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 31u)] uint rn, + [ValueSource("_W_")] [Random(RndCnt)] uint wn) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + + SingleOpcode(opcodes, x1: wn, x31: w31, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_Gp_WD([ValueSource("_SU_Cvt_F_Gp_WD_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 31u)] uint rn, + [ValueSource("_W_")] [Random(RndCnt)] uint wn) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + + SingleOpcode(opcodes, x1: wn, x31: w31, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_Gp_XS([ValueSource("_SU_Cvt_F_Gp_XS_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 31u)] uint rn, + [ValueSource("_X_")] [Random(RndCnt)] ulong xn) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + ulong x31 = TestContext.CurrentContext.Random.NextULong(); + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + + SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_Gp_XD([ValueSource("_SU_Cvt_F_Gp_XD_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 31u)] uint rn, + [ValueSource("_X_")] [Random(RndCnt)] ulong xn) + { + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + + ulong x31 = TestContext.CurrentContext.Random.NextULong(); + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + + SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0); + + CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsD); // unsigned + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_Gp_Fixed_WS([ValueSource("_SU_Cvt_F_Gp_Fixed_WS_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 31u)] uint rn, + [ValueSource("_W_")] [Random(RndCnt)] uint wn, + [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits) + { + uint scale = (64u - fBits) & 0x3Fu; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (scale << 10); + + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + + SingleOpcode(opcodes, x1: wn, x31: w31, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_Gp_Fixed_WD([ValueSource("_SU_Cvt_F_Gp_Fixed_WD_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 31u)] uint rn, + [ValueSource("_W_")] [Random(RndCnt)] uint wn, + [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits) + { + uint scale = (64u - fBits) & 0x3Fu; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (scale << 10); + + uint w31 = TestContext.CurrentContext.Random.NextUInt(); + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + + SingleOpcode(opcodes, x1: wn, x31: w31, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_Gp_Fixed_XS([ValueSource("_SU_Cvt_F_Gp_Fixed_XS_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 31u)] uint rn, + [ValueSource("_X_")] [Random(RndCnt)] ulong xn, + [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + { + uint scale = (64u - fBits) & 0x3Fu; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (scale << 10); + + ulong x31 = TestContext.CurrentContext.Random.NextULong(); + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + + SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_Gp_Fixed_XD([ValueSource("_SU_Cvt_F_Gp_Fixed_XD_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 31u)] uint rn, + [ValueSource("_X_")] [Random(RndCnt)] ulong xn, + [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + { + uint scale = (64u - fBits) & 0x3Fu; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (scale << 10); + + ulong x31 = TestContext.CurrentContext.Random.NextULong(); + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + + SingleOpcode(opcodes, x1: xn, x31: x31, v0: v0); + + CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsD); // unsigned + } +#endif + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs b/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs index f232989f77..b8548169be 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdExt.cs @@ -19,7 +19,8 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 2; + private const int RndCnt = 2; + private const int RndCntIndex = 2; [Test, Pairwise, Description("EXT .8B, .8B, .8B, #")] public void Ext_V_8B([Values(0u)] uint rd, @@ -28,7 +29,7 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_8B_")] [Random(RndCnt)] ulong z, [ValueSource("_8B_")] [Random(RndCnt)] ulong a, [ValueSource("_8B_")] [Random(RndCnt)] ulong b, - [Range(0u, 7u)] uint index) + [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index) { uint imm4 = index & 0x7u; @@ -52,7 +53,7 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_8B_")] [Random(RndCnt)] ulong z, [ValueSource("_8B_")] [Random(RndCnt)] ulong a, [ValueSource("_8B_")] [Random(RndCnt)] ulong b, - [Range(0u, 15u)] uint index) + [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint index) { uint imm4 = index & 0xFu; diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs b/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs new file mode 100644 index 0000000000..bb6e117395 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdImm.cs @@ -0,0 +1,267 @@ +#define SimdImm + +using NUnit.Framework; + +using System.Collections.Generic; +using System.Runtime.Intrinsics; + +namespace Ryujinx.Tests.Cpu +{ + [Category("SimdImm")] + public sealed class CpuTestSimdImm : CpuTest + { +#if SimdImm + +#region "Helper methods" + // abcdefgh -> aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffffgggggggghhhhhhhh + private static ulong ExpandImm8(byte imm8) + { + ulong imm64 = 0ul; + + for (int i = 0, j = 0; i < 8; i++, j += 8) + { + if (((imm8 >> i) & 0b1) != 0) + { + imm64 |= 0b11111111ul << j; + } + } + + return imm64; + } + + // aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffffgggggggghhhhhhhh -> abcdefgh + private static byte ShrinkImm64(ulong imm64) + { + byte imm8 = 0; + + for (int i = 0, j = 0; i < 8; i++, j += 8) + { + if (((imm64 >> j) & 0b11111111ul) != 0ul) // Note: no format check. + { + imm8 |= (byte)(0b1 << i); + } + } + + return imm8; + } +#endregion + +#region "ValueSource (Types)" + private static IEnumerable _8BIT_IMM_() + { + yield return 0x00; + yield return 0x7F; + yield return 0x80; + yield return 0xFF; + + for (int cnt = 1; cnt <= RndCntImm8; cnt++) + { + byte imm8 = TestContext.CurrentContext.Random.NextByte(); + + yield return imm8; + } + } + + private static IEnumerable _64BIT_IMM_() + { + yield return ExpandImm8(0x00); + yield return ExpandImm8(0x7F); + yield return ExpandImm8(0x80); + yield return ExpandImm8(0xFF); + + for (int cnt = 1; cnt <= RndCntImm64; cnt++) + { + byte imm8 = TestContext.CurrentContext.Random.NextByte(); + + yield return ExpandImm8(imm8); + } + } +#endregion + +#region "ValueSource (Opcodes)" + private static uint[] _Movi_V_8bit_() + { + return new uint[] + { + 0x0F00E400u // MOVI V0.8B, #0 + }; + } + + private static uint[] _Movi_Mvni_V_16bit_shifted_imm_() + { + return new uint[] + { + 0x0F008400u, // MOVI V0.4H, #0 + 0x2F008400u // MVNI V0.4H, #0 + }; + } + + private static uint[] _Movi_Mvni_V_32bit_shifted_imm_() + { + return new uint[] + { + 0x0F000400u, // MOVI V0.2S, #0 + 0x2F000400u // MVNI V0.2S, #0 + }; + } + + private static uint[] _Movi_Mvni_V_32bit_shifting_ones_() + { + return new uint[] + { + 0x0F00C400u, // MOVI V0.2S, #0, MSL #8 + 0x2F00C400u // MVNI V0.2S, #0, MSL #8 + }; + } + + private static uint[] _Movi_V_64bit_scalar_() + { + return new uint[] + { + 0x2F00E400u // MOVI D0, #0 + }; + } + + private static uint[] _Movi_V_64bit_vector_() + { + return new uint[] + { + 0x6F00E400u // MOVI V0.2D, #0 + }; + } +#endregion + + private const int RndCntImm8 = 2; + private const int RndCntImm64 = 2; + + [Test, Pairwise] + public void Movi_V_8bit([ValueSource("_Movi_V_8bit_")] uint opcodes, + [Values(0u)] uint rd, + [ValueSource("_8BIT_IMM_")] byte imm8, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> + { + uint abc = (imm8 & 0xE0u) >> 5; + uint defgh = (imm8 & 0x1Fu); + + opcodes |= ((rd & 31) << 0); + opcodes |= (abc << 16) | (defgh << 5); + opcodes |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(q == 0u ? z : 0ul); + + SingleOpcode(opcodes, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void Movi_Mvni_V_16bit_shifted_imm([ValueSource("_Movi_Mvni_V_16bit_shifted_imm_")] uint opcodes, + [Values(0u)] uint rd, + [ValueSource("_8BIT_IMM_")] byte imm8, + [Values(0b0u, 0b1u)] uint amount, // <0, 8> + [Values(0b0u, 0b1u)] uint q) // <4H, 8H> + { + uint abc = (imm8 & 0xE0u) >> 5; + uint defgh = (imm8 & 0x1Fu); + + opcodes |= ((rd & 31) << 0); + opcodes |= (abc << 16) | (defgh << 5); + opcodes |= ((amount & 1) << 13); + opcodes |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(q == 0u ? z : 0ul); + + SingleOpcode(opcodes, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void Movi_Mvni_V_32bit_shifted_imm([ValueSource("_Movi_Mvni_V_32bit_shifted_imm_")] uint opcodes, + [Values(0u)] uint rd, + [ValueSource("_8BIT_IMM_")] byte imm8, + [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint amount, // <0, 8, 16, 24> + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + uint abc = (imm8 & 0xE0u) >> 5; + uint defgh = (imm8 & 0x1Fu); + + opcodes |= ((rd & 31) << 0); + opcodes |= (abc << 16) | (defgh << 5); + opcodes |= ((amount & 3) << 13); + opcodes |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(q == 0u ? z : 0ul); + + SingleOpcode(opcodes, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void Movi_Mvni_V_32bit_shifting_ones([ValueSource("_Movi_Mvni_V_32bit_shifting_ones_")] uint opcodes, + [Values(0u)] uint rd, + [ValueSource("_8BIT_IMM_")] byte imm8, + [Values(0b0u, 0b1u)] uint amount, // <8, 16> + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + uint abc = (imm8 & 0xE0u) >> 5; + uint defgh = (imm8 & 0x1Fu); + + opcodes |= ((rd & 31) << 0); + opcodes |= (abc << 16) | (defgh << 5); + opcodes |= ((amount & 1) << 12); + opcodes |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(q == 0u ? z : 0ul); + + SingleOpcode(opcodes, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void Movi_V_64bit_scalar([ValueSource("_Movi_V_64bit_scalar_")] uint opcodes, + [Values(0u)] uint rd, + [ValueSource("_64BIT_IMM_")] ulong imm) + { + byte imm8 = ShrinkImm64(imm); + + uint abc = (imm8 & 0xE0u) >> 5; + uint defgh = (imm8 & 0x1Fu); + + opcodes |= ((rd & 31) << 0); + opcodes |= (abc << 16) | (defgh << 5); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE1(z); + + SingleOpcode(opcodes, v0: v0); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void Movi_V_64bit_vector([ValueSource("_Movi_V_64bit_vector_")] uint opcodes, + [Values(0u)] uint rd, + [ValueSource("_64BIT_IMM_")] ulong imm) + { + byte imm8 = ShrinkImm64(imm); + + uint abc = (imm8 & 0xE0u) >> 5; + uint defgh = (imm8 & 0x1Fu); + + opcodes |= ((rd & 31) << 0); + opcodes |= (abc << 16) | (defgh << 5); + + SingleOpcode(opcodes); + + CompareAgainstUnicorn(); + } +#endif + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs b/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs index 4ca54a2b42..fe93f06e37 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs @@ -67,7 +67,8 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 2; + private const int RndCnt = 2; + private const int RndCntIndex = 2; [Test, Pairwise, Description("DUP ., ")] public void Dup_Gp_W([Values(0u)] uint rd, @@ -109,7 +110,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("DUP B0, V1.B[]")] public void Dup_S_B([ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Range(0u, 15u)] uint index) + [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint index) { const int size = 0; @@ -129,7 +130,7 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("DUP H0, V1.H[]")] public void Dup_S_H([ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Range(0u, 7u)] uint index) + [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index) { const int size = 1; @@ -192,7 +193,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong z, [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Range(0u, 15u)] uint index, + [Values(0u, 15u)] [Random(1u, 14u, RndCntIndex)] uint index, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { const int size = 0; @@ -217,7 +218,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_4H_")] [Random(RndCnt)] ulong z, [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Range(0u, 7u)] uint index, + [Values(0u, 7u)] [Random(1u, 6u, RndCntIndex)] uint index, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { const int size = 1; diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index 8d2f4e9a34..1c418341b6 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -434,6 +434,26 @@ namespace Ryujinx.Tests.Cpu }; } + private static uint[] _Mla_Mls_Mul_V_8B_4H_2S_() + { + return new uint[] + { + 0x0E209400u, // MLA V0.8B, V0.8B, V0.8B + 0x2E209400u, // MLS V0.8B, V0.8B, V0.8B + 0x0E209C00u // MUL V0.8B, V0.8B, V0.8B + }; + } + + private static uint[] _Mla_Mls_Mul_V_16B_8H_4S_() + { + return new uint[] + { + 0x4E209400u, // MLA V0.16B, V0.16B, V0.16B + 0x6E209400u, // MLS V0.16B, V0.16B, V0.16B + 0x4E209C00u // MUL V0.16B, V0.16B, V0.16B + }; + } + private static uint[] _Sha1c_Sha1m_Sha1p_Sha1su0_V_() { return new uint[] @@ -1786,6 +1806,50 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(Fpsr.Ioc | Fpsr.Idc, FpSkips.IfUnderflow, FpTolerances.UpToOneUlpsD); } + [Test, Pairwise] + public void Mla_Mls_Mul_V_8B_4H_2S([ValueSource("_Mla_Mls_Mul_V_8B_4H_2S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((size & 3) << 22); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0(a); + Vector128 v2 = MakeVectorE0(b); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] + public void Mla_Mls_Mul_V_16B_8H_4S([ValueSource("_Mla_Mls_Mul_V_16B_8H_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [Values(2u, 0u)] uint rm, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong a, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong b, + [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((size & 3) << 22); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + Vector128 v2 = MakeVectorE0E1(b, b); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); + + CompareAgainstUnicorn(); + } + [Test, Pairwise, Description("ORN ., ., .")] public void Orn_V_8B([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs b/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs index f026158cc7..c08949a5ae 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdShImm.cs @@ -2,6 +2,8 @@ using NUnit.Framework; +using System; +using System.Collections.Generic; using System.Runtime.Intrinsics; namespace Ryujinx.Tests.Cpu @@ -47,9 +49,169 @@ namespace Ryujinx.Tests.Cpu return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; } + + private static IEnumerable _2S_F_W_() + { + // int + yield return 0xCF000001CF000001ul; // -2.1474839E9f (-2147483904) + yield return 0xCF000000CF000000ul; // -2.14748365E9f (-2147483648) + yield return 0xCEFFFFFFCEFFFFFFul; // -2.14748352E9f (-2147483520) + yield return 0x4F0000014F000001ul; // 2.1474839E9f (2147483904) + yield return 0x4F0000004F000000ul; // 2.14748365E9f (2147483648) + yield return 0x4EFFFFFF4EFFFFFFul; // 2.14748352E9f (2147483520) + + // uint + yield return 0x4F8000014F800001ul; // 4.2949678E9f (4294967808) + yield return 0x4F8000004F800000ul; // 4.2949673E9f (4294967296) + yield return 0x4F7FFFFF4F7FFFFFul; // 4.29496704E9f (4294967040) + + yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x8080000080800000ul; // -Min Normal + yield return 0x807FFFFF807FFFFFul; // -Max Subnormal + yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0080000000800000ul; // +Min Normal + yield return 0x007FFFFF007FFFFFul; // +Max Subnormal + yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFF800000FF800000ul; // -Infinity + yield return 0x7F8000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) + yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = (uint)BitConverter.SingleToInt32Bits( + (float)((int)TestContext.CurrentContext.Random.NextUInt())); + ulong rnd2 = (uint)BitConverter.SingleToInt32Bits( + (float)((uint)TestContext.CurrentContext.Random.NextUInt())); + + ulong rnd3 = GenNormalS(); + ulong rnd4 = GenSubnormalS(); + + yield return (rnd1 << 32) | rnd1; + yield return (rnd2 << 32) | rnd2; + + yield return (rnd3 << 32) | rnd3; + yield return (rnd4 << 32) | rnd4; + } + } + + private static IEnumerable _1D_F_X_() + { + // long + yield return 0xC3E0000000000001ul; // -9.2233720368547780E18d (-9223372036854778000) + yield return 0xC3E0000000000000ul; // -9.2233720368547760E18d (-9223372036854776000) + yield return 0xC3DFFFFFFFFFFFFFul; // -9.2233720368547750E18d (-9223372036854775000) + yield return 0x43E0000000000001ul; // 9.2233720368547780E18d (9223372036854778000) + yield return 0x43E0000000000000ul; // 9.2233720368547760E18d (9223372036854776000) + yield return 0x43DFFFFFFFFFFFFFul; // 9.2233720368547750E18d (9223372036854775000) + + // ulong + yield return 0x43F0000000000001ul; // 1.8446744073709556e19d (18446744073709556000) + yield return 0x43F0000000000000ul; // 1.8446744073709552E19d (18446744073709552000) + yield return 0x43EFFFFFFFFFFFFFul; // 1.8446744073709550e19d (18446744073709550000) + + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0x8010000000000000ul; // -Min Normal + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x0010000000000000ul; // +Min Normal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000000000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFFF0000000000000ul; // -Infinity + yield return 0x7FF0000000000000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + } + + for (int cnt = 1; cnt <= RndCnt; cnt++) + { + ulong rnd1 = (ulong)BitConverter.DoubleToInt64Bits( + (double)((long)TestContext.CurrentContext.Random.NextULong())); + ulong rnd2 = (ulong)BitConverter.DoubleToInt64Bits( + (double)((ulong)TestContext.CurrentContext.Random.NextULong())); + + ulong rnd3 = GenNormalD(); + ulong rnd4 = GenSubnormalD(); + + yield return rnd1; + yield return rnd2; + + yield return rnd3; + yield return rnd4; + } + } #endregion #region "ValueSource (Opcodes)" + private static uint[] _F_Cvt_Z_SU_V_Fixed_2S_4S_() + { + return new uint[] + { + 0x0F20FC00u, // FCVTZS V0.2S, V0.2S, #32 + 0x2F20FC00u // FCVTZU V0.2S, V0.2S, #32 + }; + } + + private static uint[] _F_Cvt_Z_SU_V_Fixed_2D_() + { + return new uint[] + { + 0x4F40FC00u, // FCVTZS V0.2D, V0.2D, #64 + 0x6F40FC00u // FCVTZU V0.2D, V0.2D, #64 + }; + } + + private static uint[] _SU_Cvt_F_V_Fixed_2S_4S_() + { + return new uint[] + { + 0x0F20E400u, // SCVTF V0.2S, V0.2S, #32 + 0x2F20E400u // UCVTF V0.2S, V0.2S, #32 + }; + } + + private static uint[] _SU_Cvt_F_V_Fixed_2D_() + { + return new uint[] + { + 0x4F40E400u, // SCVTF V0.2D, V0.2D, #64 + 0x6F40E400u // UCVTF V0.2D, V0.2D, #64 + }; + } + private static uint[] _SU_Shll_V_8B8H_16B8H_() { return new uint[] @@ -258,14 +420,108 @@ namespace Ryujinx.Tests.Cpu } #endregion - private const int RndCnt = 2; + private const int RndCnt = 2; + private const int RndCntFBits = 2; + private const int RndCntShift = 2; + + private static readonly bool NoZeros = false; + private static readonly bool NoInfs = false; + private static readonly bool NoNaNs = false; + + [Test, Pairwise] [Explicit] + public void F_Cvt_Z_SU_V_Fixed_2S_4S([ValueSource("_F_Cvt_Z_SU_V_Fixed_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_F_W_")] ulong z, + [ValueSource("_2S_F_W_")] ulong a, + [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + uint immHb = (64 - fBits) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void F_Cvt_Z_SU_V_Fixed_2D([ValueSource("_F_Cvt_Z_SU_V_Fixed_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_F_X_")] ulong z, + [ValueSource("_1D_F_X_")] ulong a, + [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + { + uint immHb = (128 - fBits) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_V_Fixed_2S_4S([ValueSource("_SU_Cvt_F_V_Fixed_2S_4S_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_2S_")] [Random(RndCnt)] ulong z, + [ValueSource("_2S_")] [Random(RndCnt)] ulong a, + [Values(1u, 32u)] [Random(2u, 31u, RndCntFBits)] uint fBits, + [Values(0b0u, 0b1u)] uint q) // <2S, 4S> + { + uint immHb = (64 - fBits) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + opcodes |= ((q & 1) << 30); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a * q); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise] [Explicit] + public void SU_Cvt_F_V_Fixed_2D([ValueSource("_SU_Cvt_F_V_Fixed_2D_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u, 0u)] uint rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong z, + [ValueSource("_1D_")] [Random(RndCnt)] ulong a, + [Values(1u, 64u)] [Random(2u, 63u, RndCntFBits)] uint fBits) + { + uint immHb = (128 - fBits) & 0x7F; + + opcodes |= ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= (immHb << 16); + + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(a, a); + + SingleOpcode(opcodes, v0: v0, v1: v1); + + CompareAgainstUnicorn(fpTolerances: FpTolerances.UpToOneUlpsD); // unsigned + } [Test, Pairwise, Description("SHL , , #")] public void Shl_S_D([Values(0u)] uint rd, [Values(1u, 0u)] uint rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong z, [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Range(0u, 63u)] uint shift) + [Values(0u, 63u)] [Random(1u, 62u, RndCntShift)] uint shift) { uint immHb = (64 + shift) & 0x7F; @@ -286,7 +542,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong z, [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Range(0u, 7u)] uint shift, + [Values(0u, 7u)] [Random(1u, 6u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { uint immHb = (8 + shift) & 0x7F; @@ -309,7 +565,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_4H_")] [Random(RndCnt)] ulong z, [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Range(0u, 15u)] uint shift, + [Values(0u, 15u)] [Random(1u, 14u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { uint immHb = (16 + shift) & 0x7F; @@ -332,7 +588,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_2S_")] [Random(RndCnt)] ulong z, [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [Range(0u, 31u)] uint shift, + [Values(0u, 31u)] [Random(1u, 30u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { uint immHb = (32 + shift) & 0x7F; @@ -355,7 +611,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong z, [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Range(0u, 63u)] uint shift) + [Values(0u, 63u)] [Random(1u, 62u, RndCntShift)] uint shift) { uint immHb = (64 + shift) & 0x7F; @@ -377,7 +633,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong z, [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Range(0u, 7u)] uint shift, + [Values(0u, 7u)] [Random(1u, 6u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <8B8H, 16B8H> { uint immHb = (8 + shift) & 0x7F; @@ -400,7 +656,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_4H_")] [Random(RndCnt)] ulong z, [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Range(0u, 15u)] uint shift, + [Values(0u, 15u)] [Random(1u, 14u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <4H4S, 8H4S> { uint immHb = (16 + shift) & 0x7F; @@ -423,7 +679,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_2S_")] [Random(RndCnt)] ulong z, [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [Range(0u, 31u)] uint shift, + [Values(0u, 31u)] [Random(1u, 30u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <2S2D, 4S2D> { uint immHb = (32 + shift) & 0x7F; @@ -446,7 +702,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong z, [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Range(1u, 64u)] uint shift) + [Values(1u, 64u)] [Random(2u, 63u, RndCntShift)] uint shift) { uint immHb = (128 - shift) & 0x7F; @@ -467,7 +723,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_8B_")] [Random(RndCnt)] ulong z, [ValueSource("_8B_")] [Random(RndCnt)] ulong a, - [Range(1u, 8u)] uint shift, + [Values(1u, 8u)] [Random(2u, 7u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <8B, 16B> { uint immHb = (16 - shift) & 0x7F; @@ -490,7 +746,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_4H_")] [Random(RndCnt)] ulong z, [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Range(1u, 16u)] uint shift, + [Values(1u, 16u)] [Random(2u, 15u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <4H, 8H> { uint immHb = (32 - shift) & 0x7F; @@ -513,7 +769,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_2S_")] [Random(RndCnt)] ulong z, [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [Range(1u, 32u)] uint shift, + [Values(1u, 32u)] [Random(2u, 31u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <2S, 4S> { uint immHb = (64 - shift) & 0x7F; @@ -536,7 +792,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong z, [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Range(1u, 64u)] uint shift) + [Values(1u, 64u)] [Random(2u, 63u, RndCntShift)] uint shift) { uint immHb = (128 - shift) & 0x7F; @@ -557,7 +813,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_4H_")] [Random(RndCnt)] ulong z, [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Range(1u, 8u)] uint shift, + [Values(1u, 8u)] [Random(2u, 7u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <8H8B, 8H16B> { uint immHb = (16 - shift) & 0x7F; @@ -580,7 +836,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_2S_")] [Random(RndCnt)] ulong z, [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [Range(1u, 16u)] uint shift, + [Values(1u, 16u)] [Random(2u, 15u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <4S4H, 4S8H> { uint immHb = (32 - shift) & 0x7F; @@ -603,7 +859,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong z, [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Range(1u, 32u)] uint shift, + [Values(1u, 32u)] [Random(2u, 31u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <2D2S, 2D4S> { uint immHb = (64 - shift) & 0x7F; @@ -626,7 +882,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_1H_")] [Random(RndCnt)] ulong z, [ValueSource("_1H_")] [Random(RndCnt)] ulong a, - [Range(1u, 8u)] uint shift) + [Values(1u, 8u)] [Random(2u, 7u, RndCntShift)] uint shift) { uint immHb = (16 - shift) & 0x7F; @@ -647,7 +903,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_1S_")] [Random(RndCnt)] ulong z, [ValueSource("_1S_")] [Random(RndCnt)] ulong a, - [Range(1u, 16u)] uint shift) + [Values(1u, 16u)] [Random(2u, 15u, RndCntShift)] uint shift) { uint immHb = (32 - shift) & 0x7F; @@ -668,7 +924,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong z, [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Range(1u, 32u)] uint shift) + [Values(1u, 32u)] [Random(2u, 31u, RndCntShift)] uint shift) { uint immHb = (64 - shift) & 0x7F; @@ -689,7 +945,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_4H_")] [Random(RndCnt)] ulong z, [ValueSource("_4H_")] [Random(RndCnt)] ulong a, - [Range(1u, 8u)] uint shift, + [Values(1u, 8u)] [Random(2u, 7u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <8H8B, 8H16B> { uint immHb = (16 - shift) & 0x7F; @@ -712,7 +968,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_2S_")] [Random(RndCnt)] ulong z, [ValueSource("_2S_")] [Random(RndCnt)] ulong a, - [Range(1u, 16u)] uint shift, + [Values(1u, 16u)] [Random(2u, 15u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <4S4H, 4S8H> { uint immHb = (32 - shift) & 0x7F; @@ -735,7 +991,7 @@ namespace Ryujinx.Tests.Cpu [Values(1u, 0u)] uint rn, [ValueSource("_1D_")] [Random(RndCnt)] ulong z, [ValueSource("_1D_")] [Random(RndCnt)] ulong a, - [Range(1u, 32u)] uint shift, + [Values(1u, 32u)] [Random(2u, 31u, RndCntShift)] uint shift, [Values(0b0u, 0b1u)] uint q) // <2D2S, 2D4S> { uint immHb = (64 - shift) & 0x7F; diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs b/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs new file mode 100644 index 0000000000..69195af206 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdTbl.cs @@ -0,0 +1,315 @@ +#define SimdTbl + +using NUnit.Framework; + +using System.Collections.Generic; +using System.Runtime.Intrinsics; + +namespace Ryujinx.Tests.Cpu +{ + [Category("SimdTbl")] + public sealed class CpuTestSimdTbl : CpuTest + { +#if SimdTbl + +#region "Helper methods" + private static ulong GenIdxsForTbls(int regs) + { + const byte idxInRngMin = (byte)0; + byte idxInRngMax = (byte)((16 * regs) - 1); + byte idxOutRngMin = (byte) (16 * regs); + const byte idxOutRngMax = (byte)255; + + ulong idxs = 0ul; + + for (int cnt = 1; cnt <= 8; cnt++) + { + ulong idxInRng = (ulong)TestContext.CurrentContext.Random.NextByte(idxInRngMin, idxInRngMax); + ulong idxOutRng = (ulong)TestContext.CurrentContext.Random.NextByte(idxOutRngMin, idxOutRngMax); + + ulong idx = TestContext.CurrentContext.Random.NextBool() ? idxInRng : idxOutRng; + + idxs = (idxs << 8) | idx; + } + + return idxs; + } +#endregion + +#region "ValueSource (Types)" + private static ulong[] _8B_() + { + return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0xFFFFFFFFFFFFFFFFul }; + } + + private static IEnumerable _GenIdxsForTbl1_() + { + yield return 0x0000000000000000ul; + yield return 0x7F7F7F7F7F7F7F7Ful; + yield return 0x8080808080808080ul; + yield return 0xFFFFFFFFFFFFFFFFul; + + for (int cnt = 1; cnt <= RndCntIdxs; cnt++) + { + yield return GenIdxsForTbls(regs: 1); + } + } + + private static IEnumerable _GenIdxsForTbl2_() + { + yield return 0x0000000000000000ul; + yield return 0x7F7F7F7F7F7F7F7Ful; + yield return 0x8080808080808080ul; + yield return 0xFFFFFFFFFFFFFFFFul; + + for (int cnt = 1; cnt <= RndCntIdxs; cnt++) + { + yield return GenIdxsForTbls(regs: 2); + } + } + + private static IEnumerable _GenIdxsForTbl3_() + { + yield return 0x0000000000000000ul; + yield return 0x7F7F7F7F7F7F7F7Ful; + yield return 0x8080808080808080ul; + yield return 0xFFFFFFFFFFFFFFFFul; + + for (int cnt = 1; cnt <= RndCntIdxs; cnt++) + { + yield return GenIdxsForTbls(regs: 3); + } + } + + private static IEnumerable _GenIdxsForTbl4_() + { + yield return 0x0000000000000000ul; + yield return 0x7F7F7F7F7F7F7F7Ful; + yield return 0x8080808080808080ul; + yield return 0xFFFFFFFFFFFFFFFFul; + + for (int cnt = 1; cnt <= RndCntIdxs; cnt++) + { + yield return GenIdxsForTbls(regs: 4); + } + } +#endregion + +#region "ValueSource (Opcodes)" + private static uint[] _SingleRegTbl_V_8B_16B_() + { + return new uint[] + { + 0x0E000000u, // TBL V0.8B, { V0.16B }, V0.8B + }; + } + + private static uint[] _TwoRegTbl_V_8B_16B_() + { + return new uint[] + { + 0x0E002000u, // TBL V0.8B, { V0.16B, V1.16B }, V0.8B + }; + } + + private static uint[] _ThreeRegTbl_V_8B_16B_() + { + return new uint[] + { + 0x0E004000u, // TBL V0.8B, { V0.16B, V1.16B, V2.16B }, V0.8B + }; + } + + private static uint[] _FourRegTbl_V_8B_16B_() + { + return new uint[] + { + 0x0E006000u, // TBL V0.8B, { V0.16B, V1.16B, V2.16B, V3.16B }, V0.8B + }; + } +#endregion + + private const int RndCntTbls = 2; + private const int RndCntIdxs = 2; + + [Test, Pairwise, Description("TBL ., { .16B }, .")] + public void SingleRegTbl_V_8B_16B([ValueSource("_SingleRegTbl_V_8B_16B_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u)] uint rn, + [Values(2u)] uint rm, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, + [ValueSource("_GenIdxsForTbl1_")] ulong indexes, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(table0, table0); + Vector128 v2 = MakeVectorE0E1(indexes, q == 1u ? indexes : 0ul); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("TBL ., { .16B, .16B }, .")] + public void TwoRegTbl_V_8B_16B([ValueSource("_TwoRegTbl_V_8B_16B_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u)] uint rn, + [Values(3u)] uint rm, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, + [ValueSource("_GenIdxsForTbl2_")] ulong indexes, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(table0, table0); + Vector128 v2 = MakeVectorE0E1(table1, table1); + Vector128 v3 = MakeVectorE0E1(indexes, q == 1u ? indexes : 0ul); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, v3: v3); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("TBL ., { .16B, .16B }, .")] + public void Mod_TwoRegTbl_V_8B_16B([ValueSource("_TwoRegTbl_V_8B_16B_")] uint opcodes, + [Values(30u, 1u)] uint rd, + [Values(31u)] uint rn, + [Values(1u, 30u)] uint rm, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, + [ValueSource("_GenIdxsForTbl2_")] ulong indexes, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v30 = MakeVectorE0E1(z, z); + Vector128 v31 = MakeVectorE0E1(table0, table0); + Vector128 v0 = MakeVectorE0E1(table1, table1); + Vector128 v1 = MakeVectorE0E1(indexes, indexes); + + SingleOpcode(opcodes, v0: v0, v1: v1, v30: v30, v31: v31); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("TBL ., { .16B, .16B, .16B }, .")] + public void ThreeRegTbl_V_8B_16B([ValueSource("_ThreeRegTbl_V_8B_16B_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u)] uint rn, + [Values(4u)] uint rm, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table2, + [ValueSource("_GenIdxsForTbl3_")] ulong indexes, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(table0, table0); + Vector128 v2 = MakeVectorE0E1(table1, table1); + Vector128 v3 = MakeVectorE0E1(table2, table2); + Vector128 v4 = MakeVectorE0E1(indexes, q == 1u ? indexes : 0ul); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, v3: v3, v4: v4); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("TBL ., { .16B, .16B, .16B }, .")] + public void Mod_ThreeRegTbl_V_8B_16B([ValueSource("_ThreeRegTbl_V_8B_16B_")] uint opcodes, + [Values(30u, 2u)] uint rd, + [Values(31u)] uint rn, + [Values(2u, 30u)] uint rm, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table2, + [ValueSource("_GenIdxsForTbl3_")] ulong indexes, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v30 = MakeVectorE0E1(z, z); + Vector128 v31 = MakeVectorE0E1(table0, table0); + Vector128 v0 = MakeVectorE0E1(table1, table1); + Vector128 v1 = MakeVectorE0E1(table2, table2); + Vector128 v2 = MakeVectorE0E1(indexes, indexes); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, v30: v30, v31: v31); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("TBL ., { .16B, .16B, .16B, .16B }, .")] + public void FourRegTbl_V_8B_16B([ValueSource("_FourRegTbl_V_8B_16B_")] uint opcodes, + [Values(0u)] uint rd, + [Values(1u)] uint rn, + [Values(5u)] uint rm, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table2, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table3, + [ValueSource("_GenIdxsForTbl4_")] ulong indexes, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v0 = MakeVectorE0E1(z, z); + Vector128 v1 = MakeVectorE0E1(table0, table0); + Vector128 v2 = MakeVectorE0E1(table1, table1); + Vector128 v3 = MakeVectorE0E1(table2, table2); + Vector128 v4 = MakeVectorE0E1(table3, table3); + Vector128 v5 = MakeVectorE0E1(indexes, q == 1u ? indexes : 0ul); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, v3: v3, v4: v4, v5: v5); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("TBL ., { .16B, .16B, .16B, .16B }, .")] + public void Mod_FourRegTbl_V_8B_16B([ValueSource("_FourRegTbl_V_8B_16B_")] uint opcodes, + [Values(30u, 3u)] uint rd, + [Values(31u)] uint rn, + [Values(3u, 30u)] uint rm, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table0, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table1, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table2, + [ValueSource("_8B_")] [Random(RndCntTbls)] ulong table3, + [ValueSource("_GenIdxsForTbl4_")] ulong indexes, + [Values(0b0u, 0b1u)] uint q) // <8B, 16B> + { + opcodes |= ((rm & 31) << 16) | ((rn & 31) << 5) | ((rd & 31) << 0); + opcodes |= ((q & 1) << 30); + + ulong z = TestContext.CurrentContext.Random.NextULong(); + Vector128 v30 = MakeVectorE0E1(z, z); + Vector128 v31 = MakeVectorE0E1(table0, table0); + Vector128 v0 = MakeVectorE0E1(table1, table1); + Vector128 v1 = MakeVectorE0E1(table2, table2); + Vector128 v2 = MakeVectorE0E1(table3, table3); + Vector128 v3 = MakeVectorE0E1(indexes, indexes); + + SingleOpcode(opcodes, v0: v0, v1: v1, v2: v2, v3: v3, v30: v30, v31: v31); + + CompareAgainstUnicorn(); + } +#endif + } +} diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj index 0d5c3ff214..9ddeb31409 100644 --- a/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -27,9 +27,9 @@ - + - + diff --git a/Ryujinx.sln.DotSettings b/Ryujinx.sln.DotSettings index 737b56880c..579d97a459 100644 --- a/Ryujinx.sln.DotSettings +++ b/Ryujinx.sln.DotSettings @@ -4,4 +4,11 @@ UseExplicitType UseExplicitType <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy> + True + True + True + True + True + True + True \ No newline at end of file diff --git a/Ryujinx/Config.jsonc b/Ryujinx/Config.jsonc index 1ba601647a..151756f43c 100644 --- a/Ryujinx/Config.jsonc +++ b/Ryujinx/Config.jsonc @@ -29,18 +29,24 @@ // System Language list: https://gist.github.com/HorrorTroll/b6e4a88d774c3c9b3bdf54d79a7ca43b "system_language": "AmericanEnglish", - // Enable or Disable Docked Mode + // Enable or disable Docked Mode "docked_mode": false, - - // Enable or Disable Game Vsync + + // Enable or disable Game Vsync "enable_vsync": true, - - // Enable or Disable Multi-core scheduling of threads - "enable_multicore_scheduling": false, - + + // Enable or disable Multi-core scheduling of threads + "enable_multicore_scheduling": true, + // Enable integrity checks on Switch content files "enable_fs_integrity_checks": true, - + + // Enable or disable aggressive CPU optimizations + "enable_aggressive_cpu_opts": true, + + // Enable or disable ignoring missing services, this may cause instability + "ignore_missing_services": false, + // The primary controller's type // Supported Values: Handheld, ProController, NpadPair, NpadLeft, NpadRight "controller_type": "Handheld", diff --git a/Ryujinx/Configuration.cs b/Ryujinx/Configuration.cs index 96f4d66f4b..560a6dab59 100644 --- a/Ryujinx/Configuration.cs +++ b/Ryujinx/Configuration.cs @@ -4,6 +4,7 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.HLE; using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.HLE.HOS.Services; using Ryujinx.HLE.Input; using Ryujinx.UI.Input; using System; @@ -79,13 +80,23 @@ namespace Ryujinx /// /// Enables or disables multi-core scheduling of threads /// - public bool EnableMultiCoreScheduling { get; private set; } + public bool EnableMulticoreScheduling { get; private set; } /// /// Enables integrity checks on Game content files /// public bool EnableFsIntegrityChecks { get; private set; } + /// + /// Enable or Disable aggressive CPU optimizations + /// + public bool EnableAggressiveCpuOpts { get; private set; } + + /// + /// Enable or disable ignoring missing services + /// + public bool IgnoreMissingServices { get; private set; } + /// /// The primary controller's type /// @@ -188,7 +199,7 @@ namespace Ryujinx device.System.State.SetLanguage(Instance.SystemLanguage); - if (Instance.EnableMultiCoreScheduling) + if (Instance.EnableMulticoreScheduling) { device.System.EnableMultiCoreScheduling(); } @@ -197,6 +208,13 @@ namespace Ryujinx ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; + if (Instance.EnableAggressiveCpuOpts) + { + Optimizations.AssumeStrictAbiCompliance = true; + } + + ServiceConfiguration.IgnoreMissingServices = Instance.IgnoreMissingServices; + if(Instance.GamepadControls.Enabled) { if (GamePad.GetName(Instance.GamepadControls.Index) == "Unmapped Controller") diff --git a/Ryujinx/Program.cs b/Ryujinx/Program.cs index 3b65ea7774..bc2ccded4e 100644 --- a/Ryujinx/Program.cs +++ b/Ryujinx/Program.cs @@ -17,7 +17,7 @@ namespace Ryujinx { Console.Title = "Ryujinx Console"; - IGalRenderer renderer = new OGLRenderer(); + IGalRenderer renderer = new OglRenderer(); IAalOutput audioOut = InitializeAudioEngine(); @@ -78,10 +78,14 @@ namespace Ryujinx break; } } + else + { + Logger.PrintWarning(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file"); + } } else { - Logger.PrintInfo(LogClass.Application, "Please specify the folder with the NSOs/IStorage or a NSO/NRO."); + Logger.PrintWarning(LogClass.Application, "Please specify the folder with the NSOs/IStorage or a NSO/NRO."); } using (GlScreen screen = new GlScreen(device, renderer)) diff --git a/Ryujinx/_schema.json b/Ryujinx/_schema.json index 28f3511181..11eff12b08 100644 --- a/Ryujinx/_schema.json +++ b/Ryujinx/_schema.json @@ -17,6 +17,7 @@ "enable_vsync", "enable_multicore_scheduling", "enable_fs_integrity_checks", + "enable_aggressive_cpu_opts", "controller_type", "keyboard_controls", "gamepad_controls" @@ -382,7 +383,7 @@ "type": "boolean", "title": "Enable Multicore Scheduling", "description": "Enables or disables multi-core scheduling of threads", - "default": false, + "default": true, "examples": [ true, false @@ -399,6 +400,28 @@ false ] }, + "enable_aggressive_cpu_opts": { + "$id": "#/properties/enable_aggressive_cpu_opts", + "type": "boolean", + "title": "Enable Aggressive CPU Optimizations", + "description": "Enable or disable aggressive CPU optimizations", + "default": true, + "examples": [ + true, + false + ] + }, + "ignore_missing_services": { + "$id": "#/properties/ignore_missing_services", + "type": "boolean", + "title": "Ignore Missing Services", + "description": "Enable or disable ignoring missing services, this may cause instability", + "default": false, + "examples": [ + true, + false + ] + }, "controller_type": { "$id": "#/properties/controller_type", "type": "string",