Amadeus: Final Act (#1481)
* Amadeus: Final Act This is my requiem, I present to you Amadeus, a complete reimplementation of the Audio Renderer! This reimplementation is based on my reversing of every version of the audio system module that I carried for the past 10 months. This supports every revision (at the time of writing REV1 to REV8 included) and all features proposed by the Audio Renderer on real hardware. Because this component could be used outside an emulation context, and to avoid possible "inspirations" not crediting the project, I decided to license the Ryujinx.Audio.Renderer project under LGPLv3. - FE3H voices in videos and chapter intro are not present. - Games that use two audio renderer **at the same time** are probably going to have issues right now **until we rewrite the audio output interface** (Crash Team Racing is the only known game to use two renderer at the same time). - Persona 5 Scrambler now goes ingame but audio is garbage. This is caused by the fact that the game engine is syncing audio and video in a really aggressive way. This will disappears the day this game run at full speed. * Make timing more precise when sleeping on Windows Improve precision to a 1ms resolution on Windows NT based OS. This is used to avoid having totally erratic timings and unify all Windows users to the same resolution. NOTE: This is only active when emulation is running.
This commit is contained in:
parent
2a314f3c28
commit
a389dd59bd
250 changed files with 23691 additions and 1512 deletions
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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;
|
||||
}
|
||||
}
|
||||
}
|
124
Ryujinx.Audio.Renderer/Server/Performance/PerformanceManager.cs
Normal file
124
Ryujinx.Audio.Renderer/Server/Performance/PerformanceManager.cs
Normal file
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,311 @@
|
|||
//
|
||||
// Copyright (c) 2019-2020 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 > 1)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue