diff options
Diffstat (limited to 'Ryujinx.Audio/Renderer/Server/Performance')
12 files changed, 1325 insertions, 0 deletions
diff --git a/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs new file mode 100644 index 00000000..893cc008 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceDetailEntry.cs @@ -0,0 +1,69 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Common; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// <summary> + /// Represents a detailed entry in a performance frame. + /// </summary> + public interface IPerformanceDetailEntry + { + /// <summary> + /// Get the start time of this entry event (in microseconds). + /// </summary> + /// <returns>The start time of this entry event (in microseconds).</returns> + int GetStartTime(); + + /// <summary> + /// Get the start time offset in this structure. + /// </summary> + /// <returns>The start time offset in this structure.</returns> + int GetStartTimeOffset(); + + /// <summary> + /// Get the processing time of this entry event (in microseconds). + /// </summary> + /// <returns>The processing time of this entry event (in microseconds).</returns> + int GetProcessingTime(); + + /// <summary> + /// Get the processing time offset in this structure. + /// </summary> + /// <returns>The processing time offset in this structure.</returns> + int GetProcessingTimeOffset(); + + /// <summary> + /// Set the <paramref name="nodeId"/> of this entry. + /// </summary> + /// <param name="nodeId">The node id of this entry.</param> + void SetNodeId(int nodeId); + + /// <summary> + /// Set the <see cref="PerformanceEntryType"/> of this entry. + /// </summary> + /// <param name="type">The type to use.</param> + void SetEntryType(PerformanceEntryType type); + + /// <summary> + /// Set the <see cref="PerformanceDetailType"/> of this entry. + /// </summary> + /// <param name="detailType">The type to use.</param> + void SetDetailType(PerformanceDetailType detailType); + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs new file mode 100644 index 00000000..2a2fa9cc --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceEntry.cs @@ -0,0 +1,63 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Common; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// <summary> + /// Represents an entry in a performance frame. + /// </summary> + public interface IPerformanceEntry + { + /// <summary> + /// Get the start time of this entry event (in microseconds). + /// </summary> + /// <returns>The start time of this entry event (in microseconds).</returns> + int GetStartTime(); + + /// <summary> + /// Get the start time offset in this structure. + /// </summary> + /// <returns>The start time offset in this structure.</returns> + int GetStartTimeOffset(); + + /// <summary> + /// Get the processing time of this entry event (in microseconds). + /// </summary> + /// <returns>The processing time of this entry event (in microseconds).</returns> + int GetProcessingTime(); + + /// <summary> + /// Get the processing time offset in this structure. + /// </summary> + /// <returns>The processing time offset in this structure.</returns> + int GetProcessingTimeOffset(); + + /// <summary> + /// Set the <paramref name="nodeId"/> of this entry. + /// </summary> + /// <param name="nodeId">The node id of this entry.</param> + void SetNodeId(int nodeId); + + /// <summary> + /// Set the <see cref="PerformanceEntryType"/> of this entry. + /// </summary> + /// <param name="type">The type to use.</param> + void SetEntryType(PerformanceEntryType type); + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs new file mode 100644 index 00000000..5a26754d --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/IPerformanceHeader.cs @@ -0,0 +1,97 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// <summary> + /// The header of a performance frame. + /// </summary> + public interface IPerformanceHeader + { + /// <summary> + /// Get the entry count offset in this structure. + /// </summary> + /// <returns>The entry count offset in this structure.</returns> + int GetEntryCountOffset(); + + /// <summary> + /// Set the DSP running behind flag. + /// </summary> + /// <param name="isRunningBehind">The flag.</param> + void SetDspRunningBehind(bool isRunningBehind); + + /// <summary> + /// Set the count of voices that were dropped. + /// </summary> + /// <param name="voiceCount">The count of voices that were dropped.</param> + void SetVoiceDropCount(uint voiceCount); + + /// <summary> + /// Set the start ticks of the <see cref="Dsp.AudioProcessor"/>. (before sending commands) + /// </summary> + /// <param name="startTicks">The start ticks of the <see cref="Dsp.AudioProcessor"/>. (before sending commands)</param> + void SetStartRenderingTicks(ulong startTicks); + + /// <summary> + /// Set the header magic. + /// </summary> + /// <param name="magic">The header magic.</param> + void SetMagic(uint magic); + + /// <summary> + /// Set the offset of the next performance header. + /// </summary> + /// <param name="nextOffset">The offset of the next performance header.</param> + void SetNextOffset(int nextOffset); + + /// <summary> + /// Set the total time taken by all the commands profiled. + /// </summary> + /// <param name="totalProcessingTime">The total time taken by all the commands profiled.</param> + void SetTotalProcessingTime(int totalProcessingTime); + + /// <summary> + /// Set the index of this performance frame. + /// </summary> + /// <param name="index">The index of this performance frame.</param> + void SetIndex(uint index); + + /// <summary> + /// Get the total count of entries in this frame. + /// </summary> + /// <returns>The total count of entries in this frame.</returns> + int GetEntryCount(); + + /// <summary> + /// Get the total count of detailed entries in this frame. + /// </summary> + /// <returns>The total count of detailed entries in this frame.</returns> + int GetEntryDetailCount(); + + /// <summary> + /// Set the total count of entries in this frame. + /// </summary> + /// <param name="entryCount">The total count of entries in this frame.</param> + void SetEntryCount(int entryCount); + + /// <summary> + /// Set the total count of detailed entries in this frame. + /// </summary> + /// <param name="entryDetailCount">The total count of detailed entries in this frame.</param> + void SetEntryDetailCount(int entryDetailCount); + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs new file mode 100644 index 00000000..b7df7f28 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion1.cs @@ -0,0 +1,89 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// <summary> + /// Implementation of <see cref="IPerformanceDetailEntry"/> for performance metrics version 1. + /// </summary> + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] + public struct PerformanceDetailVersion1 : IPerformanceDetailEntry + { + /// <summary> + /// The node id associated to this detailed entry. + /// </summary> + public int NodeId; + + /// <summary> + /// The start time (in microseconds) associated to this detailed entry. + /// </summary> + public int StartTime; + + /// <summary> + /// The processing time (in microseconds) associated to this detailed entry. + /// </summary> + public int ProcessingTime; + + /// <summary> + /// The detailed entry type associated to this detailed entry. + /// </summary> + public PerformanceDetailType DetailType; + + /// <summary> + /// The entry type associated to this detailed entry. + /// </summary> + 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; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs new file mode 100644 index 00000000..da898b29 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceDetailVersion2.cs @@ -0,0 +1,89 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// <summary> + /// Implementation of <see cref="IPerformanceDetailEntry"/> for performance metrics version 2. + /// </summary> + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)] + public struct PerformanceDetailVersion2 : IPerformanceDetailEntry + { + /// <summary> + /// The node id associated to this detailed entry. + /// </summary> + public int NodeId; + + /// <summary> + /// The start time (in microseconds) associated to this detailed entry. + /// </summary> + public int StartTime; + + /// <summary> + /// The processing time (in microseconds) associated to this detailed entry. + /// </summary> + public int ProcessingTime; + + /// <summary> + /// The detailed entry type associated to this detailed entry. + /// </summary> + public PerformanceDetailType DetailType; + + /// <summary> + /// The entry type associated to this detailed entry. + /// </summary> + 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; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs new file mode 100644 index 00000000..ad99867f --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryAddresses.cs @@ -0,0 +1,73 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +using System; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// <summary> + /// Information used by the performance command to store informations in the performance entry. + /// </summary> + public class PerformanceEntryAddresses + { + /// <summary> + /// The memory storing the performance entry. + /// </summary> + public Memory<int> BaseMemory; + + /// <summary> + /// The offset to the start time field. + /// </summary> + public uint StartTimeOffset; + + /// <summary> + /// The offset to the entry count field. + /// </summary> + public uint EntryCountOffset; + + /// <summary> + /// The offset to the processing time field. + /// </summary> + public uint ProcessingTimeOffset; + + /// <summary> + /// Increment the entry count. + /// </summary> + public void IncrementEntryCount() + { + BaseMemory.Span[(int)EntryCountOffset / 4]++; + } + + /// <summary> + /// Set the start time in the entry. + /// </summary> + /// <param name="startTimeNano">The start time in nanoseconds.</param> + public void SetStartTime(ulong startTimeNano) + { + BaseMemory.Span[(int)StartTimeOffset / 4] = (int)(startTimeNano / 1000); + } + + /// <summary> + /// Set the processing time in the entry. + /// </summary> + /// <param name="endTimeNano">The end time in nanoseconds.</param> + public void SetProcessingTime(ulong endTimeNano) + { + BaseMemory.Span[(int)ProcessingTimeOffset / 4] = (int)(endTimeNano / 1000) - BaseMemory.Span[(int)StartTimeOffset / 4]; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs new file mode 100644 index 00000000..ac91ca19 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion1.cs @@ -0,0 +1,79 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// <summary> + /// Implementation of <see cref="IPerformanceEntry"/> for performance metrics version 1. + /// </summary> + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] + public struct PerformanceEntryVersion1 : IPerformanceEntry + { + /// <summary> + /// The node id associated to this entry. + /// </summary> + public int NodeId; + + /// <summary> + /// The start time (in microseconds) associated to this entry. + /// </summary> + public int StartTime; + + /// <summary> + /// The processing time (in microseconds) associated to this entry. + /// </summary> + public int ProcessingTime; + + /// <summary> + /// The entry type associated to this entry. + /// </summary> + 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; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs new file mode 100644 index 00000000..84ee5bc9 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceEntryVersion2.cs @@ -0,0 +1,79 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// <summary> + /// Implementation of <see cref="IPerformanceEntry"/> for performance metrics version 2. + /// </summary> + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)] + public struct PerformanceEntryVersion2 : IPerformanceEntry + { + /// <summary> + /// The node id associated to this entry. + /// </summary> + public int NodeId; + + /// <summary> + /// The start time (in microseconds) associated to this entry. + /// </summary> + public int StartTime; + + /// <summary> + /// The processing time (in microseconds) associated to this entry. + /// </summary> + public int ProcessingTime; + + /// <summary> + /// The entry type associated to this entry. + /// </summary> + 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; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs new file mode 100644 index 00000000..b0757b37 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion1.cs @@ -0,0 +1,118 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// <summary> + /// Implementation of <see cref="IPerformanceHeader"/> for performance metrics version 1. + /// </summary> + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)] + public struct PerformanceFrameHeaderVersion1 : IPerformanceHeader + { + /// <summary> + /// The magic of the performance header. + /// </summary> + public uint Magic; + + /// <summary> + /// The total count of entries in this frame. + /// </summary> + public int EntryCount; + + /// <summary> + /// The total count of detailed entries in this frame. + /// </summary> + public int EntryDetailCount; + + /// <summary> + /// The offset of the next performance header. + /// </summary> + public int NextOffset; + + /// <summary> + /// The total time taken by all the commands profiled. + /// </summary> + public int TotalProcessingTime; + + /// <summary> + /// The count of voices that were dropped. + /// </summary> + 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; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs new file mode 100644 index 00000000..ba873239 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceFrameHeaderVersion2.cs @@ -0,0 +1,134 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + /// <summary> + /// Implementation of <see cref="IPerformanceHeader"/> for performance metrics version 2. + /// </summary> + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x30)] + public struct PerformanceFrameHeaderVersion2 : IPerformanceHeader + { + /// <summary> + /// The magic of the performance header. + /// </summary> + public uint Magic; + + /// <summary> + /// The total count of entries in this frame. + /// </summary> + public int EntryCount; + + /// <summary> + /// The total count of detailed entries in this frame. + /// </summary> + public int EntryDetailCount; + + /// <summary> + /// The offset of the next performance header. + /// </summary> + public int NextOffset; + + /// <summary> + /// The total time taken by all the commands profiled. + /// </summary> + public int TotalProcessingTime; + + /// <summary> + /// The count of voices that were dropped. + /// </summary> + public uint VoiceDropCount; + + /// <summary> + /// The start ticks of the <see cref="Dsp.AudioProcessor"/>. (before sending commands) + /// </summary> + public ulong StartRenderingTicks; + + /// <summary> + /// The index of this performance frame. + /// </summary> + public uint Index; + + /// <summary> + /// If set to true, the DSP is running behind. + /// </summary> + [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; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs new file mode 100644 index 00000000..3a336af3 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs @@ -0,0 +1,124 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Parameter; +using System; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Audio.Renderer.Server.Performance +{ + public abstract class PerformanceManager + { + /// <summary> + /// Get the required size for a single performance frame. + /// </summary> + /// <param name="parameter">The audio renderer configuration.</param> + /// <param name="behaviourContext">The behaviour context.</param> + /// <returns>The required size for a single performance frame.</returns> + public static ulong GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref AudioRendererConfiguration parameter, ref BehaviourContext behaviourContext) + { + uint version = behaviourContext.GetPerformanceMetricsDataFormat(); + + if (version == 2) + { + return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, + PerformanceEntryVersion2, + PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); + } + else if (version == 1) + { + return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, + PerformanceEntryVersion1, + PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter); + } + + throw new NotImplementedException($"Unknown Performance metrics data format version {version}"); + } + + /// <summary> + /// Copy the performance frame history to the supplied user buffer and returns the size copied. + /// </summary> + /// <param name="performanceOutput">The supplied user buffer to store the performance frame into.</param> + /// <returns>The size copied to the supplied buffer.</returns> + public abstract uint CopyHistories(Span<byte> performanceOutput); + + /// <summary> + /// Set the target node id to profile. + /// </summary> + /// <param name="target">The target node id to profile.</param> + public abstract void SetTargetNodeId(int target); + + /// <summary> + /// Check if the given target node id is profiled. + /// </summary> + /// <param name="target">The target node id to check.</param> + /// <returns>Return true, if the given target node id is profiled.</returns> + public abstract bool IsTargetNodeId(int target); + + /// <summary> + /// Get the next buffer to store a performance entry. + /// </summary> + /// <param name="performanceEntry">The output <see cref="PerformanceEntryAddresses"/>.</param> + /// <param name="entryType">The <see cref="PerformanceEntryType"/> info.</param> + /// <param name="nodeId">The node id of the entry.</param> + /// <returns>Return true, if a valid <see cref="PerformanceEntryAddresses"/> was returned.</returns> + public abstract bool GetNextEntry(out PerformanceEntryAddresses performanceEntry, PerformanceEntryType entryType, int nodeId); + + /// <summary> + /// Get the next buffer to store a performance detailed entry. + /// </summary> + /// <param name="performanceEntry">The output <see cref="PerformanceEntryAddresses"/>.</param> + /// <param name="detailType">The <see cref="PerformanceDetailType"/> info.</param> + /// <param name="entryType">The <see cref="PerformanceEntryType"/> info.</param> + /// <param name="nodeId">The node id of the entry.</param> + /// <returns>Return true, if a valid <see cref="PerformanceEntryAddresses"/> was returned.</returns> + public abstract bool GetNextEntry(out PerformanceEntryAddresses performanceEntry, PerformanceDetailType detailType, PerformanceEntryType entryType, int nodeId); + + /// <summary> + /// Finalize the current performance frame. + /// </summary> + /// <param name="dspRunningBehind">Indicate if the DSP is running behind.</param> + /// <param name="voiceDropCount">The count of voices that were dropped.</param> + /// <param name="startRenderingTicks">The start ticks of the audio rendering.</param> + public abstract void TapFrame(bool dspRunningBehind, uint voiceDropCount, ulong startRenderingTicks); + + /// <summary> + /// Create a new <see cref="PerformanceManager"/>. + /// </summary> + /// <param name="performanceBuffer">The backing memory available for use by the manager.</param> + /// <param name="parameter">The audio renderer configuration.</param> + /// <param name="behaviourContext">The behaviour context;</param> + /// <returns>A new <see cref="PerformanceManager"/>.</returns> + public static PerformanceManager Create(Memory<byte> performanceBuffer, ref AudioRendererConfiguration parameter, BehaviourContext behaviourContext) + { + uint version = behaviourContext.GetPerformanceMetricsDataFormat(); + + switch (version) + { + case 1: + return new PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, PerformanceEntryVersion1, PerformanceDetailVersion1>(performanceBuffer, + ref parameter); + case 2: + return new PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, PerformanceEntryVersion2, PerformanceDetailVersion2>(performanceBuffer, + ref parameter); + default: + throw new NotImplementedException($"Unknown Performance metrics data format version {version}"); + } + } + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs new file mode 100644 index 00000000..b17e6f3f --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManagerGeneric.cs @@ -0,0 +1,311 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +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 +{ + /// <summary> + /// A Generic implementation of <see cref="PerformanceManager"/>. + /// </summary> + /// <typeparam name="THeader">The header implementation of the performance frame.</typeparam> + /// <typeparam name="TEntry">The entry implementation of the performance frame.</typeparam> + /// <typeparam name="TEntryDetail">A detailed implementation of the performance frame.</typeparam> + public class PerformanceManagerGeneric<THeader, TEntry, TEntryDetail> : PerformanceManager where THeader: unmanaged, IPerformanceHeader where TEntry : unmanaged, IPerformanceEntry where TEntryDetail: unmanaged, IPerformanceDetailEntry + { + /// <summary> + /// The magic used for the <see cref="THeader"/>. + /// </summary> + private const uint MagicPerformanceBuffer = 0x46524550; + + /// <summary> + /// The fixed amount of <see cref="TEntryDetail"/> that can be stored in a frame. + /// </summary> + private const int MaxFrameDetailCount = 100; + + private Memory<byte> _buffer; + private Memory<byte> _historyBuffer; + + private Memory<byte> CurrentBuffer => _buffer.Slice(0, _frameSize); + private Memory<byte> CurrentBufferData => CurrentBuffer.Slice(Unsafe.SizeOf<THeader>()); + + private ref THeader CurrentHeader => ref MemoryMarshal.Cast<byte, THeader>(CurrentBuffer.Span)[0]; + + private Span<TEntry> Entries => MemoryMarshal.Cast<byte, TEntry>(CurrentBufferData.Span.Slice(0, GetEntriesSize())); + private Span<TEntryDetail> EntriesDetail => MemoryMarshal.Cast<byte, TEntryDetail>(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<byte> 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<byte> GetBufferFromIndex(Span<byte> data, int index) + { + return data.Slice(index * _frameSize, _frameSize); + } + + private ref THeader GetHeaderFromBuffer(Span<byte> data, int index) + { + return ref MemoryMarshal.Cast<byte, THeader>(GetBufferFromIndex(data, index))[0]; + } + + private Span<TEntry> GetEntriesFromBuffer(Span<byte> data, int index) + { + return MemoryMarshal.Cast<byte, TEntry>(GetBufferFromIndex(data, index).Slice(Unsafe.SizeOf<THeader>(), GetEntriesSize())); + } + + private Span<TEntryDetail> GetEntriesDetailFromBuffer(Span<byte> data, int index) + { + return MemoryMarshal.Cast<byte, TEntryDetail>(GetBufferFromIndex(data, index).Slice(Unsafe.SizeOf<THeader>() + 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<TEntry>() * _entryCountPerFrame; + } + + public static int GetEntriesDetailSize() + { + return Unsafe.SizeOf<TEntryDetail>() * MaxFrameDetailCount; + } + + public static int GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref AudioRendererConfiguration parameter) + { + return Unsafe.SizeOf<TEntry>() * (int)GetEntryCount(ref parameter) + GetEntriesDetailSize() + Unsafe.SizeOf<THeader>(); + } + + public override uint CopyHistories(Span<byte> 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<TEntry> inputEntries = GetEntriesFromBuffer(_historyBuffer.Span, _indexHistoryRead); + Span<TEntryDetail> inputEntriesDetail = GetEntriesDetailFromBuffer(_historyBuffer.Span, _indexHistoryRead); + + Span<byte> targetSpan = performanceOutput.Slice(nextOffset); + + ref THeader outputHeader = ref MemoryMarshal.Cast<byte, THeader>(targetSpan)[0]; + + nextOffset += Unsafe.SizeOf<THeader>(); + + Span<TEntry> outputEntries = MemoryMarshal.Cast<byte, TEntry>(targetSpan.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<TEntry>(); + + totalProcessingTime += input.GetProcessingTime(); + } + } + + Span<TEntryDetail> outputEntriesDetail = MemoryMarshal.Cast<byte, TEntryDetail>(targetSpan.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<TEntryDetail>(); + } + } + + 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<THeader>()) + { + ref THeader outputHeader = ref MemoryMarshal.Cast<byte, THeader>(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<int>.Cast(CurrentBuffer); + performanceEntry.EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(); + + uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + Unsafe.SizeOf<TEntry>() * _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<int>.Cast(CurrentBuffer); + performanceEntry.EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(); + + uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<IPerformanceDetailEntry>() * _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(); + } + } + } +} |
