From ee72d99947382dac8f4fbd56f1915a3eb6b65cde Mon Sep 17 00:00:00 2001 From: Vladislav Mikhalin Date: Sun, 29 Dec 2024 13:53:06 +0300 Subject: [PATCH] ajm: added stubbed statistics instance (#1924) * ajm: added stubbed statistics instance * fixed a typo, thanks poly * fixed clang-format * removed unused struct * small fixes * fixed typedefs, added per codec statistics --- CMakeLists.txt | 2 + src/core/libraries/ajm/ajm.cpp | 10 +- src/core/libraries/ajm/ajm.h | 49 ++++++- src/core/libraries/ajm/ajm_batch.cpp | 129 ++++++++++++++---- src/core/libraries/ajm/ajm_batch.h | 4 + src/core/libraries/ajm/ajm_context.cpp | 21 +-- src/core/libraries/ajm/ajm_instance.cpp | 4 +- .../libraries/ajm/ajm_instance_statistics.cpp | 37 +++++ .../libraries/ajm/ajm_instance_statistics.h | 17 +++ 9 files changed, 231 insertions(+), 42 deletions(-) create mode 100644 src/core/libraries/ajm/ajm_instance_statistics.cpp create mode 100644 src/core/libraries/ajm/ajm_instance_statistics.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d63bd1951..af811e9fb 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp src/core/libraries/ajm/ajm_context.cpp src/core/libraries/ajm/ajm_context.h src/core/libraries/ajm/ajm_error.h + src/core/libraries/ajm/ajm_instance_statistics.cpp + src/core/libraries/ajm/ajm_instance_statistics.h src/core/libraries/ajm/ajm_instance.cpp src/core/libraries/ajm/ajm_instance.h src/core/libraries/ajm/ajm_mp3.cpp diff --git a/src/core/libraries/ajm/ajm.cpp b/src/core/libraries/ajm/ajm.cpp index 3184fa64f..5c55d2c06 100644 --- a/src/core/libraries/ajm/ajm.cpp +++ b/src/core/libraries/ajm/ajm.cpp @@ -183,13 +183,15 @@ int PS4_SYSV_ABI sceAjmInstanceSwitch() { return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmMemoryRegister() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); +int PS4_SYSV_ABI sceAjmMemoryRegister(u32 context_id, void* ptr, size_t num_pages) { + // All memory is already shared with our implementation since we do not use any hardware. + LOG_TRACE(Lib_Ajm, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceAjmMemoryUnregister() { - LOG_ERROR(Lib_Ajm, "(STUBBED) called"); +int PS4_SYSV_ABI sceAjmMemoryUnregister(u32 context_id, void* ptr) { + // All memory is already shared with our implementation since we do not use any hardware. + LOG_TRACE(Lib_Ajm, "(STUBBED) called"); return ORBIS_OK; } diff --git a/src/core/libraries/ajm/ajm.h b/src/core/libraries/ajm/ajm.h index 1ac7c7629..34aeb9aa4 100644 --- a/src/core/libraries/ajm/ajm.h +++ b/src/core/libraries/ajm/ajm.h @@ -74,6 +74,26 @@ union AjmJobFlags { }; }; +enum class AjmStatisticsFlags : u64 { + Memory = 1 << 0, + EnginePerCodec = 1 << 15, + Engine = 1 << 16, +}; +DECLARE_ENUM_FLAG_OPERATORS(AjmStatisticsFlags) + +union AjmStatisticsJobFlags { + AjmStatisticsJobFlags(AjmJobFlags job_flags) : raw(job_flags.raw) {} + + u64 raw; + struct { + u64 version : 3; + u64 : 12; + AjmStatisticsFlags statistics_flags : 17; + u64 : 32; + }; +}; +static_assert(sizeof(AjmStatisticsJobFlags) == 8); + struct AjmSidebandResult { s32 result; s32 internal_result; @@ -126,6 +146,31 @@ union AjmSidebandInitParameters { u8 reserved[8]; }; +struct AjmSidebandStatisticsEngine { + float usage_batch; + float usage_interval[3]; +}; + +struct AjmSidebandStatisticsEnginePerCodec { + u8 codec_count; + u8 codec_id[3]; + float codec_percentage[3]; +}; + +struct AjmSidebandStatisticsMemory { + u32 instance_free; + u32 buffer_free; + u32 batch_size; + u32 input_size; + u32 output_size; + u32 small_size; +}; + +struct AjmSidebandStatisticsEngineParameters { + u32 interval_count; + float interval[3]; +}; + union AjmInstanceFlags { u64 raw; struct { @@ -178,8 +223,8 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context, AjmCodecType codec_type, AjmI int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context, u32 instance); int PS4_SYSV_ABI sceAjmInstanceExtend(); int PS4_SYSV_ABI sceAjmInstanceSwitch(); -int PS4_SYSV_ABI sceAjmMemoryRegister(); -int PS4_SYSV_ABI sceAjmMemoryUnregister(); +int PS4_SYSV_ABI sceAjmMemoryRegister(u32 context_id, void* ptr, size_t num_pages); +int PS4_SYSV_ABI sceAjmMemoryUnregister(u32 context_id, void* ptr); int PS4_SYSV_ABI sceAjmModuleRegister(u32 context, AjmCodecType codec_type, s64 reserved); int PS4_SYSV_ABI sceAjmModuleUnregister(); int PS4_SYSV_ABI sceAjmStrError(); diff --git a/src/core/libraries/ajm/ajm_batch.cpp b/src/core/libraries/ajm/ajm_batch.cpp index b1cec88b3..30e1deb71 100644 --- a/src/core/libraries/ajm/ajm_batch.cpp +++ b/src/core/libraries/ajm/ajm_batch.cpp @@ -54,6 +54,8 @@ public: : m_p_begin(begin), m_p_current(m_p_begin), m_size(size) {} AjmBatchBuffer(std::span data) : m_p_begin(data.data()), m_p_current(m_p_begin), m_size(data.size()) {} + AjmBatchBuffer(AjmChunkBuffer& buffer) + : AjmBatchBuffer(reinterpret_cast(buffer.p_address), buffer.size) {} AjmBatchBuffer SubBuffer(size_t size = s_dynamic_extent) { auto current = m_p_current; @@ -113,6 +115,88 @@ private: size_t m_size{}; }; +AjmJob AjmStatisticsJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { + std::optional job_flags = {}; + std::optional input_control_buffer = {}; + std::optional output_control_buffer = {}; + + AjmJob job; + job.instance_id = instance_id; + + while (!batch_buffer.IsEmpty()) { + auto& header = batch_buffer.Peek(); + switch (header.ident) { + case Identifier::AjmIdentInputControlBuf: { + ASSERT_MSG(!input_control_buffer.has_value(), + "Only one instance of input control buffer is allowed per job"); + const auto& buffer = batch_buffer.Consume(); + if (buffer.p_address != nullptr && buffer.size != 0) { + input_control_buffer = buffer; + } + break; + } + case Identifier::AjmIdentControlFlags: { + ASSERT_MSG(!job_flags.has_value(), "Only one instance of job flags is allowed per job"); + auto& chunk = batch_buffer.Consume(); + job_flags = AjmJobFlags{ + .raw = (u64(chunk.header.payload) << 32) + chunk.flags_low, + }; + break; + } + case Identifier::AjmIdentReturnAddressBuf: { + // Ignore return address buffers. + batch_buffer.Skip(); + break; + } + case Identifier::AjmIdentOutputControlBuf: { + ASSERT_MSG(!output_control_buffer.has_value(), + "Only one instance of output control buffer is allowed per job"); + const auto& buffer = batch_buffer.Consume(); + if (buffer.p_address != nullptr && buffer.size != 0) { + output_control_buffer = buffer; + } + break; + } + default: + UNREACHABLE_MSG("Unknown chunk: {}", header.ident); + } + } + + ASSERT(job_flags.has_value()); + job.flags = job_flags.value(); + + AjmStatisticsJobFlags flags(job.flags); + if (input_control_buffer.has_value()) { + AjmBatchBuffer input_batch(input_control_buffer.value()); + if (True(flags.statistics_flags & AjmStatisticsFlags::Engine)) { + job.input.statistics_engine_parameters = + input_batch.Consume(); + } + } + + if (output_control_buffer.has_value()) { + AjmBatchBuffer output_batch(output_control_buffer.value()); + job.output.p_result = &output_batch.Consume(); + *job.output.p_result = AjmSidebandResult{}; + + if (True(flags.statistics_flags & AjmStatisticsFlags::Engine)) { + job.output.p_engine = &output_batch.Consume(); + *job.output.p_engine = AjmSidebandStatisticsEngine{}; + } + if (True(flags.statistics_flags & AjmStatisticsFlags::EnginePerCodec)) { + job.output.p_engine_per_codec = + &output_batch.Consume(); + *job.output.p_engine_per_codec = AjmSidebandStatisticsEnginePerCodec{}; + } + if (True(flags.statistics_flags & AjmStatisticsFlags::Memory)) { + job.output.p_memory = &output_batch.Consume(); + *job.output.p_memory = AjmSidebandStatisticsMemory{}; + } + } + + return job; +} + AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { std::optional job_flags = {}; std::optional input_control_buffer = {}; @@ -155,15 +239,6 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { batch_buffer.Skip(); break; } - case Identifier::AjmIdentInlineBuf: { - ASSERT_MSG(!output_control_buffer.has_value(), - "Only one instance of inline buffer is allowed per job"); - const auto& buffer = batch_buffer.Consume(); - if (buffer.p_address != nullptr && buffer.size != 0) { - inline_buffer = buffer; - } - break; - } case Identifier::AjmIdentOutputRunBuf: { auto& buffer = batch_buffer.Consume(); u8* p_begin = reinterpret_cast(buffer.p_address); @@ -186,13 +261,12 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { } } + ASSERT(job_flags.has_value()); job.flags = job_flags.value(); // Initialize sideband input parameters if (input_control_buffer.has_value()) { - AjmBatchBuffer input_batch(reinterpret_cast(input_control_buffer->p_address), - input_control_buffer->size); - + AjmBatchBuffer input_batch(input_control_buffer.value()); const auto sideband_flags = job_flags->sideband_flags; if (True(sideband_flags & AjmJobSidebandFlags::Format) && !input_batch.IsEmpty()) { job.input.format = input_batch.Consume(); @@ -202,6 +276,9 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { } const auto control_flags = job_flags.value().control_flags; + if (True(control_flags & AjmJobControlFlags::Resample)) { + job.input.resample_parameters = input_batch.Consume(); + } if (True(control_flags & AjmJobControlFlags::Initialize)) { job.input.init_params = AjmDecAt9InitializeParameters{}; std::memcpy(&job.input.init_params.value(), input_batch.GetCurrent(), @@ -209,21 +286,9 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) { } } - if (inline_buffer.has_value()) { - AjmBatchBuffer inline_batch(reinterpret_cast(inline_buffer->p_address), - inline_buffer->size); - - const auto control_flags = job_flags.value().control_flags; - if (True(control_flags & AjmJobControlFlags::Resample)) { - job.input.resample_parameters = inline_batch.Consume(); - } - } - // Initialize sideband output parameters if (output_control_buffer.has_value()) { - AjmBatchBuffer output_batch(reinterpret_cast(output_control_buffer->p_address), - output_control_buffer->size); - + AjmBatchBuffer output_batch(output_control_buffer.value()); job.output.p_result = &output_batch.Consume(); *job.output.p_result = AjmSidebandResult{}; @@ -260,9 +325,21 @@ std::shared_ptr AjmBatch::FromBatchBuffer(std::span data) { AjmBatchBuffer buffer(data); while (!buffer.IsEmpty()) { auto& job_chunk = buffer.Consume(); + if (job_chunk.header.ident == AjmIdentInlineBuf) { + // Inline buffers are used to store sideband input data. + // We should just skip them as they do not require any special handling. + buffer.Advance(job_chunk.size); + continue; + } ASSERT(job_chunk.header.ident == AjmIdentJob); auto instance_id = job_chunk.header.payload; - batch->jobs.push_back(AjmJobFromBatchBuffer(instance_id, buffer.SubBuffer(job_chunk.size))); + if (instance_id == AJM_INSTANCE_STATISTICS) { + batch->jobs.push_back( + AjmStatisticsJobFromBatchBuffer(instance_id, buffer.SubBuffer(job_chunk.size))); + } else { + batch->jobs.push_back( + AjmJobFromBatchBuffer(instance_id, buffer.SubBuffer(job_chunk.size))); + } } return batch; diff --git a/src/core/libraries/ajm/ajm_batch.h b/src/core/libraries/ajm/ajm_batch.h index 3c586b773..09daa630d 100644 --- a/src/core/libraries/ajm/ajm_batch.h +++ b/src/core/libraries/ajm/ajm_batch.h @@ -23,6 +23,7 @@ struct AjmJob { struct Input { std::optional init_params; std::optional resample_parameters; + std::optional statistics_engine_parameters; std::optional format; std::optional gapless_decode; std::vector buffer; @@ -33,6 +34,9 @@ struct AjmJob { AjmSidebandResult* p_result = nullptr; AjmSidebandStream* p_stream = nullptr; AjmSidebandFormat* p_format = nullptr; + AjmSidebandStatisticsMemory* p_memory = nullptr; + AjmSidebandStatisticsEnginePerCodec* p_engine_per_codec = nullptr; + AjmSidebandStatisticsEngine* p_engine = nullptr; AjmSidebandGaplessDecode* p_gapless_decode = nullptr; AjmSidebandMFrame* p_mframe = nullptr; u8* p_codec_info = nullptr; diff --git a/src/core/libraries/ajm/ajm_context.cpp b/src/core/libraries/ajm/ajm_context.cpp index 09255110c..8992dd83b 100644 --- a/src/core/libraries/ajm/ajm_context.cpp +++ b/src/core/libraries/ajm/ajm_context.cpp @@ -9,6 +9,7 @@ #include "core/libraries/ajm/ajm_context.h" #include "core/libraries/ajm/ajm_error.h" #include "core/libraries/ajm/ajm_instance.h" +#include "core/libraries/ajm/ajm_instance_statistics.h" #include "core/libraries/ajm/ajm_mp3.h" #include "core/libraries/error_codes.h" @@ -70,15 +71,19 @@ void AjmContext::ProcessBatch(u32 id, std::span jobs) { LOG_TRACE(Lib_Ajm, "Processing job {} for instance {}. flags = {:#x}", id, job.instance_id, job.flags.raw); - std::shared_ptr instance; - { - std::shared_lock lock(instances_mutex); - auto* p_instance = instances.Get(job.instance_id); - ASSERT_MSG(p_instance != nullptr, "Attempting to execute job on null instance"); - instance = *p_instance; - } + if (job.instance_id == AJM_INSTANCE_STATISTICS) { + AjmInstanceStatistics::Getinstance().ExecuteJob(job); + } else { + std::shared_ptr instance; + { + std::shared_lock lock(instances_mutex); + auto* p_instance = instances.Get(job.instance_id); + ASSERT_MSG(p_instance != nullptr, "Attempting to execute job on null instance"); + instance = *p_instance; + } - instance->ExecuteJob(job); + instance->ExecuteJob(job); + } } } diff --git a/src/core/libraries/ajm/ajm_instance.cpp b/src/core/libraries/ajm/ajm_instance.cpp index ea7fd5617..8af105c77 100644 --- a/src/core/libraries/ajm/ajm_instance.cpp +++ b/src/core/libraries/ajm/ajm_instance.cpp @@ -68,11 +68,11 @@ void AjmInstance::ExecuteJob(AjmJob& job) { m_codec->Initialize(¶ms, sizeof(params)); } if (job.input.resample_parameters.has_value()) { - UNREACHABLE_MSG("Unimplemented: resample parameters"); + LOG_ERROR(Lib_Ajm, "Unimplemented: resample parameters"); m_resample_parameters = job.input.resample_parameters.value(); } if (job.input.format.has_value()) { - UNREACHABLE_MSG("Unimplemented: format parameters"); + LOG_ERROR(Lib_Ajm, "Unimplemented: format parameters"); m_format = job.input.format.value(); } if (job.input.gapless_decode.has_value()) { diff --git a/src/core/libraries/ajm/ajm_instance_statistics.cpp b/src/core/libraries/ajm/ajm_instance_statistics.cpp new file mode 100644 index 000000000..c0c1af8bb --- /dev/null +++ b/src/core/libraries/ajm/ajm_instance_statistics.cpp @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/libraries/ajm/ajm.h" +#include "core/libraries/ajm/ajm_instance_statistics.h" + +namespace Libraries::Ajm { + +void AjmInstanceStatistics::ExecuteJob(AjmJob& job) { + if (job.output.p_engine) { + job.output.p_engine->usage_batch = 0.01; + const auto ic = job.input.statistics_engine_parameters->interval_count; + for (u32 idx = 0; idx < ic; ++idx) { + job.output.p_engine->usage_interval[idx] = 0.01; + } + } + if (job.output.p_engine_per_codec) { + job.output.p_engine_per_codec->codec_count = 1; + job.output.p_engine_per_codec->codec_id[0] = static_cast(AjmCodecType::At9Dec); + job.output.p_engine_per_codec->codec_percentage[0] = 0.01; + } + if (job.output.p_memory) { + job.output.p_memory->instance_free = 0x400000; + job.output.p_memory->buffer_free = 0x400000; + job.output.p_memory->batch_size = 0x4200; + job.output.p_memory->input_size = 0x2000; + job.output.p_memory->output_size = 0x2000; + job.output.p_memory->small_size = 0x200; + } +} + +AjmInstanceStatistics& AjmInstanceStatistics::Getinstance() { + static AjmInstanceStatistics instance; + return instance; +} + +} // namespace Libraries::Ajm diff --git a/src/core/libraries/ajm/ajm_instance_statistics.h b/src/core/libraries/ajm/ajm_instance_statistics.h new file mode 100644 index 000000000..ea70c9d56 --- /dev/null +++ b/src/core/libraries/ajm/ajm_instance_statistics.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/ajm/ajm_batch.h" + +namespace Libraries::Ajm { + +class AjmInstanceStatistics { +public: + void ExecuteJob(AjmJob& job); + + static AjmInstanceStatistics& Getinstance(); +}; + +} // namespace Libraries::Ajm