Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cc00bb4b1 | ||
|
|
c98b7fc702 | ||
|
|
e65effcb05 | ||
|
|
c1ed150949 |
@@ -20,7 +20,7 @@
|
|||||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.5.1" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.5.2" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
|
|||||||
@@ -711,5 +711,36 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
{
|
{
|
||||||
return format.IsUint() || format.IsSint();
|
return format.IsUint() || format.IsSint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the texture format is a float or sRGB color format.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Does not include normalized, compressed or depth formats.
|
||||||
|
/// Float and sRGB formats do not participate in logical operations.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="format">Texture format</param>
|
||||||
|
/// <returns>True if the format is a float or sRGB color format, false otherwise</returns>
|
||||||
|
public static bool IsFloatOrSrgb(this Format format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case Format.R8G8B8A8Srgb:
|
||||||
|
case Format.B8G8R8A8Srgb:
|
||||||
|
case Format.R16Float:
|
||||||
|
case Format.R16G16Float:
|
||||||
|
case Format.R16G16B16Float:
|
||||||
|
case Format.R16G16B16A16Float:
|
||||||
|
case Format.R32Float:
|
||||||
|
case Format.R32G32Float:
|
||||||
|
case Format.R32G32B32Float:
|
||||||
|
case Format.R32G32B32A32Float:
|
||||||
|
case Format.R11G11B10Float:
|
||||||
|
case Format.R9G9B9E5Float:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public VkFormat[] AttachmentFormats { get; }
|
public VkFormat[] AttachmentFormats { get; }
|
||||||
public int[] AttachmentIndices { get; }
|
public int[] AttachmentIndices { get; }
|
||||||
public uint AttachmentIntegerFormatMask { get; }
|
public uint AttachmentIntegerFormatMask { get; }
|
||||||
|
public bool LogicOpsAllowed { get; }
|
||||||
|
|
||||||
public int AttachmentsCount { get; }
|
public int AttachmentsCount { get; }
|
||||||
public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[^1] : -1;
|
public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[^1] : -1;
|
||||||
@@ -32,7 +33,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public FramebufferParams(Device device, TextureView view, uint width, uint height)
|
public FramebufferParams(Device device, TextureView view, uint width, uint height)
|
||||||
{
|
{
|
||||||
bool isDepthStencil = view.Info.Format.IsDepthOrStencil();
|
var format = view.Info.Format;
|
||||||
|
|
||||||
|
bool isDepthStencil = format.IsDepthOrStencil();
|
||||||
|
|
||||||
_device = device;
|
_device = device;
|
||||||
_attachments = new[] { view.GetImageViewForAttachment() };
|
_attachments = new[] { view.GetImageViewForAttachment() };
|
||||||
@@ -56,6 +59,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
AttachmentSamples = new[] { (uint)view.Info.Samples };
|
AttachmentSamples = new[] { (uint)view.Info.Samples };
|
||||||
AttachmentFormats = new[] { view.VkFormat };
|
AttachmentFormats = new[] { view.VkFormat };
|
||||||
AttachmentIndices = isDepthStencil ? Array.Empty<int>() : new[] { 0 };
|
AttachmentIndices = isDepthStencil ? Array.Empty<int>() : new[] { 0 };
|
||||||
|
AttachmentIntegerFormatMask = format.IsInteger() ? 1u : 0u;
|
||||||
|
LogicOpsAllowed = !format.IsFloatOrSrgb();
|
||||||
|
|
||||||
AttachmentsCount = 1;
|
AttachmentsCount = 1;
|
||||||
|
|
||||||
@@ -85,6 +90,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
int bindIndex = 0;
|
int bindIndex = 0;
|
||||||
uint attachmentIntegerFormatMask = 0;
|
uint attachmentIntegerFormatMask = 0;
|
||||||
|
bool allFormatsFloatOrSrgb = colorsCount != 0;
|
||||||
|
|
||||||
foreach (ITexture color in colors)
|
foreach (ITexture color in colors)
|
||||||
{
|
{
|
||||||
@@ -101,11 +107,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
AttachmentFormats[index] = texture.VkFormat;
|
AttachmentFormats[index] = texture.VkFormat;
|
||||||
AttachmentIndices[index] = bindIndex;
|
AttachmentIndices[index] = bindIndex;
|
||||||
|
|
||||||
if (texture.Info.Format.IsInteger())
|
var format = texture.Info.Format;
|
||||||
|
|
||||||
|
if (format.IsInteger())
|
||||||
{
|
{
|
||||||
attachmentIntegerFormatMask |= 1u << bindIndex;
|
attachmentIntegerFormatMask |= 1u << bindIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allFormatsFloatOrSrgb &= format.IsFloatOrSrgb();
|
||||||
|
|
||||||
width = Math.Min(width, (uint)texture.Width);
|
width = Math.Min(width, (uint)texture.Width);
|
||||||
height = Math.Min(height, (uint)texture.Height);
|
height = Math.Min(height, (uint)texture.Height);
|
||||||
layers = Math.Min(layers, (uint)texture.Layers);
|
layers = Math.Min(layers, (uint)texture.Layers);
|
||||||
@@ -120,6 +130,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
||||||
|
LogicOpsAllowed = !allFormatsFloatOrSrgb;
|
||||||
|
|
||||||
if (depthStencil is TextureView dsTexture && dsTexture.Valid)
|
if (depthStencil is TextureView dsTexture && dsTexture.Valid)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1498,6 +1498,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
|
var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
|
||||||
FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats);
|
FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats);
|
||||||
_newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask;
|
_newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask;
|
||||||
|
_newState.Internal.LogicOpsAllowed = FramebufferParams.LogicOpsAllowed;
|
||||||
|
|
||||||
for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++)
|
for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
int attachmentCount = 0;
|
int attachmentCount = 0;
|
||||||
int maxColorAttachmentIndex = -1;
|
int maxColorAttachmentIndex = -1;
|
||||||
uint attachmentIntegerFormatMask = 0;
|
uint attachmentIntegerFormatMask = 0;
|
||||||
|
bool allFormatsFloatOrSrgb = true;
|
||||||
|
|
||||||
for (int i = 0; i < Constants.MaxRenderTargets; i++)
|
for (int i = 0; i < Constants.MaxRenderTargets; i++)
|
||||||
{
|
{
|
||||||
@@ -314,6 +315,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
attachmentIntegerFormatMask |= 1u << i;
|
attachmentIntegerFormatMask |= 1u << i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allFormatsFloatOrSrgb &= state.AttachmentFormats[i].IsFloatOrSrgb();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,6 +328,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1);
|
pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1);
|
||||||
pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
|
pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
|
||||||
pipeline.Internal.AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
pipeline.Internal.AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
||||||
|
pipeline.Internal.LogicOpsAllowed = attachmentCount == 0 || !allFormatsFloatOrSrgb;
|
||||||
|
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -560,10 +560,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vendors other than NVIDIA have a bug where it enables logical operations even for float formats,
|
||||||
|
// so we need to force disable them here.
|
||||||
|
bool logicOpEnable = LogicOpEnable && (gd.Vendor == Vendor.Nvidia || Internal.LogicOpsAllowed);
|
||||||
|
|
||||||
var colorBlendState = new PipelineColorBlendStateCreateInfo
|
var colorBlendState = new PipelineColorBlendStateCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.PipelineColorBlendStateCreateInfo,
|
SType = StructureType.PipelineColorBlendStateCreateInfo,
|
||||||
LogicOpEnable = LogicOpEnable,
|
LogicOpEnable = logicOpEnable,
|
||||||
LogicOp = LogicOp,
|
LogicOp = LogicOp,
|
||||||
AttachmentCount = ColorBlendAttachmentStateCount,
|
AttachmentCount = ColorBlendAttachmentStateCount,
|
||||||
PAttachments = pColorBlendAttachmentState,
|
PAttachments = pColorBlendAttachmentState,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState;
|
public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState;
|
||||||
public Array9<Format> AttachmentFormats;
|
public Array9<Format> AttachmentFormats;
|
||||||
public uint AttachmentIntegerFormatMask;
|
public uint AttachmentIntegerFormatMask;
|
||||||
|
public bool LogicOpsAllowed;
|
||||||
|
|
||||||
public readonly override bool Equals(object obj)
|
public readonly override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,42 +28,25 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
private SchedulingState _state;
|
private SchedulingState _state;
|
||||||
|
|
||||||
private AutoResetEvent _idleInterruptEvent;
|
|
||||||
private readonly object _idleInterruptEventLock;
|
|
||||||
|
|
||||||
private KThread _previousThread;
|
private KThread _previousThread;
|
||||||
private KThread _currentThread;
|
private KThread _currentThread;
|
||||||
private readonly KThread _idleThread;
|
|
||||||
|
private int _coreIdleLock;
|
||||||
|
private bool _idleSignalled = true;
|
||||||
|
private bool _idleActive = true;
|
||||||
|
private long _idleTimeRunning;
|
||||||
|
|
||||||
public KThread PreviousThread => _previousThread;
|
public KThread PreviousThread => _previousThread;
|
||||||
public KThread CurrentThread => _currentThread;
|
public KThread CurrentThread => _currentThread;
|
||||||
public long LastContextSwitchTime { get; private set; }
|
public long LastContextSwitchTime { get; private set; }
|
||||||
public long TotalIdleTimeTicks => _idleThread.TotalTimeRunning;
|
public long TotalIdleTimeTicks => _idleTimeRunning;
|
||||||
|
|
||||||
public KScheduler(KernelContext context, int coreId)
|
public KScheduler(KernelContext context, int coreId)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_coreId = coreId;
|
_coreId = coreId;
|
||||||
|
|
||||||
_idleInterruptEvent = new AutoResetEvent(false);
|
_currentThread = null;
|
||||||
_idleInterruptEventLock = new object();
|
|
||||||
|
|
||||||
KThread idleThread = CreateIdleThread(context, coreId);
|
|
||||||
|
|
||||||
_currentThread = idleThread;
|
|
||||||
_idleThread = idleThread;
|
|
||||||
|
|
||||||
idleThread.StartHostThread();
|
|
||||||
idleThread.SchedulerWaitEvent.Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
private KThread CreateIdleThread(KernelContext context, int cpuCore)
|
|
||||||
{
|
|
||||||
KThread idleThread = new(context);
|
|
||||||
|
|
||||||
idleThread.Initialize(0UL, 0UL, 0UL, PrioritiesCount, cpuCore, null, ThreadType.Dummy, IdleThreadLoop);
|
|
||||||
|
|
||||||
return idleThread;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ulong SelectThreads(KernelContext context)
|
public static ulong SelectThreads(KernelContext context)
|
||||||
@@ -237,39 +220,64 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
KThread threadToSignal = context.Schedulers[coreToSignal]._currentThread;
|
KThread threadToSignal = context.Schedulers[coreToSignal]._currentThread;
|
||||||
|
|
||||||
// Request the thread running on that core to stop and reschedule, if we have one.
|
// Request the thread running on that core to stop and reschedule, if we have one.
|
||||||
if (threadToSignal != context.Schedulers[coreToSignal]._idleThread)
|
threadToSignal?.Context.RequestInterrupt();
|
||||||
{
|
|
||||||
threadToSignal.Context.RequestInterrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the core is idle, ensure that the idle thread is awaken.
|
// If the core is idle, ensure that the idle thread is awaken.
|
||||||
context.Schedulers[coreToSignal]._idleInterruptEvent.Set();
|
context.Schedulers[coreToSignal].NotifyIdleThread();
|
||||||
|
|
||||||
scheduledCoresMask &= ~(1UL << coreToSignal);
|
scheduledCoresMask &= ~(1UL << coreToSignal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void IdleThreadLoop()
|
private void ActivateIdleThread()
|
||||||
{
|
{
|
||||||
while (_context.Running)
|
while (Interlocked.CompareExchange(ref _coreIdleLock, 1, 0) != 0)
|
||||||
|
{
|
||||||
|
Thread.SpinWait(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.MemoryBarrier();
|
||||||
|
|
||||||
|
// Signals that idle thread is now active on this core.
|
||||||
|
_idleActive = true;
|
||||||
|
|
||||||
|
TryLeaveIdle();
|
||||||
|
|
||||||
|
Interlocked.Exchange(ref _coreIdleLock, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NotifyIdleThread()
|
||||||
|
{
|
||||||
|
while (Interlocked.CompareExchange(ref _coreIdleLock, 1, 0) != 0)
|
||||||
|
{
|
||||||
|
Thread.SpinWait(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.MemoryBarrier();
|
||||||
|
|
||||||
|
// Signals that the idle core may be able to exit idle.
|
||||||
|
_idleSignalled = true;
|
||||||
|
|
||||||
|
TryLeaveIdle();
|
||||||
|
|
||||||
|
Interlocked.Exchange(ref _coreIdleLock, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TryLeaveIdle()
|
||||||
|
{
|
||||||
|
if (_idleSignalled && _idleActive)
|
||||||
{
|
{
|
||||||
_state.NeedsScheduling = false;
|
_state.NeedsScheduling = false;
|
||||||
Thread.MemoryBarrier();
|
Thread.MemoryBarrier();
|
||||||
KThread nextThread = PickNextThread(_state.SelectedThread);
|
KThread nextThread = PickNextThread(null, _state.SelectedThread);
|
||||||
|
|
||||||
if (_idleThread != nextThread)
|
if (nextThread != null)
|
||||||
{
|
{
|
||||||
_idleThread.SchedulerWaitEvent.Reset();
|
_idleActive = false;
|
||||||
WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, _idleThread.SchedulerWaitEvent);
|
nextThread.SchedulerWaitEvent.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
_idleInterruptEvent.WaitOne();
|
_idleSignalled = false;
|
||||||
}
|
|
||||||
|
|
||||||
lock (_idleInterruptEventLock)
|
|
||||||
{
|
|
||||||
_idleInterruptEvent.Dispose();
|
|
||||||
_idleInterruptEvent = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,20 +300,37 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
// Wake all the threads that might be waiting until this thread context is unlocked.
|
// Wake all the threads that might be waiting until this thread context is unlocked.
|
||||||
for (int core = 0; core < CpuCoresCount; core++)
|
for (int core = 0; core < CpuCoresCount; core++)
|
||||||
{
|
{
|
||||||
_context.Schedulers[core]._idleInterruptEvent.Set();
|
_context.Schedulers[core].NotifyIdleThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
KThread nextThread = PickNextThread(selectedThread);
|
KThread nextThread = PickNextThread(KernelStatic.GetCurrentThread(), selectedThread);
|
||||||
|
|
||||||
if (currentThread.Context.Running)
|
if (currentThread.Context.Running)
|
||||||
{
|
{
|
||||||
// Wait until this thread is scheduled again, and allow the next thread to run.
|
// Wait until this thread is scheduled again, and allow the next thread to run.
|
||||||
WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, currentThread.SchedulerWaitEvent);
|
|
||||||
|
if (nextThread == null)
|
||||||
|
{
|
||||||
|
ActivateIdleThread();
|
||||||
|
currentThread.SchedulerWaitEvent.WaitOne();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WaitHandle.SignalAndWait(nextThread.SchedulerWaitEvent, currentThread.SchedulerWaitEvent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Allow the next thread to run.
|
// Allow the next thread to run.
|
||||||
nextThread.SchedulerWaitEvent.Set();
|
|
||||||
|
if (nextThread == null)
|
||||||
|
{
|
||||||
|
ActivateIdleThread();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextThread.SchedulerWaitEvent.Set();
|
||||||
|
}
|
||||||
|
|
||||||
// We don't need to wait since the thread is exiting, however we need to
|
// We don't need to wait since the thread is exiting, however we need to
|
||||||
// make sure this thread will never call the scheduler again, since it is
|
// make sure this thread will never call the scheduler again, since it is
|
||||||
@@ -319,7 +344,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private KThread PickNextThread(KThread selectedThread)
|
private KThread PickNextThread(KThread currentThread, KThread selectedThread)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -335,7 +360,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
// on the core, as the scheduled thread will handle the next switch.
|
// on the core, as the scheduled thread will handle the next switch.
|
||||||
if (selectedThread.ThreadContext.Lock())
|
if (selectedThread.ThreadContext.Lock())
|
||||||
{
|
{
|
||||||
SwitchTo(selectedThread);
|
SwitchTo(currentThread, selectedThread);
|
||||||
|
|
||||||
if (!_state.NeedsScheduling)
|
if (!_state.NeedsScheduling)
|
||||||
{
|
{
|
||||||
@@ -346,15 +371,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return _idleThread;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// The core is idle now, make sure that the idle thread can run
|
// The core is idle now, make sure that the idle thread can run
|
||||||
// and switch the core when a thread is available.
|
// and switch the core when a thread is available.
|
||||||
SwitchTo(null);
|
SwitchTo(currentThread, null);
|
||||||
return _idleThread;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_state.NeedsScheduling = false;
|
_state.NeedsScheduling = false;
|
||||||
@@ -363,12 +388,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SwitchTo(KThread nextThread)
|
private void SwitchTo(KThread currentThread, KThread nextThread)
|
||||||
{
|
{
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = currentThread?.Owner;
|
||||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
|
||||||
|
|
||||||
nextThread ??= _idleThread;
|
|
||||||
|
|
||||||
if (currentThread != nextThread)
|
if (currentThread != nextThread)
|
||||||
{
|
{
|
||||||
@@ -376,7 +398,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
long currentTicks = PerformanceCounter.ElapsedTicks;
|
long currentTicks = PerformanceCounter.ElapsedTicks;
|
||||||
long ticksDelta = currentTicks - previousTicks;
|
long ticksDelta = currentTicks - previousTicks;
|
||||||
|
|
||||||
currentThread.AddCpuTime(ticksDelta);
|
if (currentThread == null)
|
||||||
|
{
|
||||||
|
Interlocked.Add(ref _idleTimeRunning, ticksDelta);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentThread.AddCpuTime(ticksDelta);
|
||||||
|
}
|
||||||
|
|
||||||
currentProcess?.AddCpuTime(ticksDelta);
|
currentProcess?.AddCpuTime(ticksDelta);
|
||||||
|
|
||||||
@@ -386,13 +415,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
{
|
{
|
||||||
_previousThread = !currentThread.TerminationRequested && currentThread.ActiveCore == _coreId ? currentThread : null;
|
_previousThread = !currentThread.TerminationRequested && currentThread.ActiveCore == _coreId ? currentThread : null;
|
||||||
}
|
}
|
||||||
else if (currentThread == _idleThread)
|
else if (currentThread == null)
|
||||||
{
|
{
|
||||||
_previousThread = null;
|
_previousThread = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextThread.CurrentCore != _coreId)
|
if (nextThread != null && nextThread.CurrentCore != _coreId)
|
||||||
{
|
{
|
||||||
nextThread.CurrentCore = _coreId;
|
nextThread.CurrentCore = _coreId;
|
||||||
}
|
}
|
||||||
@@ -645,11 +674,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// Ensure that the idle thread is not blocked and can exit.
|
// No resources to dispose for now.
|
||||||
lock (_idleInterruptEventLock)
|
|
||||||
{
|
|
||||||
_idleInterruptEvent?.Set();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,9 +143,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
PreferredCore = cpuCore;
|
PreferredCore = cpuCore;
|
||||||
AffinityMask |= 1UL << cpuCore;
|
AffinityMask |= 1UL << cpuCore;
|
||||||
|
|
||||||
SchedFlags = type == ThreadType.Dummy
|
SchedFlags = ThreadSchedState.None;
|
||||||
? ThreadSchedState.Running
|
|
||||||
: ThreadSchedState.None;
|
|
||||||
|
|
||||||
ActiveCore = cpuCore;
|
ActiveCore = cpuCore;
|
||||||
ObjSyncResult = KernelResult.ThreadNotStarted;
|
ObjSyncResult = KernelResult.ThreadNotStarted;
|
||||||
@@ -1055,6 +1053,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
// If the thread is not schedulable, we want to just run or pause
|
// If the thread is not schedulable, we want to just run or pause
|
||||||
// it directly as we don't care about priority or the core it is
|
// it directly as we don't care about priority or the core it is
|
||||||
// running on in this case.
|
// running on in this case.
|
||||||
|
|
||||||
if (SchedFlags == ThreadSchedState.Running)
|
if (SchedFlags == ThreadSchedState.Running)
|
||||||
{
|
{
|
||||||
_schedulerWaitEvent.Set();
|
_schedulerWaitEvent.Set();
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
{
|
{
|
||||||
enum ThreadType
|
enum ThreadType
|
||||||
{
|
{
|
||||||
Dummy,
|
|
||||||
Kernel,
|
Kernel,
|
||||||
Kernel2,
|
Kernel2,
|
||||||
User,
|
User,
|
||||||
|
|||||||
Reference in New Issue
Block a user