Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ajm: added stubbed statistics instance #1924

Merged
merged 6 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,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
Expand Down
10 changes: 6 additions & 4 deletions src/core/libraries/ajm/ajm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
49 changes: 47 additions & 2 deletions src/core/libraries/ajm/ajm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
Expand Down
129 changes: 103 additions & 26 deletions src/core/libraries/ajm/ajm_batch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class AjmBatchBuffer {
: m_p_begin(begin), m_p_current(m_p_begin), m_size(size) {}
AjmBatchBuffer(std::span<u8> data)
: m_p_begin(data.data()), m_p_current(m_p_begin), m_size(data.size()) {}
AjmBatchBuffer(AjmChunkBuffer& buffer)
: AjmBatchBuffer(reinterpret_cast<u8*>(buffer.p_address), buffer.size) {}

AjmBatchBuffer SubBuffer(size_t size = s_dynamic_extent) {
auto current = m_p_current;
Expand Down Expand Up @@ -113,6 +115,88 @@ class AjmBatchBuffer {
size_t m_size{};
};

AjmJob AjmStatisticsJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
std::optional<AjmJobFlags> job_flags = {};
std::optional<AjmChunkBuffer> input_control_buffer = {};
std::optional<AjmChunkBuffer> output_control_buffer = {};

AjmJob job;
job.instance_id = instance_id;

while (!batch_buffer.IsEmpty()) {
auto& header = batch_buffer.Peek<AjmChunkHeader>();
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<AjmChunkBuffer>();
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<AjmChunkFlags>();
job_flags = AjmJobFlags{
.raw = (u64(chunk.header.payload) << 32) + chunk.flags_low,
};
break;
}
case Identifier::AjmIdentReturnAddressBuf: {
// Ignore return address buffers.
batch_buffer.Skip<AjmChunkBuffer>();
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<AjmChunkBuffer>();
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<AjmSidebandStatisticsEngineParameters>();
}
}

if (output_control_buffer.has_value()) {
AjmBatchBuffer output_batch(output_control_buffer.value());
job.output.p_result = &output_batch.Consume<AjmSidebandResult>();
*job.output.p_result = AjmSidebandResult{};

if (True(flags.statistics_flags & AjmStatisticsFlags::Engine)) {
job.output.p_engine = &output_batch.Consume<AjmSidebandStatisticsEngine>();
*job.output.p_engine = AjmSidebandStatisticsEngine{};
}
if (True(flags.statistics_flags & AjmStatisticsFlags::EnginePerCodec)) {
job.output.p_engine_per_codec =
&output_batch.Consume<AjmSidebandStatisticsEnginePerCodec>();
*job.output.p_engine_per_codec = AjmSidebandStatisticsEnginePerCodec{};
}
if (True(flags.statistics_flags & AjmStatisticsFlags::Memory)) {
job.output.p_memory = &output_batch.Consume<AjmSidebandStatisticsMemory>();
*job.output.p_memory = AjmSidebandStatisticsMemory{};
}
}

return job;
}

AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
std::optional<AjmJobFlags> job_flags = {};
std::optional<AjmChunkBuffer> input_control_buffer = {};
Expand Down Expand Up @@ -155,15 +239,6 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
batch_buffer.Skip<AjmChunkBuffer>();
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<AjmChunkBuffer>();
if (buffer.p_address != nullptr && buffer.size != 0) {
inline_buffer = buffer;
}
break;
}
case Identifier::AjmIdentOutputRunBuf: {
auto& buffer = batch_buffer.Consume<AjmChunkBuffer>();
u8* p_begin = reinterpret_cast<u8*>(buffer.p_address);
Expand All @@ -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<u8*>(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<AjmSidebandFormat>();
Expand All @@ -202,28 +276,19 @@ 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<AjmSidebandResampleParameters>();
}
if (True(control_flags & AjmJobControlFlags::Initialize)) {
job.input.init_params = AjmDecAt9InitializeParameters{};
std::memcpy(&job.input.init_params.value(), input_batch.GetCurrent(),
input_batch.BytesRemaining());
}
}

if (inline_buffer.has_value()) {
AjmBatchBuffer inline_batch(reinterpret_cast<u8*>(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<AjmSidebandResampleParameters>();
}
}

// Initialize sideband output parameters
if (output_control_buffer.has_value()) {
AjmBatchBuffer output_batch(reinterpret_cast<u8*>(output_control_buffer->p_address),
output_control_buffer->size);

AjmBatchBuffer output_batch(output_control_buffer.value());
job.output.p_result = &output_batch.Consume<AjmSidebandResult>();
*job.output.p_result = AjmSidebandResult{};

Expand Down Expand Up @@ -260,9 +325,21 @@ std::shared_ptr<AjmBatch> AjmBatch::FromBatchBuffer(std::span<u8> data) {
AjmBatchBuffer buffer(data);
while (!buffer.IsEmpty()) {
auto& job_chunk = buffer.Consume<AjmChunkJob>();
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;
Expand Down
4 changes: 4 additions & 0 deletions src/core/libraries/ajm/ajm_batch.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct AjmJob {
struct Input {
std::optional<AjmDecAt9InitializeParameters> init_params;
std::optional<AjmSidebandResampleParameters> resample_parameters;
std::optional<AjmSidebandStatisticsEngineParameters> statistics_engine_parameters;
std::optional<AjmSidebandFormat> format;
std::optional<AjmSidebandGaplessDecode> gapless_decode;
std::vector<u8> buffer;
Expand All @@ -32,6 +33,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;
Expand Down
21 changes: 13 additions & 8 deletions src/core/libraries/ajm/ajm_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -70,15 +71,19 @@ void AjmContext::ProcessBatch(u32 id, std::span<AjmJob> jobs) {
LOG_TRACE(Lib_Ajm, "Processing job {} for instance {}. flags = {:#x}", id, job.instance_id,
job.flags.raw);

std::shared_ptr<AjmInstance> 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<AjmInstance> 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);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/core/libraries/ajm/ajm_instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ void AjmInstance::ExecuteJob(AjmJob& job) {
m_codec->Initialize(&params, 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()) {
Expand Down
37 changes: 37 additions & 0 deletions src/core/libraries/ajm/ajm_instance_statistics.cpp
Original file line number Diff line number Diff line change
@@ -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<u8>(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
Loading
Loading