diff --git a/ChocolArm64/AThread.cs b/ChocolArm64/AThread.cs index 62f9d2d39..16804a7c5 100644 --- a/ChocolArm64/AThread.cs +++ b/ChocolArm64/AThread.cs @@ -54,6 +54,14 @@ namespace ChocolArm64 return true; } - public void StopExecution() => ThreadState.Running = false; + public void StopExecution() + { + ThreadState.Running = false; + } + + public bool IsCurrentThread() + { + return Thread.CurrentThread == Work; + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs index 1300d2185..2f6946001 100644 --- a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs +++ b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs @@ -15,11 +15,15 @@ namespace Ryujinx.Core.OsHle.Handles public AutoResetEvent WaitEvent { get; private set; } + public bool Active { get; set; } + public SchedulerThread(KThread Thread) { this.Thread = Thread; WaitEvent = new AutoResetEvent(false); + + Active = true; } public void Dispose() @@ -98,7 +102,10 @@ namespace Ryujinx.Core.OsHle.Handles public bool Remove(SchedulerThread SchedThread) { - return Threads.Remove(SchedThread); + lock (Threads) + { + return Threads.Remove(SchedThread); + } } } @@ -180,6 +187,55 @@ namespace Ryujinx.Core.OsHle.Handles } } + public void SetThreadActivity(KThread Thread, bool Active) + { + if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) + { + throw new InvalidOperationException(); + } + + lock (SchedLock) + { + bool OldState = SchedThread.Active; + + SchedThread.Active = Active; + + if (!OldState && Active) + { + if (ActiveProcessors.Add(Thread.ProcessorId)) + { + RunThread(SchedThread); + } + else + { + WaitingToRun[Thread.ProcessorId].Push(SchedThread); + + PrintDbgThreadInfo(Thread, "entering wait state..."); + } + } + else if (OldState && !Active) + { + if (Thread.Thread.IsCurrentThread()) + { + Suspend(Thread.ProcessorId); + + PrintDbgThreadInfo(Thread, "entering inactive wait state..."); + } + else + { + WaitingToRun[Thread.ProcessorId].Remove(SchedThread); + } + } + } + + if (!Active && Thread.Thread.IsCurrentThread()) + { + SchedThread.WaitEvent.WaitOne(); + + PrintDbgThreadInfo(Thread, "resuming execution..."); + } + } + public void Suspend(int ProcessorId) { lock (SchedLock) @@ -222,9 +278,7 @@ namespace Ryujinx.Core.OsHle.Handles public void Resume(KThread Thread) { - SchedulerThread SchedThread; - - if (!AllThreads.TryGetValue(Thread, out SchedThread)) + if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread)) { throw new InvalidOperationException(); } @@ -236,18 +290,25 @@ namespace Ryujinx.Core.OsHle.Handles { KThread Thread = SchedThread.Thread; - lock (SchedLock) + if (SchedThread.Active) { - if (ActiveProcessors.Add(Thread.ProcessorId)) + lock (SchedLock) { - PrintDbgThreadInfo(Thread, "resuming execution..."); + if (ActiveProcessors.Add(Thread.ProcessorId)) + { + PrintDbgThreadInfo(Thread, "resuming execution..."); - return; + return; + } + + WaitingToRun[Thread.ProcessorId].Push(SchedThread); + + PrintDbgThreadInfo(Thread, "entering wait state..."); } - - PrintDbgThreadInfo(Thread, "entering wait state..."); - - WaitingToRun[Thread.ProcessorId].Push(SchedThread); + } + else + { + PrintDbgThreadInfo(Thread, "entering inactive wait state..."); } SchedThread.WaitEvent.WaitOne(); diff --git a/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs b/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs index 91ed91587..f76573762 100644 --- a/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs +++ b/Ryujinx.Core/OsHle/Kernel/ConditionVariable.cs @@ -57,7 +57,7 @@ namespace Ryujinx.Core.OsHle.Kernel Count = Process.Memory.ReadInt32(CondVarAddress); - if (Count > 0) + if (Result && Count > 0) { Process.Memory.WriteInt32(CondVarAddress, Count - 1); } @@ -73,10 +73,10 @@ namespace Ryujinx.Core.OsHle.Kernel { if (Count < 0) { - Process.Memory.WriteInt32(CondVarAddress, WaitingThreads.Count); - foreach ((_, AutoResetEvent WaitEvent) in WaitingThreads) { + IncrementCondVarValue(); + WaitEvent.Set(); } @@ -84,8 +84,6 @@ namespace Ryujinx.Core.OsHle.Kernel } else { - Process.Memory.WriteInt32(CondVarAddress, Count); - while (WaitingThreads.Count > 0 && Count-- > 0) { int HighestPriority = WaitingThreads[0].Thread.Priority; @@ -101,6 +99,8 @@ namespace Ryujinx.Core.OsHle.Kernel } } + IncrementCondVarValue(); + WaitingThreads[HighestPrioIndex].WaitEvent.Set(); WaitingThreads.RemoveAt(HighestPrioIndex); @@ -111,6 +111,17 @@ namespace Ryujinx.Core.OsHle.Kernel Process.Scheduler.Yield(Thread); } + private void IncrementCondVarValue() + { + AcquireCondVarValue(); + + int Count = Process.Memory.ReadInt32(CondVarAddress); + + Process.Memory.WriteInt32(CondVarAddress, Count + 1); + + ReleaseCondVarValue(); + } + private void AcquireCondVarValue() { if (!OwnsCondVarValue) diff --git a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs index fa772988f..16ef86978 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs @@ -63,7 +63,8 @@ namespace Ryujinx.Core.OsHle.Kernel { 0x25, SvcGetThreadId }, { 0x26, SvcBreak }, { 0x27, SvcOutputDebugString }, - { 0x29, SvcGetInfo } + { 0x29, SvcGetInfo }, + { 0x32, SvcSetThreadActivity } }; this.Ns = Ns; diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs index 2534c9d99..06147b28f 100644 --- a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs +++ b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs @@ -147,7 +147,28 @@ namespace Ryujinx.Core.OsHle.Kernel } else { - Logging.Warn(LogClass.KernelSvc, $"Tried to GetThreadId on invalid thread handle 0x{Handle:x8}!"); + Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + } + } + + private void SvcSetThreadActivity(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + bool Active = (int)ThreadState.X1 != 0; + + KThread CurrThread = Process.HandleTable.GetData(Handle); + + if (CurrThread != null) + { + Process.Scheduler.SetThreadActivity(CurrThread, Active); + + ThreadState.X0 = 0; + } + else + { + Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 457192dc2..d7173bcd6 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -193,7 +193,7 @@ namespace Ryujinx.Graphics.Gal.Shader } else if (DeclInfo.Name == GlslDecl.FragmentOutputName) { - Name = "layout (location = 0) out " + GetDecl(DeclInfo) + ";"; + Name = "layout (location = 0) out " + GetDecl(DeclInfo) + ";" + Environment.NewLine; } else { diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index b796ab288..830aeb8cb 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -41,6 +41,16 @@ namespace Ryujinx.Graphics.Gal.Shader EmitAluFfma(Block, OpCode, ShaderOper.RR); } + public static void Fmul32i(ShaderIrBlock Block, long OpCode) + { + ShaderIrNode OperA = GetOperGpr8 (OpCode); + ShaderIrNode OperB = GetOperImmf32_20(OpCode); + + ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Fmul, OperA, OperB); + + Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); + } + public static void Fmul_C(ShaderIrBlock Block, long OpCode) { EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fmul); @@ -212,7 +222,6 @@ namespace Ryujinx.Graphics.Gal.Shader bool Aa = ((OpCode >> 46) & 1) != 0; bool Na = ((OpCode >> 48) & 1) != 0; bool Ab = ((OpCode >> 49) & 1) != 0; - bool Ad = ((OpCode >> 50) & 1) != 0; ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; @@ -234,8 +243,6 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB); - Op = GetAluAbs(Op, Ad); - Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs index de932dce6..efbce20f2 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs @@ -70,6 +70,11 @@ namespace Ryujinx.Graphics.Gal.Shader return new ShaderIrOperImm((int)(OpCode >> 20)); } + public static ShaderIrOperImmf GetOperImmf32_20(long OpCode) + { + return new ShaderIrOperImmf(BitConverter.Int32BitsToSingle((int)(OpCode >> 20))); + } + public static ShaderIrOperImm GetOperImm19_20(long OpCode) { int Value = (int)(OpCode >> 20) & 0x7ffff; diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs index a234f7f74..762544cb9 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs @@ -27,6 +27,7 @@ namespace Ryujinx.Graphics.Gal.Shader Set("001100101xxxxx", ShaderDecode.Ffma_I); Set("010100011xxxxx", ShaderDecode.Ffma_RC); Set("010110011xxxxx", ShaderDecode.Ffma_RR); + Set("00011110xxxxxx", ShaderDecode.Fmul32i); Set("0100110001101x", ShaderDecode.Fmul_C); Set("0011100x01101x", ShaderDecode.Fmul_I); Set("0101110001101x", ShaderDecode.Fmul_R);