Project Andio
This commit is contained in:
191
src/audio_core/renderer/behavior/behavior_info.cpp
Normal file
191
src/audio_core/renderer/behavior/behavior_info.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/common/feature_support.h"
|
||||
#include "audio_core/renderer/behavior/behavior_info.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
||||
BehaviorInfo::BehaviorInfo() : process_revision{CurrentRevision} {}
|
||||
|
||||
u32 BehaviorInfo::GetProcessRevisionNum() const {
|
||||
return process_revision;
|
||||
}
|
||||
|
||||
u32 BehaviorInfo::GetProcessRevision() const {
|
||||
return Common::MakeMagic('R', 'E', 'V',
|
||||
static_cast<char>(static_cast<u8>('0') + process_revision));
|
||||
}
|
||||
|
||||
u32 BehaviorInfo::GetUserRevisionNum() const {
|
||||
return user_revision;
|
||||
}
|
||||
|
||||
u32 BehaviorInfo::GetUserRevision() const {
|
||||
return Common::MakeMagic('R', 'E', 'V',
|
||||
static_cast<char>(static_cast<u8>('0') + user_revision));
|
||||
}
|
||||
|
||||
void BehaviorInfo::SetUserLibRevision(const u32 user_revision_) {
|
||||
user_revision = GetRevisionNum(user_revision_);
|
||||
}
|
||||
|
||||
void BehaviorInfo::ClearError() {
|
||||
error_count = 0;
|
||||
}
|
||||
|
||||
void BehaviorInfo::AppendError(ErrorInfo& error) {
|
||||
LOG_ERROR(Service_Audio, "Error during RequestUpdate, reporting code {:04X} address {:08X}",
|
||||
error.error_code.raw, error.address);
|
||||
if (error_count < MaxErrors) {
|
||||
errors[error_count++] = error;
|
||||
}
|
||||
}
|
||||
|
||||
void BehaviorInfo::CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count) {
|
||||
auto error_count_{std::min(error_count, MaxErrors)};
|
||||
std::memset(out_errors.data(), 0, MaxErrors * sizeof(ErrorInfo));
|
||||
|
||||
for (size_t i = 0; i < error_count_; i++) {
|
||||
out_errors[i] = errors[i];
|
||||
}
|
||||
out_count = error_count_;
|
||||
}
|
||||
|
||||
void BehaviorInfo::UpdateFlags(const Flags flags_) {
|
||||
flags = flags_;
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsMemoryForceMappingEnabled() const {
|
||||
return flags.IsMemoryForceMappingEnabled;
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsAdpcmLoopContextBugFixed() const {
|
||||
return CheckFeatureSupported(SupportTags::AdpcmLoopContextBugFix, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsSplitterSupported() const {
|
||||
return CheckFeatureSupported(SupportTags::Splitter, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsSplitterBugFixed() const {
|
||||
return CheckFeatureSupported(SupportTags::SplitterBugFix, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsEffectInfoVersion2Supported() const {
|
||||
return CheckFeatureSupported(SupportTags::EffectInfoVer2, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsVariadicCommandBufferSizeSupported() const {
|
||||
return CheckFeatureSupported(SupportTags::AudioRendererVariadicCommandBufferSize,
|
||||
user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsWaveBufferVer2Supported() const {
|
||||
return CheckFeatureSupported(SupportTags::WaveBufferVer2, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsLongSizePreDelaySupported() const {
|
||||
return CheckFeatureSupported(SupportTags::LongSizePreDelay, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsCommandProcessingTimeEstimatorVersion2Supported() const {
|
||||
return CheckFeatureSupported(SupportTags::CommandProcessingTimeEstimatorVersion2,
|
||||
user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsCommandProcessingTimeEstimatorVersion3Supported() const {
|
||||
return CheckFeatureSupported(SupportTags::CommandProcessingTimeEstimatorVersion3,
|
||||
user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsCommandProcessingTimeEstimatorVersion4Supported() const {
|
||||
return CheckFeatureSupported(SupportTags::CommandProcessingTimeEstimatorVersion4,
|
||||
user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsCommandProcessingTimeEstimatorVersion5Supported() const {
|
||||
return CheckFeatureSupported(SupportTags::CommandProcessingTimeEstimatorVersion4,
|
||||
user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit70PercentSupported() const {
|
||||
return CheckFeatureSupported(SupportTags::AudioRendererProcessingTimeLimit70Percent,
|
||||
user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit75PercentSupported() const {
|
||||
return CheckFeatureSupported(SupportTags::AudioRendererProcessingTimeLimit75Percent,
|
||||
user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit80PercentSupported() const {
|
||||
return CheckFeatureSupported(SupportTags::AudioRendererProcessingTimeLimit80Percent,
|
||||
user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsFlushVoiceWaveBuffersSupported() const {
|
||||
return CheckFeatureSupported(SupportTags::FlushVoiceWaveBuffers, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsElapsedFrameCountSupported() const {
|
||||
return CheckFeatureSupported(SupportTags::ElapsedFrameCount, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsPerformanceMetricsDataFormatVersion2Supported() const {
|
||||
return CheckFeatureSupported(SupportTags::PerformanceMetricsDataFormatVersion2, user_revision);
|
||||
}
|
||||
|
||||
size_t BehaviorInfo::GetPerformanceMetricsDataFormat() const {
|
||||
if (CheckFeatureSupported(SupportTags::PerformanceMetricsDataFormatVersion2, user_revision)) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsVoicePitchAndSrcSkippedSupported() const {
|
||||
return CheckFeatureSupported(SupportTags::VoicePitchAndSrcSkipped, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsVoicePlayedSampleCountResetAtLoopPointSupported() const {
|
||||
return CheckFeatureSupported(SupportTags::VoicePlayedSampleCountResetAtLoopPoint,
|
||||
user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsBiquadFilterEffectStateClearBugFixed() const {
|
||||
return CheckFeatureSupported(SupportTags::BiquadFilterEffectStateClearBugFix, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsVolumeMixParameterPrecisionQ23Supported() const {
|
||||
return CheckFeatureSupported(SupportTags::VolumeMixParameterPrecisionQ23, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::UseBiquadFilterFloatProcessing() const {
|
||||
return CheckFeatureSupported(SupportTags::BiquadFilterFloatProcessing, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsMixInParameterDirtyOnlyUpdateSupported() const {
|
||||
return CheckFeatureSupported(SupportTags::MixInParameterDirtyOnlyUpdate, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::UseMultiTapBiquadFilterProcessing() const {
|
||||
return CheckFeatureSupported(SupportTags::MultiTapBiquadFilterProcessing, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsDeviceApiVersion2Supported() const {
|
||||
return CheckFeatureSupported(SupportTags::DeviceApiVersion2, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsDelayChannelMappingChanged() const {
|
||||
return CheckFeatureSupported(SupportTags::DelayChannelMappingChange, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsReverbChannelMappingChanged() const {
|
||||
return CheckFeatureSupported(SupportTags::ReverbChannelMappingChange, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsI3dl2ReverbChannelMappingChanged() const {
|
||||
return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision);
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
376
src/audio_core/renderer/behavior/behavior_info.h
Normal file
376
src/audio_core/renderer/behavior/behavior_info.h
Normal file
@ -0,0 +1,376 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
|
||||
#include "audio_core/common/common.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
/**
|
||||
* Holds host and user revisions, checks whether render features can be enabled, and reports errors.
|
||||
*/
|
||||
class BehaviorInfo {
|
||||
static constexpr u32 MaxErrors = 10;
|
||||
|
||||
public:
|
||||
struct ErrorInfo {
|
||||
/* 0x00 */ Result error_code{0};
|
||||
/* 0x04 */ u32 unk_04;
|
||||
/* 0x08 */ CpuAddr address;
|
||||
};
|
||||
static_assert(sizeof(ErrorInfo) == 0x10, "BehaviorInfo::ErrorInfo has the wrong size!");
|
||||
|
||||
struct Flags {
|
||||
u64 IsMemoryForceMappingEnabled : 1;
|
||||
};
|
||||
|
||||
struct InParameter {
|
||||
/* 0x00 */ u32 revision;
|
||||
/* 0x08 */ Flags flags;
|
||||
};
|
||||
static_assert(sizeof(InParameter) == 0x10, "BehaviorInfo::InParameter has the wrong size!");
|
||||
|
||||
struct OutStatus {
|
||||
/* 0x00 */ std::array<ErrorInfo, MaxErrors> errors;
|
||||
/* 0xA0 */ u32 error_count;
|
||||
/* 0xA4 */ char unkA4[0xC];
|
||||
};
|
||||
static_assert(sizeof(OutStatus) == 0xB0, "BehaviorInfo::OutStatus has the wrong size!");
|
||||
|
||||
BehaviorInfo();
|
||||
|
||||
/**
|
||||
* Get the host revision as a number.
|
||||
*
|
||||
* @return The host revision.
|
||||
*/
|
||||
u32 GetProcessRevisionNum() const;
|
||||
|
||||
/**
|
||||
* Get the host revision in chars, e.g REV8.
|
||||
* Rev 10 and higher use the ascii characters above 9.
|
||||
* E.g:
|
||||
* Rev 10 = REV:
|
||||
* Rev 11 = REV;
|
||||
*
|
||||
* @return The host revision.
|
||||
*/
|
||||
u32 GetProcessRevision() const;
|
||||
|
||||
/**
|
||||
* Get the user revision as a number.
|
||||
*
|
||||
* @return The user revision.
|
||||
*/
|
||||
u32 GetUserRevisionNum() const;
|
||||
|
||||
/**
|
||||
* Get the user revision in chars, e.g REV8.
|
||||
* Rev 10 and higher use the ascii characters above 9. REV: REV; etc.
|
||||
*
|
||||
* @return The user revision.
|
||||
*/
|
||||
u32 GetUserRevision() const;
|
||||
|
||||
/**
|
||||
* Set the user revision.
|
||||
*
|
||||
* @param user_revision - The user's revision.
|
||||
*/
|
||||
void SetUserLibRevision(u32 user_revision);
|
||||
|
||||
/**
|
||||
* Clear the current error count.
|
||||
*/
|
||||
void ClearError();
|
||||
|
||||
/**
|
||||
* Append an error to the error list.
|
||||
*
|
||||
* @param error - The new error.
|
||||
*/
|
||||
void AppendError(ErrorInfo& error);
|
||||
|
||||
/**
|
||||
* Copy errors to the given output container.
|
||||
*
|
||||
* @param out_errors - Output container to receive the errors.
|
||||
* @param out_count - The number of errors written.
|
||||
*/
|
||||
void CopyErrorInfo(std::span<ErrorInfo> out_errors, u32& out_count);
|
||||
|
||||
/**
|
||||
* Update the behaviour flags.
|
||||
*
|
||||
* @param flags - New flags to use.
|
||||
*/
|
||||
void UpdateFlags(Flags flags);
|
||||
|
||||
/**
|
||||
* Check if memory pools can be forcibly mapped.
|
||||
*
|
||||
* @return True if enabled, otherwise false.
|
||||
*/
|
||||
bool IsMemoryForceMappingEnabled() const;
|
||||
|
||||
/**
|
||||
* Check if the ADPCM context bug is fixed.
|
||||
* The ADPCM context was not being sent to the AudioRenderer, leading to incorrect scaling being
|
||||
* used.
|
||||
*
|
||||
* @return True if fixed, otherwise false.
|
||||
*/
|
||||
bool IsAdpcmLoopContextBugFixed() const;
|
||||
|
||||
/**
|
||||
* Check if the splitter is supported.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsSplitterSupported() const;
|
||||
|
||||
/**
|
||||
* Check if the splitter bug is fixed.
|
||||
* Update is given the wrong number of splitter destinations, leading to invalid data
|
||||
* being processed.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsSplitterBugFixed() const;
|
||||
|
||||
/**
|
||||
* Check if effects version 2 are supported.
|
||||
* This gives support for returning effect states from the AudioRenderer, currently only used
|
||||
* for Limiter statistics.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsEffectInfoVersion2Supported() const;
|
||||
|
||||
/**
|
||||
* Check if a variadic command buffer is supported.
|
||||
* As of Rev 5 with the added optional performance metric logging, the command
|
||||
* buffer can be a variable size, so take that into account for calcualting its size.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsVariadicCommandBufferSizeSupported() const;
|
||||
|
||||
/**
|
||||
* Check if wave buffers version 2 are supported.
|
||||
* See WaveBufferVersion1 and WaveBufferVersion2.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsWaveBufferVer2Supported() const;
|
||||
|
||||
/**
|
||||
* Check if long size pre delay is supported.
|
||||
* This allows a longer initial delay time for the Reverb command.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsLongSizePreDelaySupported() const;
|
||||
|
||||
/**
|
||||
* Check if the command time estimator version 2 is supported.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsCommandProcessingTimeEstimatorVersion2Supported() const;
|
||||
|
||||
/**
|
||||
* Check if the command time estimator version 3 is supported.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsCommandProcessingTimeEstimatorVersion3Supported() const;
|
||||
|
||||
/**
|
||||
* Check if the command time estimator version 4 is supported.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsCommandProcessingTimeEstimatorVersion4Supported() const;
|
||||
|
||||
/**
|
||||
* Check if the command time estimator version 5 is supported.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsCommandProcessingTimeEstimatorVersion5Supported() const;
|
||||
|
||||
/**
|
||||
* Check if the AudioRenderer can use up to 70% of the allocated processing timeslice.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
|
||||
|
||||
/**
|
||||
* Check if the AudioRenderer can use up to 75% of the allocated processing timeslice.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
|
||||
|
||||
/**
|
||||
* Check if the AudioRenderer can use up to 80% of the allocated processing timeslice.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
|
||||
|
||||
/**
|
||||
* Check if voice flushing is supported
|
||||
* This allowws low-priority voices to be dropped if the AudioRenderer is running behind.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsFlushVoiceWaveBuffersSupported() const;
|
||||
|
||||
/**
|
||||
* Check if counting the number of elapsed frames is supported.
|
||||
* This adds extra output to RequestUpdate, returning the number of times the AudioRenderer
|
||||
* processed a command list.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsElapsedFrameCountSupported() const;
|
||||
|
||||
/**
|
||||
* Check if performance metrics version 2 are supported.
|
||||
* This adds extra output to RequestUpdate, returning the number of times the AudioRenderer
|
||||
* (Unused?).
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsPerformanceMetricsDataFormatVersion2Supported() const;
|
||||
|
||||
/**
|
||||
* Get the supported performance metrics version.
|
||||
* Version 2 logs some extra fields in output, such as number of voices dropped,
|
||||
* processing start time, if the AudioRenderer exceeded its time, etc.
|
||||
*
|
||||
* @return Version supported, either 1 or 2.
|
||||
*/
|
||||
size_t GetPerformanceMetricsDataFormat() const;
|
||||
|
||||
/**
|
||||
* Check if skipping voice pitch and sample rate conversion is supported.
|
||||
* This speeds up the data source commands by skipping resampling if unwanted.
|
||||
* See AudioCore::AudioRenderer::DecodeFromWaveBuffers
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsVoicePitchAndSrcSkippedSupported() const;
|
||||
|
||||
/**
|
||||
* Check if resetting played sample count at loop points is supported.
|
||||
* This resets the number of samples played in a voice state when a loop point is reached.
|
||||
* See AudioCore::AudioRenderer::DecodeFromWaveBuffers
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
|
||||
|
||||
/**
|
||||
* Check if the clear state bug for biquad filters is fixed.
|
||||
* The biquad state was not marked as needing re-initialisation when the effect was updated, it
|
||||
* was only initialized once with a new effect.
|
||||
*
|
||||
* @return True if fixed, otherwise false.
|
||||
*/
|
||||
bool IsBiquadFilterEffectStateClearBugFixed() const;
|
||||
|
||||
/**
|
||||
* Check if Q23 precision is supported for fixed point.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsVolumeMixParameterPrecisionQ23Supported() const;
|
||||
|
||||
/**
|
||||
* Check if float processing for biuad filters is supported.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool UseBiquadFilterFloatProcessing() const;
|
||||
|
||||
/**
|
||||
* Check if dirty-only mix updates are supported.
|
||||
* This saves a lot of buffer size as mixes can be large and not change much.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsMixInParameterDirtyOnlyUpdateSupported() const;
|
||||
|
||||
/**
|
||||
* Check if multi-tap biquad filters are supported.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool UseMultiTapBiquadFilterProcessing() const;
|
||||
|
||||
/**
|
||||
* Check if device api version 2 is supported.
|
||||
* In the SDK but not in any sysmodule? Not sure, left here for completeness anyway.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsDeviceApiVersion2Supported() const;
|
||||
|
||||
/**
|
||||
* Check if new channel mappings are used for Delay commands.
|
||||
* Older commands used:
|
||||
* front left/front right/back left/back right/center/lfe
|
||||
* Whereas everywhere else in the code uses:
|
||||
* front left/front right/center/lfe/back left/back right
|
||||
* This corrects that and makes everything standardised.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsDelayChannelMappingChanged() const;
|
||||
|
||||
/**
|
||||
* Check if new channel mappings are used for Reverb commands.
|
||||
* Older commands used:
|
||||
* front left/front right/back left/back right/center/lfe
|
||||
* Whereas everywhere else in the code uses:
|
||||
* front left/front right/center/lfe/back left/back right
|
||||
* This corrects that and makes everything standardised.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsReverbChannelMappingChanged() const;
|
||||
|
||||
/**
|
||||
* Check if new channel mappings are used for I3dl2Reverb commands.
|
||||
* Older commands used:
|
||||
* front left/front right/back left/back right/center/lfe
|
||||
* Whereas everywhere else in the code uses:
|
||||
* front left/front right/center/lfe/back left/back right
|
||||
* This corrects that and makes everything standardised.
|
||||
*
|
||||
* @return True if supported, otherwise false.
|
||||
*/
|
||||
bool IsI3dl2ReverbChannelMappingChanged() const;
|
||||
|
||||
/// Host version
|
||||
u32 process_revision;
|
||||
/// User version
|
||||
u32 user_revision{};
|
||||
/// Behaviour flags
|
||||
Flags flags{};
|
||||
/// Errors generated and reported during Update
|
||||
std::array<ErrorInfo, MaxErrors> errors{};
|
||||
/// Error count
|
||||
u32 error_count{};
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
539
src/audio_core/renderer/behavior/info_updater.cpp
Normal file
539
src/audio_core/renderer/behavior/info_updater.cpp
Normal file
@ -0,0 +1,539 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/common/feature_support.h"
|
||||
#include "audio_core/renderer/behavior/behavior_info.h"
|
||||
#include "audio_core/renderer/behavior/info_updater.h"
|
||||
#include "audio_core/renderer/effect/effect_context.h"
|
||||
#include "audio_core/renderer/effect/effect_reset.h"
|
||||
#include "audio_core/renderer/memory/memory_pool_info.h"
|
||||
#include "audio_core/renderer/mix/mix_context.h"
|
||||
#include "audio_core/renderer/performance/performance_manager.h"
|
||||
#include "audio_core/renderer/sink/circular_buffer_sink_info.h"
|
||||
#include "audio_core/renderer/sink/device_sink_info.h"
|
||||
#include "audio_core/renderer/sink/sink_context.h"
|
||||
#include "audio_core/renderer/splitter/splitter_context.h"
|
||||
#include "audio_core/renderer/voice/voice_context.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
||||
InfoUpdater::InfoUpdater(std::span<const u8> input_, std::span<u8> output_,
|
||||
const u32 process_handle_, BehaviorInfo& behaviour_)
|
||||
: input{input_.data() + sizeof(UpdateDataHeader)},
|
||||
input_origin{input_}, output{output_.data() + sizeof(UpdateDataHeader)},
|
||||
output_origin{output_}, in_header{reinterpret_cast<const UpdateDataHeader*>(
|
||||
input_origin.data())},
|
||||
out_header{reinterpret_cast<UpdateDataHeader*>(output_origin.data())},
|
||||
expected_input_size{input_.size()}, expected_output_size{output_.size()},
|
||||
process_handle{process_handle_}, behaviour{behaviour_} {
|
||||
std::construct_at<UpdateDataHeader>(out_header, behaviour.GetProcessRevision());
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateVoiceChannelResources(VoiceContext& voice_context) {
|
||||
const auto voice_count{voice_context.GetCount()};
|
||||
std::span<const VoiceChannelResource::InParameter> in_params{
|
||||
reinterpret_cast<const VoiceChannelResource::InParameter*>(input), voice_count};
|
||||
|
||||
for (u32 i = 0; i < voice_count; i++) {
|
||||
auto& resource{voice_context.GetChannelResource(i)};
|
||||
resource.in_use = in_params[i].in_use;
|
||||
if (in_params[i].in_use) {
|
||||
resource.mix_volumes = in_params[i].mix_volumes;
|
||||
}
|
||||
}
|
||||
|
||||
const auto consumed_input_size{voice_count *
|
||||
static_cast<u32>(sizeof(VoiceChannelResource::InParameter))};
|
||||
if (consumed_input_size != in_header->voice_resources_size) {
|
||||
LOG_ERROR(Service_Audio,
|
||||
"Consumed an incorrect voice resource size, header size={}, consumed={}",
|
||||
in_header->voice_resources_size, consumed_input_size);
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
input += consumed_input_size;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||
std::span<MemoryPoolInfo> memory_pools,
|
||||
const u32 memory_pool_count) {
|
||||
const PoolMapper pool_mapper(process_handle, memory_pools, memory_pool_count,
|
||||
behaviour.IsMemoryForceMappingEnabled());
|
||||
const auto voice_count{voice_context.GetCount()};
|
||||
std::span<const VoiceInfo::InParameter> in_params{
|
||||
reinterpret_cast<const VoiceInfo::InParameter*>(input), voice_count};
|
||||
std::span<VoiceInfo::OutStatus> out_params{reinterpret_cast<VoiceInfo::OutStatus*>(output),
|
||||
voice_count};
|
||||
|
||||
for (u32 i = 0; i < voice_count; i++) {
|
||||
auto& voice_info{voice_context.GetInfo(i)};
|
||||
voice_info.in_use = false;
|
||||
}
|
||||
|
||||
u32 new_voice_count{0};
|
||||
|
||||
for (u32 i = 0; i < voice_count; i++) {
|
||||
const auto& in_param{in_params[i]};
|
||||
std::array<VoiceState*, MaxChannels> voice_states{};
|
||||
|
||||
if (!in_param.in_use) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& voice_info{voice_context.GetInfo(in_param.id)};
|
||||
|
||||
for (u32 channel = 0; channel < in_param.channel_count; channel++) {
|
||||
voice_states[channel] = &voice_context.GetState(in_param.channel_resource_ids[channel]);
|
||||
}
|
||||
|
||||
if (in_param.is_new) {
|
||||
voice_info.Initialize();
|
||||
|
||||
for (u32 channel = 0; channel < in_param.channel_count; channel++) {
|
||||
std::memset(voice_states[channel], 0, sizeof(VoiceState));
|
||||
}
|
||||
}
|
||||
|
||||
BehaviorInfo::ErrorInfo update_error{};
|
||||
voice_info.UpdateParameters(update_error, in_param, pool_mapper, behaviour);
|
||||
|
||||
if (!update_error.error_code.IsSuccess()) {
|
||||
behaviour.AppendError(update_error);
|
||||
}
|
||||
|
||||
std::array<std::array<BehaviorInfo::ErrorInfo, 2>, MaxWaveBuffers> wavebuffer_errors{};
|
||||
voice_info.UpdateWaveBuffers(wavebuffer_errors, MaxWaveBuffers * 2, in_param, voice_states,
|
||||
pool_mapper, behaviour);
|
||||
|
||||
for (auto& wavebuffer_error : wavebuffer_errors) {
|
||||
for (auto& error : wavebuffer_error) {
|
||||
if (error.error_code.IsError()) {
|
||||
behaviour.AppendError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
voice_info.WriteOutStatus(out_params[i], in_param, voice_states);
|
||||
new_voice_count += in_param.channel_count;
|
||||
}
|
||||
|
||||
auto consumed_input_size{voice_count * static_cast<u32>(sizeof(VoiceInfo::InParameter))};
|
||||
auto consumed_output_size{voice_count * static_cast<u32>(sizeof(VoiceInfo::OutStatus))};
|
||||
if (consumed_input_size != in_header->voices_size) {
|
||||
LOG_ERROR(Service_Audio, "Consumed an incorrect voices size, header size={}, consumed={}",
|
||||
in_header->voices_size, consumed_input_size);
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
out_header->voices_size = consumed_output_size;
|
||||
out_header->size += consumed_output_size;
|
||||
input += consumed_input_size;
|
||||
output += consumed_output_size;
|
||||
|
||||
voice_context.SetActiveCount(new_voice_count);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateEffects(EffectContext& effect_context, const bool renderer_active,
|
||||
std::span<MemoryPoolInfo> memory_pools,
|
||||
const u32 memory_pool_count) {
|
||||
if (behaviour.IsEffectInfoVersion2Supported()) {
|
||||
return UpdateEffectsVersion2(effect_context, renderer_active, memory_pools,
|
||||
memory_pool_count);
|
||||
} else {
|
||||
return UpdateEffectsVersion1(effect_context, renderer_active, memory_pools,
|
||||
memory_pool_count);
|
||||
}
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateEffectsVersion1(EffectContext& effect_context, const bool renderer_active,
|
||||
std::span<MemoryPoolInfo> memory_pools,
|
||||
const u32 memory_pool_count) {
|
||||
PoolMapper pool_mapper(process_handle, memory_pools, memory_pool_count,
|
||||
behaviour.IsMemoryForceMappingEnabled());
|
||||
|
||||
const auto effect_count{effect_context.GetCount()};
|
||||
|
||||
std::span<const EffectInfoBase::InParameterVersion1> in_params{
|
||||
reinterpret_cast<const EffectInfoBase::InParameterVersion1*>(input), effect_count};
|
||||
std::span<EffectInfoBase::OutStatusVersion1> out_params{
|
||||
reinterpret_cast<EffectInfoBase::OutStatusVersion1*>(output), effect_count};
|
||||
|
||||
for (u32 i = 0; i < effect_count; i++) {
|
||||
auto effect_info{&effect_context.GetInfo(i)};
|
||||
if (effect_info->GetType() != in_params[i].type) {
|
||||
effect_info->ForceUnmapBuffers(pool_mapper);
|
||||
ResetEffect(effect_info, in_params[i].type);
|
||||
}
|
||||
|
||||
BehaviorInfo::ErrorInfo error_info{};
|
||||
effect_info->Update(error_info, in_params[i], pool_mapper);
|
||||
if (error_info.error_code.IsError()) {
|
||||
behaviour.AppendError(error_info);
|
||||
}
|
||||
|
||||
effect_info->StoreStatus(out_params[i], renderer_active);
|
||||
}
|
||||
|
||||
auto consumed_input_size{effect_count *
|
||||
static_cast<u32>(sizeof(EffectInfoBase::InParameterVersion1))};
|
||||
auto consumed_output_size{effect_count *
|
||||
static_cast<u32>(sizeof(EffectInfoBase::OutStatusVersion1))};
|
||||
if (consumed_input_size != in_header->effects_size) {
|
||||
LOG_ERROR(Service_Audio, "Consumed an incorrect effects size, header size={}, consumed={}",
|
||||
in_header->effects_size, consumed_input_size);
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
out_header->effects_size = consumed_output_size;
|
||||
out_header->size += consumed_output_size;
|
||||
input += consumed_input_size;
|
||||
output += consumed_output_size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateEffectsVersion2(EffectContext& effect_context, const bool renderer_active,
|
||||
std::span<MemoryPoolInfo> memory_pools,
|
||||
const u32 memory_pool_count) {
|
||||
PoolMapper pool_mapper(process_handle, memory_pools, memory_pool_count,
|
||||
behaviour.IsMemoryForceMappingEnabled());
|
||||
|
||||
const auto effect_count{effect_context.GetCount()};
|
||||
|
||||
std::span<const EffectInfoBase::InParameterVersion2> in_params{
|
||||
reinterpret_cast<const EffectInfoBase::InParameterVersion2*>(input), effect_count};
|
||||
std::span<EffectInfoBase::OutStatusVersion2> out_params{
|
||||
reinterpret_cast<EffectInfoBase::OutStatusVersion2*>(output), effect_count};
|
||||
|
||||
for (u32 i = 0; i < effect_count; i++) {
|
||||
auto effect_info{&effect_context.GetInfo(i)};
|
||||
if (effect_info->GetType() != in_params[i].type) {
|
||||
effect_info->ForceUnmapBuffers(pool_mapper);
|
||||
ResetEffect(effect_info, in_params[i].type);
|
||||
}
|
||||
|
||||
BehaviorInfo::ErrorInfo error_info{};
|
||||
effect_info->Update(error_info, in_params[i], pool_mapper);
|
||||
|
||||
if (error_info.error_code.IsError()) {
|
||||
behaviour.AppendError(error_info);
|
||||
}
|
||||
|
||||
effect_info->StoreStatus(out_params[i], renderer_active);
|
||||
|
||||
if (in_params[i].is_new) {
|
||||
effect_info->InitializeResultState(effect_context.GetDspSharedResultState(i));
|
||||
effect_info->InitializeResultState(effect_context.GetResultState(i));
|
||||
}
|
||||
effect_info->UpdateResultState(out_params[i].result_state,
|
||||
effect_context.GetResultState(i));
|
||||
}
|
||||
|
||||
auto consumed_input_size{effect_count *
|
||||
static_cast<u32>(sizeof(EffectInfoBase::InParameterVersion2))};
|
||||
auto consumed_output_size{effect_count *
|
||||
static_cast<u32>(sizeof(EffectInfoBase::OutStatusVersion2))};
|
||||
if (consumed_input_size != in_header->effects_size) {
|
||||
LOG_ERROR(Service_Audio, "Consumed an incorrect effects size, header size={}, consumed={}",
|
||||
in_header->effects_size, consumed_input_size);
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
out_header->effects_size = consumed_output_size;
|
||||
out_header->size += consumed_output_size;
|
||||
input += consumed_input_size;
|
||||
output += consumed_output_size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateMixes(MixContext& mix_context, const u32 mix_buffer_count,
|
||||
EffectContext& effect_context, SplitterContext& splitter_context) {
|
||||
s32 mix_count{0};
|
||||
u32 consumed_input_size{0};
|
||||
|
||||
if (behaviour.IsMixInParameterDirtyOnlyUpdateSupported()) {
|
||||
auto in_dirty_params{reinterpret_cast<const MixInfo::InDirtyParameter*>(input)};
|
||||
mix_count = in_dirty_params->count;
|
||||
input += sizeof(MixInfo::InDirtyParameter);
|
||||
consumed_input_size = static_cast<u32>(sizeof(MixInfo::InDirtyParameter) +
|
||||
mix_count * sizeof(MixInfo::InParameter));
|
||||
} else {
|
||||
mix_count = mix_context.GetCount();
|
||||
consumed_input_size = static_cast<u32>(mix_count * sizeof(MixInfo::InParameter));
|
||||
}
|
||||
|
||||
if (mix_buffer_count == 0) {
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
std::span<const MixInfo::InParameter> in_params{
|
||||
reinterpret_cast<const MixInfo::InParameter*>(input), static_cast<size_t>(mix_count)};
|
||||
|
||||
u32 total_buffer_count{0};
|
||||
for (s32 i = 0; i < mix_count; i++) {
|
||||
const auto& params{in_params[i]};
|
||||
|
||||
if (params.in_use) {
|
||||
total_buffer_count += params.buffer_count;
|
||||
if (params.dest_mix_id > static_cast<s32>(mix_context.GetCount()) &&
|
||||
params.dest_mix_id != UnusedMixId && params.mix_id != FinalMixId) {
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (total_buffer_count > mix_buffer_count) {
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
bool mix_dirty{false};
|
||||
for (s32 i = 0; i < mix_count; i++) {
|
||||
const auto& params{in_params[i]};
|
||||
|
||||
s32 mix_id{i};
|
||||
if (behaviour.IsMixInParameterDirtyOnlyUpdateSupported()) {
|
||||
mix_id = params.mix_id;
|
||||
}
|
||||
|
||||
auto mix_info{mix_context.GetInfo(mix_id)};
|
||||
if (mix_info->in_use != params.in_use) {
|
||||
mix_info->in_use = params.in_use;
|
||||
if (!params.in_use) {
|
||||
mix_info->ClearEffectProcessingOrder();
|
||||
}
|
||||
mix_dirty = true;
|
||||
}
|
||||
|
||||
if (params.in_use) {
|
||||
mix_dirty |= mix_info->Update(mix_context.GetEdgeMatrix(), params, effect_context,
|
||||
splitter_context, behaviour);
|
||||
}
|
||||
}
|
||||
|
||||
if (mix_dirty) {
|
||||
if (behaviour.IsSplitterSupported() && splitter_context.UsingSplitter()) {
|
||||
if (!mix_context.TSortInfo(splitter_context)) {
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
} else {
|
||||
mix_context.SortInfo();
|
||||
}
|
||||
}
|
||||
|
||||
if (consumed_input_size != in_header->mix_size) {
|
||||
LOG_ERROR(Service_Audio, "Consumed an incorrect mixes size, header size={}, consumed={}",
|
||||
in_header->mix_size, consumed_input_size);
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
input += mix_count * sizeof(MixInfo::InParameter);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateSinks(SinkContext& sink_context, std::span<MemoryPoolInfo> memory_pools,
|
||||
const u32 memory_pool_count) {
|
||||
PoolMapper pool_mapper(process_handle, memory_pools, memory_pool_count,
|
||||
behaviour.IsMemoryForceMappingEnabled());
|
||||
|
||||
std::span<const SinkInfoBase::InParameter> in_params{
|
||||
reinterpret_cast<const SinkInfoBase::InParameter*>(input), memory_pool_count};
|
||||
std::span<SinkInfoBase::OutStatus> out_params{
|
||||
reinterpret_cast<SinkInfoBase::OutStatus*>(output), memory_pool_count};
|
||||
|
||||
const auto sink_count{sink_context.GetCount()};
|
||||
|
||||
for (u32 i = 0; i < sink_count; i++) {
|
||||
const auto& params{in_params[i]};
|
||||
auto sink_info{sink_context.GetInfo(i)};
|
||||
|
||||
if (sink_info->GetType() != params.type) {
|
||||
sink_info->CleanUp();
|
||||
switch (params.type) {
|
||||
case SinkInfoBase::Type::Invalid:
|
||||
std::construct_at<SinkInfoBase>(reinterpret_cast<SinkInfoBase*>(sink_info));
|
||||
break;
|
||||
case SinkInfoBase::Type::DeviceSink:
|
||||
std::construct_at<DeviceSinkInfo>(reinterpret_cast<DeviceSinkInfo*>(sink_info));
|
||||
break;
|
||||
case SinkInfoBase::Type::CircularBufferSink:
|
||||
std::construct_at<CircularBufferSinkInfo>(
|
||||
reinterpret_cast<CircularBufferSinkInfo*>(sink_info));
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_Audio, "Invalid sink type {}", static_cast<u32>(params.type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BehaviorInfo::ErrorInfo error_info{};
|
||||
sink_info->Update(error_info, out_params[i], params, pool_mapper);
|
||||
|
||||
if (error_info.error_code.IsError()) {
|
||||
behaviour.AppendError(error_info);
|
||||
}
|
||||
}
|
||||
|
||||
const auto consumed_input_size{sink_count *
|
||||
static_cast<u32>(sizeof(SinkInfoBase::InParameter))};
|
||||
const auto consumed_output_size{sink_count * static_cast<u32>(sizeof(SinkInfoBase::OutStatus))};
|
||||
if (consumed_input_size != in_header->sinks_size) {
|
||||
LOG_ERROR(Service_Audio, "Consumed an incorrect sinks size, header size={}, consumed={}",
|
||||
in_header->sinks_size, consumed_input_size);
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
input += consumed_input_size;
|
||||
output += consumed_output_size;
|
||||
out_header->sinks_size = consumed_output_size;
|
||||
out_header->size += consumed_output_size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateMemoryPools(std::span<MemoryPoolInfo> memory_pools,
|
||||
const u32 memory_pool_count) {
|
||||
PoolMapper pool_mapper(process_handle, memory_pools, memory_pool_count,
|
||||
behaviour.IsMemoryForceMappingEnabled());
|
||||
std::span<const MemoryPoolInfo::InParameter> in_params{
|
||||
reinterpret_cast<const MemoryPoolInfo::InParameter*>(input), memory_pool_count};
|
||||
std::span<MemoryPoolInfo::OutStatus> out_params{
|
||||
reinterpret_cast<MemoryPoolInfo::OutStatus*>(output), memory_pool_count};
|
||||
|
||||
for (size_t i = 0; i < memory_pool_count; i++) {
|
||||
auto state{pool_mapper.Update(memory_pools[i], in_params[i], out_params[i])};
|
||||
if (state != MemoryPoolInfo::ResultState::Success &&
|
||||
state != MemoryPoolInfo::ResultState::BadParam &&
|
||||
state != MemoryPoolInfo::ResultState::MapFailed &&
|
||||
state != MemoryPoolInfo::ResultState::InUse) {
|
||||
LOG_WARNING(Service_Audio, "Invalid ResultState from updating memory pools");
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
const auto consumed_input_size{memory_pool_count *
|
||||
static_cast<u32>(sizeof(MemoryPoolInfo::InParameter))};
|
||||
const auto consumed_output_size{memory_pool_count *
|
||||
static_cast<u32>(sizeof(MemoryPoolInfo::OutStatus))};
|
||||
if (consumed_input_size != in_header->memory_pool_size) {
|
||||
LOG_ERROR(Service_Audio,
|
||||
"Consumed an incorrect memory pool size, header size={}, consumed={}",
|
||||
in_header->memory_pool_size, consumed_input_size);
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
input += consumed_input_size;
|
||||
output += consumed_output_size;
|
||||
out_header->memory_pool_size = consumed_output_size;
|
||||
out_header->size += consumed_output_size;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdatePerformanceBuffer(std::span<u8> performance_output,
|
||||
const u64 performance_output_size,
|
||||
PerformanceManager* performance_manager) {
|
||||
auto in_params{reinterpret_cast<const PerformanceManager::InParameter*>(input)};
|
||||
auto out_params{reinterpret_cast<PerformanceManager::OutStatus*>(output)};
|
||||
|
||||
if (performance_manager != nullptr) {
|
||||
out_params->history_size =
|
||||
performance_manager->CopyHistories(performance_output.data(), performance_output_size);
|
||||
performance_manager->SetDetailTarget(in_params->target_node_id);
|
||||
} else {
|
||||
out_params->history_size = 0;
|
||||
}
|
||||
|
||||
const auto consumed_input_size{static_cast<u32>(sizeof(PerformanceManager::InParameter))};
|
||||
const auto consumed_output_size{static_cast<u32>(sizeof(PerformanceManager::OutStatus))};
|
||||
if (consumed_input_size != in_header->performance_buffer_size) {
|
||||
LOG_ERROR(Service_Audio,
|
||||
"Consumed an incorrect performance size, header size={}, consumed={}",
|
||||
in_header->performance_buffer_size, consumed_input_size);
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
input += consumed_input_size;
|
||||
output += consumed_output_size;
|
||||
out_header->performance_buffer_size = consumed_output_size;
|
||||
out_header->size += consumed_output_size;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateBehaviorInfo(BehaviorInfo& behaviour_) {
|
||||
const auto in_params{reinterpret_cast<const BehaviorInfo::InParameter*>(input)};
|
||||
|
||||
if (!CheckValidRevision(in_params->revision)) {
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
if (in_params->revision != behaviour_.GetUserRevision()) {
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
behaviour_.ClearError();
|
||||
behaviour_.UpdateFlags(in_params->flags);
|
||||
|
||||
if (in_header->behaviour_size != sizeof(BehaviorInfo::InParameter)) {
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
input += sizeof(BehaviorInfo::InParameter);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateErrorInfo(BehaviorInfo& behaviour_) {
|
||||
auto out_params{reinterpret_cast<BehaviorInfo::OutStatus*>(output)};
|
||||
behaviour_.CopyErrorInfo(out_params->errors, out_params->error_count);
|
||||
|
||||
const auto consumed_output_size{static_cast<u32>(sizeof(BehaviorInfo::OutStatus))};
|
||||
|
||||
output += consumed_output_size;
|
||||
out_header->behaviour_size = consumed_output_size;
|
||||
out_header->size += consumed_output_size;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateSplitterInfo(SplitterContext& splitter_context) {
|
||||
u32 consumed_size{0};
|
||||
if (!splitter_context.Update(input, consumed_size)) {
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
|
||||
input += consumed_size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::UpdateRendererInfo(const u64 elapsed_frames) {
|
||||
struct RenderInfo {
|
||||
/* 0x00 */ u64 frames_elapsed;
|
||||
/* 0x08 */ char unk08[0x8];
|
||||
};
|
||||
static_assert(sizeof(RenderInfo) == 0x10, "RenderInfo has the wrong size!");
|
||||
|
||||
auto out_params{reinterpret_cast<RenderInfo*>(output)};
|
||||
out_params->frames_elapsed = elapsed_frames;
|
||||
|
||||
const auto consumed_output_size{static_cast<u32>(sizeof(RenderInfo))};
|
||||
|
||||
output += consumed_output_size;
|
||||
out_header->render_info_size = consumed_output_size;
|
||||
out_header->size += consumed_output_size;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result InfoUpdater::CheckConsumedSize() {
|
||||
if (CpuAddr(input) - CpuAddr(input_origin.data()) != expected_input_size) {
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
} else if (CpuAddr(output) - CpuAddr(output_origin.data()) != expected_output_size) {
|
||||
return Service::Audio::ERR_INVALID_UPDATE_DATA;
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
205
src/audio_core/renderer/behavior/info_updater.h
Normal file
205
src/audio_core/renderer/behavior/info_updater.h
Normal file
@ -0,0 +1,205 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
class BehaviorInfo;
|
||||
class VoiceContext;
|
||||
class MixContext;
|
||||
class SinkContext;
|
||||
class SplitterContext;
|
||||
class EffectContext;
|
||||
class MemoryPoolInfo;
|
||||
class PerformanceManager;
|
||||
|
||||
class InfoUpdater {
|
||||
struct UpdateDataHeader {
|
||||
explicit UpdateDataHeader(u32 revision_) : revision{revision_} {}
|
||||
|
||||
/* 0x00 */ u32 revision;
|
||||
/* 0x04 */ u32 behaviour_size{};
|
||||
/* 0x08 */ u32 memory_pool_size{};
|
||||
/* 0x0C */ u32 voices_size{};
|
||||
/* 0x10 */ u32 voice_resources_size{};
|
||||
/* 0x14 */ u32 effects_size{};
|
||||
/* 0x18 */ u32 mix_size{};
|
||||
/* 0x1C */ u32 sinks_size{};
|
||||
/* 0x20 */ u32 performance_buffer_size{};
|
||||
/* 0x24 */ char unk24[4];
|
||||
/* 0x28 */ u32 render_info_size{};
|
||||
/* 0x2C */ char unk2C[0x10];
|
||||
/* 0x3C */ u32 size{sizeof(UpdateDataHeader)};
|
||||
};
|
||||
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has the wrong size!");
|
||||
|
||||
public:
|
||||
explicit InfoUpdater(std::span<const u8> input, std::span<u8> output, u32 process_handle,
|
||||
BehaviorInfo& behaviour);
|
||||
|
||||
/**
|
||||
* Update the voice channel resources.
|
||||
*
|
||||
* @param voice_context - Voice context to update.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateVoiceChannelResources(VoiceContext& voice_context);
|
||||
|
||||
/**
|
||||
* Update voices.
|
||||
*
|
||||
* @param voice_context - Voice context to update.
|
||||
* @param memory_pools - Memory pools to use for these voices.
|
||||
* @param memory_pool_count - Number of memory pools.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateVoices(VoiceContext& voice_context, std::span<MemoryPoolInfo> memory_pools,
|
||||
u32 memory_pool_count);
|
||||
|
||||
/**
|
||||
* Update effects.
|
||||
*
|
||||
* @param effect_context - Effect context to update.
|
||||
* @param renderer_active - Whether the AudioRenderer is active.
|
||||
* @param memory_pools - Memory pools to use for these voices.
|
||||
* @param memory_pool_count - Number of memory pools.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateEffects(EffectContext& effect_context, bool renderer_active,
|
||||
std::span<MemoryPoolInfo> memory_pools, u32 memory_pool_count);
|
||||
|
||||
/**
|
||||
* Update mixes.
|
||||
*
|
||||
* @param mix_context - Mix context to update.
|
||||
* @param mix_buffer_count - Number of mix buffers.
|
||||
* @param effect_context - Effect context to update effort order.
|
||||
* @param splitter_context - Splitter context for the mixes.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateMixes(MixContext& mix_context, u32 mix_buffer_count, EffectContext& effect_context,
|
||||
SplitterContext& splitter_context);
|
||||
|
||||
/**
|
||||
* Update sinks.
|
||||
*
|
||||
* @param sink_context - Sink context to update.
|
||||
* @param memory_pools - Memory pools to use for these voices.
|
||||
* @param memory_pool_count - Number of memory pools.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateSinks(SinkContext& sink_context, std::span<MemoryPoolInfo> memory_pools,
|
||||
u32 memory_pool_count);
|
||||
|
||||
/**
|
||||
* Update memory pools.
|
||||
*
|
||||
* @param memory_pools - Memory pools to use for these voices.
|
||||
* @param memory_pool_count - Number of memory pools.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateMemoryPools(std::span<MemoryPoolInfo> memory_pools, u32 memory_pool_count);
|
||||
|
||||
/**
|
||||
* Update the performance buffer.
|
||||
*
|
||||
* @param output - Output buffer for performance metrics.
|
||||
* @param output_size - Output buffer size.
|
||||
* @param performance_manager - Performance manager..
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdatePerformanceBuffer(std::span<u8> output, u64 output_size,
|
||||
PerformanceManager* performance_manager);
|
||||
|
||||
/**
|
||||
* Update behaviour.
|
||||
*
|
||||
* @param behaviour - Behaviour to update.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateBehaviorInfo(BehaviorInfo& behaviour);
|
||||
|
||||
/**
|
||||
* Update errors.
|
||||
*
|
||||
* @param behaviour - Behaviour to update.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateErrorInfo(BehaviorInfo& behaviour);
|
||||
|
||||
/**
|
||||
* Update splitter.
|
||||
*
|
||||
* @param splitter_context - Splitter context to update.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateSplitterInfo(SplitterContext& splitter_context);
|
||||
|
||||
/**
|
||||
* Update renderer info.
|
||||
*
|
||||
* @param elapsed_frames - Number of elapsed frames.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateRendererInfo(u64 elapsed_frames);
|
||||
|
||||
/**
|
||||
* Check that the input.output sizes match their expected values.
|
||||
*
|
||||
* @return Result code.
|
||||
*/
|
||||
Result CheckConsumedSize();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Update effects version 1.
|
||||
*
|
||||
* @param effect_context - Effect context to update.
|
||||
* @param renderer_active - Is the AudioRenderer active?
|
||||
* @param memory_pools - Memory pools to use for these voices.
|
||||
* @param memory_pool_count - Number of memory pools.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateEffectsVersion1(EffectContext& effect_context, bool renderer_active,
|
||||
std::span<MemoryPoolInfo> memory_pools, u32 memory_pool_count);
|
||||
|
||||
/**
|
||||
* Update effects version 2.
|
||||
*
|
||||
* @param effect_context - Effect context to update.
|
||||
* @param renderer_active - Is the AudioRenderer active?
|
||||
* @param memory_pools - Memory pools to use for these voices.
|
||||
* @param memory_pool_count - Number of memory pools.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result UpdateEffectsVersion2(EffectContext& effect_context, bool renderer_active,
|
||||
std::span<MemoryPoolInfo> memory_pools, u32 memory_pool_count);
|
||||
|
||||
/// Input buffer
|
||||
u8 const* input;
|
||||
/// Input buffer start
|
||||
std::span<const u8> input_origin;
|
||||
/// Output buffer start
|
||||
u8* output;
|
||||
/// Output buffer start
|
||||
std::span<u8> output_origin;
|
||||
/// Input header
|
||||
const UpdateDataHeader* in_header;
|
||||
/// Output header
|
||||
UpdateDataHeader* out_header;
|
||||
/// Expected input size, see CheckConsumedSize
|
||||
u64 expected_input_size;
|
||||
/// Expected output size, see CheckConsumedSize
|
||||
u64 expected_output_size;
|
||||
/// Unused
|
||||
u32 process_handle;
|
||||
/// Behaviour
|
||||
BehaviorInfo& behaviour;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
Reference in New Issue
Block a user