forked from eden-emu/eden
		
	Project Andio
This commit is contained in:
		
							parent
							
								
									6e36f4d230
								
							
						
					
					
						commit
						458da8a948
					
				
					 270 changed files with 33712 additions and 8445 deletions
				
			
		
							
								
								
									
										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
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Kelebek1
						Kelebek1