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