From cee712105850ac3385cd0091a923438167433f9f Mon Sep 17 00:00:00 2001 From: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 8 Apr 2023 01:22:00 +0200 Subject: Move solution and projects to src --- .../Server/Performance/IPerformanceDetailEntry.cs | 52 ++++ .../Server/Performance/IPerformanceEntry.cs | 46 ++++ .../Server/Performance/IPerformanceHeader.cs | 80 ++++++ .../Performance/PerformanceDetailVersion1.cs | 72 +++++ .../Performance/PerformanceDetailVersion2.cs | 72 +++++ .../Performance/PerformanceEntryAddresses.cs | 56 ++++ .../Server/Performance/PerformanceEntryVersion1.cs | 62 +++++ .../Server/Performance/PerformanceEntryVersion2.cs | 62 +++++ .../Performance/PerformanceFrameHeaderVersion1.cs | 101 +++++++ .../Performance/PerformanceFrameHeaderVersion2.cs | 117 ++++++++ .../Server/Performance/PerformanceManager.cs | 106 +++++++ .../Performance/PerformanceManagerGeneric.cs | 304 +++++++++++++++++++++ 12 files changed, 1130 insertions(+) create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs create mode 100644 src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs (limited to 'src/Ryujinx.Audio/Renderer/Server/Performance') diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs new file mode 100644 index 00000000..dbe59cb0 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs @@ -0,0 +1,52 @@ +using Ryujinx.Audio.Renderer.Common; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// + /// Represents a detailed entry in a performance frame. + /// + public interface IPerformanceDetailEntry + { + /// + /// Get the start time of this entry event (in microseconds). + /// + /// The start time of this entry event (in microseconds). + int GetStartTime(); + + /// + /// Get the start time offset in this structure. + /// + /// The start time offset in this structure. + int GetStartTimeOffset(); + + /// + /// Get the processing time of this entry event (in microseconds). + /// + /// The processing time of this entry event (in microseconds). + int GetProcessingTime(); + + /// + /// Get the processing time offset in this structure. + /// + /// The processing time offset in this structure. + int GetProcessingTimeOffset(); + + /// + /// Set the of this entry. + /// + /// The node id of this entry. + void SetNodeId(int nodeId); + + /// + /// Set the of this entry. + /// + /// The type to use. + void SetEntryType(PerformanceEntryType type); + + /// + /// Set the of this entry. + /// + /// The type to use. + void SetDetailType(PerformanceDetailType detailType); + } +} \ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs new file mode 100644 index 00000000..9888a4cc --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs @@ -0,0 +1,46 @@ +using Ryujinx.Audio.Renderer.Common; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// + /// Represents an entry in a performance frame. + /// + public interface IPerformanceEntry + { + /// + /// Get the start time of this entry event (in microseconds). + /// + /// The start time of this entry event (in microseconds). + int GetStartTime(); + + /// + /// Get the start time offset in this structure. + /// + /// The start time offset in this structure. + int GetStartTimeOffset(); + + /// + /// Get the processing time of this entry event (in microseconds). + /// + /// The processing time of this entry event (in microseconds). + int GetProcessingTime(); + + /// + /// Get the processing time offset in this structure. + /// + /// The processing time offset in this structure. + int GetProcessingTimeOffset(); + + /// + /// Set the of this entry. + /// + /// The node id of this entry. + void SetNodeId(int nodeId); + + /// + /// Set the of this entry. + /// + /// The type to use. + void SetEntryType(PerformanceEntryType type); + } +} \ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs new file mode 100644 index 00000000..21876b4b --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs @@ -0,0 +1,80 @@ +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// + /// The header of a performance frame. + /// + public interface IPerformanceHeader + { + /// + /// Get the entry count offset in this structure. + /// + /// The entry count offset in this structure. + int GetEntryCountOffset(); + + /// + /// Set the DSP running behind flag. + /// + /// The flag. + void SetDspRunningBehind(bool isRunningBehind); + + /// + /// Set the count of voices that were dropped. + /// + /// The count of voices that were dropped. + void SetVoiceDropCount(uint voiceCount); + + /// + /// Set the start ticks of the . (before sending commands) + /// + /// The start ticks of the . (before sending commands) + void SetStartRenderingTicks(ulong startTicks); + + /// + /// Set the header magic. + /// + /// The header magic. + void SetMagic(uint magic); + + /// + /// Set the offset of the next performance header. + /// + /// The offset of the next performance header. + void SetNextOffset(int nextOffset); + + /// + /// Set the total time taken by all the commands profiled. + /// + /// The total time taken by all the commands profiled. + void SetTotalProcessingTime(int totalProcessingTime); + + /// + /// Set the index of this performance frame. + /// + /// The index of this performance frame. + void SetIndex(uint index); + + /// + /// Get the total count of entries in this frame. + /// + /// The total count of entries in this frame. + int GetEntryCount(); + + /// + /// Get the total count of detailed entries in this frame. + /// + /// The total count of detailed entries in this frame. + int GetEntryDetailCount(); + + /// + /// Set the total count of entries in this frame. + /// + /// The total count of entries in this frame. + void SetEntryCount(int entryCount); + + /// + /// Set the total count of detailed entries in this frame. + /// + /// The total count of detailed entries in this frame. + void SetEntryDetailCount(int entryDetailCount); + } +} \ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs new file mode 100644 index 00000000..22704c0d --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs @@ -0,0 +1,72 @@ +using Ryujinx.Audio.Renderer.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// + /// Implementation of for performance metrics version 1. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] + public struct PerformanceDetailVersion1 : IPerformanceDetailEntry + { + /// + /// The node id associated to this detailed entry. + /// + public int NodeId; + + /// + /// The start time (in microseconds) associated to this detailed entry. + /// + public int StartTime; + + /// + /// The processing time (in microseconds) associated to this detailed entry. + /// + public int ProcessingTime; + + /// + /// The detailed entry type associated to this detailed entry. + /// + public PerformanceDetailType DetailType; + + /// + /// The entry type associated to this detailed entry. + /// + public PerformanceEntryType EntryType; + + public int GetProcessingTime() + { + return ProcessingTime; + } + + public int GetProcessingTimeOffset() + { + return 8; + } + + public int GetStartTime() + { + return StartTime; + } + + public int GetStartTimeOffset() + { + return 4; + } + + public void SetDetailType(PerformanceDetailType detailType) + { + DetailType = detailType; + } + + public void SetEntryType(PerformanceEntryType type) + { + EntryType = type; + } + + public void SetNodeId(int nodeId) + { + NodeId = nodeId; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs new file mode 100644 index 00000000..05ecda9b --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs @@ -0,0 +1,72 @@ +using Ryujinx.Audio.Renderer.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// + /// Implementation of for performance metrics version 2. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)] + public struct PerformanceDetailVersion2 : IPerformanceDetailEntry + { + /// + /// The node id associated to this detailed entry. + /// + public int NodeId; + + /// + /// The start time (in microseconds) associated to this detailed entry. + /// + public int StartTime; + + /// + /// The processing time (in microseconds) associated to this detailed entry. + /// + public int ProcessingTime; + + /// + /// The detailed entry type associated to this detailed entry. + /// + public PerformanceDetailType DetailType; + + /// + /// The entry type associated to this detailed entry. + /// + public PerformanceEntryType EntryType; + + public int GetProcessingTime() + { + return ProcessingTime; + } + + public int GetProcessingTimeOffset() + { + return 8; + } + + public int GetStartTime() + { + return StartTime; + } + + public int GetStartTimeOffset() + { + return 4; + } + + public void SetDetailType(PerformanceDetailType detailType) + { + DetailType = detailType; + } + + public void SetEntryType(PerformanceEntryType type) + { + EntryType = type; + } + + public void SetNodeId(int nodeId) + { + NodeId = nodeId; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs new file mode 100644 index 00000000..1b8d8668 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs @@ -0,0 +1,56 @@ +using System; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// + /// Information used by the performance command to store informations in the performance entry. + /// + public class PerformanceEntryAddresses + { + /// + /// The memory storing the performance entry. + /// + public Memory BaseMemory; + + /// + /// The offset to the start time field. + /// + public uint StartTimeOffset; + + /// + /// The offset to the entry count field. + /// + public uint EntryCountOffset; + + /// + /// The offset to the processing time field. + /// + public uint ProcessingTimeOffset; + + /// + /// Increment the entry count. + /// + public void IncrementEntryCount() + { + BaseMemory.Span[(int)EntryCountOffset / 4]++; + } + + /// + /// Set the start time in the entry. + /// + /// The start time in nanoseconds. + public void SetStartTime(ulong startTimeNano) + { + BaseMemory.Span[(int)StartTimeOffset / 4] = (int)(startTimeNano / 1000); + } + + /// + /// Set the processing time in the entry. + /// + /// The end time in nanoseconds. + public void SetProcessingTime(ulong endTimeNano) + { + BaseMemory.Span[(int)ProcessingTimeOffset / 4] = (int)(endTimeNano / 1000) - BaseMemory.Span[(int)StartTimeOffset / 4]; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs new file mode 100644 index 00000000..fa2d3216 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs @@ -0,0 +1,62 @@ +using Ryujinx.Audio.Renderer.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// + /// Implementation of for performance metrics version 1. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] + public struct PerformanceEntryVersion1 : IPerformanceEntry + { + /// + /// The node id associated to this entry. + /// + public int NodeId; + + /// + /// The start time (in microseconds) associated to this entry. + /// + public int StartTime; + + /// + /// The processing time (in microseconds) associated to this entry. + /// + public int ProcessingTime; + + /// + /// The entry type associated to this entry. + /// + public PerformanceEntryType EntryType; + + public int GetProcessingTime() + { + return ProcessingTime; + } + + public int GetProcessingTimeOffset() + { + return 8; + } + + public int GetStartTime() + { + return StartTime; + } + + public int GetStartTimeOffset() + { + return 4; + } + + public void SetEntryType(PerformanceEntryType type) + { + EntryType = type; + } + + public void SetNodeId(int nodeId) + { + NodeId = nodeId; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs new file mode 100644 index 00000000..49d4b3ce --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs @@ -0,0 +1,62 @@ +using Ryujinx.Audio.Renderer.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// + /// Implementation of for performance metrics version 2. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)] + public struct PerformanceEntryVersion2 : IPerformanceEntry + { + /// + /// The node id associated to this entry. + /// + public int NodeId; + + /// + /// The start time (in microseconds) associated to this entry. + /// + public int StartTime; + + /// + /// The processing time (in microseconds) associated to this entry. + /// + public int ProcessingTime; + + /// + /// The entry type associated to this entry. + /// + public PerformanceEntryType EntryType; + + public int GetProcessingTime() + { + return ProcessingTime; + } + + public int GetProcessingTimeOffset() + { + return 8; + } + + public int GetStartTime() + { + return StartTime; + } + + public int GetStartTimeOffset() + { + return 4; + } + + public void SetEntryType(PerformanceEntryType type) + { + EntryType = type; + } + + public void SetNodeId(int nodeId) + { + NodeId = nodeId; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs new file mode 100644 index 00000000..5fe6bff0 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs @@ -0,0 +1,101 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// + /// Implementation of for performance metrics version 1. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)] + public struct PerformanceFrameHeaderVersion1 : IPerformanceHeader + { + /// + /// The magic of the performance header. + /// + public uint Magic; + + /// + /// The total count of entries in this frame. + /// + public int EntryCount; + + /// + /// The total count of detailed entries in this frame. + /// + public int EntryDetailCount; + + /// + /// The offset of the next performance header. + /// + public int NextOffset; + + /// + /// The total time taken by all the commands profiled. + /// + public int TotalProcessingTime; + + /// + /// The count of voices that were dropped. + /// + public uint VoiceDropCount; + + public int GetEntryCount() + { + return EntryCount; + } + + public int GetEntryCountOffset() + { + return 4; + } + + public int GetEntryDetailCount() + { + return EntryDetailCount; + } + + public void SetDspRunningBehind(bool isRunningBehind) + { + // NOTE: Not present in version 1 + } + + public void SetEntryCount(int entryCount) + { + EntryCount = entryCount; + } + + public void SetEntryDetailCount(int entryDetailCount) + { + EntryDetailCount = entryDetailCount; + } + + public void SetIndex(uint index) + { + // NOTE: Not present in version 1 + } + + public void SetMagic(uint magic) + { + Magic = magic; + } + + public void SetNextOffset(int nextOffset) + { + NextOffset = nextOffset; + } + + public void SetStartRenderingTicks(ulong startTicks) + { + // NOTE: not present in version 1 + } + + public void SetTotalProcessingTime(int totalProcessingTime) + { + TotalProcessingTime = totalProcessingTime; + } + + public void SetVoiceDropCount(uint voiceCount) + { + VoiceDropCount = voiceCount; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs new file mode 100644 index 00000000..a1822968 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs @@ -0,0 +1,117 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// + /// Implementation of for performance metrics version 2. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x30)] + public struct PerformanceFrameHeaderVersion2 : IPerformanceHeader + { + /// + /// The magic of the performance header. + /// + public uint Magic; + + /// + /// The total count of entries in this frame. + /// + public int EntryCount; + + /// + /// The total count of detailed entries in this frame. + /// + public int EntryDetailCount; + + /// + /// The offset of the next performance header. + /// + public int NextOffset; + + /// + /// The total time taken by all the commands profiled. + /// + public int TotalProcessingTime; + + /// + /// The count of voices that were dropped. + /// + public uint VoiceDropCount; + + /// + /// The start ticks of the . (before sending commands) + /// + public ulong StartRenderingTicks; + + /// + /// The index of this performance frame. + /// + public uint Index; + + /// + /// If set to true, the DSP is running behind. + /// + [MarshalAs(UnmanagedType.I1)] + public bool IsDspRunningBehind; + + public int GetEntryCount() + { + return EntryCount; + } + + public int GetEntryCountOffset() + { + return 4; + } + + public int GetEntryDetailCount() + { + return EntryDetailCount; + } + + public void SetDspRunningBehind(bool isRunningBehind) + { + IsDspRunningBehind = isRunningBehind; + } + + public void SetEntryCount(int entryCount) + { + EntryCount = entryCount; + } + + public void SetEntryDetailCount(int entryDetailCount) + { + EntryDetailCount = entryDetailCount; + } + + public void SetIndex(uint index) + { + Index = index; + } + + public void SetMagic(uint magic) + { + Magic = magic; + } + + public void SetNextOffset(int nextOffset) + { + NextOffset = nextOffset; + } + + public void SetStartRenderingTicks(ulong startTicks) + { + StartRenderingTicks = startTicks; + } + + public void SetTotalProcessingTime(int totalProcessingTime) + { + TotalProcessingTime = totalProcessingTime; + } + + public void SetVoiceDropCount(uint voiceCount) + { + VoiceDropCount = voiceCount; + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs new file mode 100644 index 00000000..f996441c --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs @@ -0,0 +1,106 @@ +using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Parameter; +using System; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + public abstract class PerformanceManager + { + /// + /// Get the required size for a single performance frame. + /// + /// The audio renderer configuration. + /// The behaviour context. + /// The required size for a single performance frame. + public static ulong GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref AudioRendererConfiguration parameter, ref BehaviourContext behaviourContext) + { + uint version = behaviourContext.GetPerformanceMetricsDataFormat(); + + if (version == 2) + { + return (ulong)PerformanceManagerGeneric.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); + } + else if (version == 1) + { + return (ulong)PerformanceManagerGeneric.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); + } + + throw new NotImplementedException($"Unknown Performance metrics data format version {version}"); + } + + /// + /// Copy the performance frame history to the supplied user buffer and returns the size copied. + /// + /// The supplied user buffer to store the performance frame into. + /// The size copied to the supplied buffer. + public abstract uint CopyHistories(Span performanceOutput); + + /// + /// Set the target node id to profile. + /// + /// The target node id to profile. + public abstract void SetTargetNodeId(int target); + + /// + /// Check if the given target node id is profiled. + /// + /// The target node id to check. + /// Return true, if the given target node id is profiled. + public abstract bool IsTargetNodeId(int target); + + /// + /// Get the next buffer to store a performance entry. + /// + /// The output . + /// The info. + /// The node id of the entry. + /// Return true, if a valid was returned. + public abstract bool GetNextEntry(out PerformanceEntryAddresses performanceEntry, PerformanceEntryType entryType, int nodeId); + + /// + /// Get the next buffer to store a performance detailed entry. + /// + /// The output . + /// The info. + /// The info. + /// The node id of the entry. + /// Return true, if a valid was returned. + public abstract bool GetNextEntry(out PerformanceEntryAddresses performanceEntry, PerformanceDetailType detailType, PerformanceEntryType entryType, int nodeId); + + /// + /// Finalize the current performance frame. + /// + /// Indicate if the DSP is running behind. + /// The count of voices that were dropped. + /// The start ticks of the audio rendering. + public abstract void TapFrame(bool dspRunningBehind, uint voiceDropCount, ulong startRenderingTicks); + + /// + /// Create a new . + /// + /// The backing memory available for use by the manager. + /// The audio renderer configuration. + /// The behaviour context; + /// A new . + public static PerformanceManager Create(Memory performanceBuffer, ref AudioRendererConfiguration parameter, BehaviourContext behaviourContext) + { + uint version = behaviourContext.GetPerformanceMetricsDataFormat(); + + switch (version) + { + case 1: + return new PerformanceManagerGeneric(performanceBuffer, + ref parameter); + case 2: + return new PerformanceManagerGeneric(performanceBuffer, + ref parameter); + default: + throw new NotImplementedException($"Unknown Performance metrics data format version {version}"); + } + } + } +} \ No newline at end of file diff --git a/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs new file mode 100644 index 00000000..18e77391 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs @@ -0,0 +1,304 @@ +using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Audio.Renderer.Utils; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// + /// A Generic implementation of . + /// + /// The header implementation of the performance frame. + /// The entry implementation of the performance frame. + /// A detailed implementation of the performance frame. + public class PerformanceManagerGeneric : PerformanceManager where THeader : unmanaged, IPerformanceHeader where TEntry : unmanaged, IPerformanceEntry where TEntryDetail : unmanaged, IPerformanceDetailEntry + { + /// + /// The magic used for the . + /// + private const uint MagicPerformanceBuffer = 0x46524550; + + /// + /// The fixed amount of that can be stored in a frame. + /// + private const int MaxFrameDetailCount = 100; + + private Memory _buffer; + private Memory _historyBuffer; + + private Memory CurrentBuffer => _buffer.Slice(0, _frameSize); + private Memory CurrentBufferData => CurrentBuffer.Slice(Unsafe.SizeOf()); + + private ref THeader CurrentHeader => ref MemoryMarshal.Cast(CurrentBuffer.Span)[0]; + + private Span Entries => MemoryMarshal.Cast(CurrentBufferData.Span.Slice(0, GetEntriesSize())); + private Span EntriesDetail => MemoryMarshal.Cast(CurrentBufferData.Span.Slice(GetEntriesSize(), GetEntriesDetailSize())); + + private int _frameSize; + private int _availableFrameCount; + private int _entryCountPerFrame; + private int _detailTarget; + private int _entryIndex; + private int _entryDetailIndex; + private int _indexHistoryWrite; + private int _indexHistoryRead; + private uint _historyFrameIndex; + + public PerformanceManagerGeneric(Memory buffer, ref AudioRendererConfiguration parameter) + { + _buffer = buffer; + _frameSize = GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); + + _entryCountPerFrame = (int)GetEntryCount(ref parameter); + _availableFrameCount = buffer.Length / _frameSize - 1; + + _historyFrameIndex = 0; + + _historyBuffer = _buffer.Slice(_frameSize); + + SetupNewHeader(); + } + + private Span GetBufferFromIndex(Span data, int index) + { + return data.Slice(index * _frameSize, _frameSize); + } + + private ref THeader GetHeaderFromBuffer(Span data, int index) + { + return ref MemoryMarshal.Cast(GetBufferFromIndex(data, index))[0]; + } + + private Span GetEntriesFromBuffer(Span data, int index) + { + return MemoryMarshal.Cast(GetBufferFromIndex(data, index).Slice(Unsafe.SizeOf(), GetEntriesSize())); + } + + private Span GetEntriesDetailFromBuffer(Span data, int index) + { + return MemoryMarshal.Cast(GetBufferFromIndex(data, index).Slice(Unsafe.SizeOf() + GetEntriesSize(), GetEntriesDetailSize())); + } + + private void SetupNewHeader() + { + _entryIndex = 0; + _entryDetailIndex = 0; + + CurrentHeader.SetEntryCount(0); + CurrentHeader.SetEntryDetailCount(0); + } + + public static uint GetEntryCount(ref AudioRendererConfiguration parameter) + { + return parameter.VoiceCount + parameter.EffectCount + parameter.SubMixBufferCount + parameter.SinkCount + 1; + } + + public int GetEntriesSize() + { + return Unsafe.SizeOf() * _entryCountPerFrame; + } + + public static int GetEntriesDetailSize() + { + return Unsafe.SizeOf() * MaxFrameDetailCount; + } + + public static int GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref AudioRendererConfiguration parameter) + { + return Unsafe.SizeOf() * (int)GetEntryCount(ref parameter) + GetEntriesDetailSize() + Unsafe.SizeOf(); + } + + public override uint CopyHistories(Span performanceOutput) + { + if (performanceOutput.IsEmpty) + { + return 0; + } + + int nextOffset = 0; + + while (_indexHistoryRead != _indexHistoryWrite) + { + if (nextOffset >= performanceOutput.Length) + { + break; + } + + ref THeader inputHeader = ref GetHeaderFromBuffer(_historyBuffer.Span, _indexHistoryRead); + Span inputEntries = GetEntriesFromBuffer(_historyBuffer.Span, _indexHistoryRead); + Span inputEntriesDetail = GetEntriesDetailFromBuffer(_historyBuffer.Span, _indexHistoryRead); + + Span targetSpan = performanceOutput.Slice(nextOffset); + + // NOTE: We check for the space for two headers for the final blank header. + int requiredSpace = Unsafe.SizeOf() + Unsafe.SizeOf() * inputHeader.GetEntryCount() + + Unsafe.SizeOf() * inputHeader.GetEntryDetailCount() + + Unsafe.SizeOf(); + + if (targetSpan.Length < requiredSpace) + { + break; + } + + ref THeader outputHeader = ref MemoryMarshal.Cast(targetSpan)[0]; + + nextOffset += Unsafe.SizeOf(); + + Span outputEntries = MemoryMarshal.Cast(performanceOutput.Slice(nextOffset)); + + int totalProcessingTime = 0; + + int effectiveEntryCount = 0; + + for (int entryIndex = 0; entryIndex < inputHeader.GetEntryCount(); entryIndex++) + { + ref TEntry input = ref inputEntries[entryIndex]; + + if (input.GetProcessingTime() != 0 || input.GetStartTime() != 0) + { + ref TEntry output = ref outputEntries[effectiveEntryCount++]; + + output = input; + + nextOffset += Unsafe.SizeOf(); + + totalProcessingTime += input.GetProcessingTime(); + } + } + + Span outputEntriesDetail = MemoryMarshal.Cast(performanceOutput.Slice(nextOffset)); + + int effectiveEntryDetailCount = 0; + + for (int entryDetailIndex = 0; entryDetailIndex < inputHeader.GetEntryDetailCount(); entryDetailIndex++) + { + ref TEntryDetail input = ref inputEntriesDetail[entryDetailIndex]; + + if (input.GetProcessingTime() != 0 || input.GetStartTime() != 0) + { + ref TEntryDetail output = ref outputEntriesDetail[effectiveEntryDetailCount++]; + + output = input; + + nextOffset += Unsafe.SizeOf(); + } + } + + outputHeader = inputHeader; + outputHeader.SetMagic(MagicPerformanceBuffer); + outputHeader.SetTotalProcessingTime(totalProcessingTime); + outputHeader.SetNextOffset(nextOffset); + outputHeader.SetEntryCount(effectiveEntryCount); + outputHeader.SetEntryDetailCount(effectiveEntryDetailCount); + + _indexHistoryRead = (_indexHistoryRead + 1) % _availableFrameCount; + } + + if (nextOffset < performanceOutput.Length && (performanceOutput.Length - nextOffset) >= Unsafe.SizeOf()) + { + ref THeader outputHeader = ref MemoryMarshal.Cast(performanceOutput.Slice(nextOffset))[0]; + + outputHeader = default; + } + + return (uint)nextOffset; + } + + public override bool GetNextEntry(out PerformanceEntryAddresses performanceEntry, PerformanceEntryType entryType, int nodeId) + { + performanceEntry = new PerformanceEntryAddresses(); + performanceEntry.BaseMemory = SpanMemoryManager.Cast(CurrentBuffer); + performanceEntry.EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(); + + uint baseEntryOffset = (uint)(Unsafe.SizeOf() + Unsafe.SizeOf() * _entryIndex); + + ref TEntry entry = ref Entries[_entryIndex]; + + performanceEntry.StartTimeOffset = baseEntryOffset + (uint)entry.GetStartTimeOffset(); + performanceEntry.ProcessingTimeOffset = baseEntryOffset + (uint)entry.GetProcessingTimeOffset(); + + entry = default; + entry.SetEntryType(entryType); + entry.SetNodeId(nodeId); + + _entryIndex++; + + return true; + } + + public override bool GetNextEntry(out PerformanceEntryAddresses performanceEntry, PerformanceDetailType detailType, PerformanceEntryType entryType, int nodeId) + { + performanceEntry = null; + + if (_entryDetailIndex > MaxFrameDetailCount) + { + return false; + } + + performanceEntry = new PerformanceEntryAddresses(); + performanceEntry.BaseMemory = SpanMemoryManager.Cast(CurrentBuffer); + performanceEntry.EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(); + + uint baseEntryOffset = (uint)(Unsafe.SizeOf() + GetEntriesSize() + Unsafe.SizeOf() * _entryDetailIndex); + + ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex]; + + performanceEntry.StartTimeOffset = baseEntryOffset + (uint)entryDetail.GetStartTimeOffset(); + performanceEntry.ProcessingTimeOffset = baseEntryOffset + (uint)entryDetail.GetProcessingTimeOffset(); + + entryDetail = default; + entryDetail.SetDetailType(detailType); + entryDetail.SetEntryType(entryType); + entryDetail.SetNodeId(nodeId); + + _entryDetailIndex++; + + return true; + } + + public override bool IsTargetNodeId(int target) + { + return _detailTarget == target; + } + + public override void SetTargetNodeId(int target) + { + _detailTarget = target; + } + + public override void TapFrame(bool dspRunningBehind, uint voiceDropCount, ulong startRenderingTicks) + { + if (_availableFrameCount > 0) + { + int targetIndexForHistory = _indexHistoryWrite; + + _indexHistoryWrite = (_indexHistoryWrite + 1) % _availableFrameCount; + + ref THeader targetHeader = ref GetHeaderFromBuffer(_historyBuffer.Span, targetIndexForHistory); + + CurrentBuffer.Span.CopyTo(GetBufferFromIndex(_historyBuffer.Span, targetIndexForHistory)); + + uint targetHistoryFrameIndex = _historyFrameIndex; + + if (_historyFrameIndex == uint.MaxValue) + { + _historyFrameIndex = 0; + } + else + { + _historyFrameIndex++; + } + + targetHeader.SetDspRunningBehind(dspRunningBehind); + targetHeader.SetVoiceDropCount(voiceDropCount); + targetHeader.SetStartRenderingTicks(startRenderingTicks); + targetHeader.SetIndex(targetHistoryFrameIndex); + + // Finally setup the new header + SetupNewHeader(); + } + } + } +} \ No newline at end of file -- cgit v1.2.3