Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
52c115a1f8 | ||
|
e20abbf9cc |
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
ISampler CreateSampler(SamplerCreateInfo info);
|
||||
ITexture CreateTexture(TextureCreateInfo info, float scale);
|
||||
|
||||
void CreateSync(ulong id);
|
||||
void CreateSync(ulong id, bool strict);
|
||||
|
||||
void DeleteBuffer(BufferHandle buffer);
|
||||
|
||||
@@ -53,6 +53,8 @@ namespace Ryujinx.Graphics.GAL
|
||||
|
||||
void Initialize(GraphicsDebugLevel logLevel);
|
||||
|
||||
void SetInterruptAction(Action<Action> interruptAction);
|
||||
|
||||
void Screenshot();
|
||||
}
|
||||
}
|
||||
|
@@ -4,15 +4,17 @@
|
||||
{
|
||||
public CommandType CommandType => CommandType.CreateSync;
|
||||
private ulong _id;
|
||||
private bool _strict;
|
||||
|
||||
public void Set(ulong id)
|
||||
public void Set(ulong id, bool strict)
|
||||
{
|
||||
_id = id;
|
||||
_strict = strict;
|
||||
}
|
||||
|
||||
public static void Run(ref CreateSyncCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.CreateSync(command._id);
|
||||
renderer.CreateSync(command._id, command._strict);
|
||||
|
||||
threaded.Sync.AssignSync(command._id);
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
private int _elementSize;
|
||||
private IRenderer _baseRenderer;
|
||||
private Thread _gpuThread;
|
||||
private Thread _backendThread;
|
||||
private bool _disposed;
|
||||
private bool _running;
|
||||
|
||||
@@ -38,6 +39,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
private CircularSpanPool _spanPool;
|
||||
|
||||
private ManualResetEventSlim _invokeRun;
|
||||
private AutoResetEvent _interruptRun;
|
||||
|
||||
private bool _lastSampleCounterClear = true;
|
||||
|
||||
@@ -54,6 +56,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
private int _refProducerPtr;
|
||||
private int _refConsumerPtr;
|
||||
|
||||
private Action _interruptAction;
|
||||
|
||||
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
|
||||
|
||||
internal BufferMap Buffers { get; }
|
||||
@@ -73,6 +77,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_baseRenderer = renderer;
|
||||
|
||||
renderer.ScreenCaptured += (sender, info) => ScreenCaptured?.Invoke(this, info);
|
||||
renderer.SetInterruptAction(Interrupt);
|
||||
|
||||
Pipeline = new ThreadedPipeline(this, renderer.Pipeline);
|
||||
Window = new ThreadedWindow(this, renderer);
|
||||
@@ -82,6 +87,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
|
||||
_galWorkAvailable = new ManualResetEventSlim(false);
|
||||
_invokeRun = new ManualResetEventSlim();
|
||||
_interruptRun = new AutoResetEvent(false);
|
||||
_spanPool = new CircularSpanPool(this, SpanPoolBytes);
|
||||
SpanPool = _spanPool;
|
||||
|
||||
@@ -95,6 +101,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
{
|
||||
_running = true;
|
||||
|
||||
_backendThread = Thread.CurrentThread;
|
||||
|
||||
_gpuThread = new Thread(() => {
|
||||
gpuLoop();
|
||||
_running = false;
|
||||
@@ -116,10 +124,18 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_galWorkAvailable.Wait();
|
||||
_galWorkAvailable.Reset();
|
||||
|
||||
if (Volatile.Read(ref _interruptAction) != null)
|
||||
{
|
||||
_interruptAction();
|
||||
_interruptRun.Set();
|
||||
|
||||
Interlocked.Exchange(ref _interruptAction, null);
|
||||
}
|
||||
|
||||
// The other thread can only increase the command count.
|
||||
// We can assume that if it is above 0, it will stay there or get higher.
|
||||
|
||||
while (_commandCount > 0)
|
||||
while (_commandCount > 0 && Volatile.Read(ref _interruptAction) == null)
|
||||
{
|
||||
int commandPtr = _consumerPtr;
|
||||
|
||||
@@ -281,10 +297,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
return sampler;
|
||||
}
|
||||
|
||||
public void CreateSync(ulong id)
|
||||
public void CreateSync(ulong id, bool strict)
|
||||
{
|
||||
Sync.CreateSyncHandle(id);
|
||||
New<CreateSyncCommand>().Set(id);
|
||||
New<CreateSyncCommand>().Set(id, strict);
|
||||
QueueCommand();
|
||||
}
|
||||
|
||||
@@ -421,6 +437,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_baseRenderer.WaitSync(id);
|
||||
}
|
||||
|
||||
private void Interrupt(Action action)
|
||||
{
|
||||
// Interrupt the backend thread from any external thread and invoke the given action.
|
||||
|
||||
if (Thread.CurrentThread == _backendThread)
|
||||
{
|
||||
// If this is called from the backend thread, the action can run immediately.
|
||||
action();
|
||||
}
|
||||
else
|
||||
{
|
||||
while (Interlocked.CompareExchange(ref _interruptAction, action, null) != null) { }
|
||||
|
||||
_galWorkAvailable.Set();
|
||||
|
||||
_interruptRun.WaitOne();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetInterruptAction(Action<Action> interruptAction)
|
||||
{
|
||||
// Threaded renderer ignores given interrupt action, as it provides its own to the child renderer.
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Dispose must happen from the render thread, after all commands have completed.
|
||||
@@ -440,6 +480,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_frameComplete.Dispose();
|
||||
_galWorkAvailable.Dispose();
|
||||
_invokeRun.Dispose();
|
||||
_interruptRun.Dispose();
|
||||
|
||||
Sync.Dispose();
|
||||
}
|
||||
|
@@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||
if (_createSyncPending)
|
||||
{
|
||||
_createSyncPending = false;
|
||||
_context.CreateHostSyncIfNeeded(false);
|
||||
_context.CreateHostSyncIfNeeded(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||
}
|
||||
else if (operation == SyncpointbOperation.Incr)
|
||||
{
|
||||
_context.CreateHostSyncIfNeeded(true);
|
||||
_context.CreateHostSyncIfNeeded(true, true);
|
||||
_context.Synchronization.IncrementSyncpoint(syncpointId);
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||
{
|
||||
_context.Renderer.Pipeline.CommandBufferBarrier();
|
||||
|
||||
_context.CreateHostSyncIfNeeded(false);
|
||||
_context.CreateHostSyncIfNeeded(false, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -250,7 +250,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
uint syncpointId = (uint)argument & 0xFFFF;
|
||||
|
||||
_context.AdvanceSequence();
|
||||
_context.CreateHostSyncIfNeeded(true);
|
||||
_context.CreateHostSyncIfNeeded(true, true);
|
||||
_context.Renderer.UpdateCounters(); // Poll the query counters, the game may want an updated result.
|
||||
_context.Synchronization.IncrementSyncpoint(syncpointId);
|
||||
}
|
||||
|
@@ -316,7 +316,8 @@ namespace Ryujinx.Graphics.Gpu
|
||||
/// If no actions are present, a host sync object is not created.
|
||||
/// </summary>
|
||||
/// <param name="syncpoint">True if host sync is being created by a syncpoint</param>
|
||||
public void CreateHostSyncIfNeeded(bool syncpoint)
|
||||
/// <param name="strict">True if the sync should signal as soon as possible</param>
|
||||
public void CreateHostSyncIfNeeded(bool syncpoint, bool strict)
|
||||
{
|
||||
if (BufferMigrations.Count > 0)
|
||||
{
|
||||
@@ -337,7 +338,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||
|
||||
if (_pendingSync || (syncpoint && SyncpointActions.Count > 0))
|
||||
{
|
||||
Renderer.CreateSync(SyncNumber);
|
||||
Renderer.CreateSync(SyncNumber, strict);
|
||||
|
||||
SyncNumber++;
|
||||
|
||||
|
@@ -232,7 +232,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
return new Program(programBinary, hasFragmentShader, info.FragmentOutputMap);
|
||||
}
|
||||
|
||||
public void CreateSync(ulong id)
|
||||
public void CreateSync(ulong id, bool strict)
|
||||
{
|
||||
_sync.Create(id);
|
||||
}
|
||||
@@ -247,6 +247,11 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
return _sync.GetCurrent();
|
||||
}
|
||||
|
||||
public void SetInterruptAction(Action<Action> interruptAction)
|
||||
{
|
||||
// Currently no need for an interrupt action.
|
||||
}
|
||||
|
||||
public void Screenshot()
|
||||
{
|
||||
_window.ScreenCaptureRequested = true;
|
||||
|
@@ -116,6 +116,22 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
public void AddInUseWaitable(MultiFenceHolder waitable)
|
||||
{
|
||||
lock (_commandBuffers)
|
||||
{
|
||||
for (int i = 0; i < _totalCommandBuffers; i++)
|
||||
{
|
||||
ref var entry = ref _commandBuffers[i];
|
||||
|
||||
if (entry.InUse)
|
||||
{
|
||||
AddWaitable(i, waitable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddDependency(int cbIndex, CommandBufferScoped dependencyCbs)
|
||||
{
|
||||
Debug.Assert(_commandBuffers[cbIndex].InUse);
|
||||
|
@@ -227,6 +227,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
|
||||
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
||||
Gd.RegisterFlush();
|
||||
|
||||
// Restore per-command buffer state.
|
||||
|
||||
|
@@ -11,7 +11,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
public ulong ID;
|
||||
public MultiFenceHolder Waitable;
|
||||
public ulong FlushId;
|
||||
public bool Signalled;
|
||||
|
||||
public bool NeedsFlush(ulong currentFlushId)
|
||||
{
|
||||
return (long)(FlushId - currentFlushId) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
private ulong _firstHandle = 0;
|
||||
@@ -19,6 +25,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly VulkanRenderer _gd;
|
||||
private readonly Device _device;
|
||||
private List<SyncHandle> _handles;
|
||||
private ulong FlushId;
|
||||
|
||||
public SyncManager(VulkanRenderer gd, Device device)
|
||||
{
|
||||
@@ -27,17 +34,33 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_handles = new List<SyncHandle>();
|
||||
}
|
||||
|
||||
public void Create(ulong id)
|
||||
public void RegisterFlush()
|
||||
{
|
||||
MultiFenceHolder waitable = new MultiFenceHolder();
|
||||
FlushId++;
|
||||
}
|
||||
|
||||
_gd.FlushAllCommands();
|
||||
_gd.CommandBufferPool.AddWaitable(waitable);
|
||||
public void Create(ulong id, bool strict)
|
||||
{
|
||||
ulong flushId = FlushId;
|
||||
MultiFenceHolder waitable = new MultiFenceHolder();
|
||||
if (strict || _gd.InterruptAction == null)
|
||||
{
|
||||
_gd.FlushAllCommands();
|
||||
_gd.CommandBufferPool.AddWaitable(waitable);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't flush commands, instead wait for the current command buffer to finish.
|
||||
// If this sync is waited on before the command buffer is submitted, interrupt the gpu thread and flush it manually.
|
||||
|
||||
_gd.CommandBufferPool.AddInUseWaitable(waitable);
|
||||
}
|
||||
|
||||
SyncHandle handle = new SyncHandle
|
||||
{
|
||||
ID = id,
|
||||
Waitable = waitable
|
||||
Waitable = waitable,
|
||||
FlushId = flushId
|
||||
};
|
||||
|
||||
lock (_handles)
|
||||
@@ -107,6 +130,17 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.NeedsFlush(FlushId))
|
||||
{
|
||||
_gd.InterruptAction(() =>
|
||||
{
|
||||
if (result.NeedsFlush(FlushId))
|
||||
{
|
||||
_gd.FlushAllCommands();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000);
|
||||
if (!signaled)
|
||||
{
|
||||
@@ -132,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
first = _handles.FirstOrDefault();
|
||||
}
|
||||
|
||||
if (first == null) break;
|
||||
if (first == null || first.NeedsFlush(FlushId)) break;
|
||||
|
||||
bool signaled = first.Waitable.WaitForFences(_gd.Api, _device, 0);
|
||||
if (signaled)
|
||||
|
@@ -48,6 +48,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
internal DescriptorSetManager DescriptorSetManager { get; private set; }
|
||||
internal PipelineLayoutCache PipelineLayoutCache { get; private set; }
|
||||
internal BackgroundResources BackgroundResources { get; private set; }
|
||||
internal Action<Action> InterruptAction { get; private set; }
|
||||
|
||||
internal BufferManager BufferManager { get; private set; }
|
||||
|
||||
@@ -354,6 +355,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_pipeline?.FlushCommandsImpl();
|
||||
}
|
||||
|
||||
internal void RegisterFlush()
|
||||
{
|
||||
_syncManager.RegisterFlush();
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
|
||||
{
|
||||
return BufferManager.GetData(buffer, offset, size);
|
||||
@@ -593,9 +599,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
action();
|
||||
}
|
||||
|
||||
public void CreateSync(ulong id)
|
||||
public void CreateSync(ulong id, bool strict)
|
||||
{
|
||||
_syncManager.Create(id);
|
||||
_syncManager.Create(id, strict);
|
||||
}
|
||||
|
||||
public IProgram LoadProgramBinary(byte[] programBinary, bool isFragment, ShaderInfo info)
|
||||
@@ -613,6 +619,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return _syncManager.GetCurrent();
|
||||
}
|
||||
|
||||
public void SetInterruptAction(Action<Action> interruptAction)
|
||||
{
|
||||
InterruptAction = interruptAction;
|
||||
}
|
||||
|
||||
public void Screenshot()
|
||||
{
|
||||
_window.ScreenCaptureRequested = true;
|
||||
|
@@ -298,6 +298,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(67)] //3.0.0+
|
||||
// IsIlluminanceAvailable() -> bool
|
||||
public ResultCode IsIlluminanceAvailable(ServiceCtx context)
|
||||
{
|
||||
// NOTE: This should call IsAmbientLightSensorAvailable through to Lbl, but there's no situation where we'd want false.
|
||||
context.ResponseData.Write(true);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(68)]
|
||||
// SetAutoSleepDisabled(u8)
|
||||
public ResultCode SetAutoSleepDisabled(ServiceCtx context)
|
||||
@@ -318,6 +330,19 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(71)] //5.0.0+
|
||||
// GetCurrentIlluminanceEx() -> (bool, f32)
|
||||
public ResultCode GetCurrentIlluminanceEx(ServiceCtx context)
|
||||
{
|
||||
// TODO: The light value should be configurable - presumably users using software that takes advantage will want control.
|
||||
context.ResponseData.Write(1); // OverLimit
|
||||
context.ResponseData.Write(10000f); // Lux - 10K lux is ambient light.
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(80)] // 4.0.0+
|
||||
// SetWirelessPriorityMode(s32 wireless_priority_mode)
|
||||
public ResultCode SetWirelessPriorityMode(ServiceCtx context)
|
||||
|
Reference in New Issue
Block a user