mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-29 15:33:35 -06:00
Merge branch 'fontlib' of https://github.com/w1naenator/shadPS4 into fontlib
This commit is contained in:
commit
419909e786
@ -150,7 +150,7 @@ The following firmware modules are supported and must be placed in shadPS4's `sy
|
||||
| libSceCesCs.sprx | libSceFont.sprx | libSceFontFt.sprx | libSceFreeTypeOt.sprx |
|
||||
| libSceJpegDec.sprx | libSceJpegEnc.sprx | libSceJson.sprx | libSceJson2.sprx |
|
||||
| libSceLibcInternal.sprx | libSceNgs2.sprx | libScePngEnc.sprx | libSceRtc.sprx |
|
||||
| libSceUlt.sprx | | | |
|
||||
| libSceUlt.sprx | libSceAudiodec.sprx | | |
|
||||
</div>
|
||||
|
||||
> [!Caution]
|
||||
|
||||
@ -59,7 +59,7 @@ static void hexToBytes(const char* hex, unsigned char* dst) {
|
||||
bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string titleId) {
|
||||
std::filesystem::path gameSysDir = trophyPath / "sce_sys/trophy/";
|
||||
if (!std::filesystem::exists(gameSysDir)) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Game sce_sys directory doesn't exist");
|
||||
LOG_WARNING(Common_Filesystem, "Game trophy directory doesn't exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
@ -34,7 +34,7 @@ u32 GetChannelMask(u32 num_channels) {
|
||||
case 8:
|
||||
return ORBIS_AJM_CHANNELMASK_7POINT1;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
UNREACHABLE_MSG("Unexpected number of channels: {}", num_channels);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,20 +5,45 @@
|
||||
#include "ajm_aac.h"
|
||||
#include "ajm_result.h"
|
||||
|
||||
#include <aacdecoder_lib.h>
|
||||
// using this internal header to manually configure the decoder in RAW mode
|
||||
#include "externals/aacdec/fdk-aac/libAACdec/src/aacdecoder.h"
|
||||
|
||||
#include <aacdecoder_lib.h>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include <algorithm> // std::transform
|
||||
#include <iterator> // std::back_inserter
|
||||
#include <limits>
|
||||
|
||||
namespace Libraries::Ajm {
|
||||
|
||||
std::span<const s16> AjmAacDecoder::GetOuputPcm(u32 skipped_pcm, u32 max_pcm) const {
|
||||
const auto pcm_data = std::span(m_pcm_buffer).subspan(skipped_pcm);
|
||||
return pcm_data.subspan(0, std::min<u32>(pcm_data.size(), max_pcm));
|
||||
}
|
||||
|
||||
template <>
|
||||
size_t AjmAacDecoder::WriteOutputSamples<float>(SparseOutputBuffer& out, std::span<const s16> pcm) {
|
||||
if (pcm.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_resample_buffer.clear();
|
||||
constexpr float inv_scale = 1.0f / std::numeric_limits<s16>::max();
|
||||
std::transform(pcm.begin(), pcm.end(), std::back_inserter(m_resample_buffer),
|
||||
[](auto sample) { return float(sample) * inv_scale; });
|
||||
|
||||
return out.Write(std::span(m_resample_buffer));
|
||||
}
|
||||
|
||||
AjmAacDecoder::AjmAacDecoder(AjmFormatEncoding format, AjmAacCodecFlags flags, u32 channels)
|
||||
: m_format(format), m_flags(flags), m_channels(channels), m_pcm_buffer(2048 * 8),
|
||||
m_skip_frames(True(flags & AjmAacCodecFlags::EnableNondelayOutput) ? 0 : 2) {}
|
||||
: m_format(format), m_flags(flags), m_channels(channels), m_pcm_buffer(1024 * 8),
|
||||
m_skip_frames(True(flags & AjmAacCodecFlags::EnableNondelayOutput) ? 0 : 2) {
|
||||
m_resample_buffer.reserve(m_pcm_buffer.size());
|
||||
}
|
||||
|
||||
AjmAacDecoder::~AjmAacDecoder() {
|
||||
aacDecoder_Close(m_decoder);
|
||||
if (m_decoder) {
|
||||
aacDecoder_Close(m_decoder);
|
||||
}
|
||||
}
|
||||
|
||||
TRANSPORT_TYPE TransportTypeFromConfigType(ConfigType config_type) {
|
||||
@ -98,7 +123,7 @@ AjmSidebandFormat AjmAacDecoder::GetFormat() const {
|
||||
.num_channels = static_cast<u32>(info->numChannels),
|
||||
.channel_mask = GetChannelMask(info->numChannels),
|
||||
.sampl_freq = static_cast<u32>(info->sampleRate),
|
||||
.sample_encoding = m_format, // AjmFormatEncoding
|
||||
.sample_encoding = m_format,
|
||||
.bitrate = static_cast<u32>(info->bitRate),
|
||||
};
|
||||
}
|
||||
@ -130,8 +155,7 @@ DecoderResult AjmAacDecoder::ProcessData(std::span<u8>& input, SparseOutputBuffe
|
||||
const UINT sizes[] = {static_cast<UINT>(input.size())};
|
||||
UINT valid = sizes[0];
|
||||
aacDecoder_Fill(m_decoder, buffers, sizes, &valid);
|
||||
auto ret = aacDecoder_DecodeFrame(m_decoder, reinterpret_cast<s16*>(m_pcm_buffer.data()),
|
||||
m_pcm_buffer.size() / 2, 0);
|
||||
auto ret = aacDecoder_DecodeFrame(m_decoder, m_pcm_buffer.data(), m_pcm_buffer.size(), 0);
|
||||
|
||||
switch (ret) {
|
||||
case AAC_DEC_OK:
|
||||
@ -167,16 +191,16 @@ DecoderResult AjmAacDecoder::ProcessData(std::span<u8>& input, SparseOutputBuffe
|
||||
gapless.init.total_samples != 0 ? gapless.current.total_samples : info->aacSamplesPerFrame;
|
||||
|
||||
size_t pcm_written = 0;
|
||||
auto pcm = GetOuputPcm(skip_samples * info->numChannels, max_samples * info->numChannels);
|
||||
switch (m_format) {
|
||||
case AjmFormatEncoding::S16:
|
||||
pcm_written = WriteOutputSamples<s16>(output, skip_samples * info->numChannels,
|
||||
max_samples * info->numChannels);
|
||||
pcm_written = output.Write(pcm);
|
||||
break;
|
||||
case AjmFormatEncoding::S32:
|
||||
UNREACHABLE_MSG("NOT IMPLEMENTED");
|
||||
break;
|
||||
case AjmFormatEncoding::Float:
|
||||
UNREACHABLE_MSG("NOT IMPLEMENTED");
|
||||
pcm_written = WriteOutputSamples<float>(output, pcm);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@ -191,4 +215,4 @@ DecoderResult AjmAacDecoder::ProcessData(std::span<u8>& input, SparseOutputBuffe
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Libraries::Ajm
|
||||
} // namespace Libraries::Ajm
|
||||
@ -52,22 +52,18 @@ private:
|
||||
};
|
||||
|
||||
template <class T>
|
||||
size_t WriteOutputSamples(SparseOutputBuffer& output, u32 skipped_pcm, u32 max_pcm) {
|
||||
std::span<T> pcm_data{reinterpret_cast<T*>(m_pcm_buffer.data()),
|
||||
m_pcm_buffer.size() / sizeof(T)};
|
||||
pcm_data = pcm_data.subspan(skipped_pcm);
|
||||
const auto pcm_size = std::min(u32(pcm_data.size()), max_pcm);
|
||||
return output.Write(pcm_data.subspan(0, pcm_size));
|
||||
}
|
||||
size_t WriteOutputSamples(SparseOutputBuffer& output, std::span<const s16> pcm);
|
||||
std::span<const s16> GetOuputPcm(u32 skipped_pcm, u32 max_pcm) const;
|
||||
|
||||
const AjmFormatEncoding m_format;
|
||||
const AjmAacCodecFlags m_flags;
|
||||
const u32 m_channels;
|
||||
std::vector<u8> m_pcm_buffer;
|
||||
std::vector<s16> m_pcm_buffer;
|
||||
std::vector<float> m_resample_buffer;
|
||||
|
||||
u32 m_skip_frames = 0;
|
||||
InitializeParameters m_init_params = {};
|
||||
AAC_DECODER_INSTANCE* m_decoder = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Libraries::Ajm
|
||||
} // namespace Libraries::Ajm
|
||||
@ -280,9 +280,7 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
|
||||
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());
|
||||
job.input.init_params = input_batch.Consume<AjmSidebandInitParameters>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ namespace Libraries::Ajm {
|
||||
|
||||
struct AjmJob {
|
||||
struct Input {
|
||||
std::optional<AjmDecAt9InitializeParameters> init_params;
|
||||
std::optional<AjmSidebandInitParameters> init_params;
|
||||
std::optional<AjmSidebandResampleParameters> resample_parameters;
|
||||
std::optional<AjmSidebandStatisticsEngineParameters> statistics_engine_parameters;
|
||||
std::optional<AjmSidebandFormat> format;
|
||||
|
||||
@ -189,7 +189,7 @@ s32 PS4_SYSV_ABI sceAudio3dDeleteSpeakerArray() {
|
||||
s32 PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters* params) {
|
||||
LOG_DEBUG(Lib_Audio3d, "called");
|
||||
if (params) {
|
||||
*params = OrbisAudio3dOpenParameters{
|
||||
auto default_params = OrbisAudio3dOpenParameters{
|
||||
.size_this = 0x20,
|
||||
.granularity = 0x100,
|
||||
.rate = OrbisAudio3dRate::ORBIS_AUDIO3D_RATE_48000,
|
||||
@ -197,6 +197,7 @@ s32 PS4_SYSV_ABI sceAudio3dGetDefaultOpenParameters(OrbisAudio3dOpenParameters*
|
||||
.queue_depth = 2,
|
||||
.buffer_mode = OrbisAudio3dBufferMode::ORBIS_AUDIO3D_BUFFER_ADVANCE_AND_PUSH,
|
||||
};
|
||||
memcpy(params, &default_params, 0x20);
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -445,7 +446,7 @@ s32 PS4_SYSV_ABI sceAudio3dPortOpen(const OrbisUserServiceUserId user_id,
|
||||
}
|
||||
|
||||
*port_id = id;
|
||||
std::memcpy(&state->ports[id].parameters, parameters, sizeof(OrbisAudio3dOpenParameters));
|
||||
std::memcpy(&state->ports[id].parameters, parameters, parameters->size_this);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -532,6 +532,7 @@ void Emulator::LoadSystemModules(const std::string& game_serial) {
|
||||
{"libSceJson2.sprx", nullptr},
|
||||
{"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterLib},
|
||||
{"libSceCesCs.sprx", nullptr},
|
||||
{"libSceAudiodec.sprx", nullptr},
|
||||
{"libSceFont.sprx", &Libraries::Font::RegisterlibSceFont},
|
||||
{"libSceFontFt.sprx", &Libraries::FontFt::RegisterlibSceFontFt},
|
||||
{"libSceFreeTypeOt.sprx", nullptr}});
|
||||
|
||||
@ -153,9 +153,9 @@ std::string NameOf(Attribute attribute) {
|
||||
case Attribute::TessellationEvaluationPointV:
|
||||
return "TessellationEvaluationPointV";
|
||||
case Attribute::PackedHullInvocationInfo:
|
||||
return "OffChipLdsBase";
|
||||
case Attribute::OffChipLdsBase:
|
||||
return "PackedHullInvocationInfo";
|
||||
case Attribute::OffChipLdsBase:
|
||||
return "OffChipLdsBase";
|
||||
case Attribute::TessFactorsBufferBase:
|
||||
return "TessFactorsBufferBase";
|
||||
case Attribute::PointSize:
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <unordered_map>
|
||||
#include "common/assert.h"
|
||||
#include "shader_recompiler/info.h"
|
||||
#include "shader_recompiler/ir/attribute.h"
|
||||
@ -189,7 +190,7 @@ std::optional<TessSharpLocation> FindTessConstantSharp(IR::Inst* read_const_buff
|
||||
// Walker that helps deduce what type of attribute a DS instruction is reading
|
||||
// or writing, which could be an input control point, output control point,
|
||||
// or per-patch constant (PatchConst).
|
||||
// For certain ReadConstBuffer instructions using the tess constants V#,, we visit the users
|
||||
// For certain ReadConstBuffer instructions using the tess constants V#, we visit the users
|
||||
// recursively and increment a counter on the Load/WriteShared users.
|
||||
// Namely NumPatch (from m_hsNumPatch), HsOutputBase (m_hsOutputBase),
|
||||
// and PatchConstBase (m_patchConstBase).
|
||||
@ -200,9 +201,11 @@ std::optional<TessSharpLocation> FindTessConstantSharp(IR::Inst* read_const_buff
|
||||
//
|
||||
// TODO: this will break if AMD compiler used distributive property like
|
||||
// TcsNumPatches * (ls_stride * #input_cp_in_patch + hs_cp_stride * #output_cp_in_patch)
|
||||
//
|
||||
// Assert if the region is ambiguous due to phi nodes in the addr calculation for a DS instruction,
|
||||
class TessConstantUseWalker {
|
||||
public:
|
||||
void MarkTessAttributeUsers(IR::Inst* read_const_buffer, TessConstantAttribute attr) {
|
||||
void WalkUsersOfTessConstant(IR::Inst* read_const_buffer, TessConstantAttribute attr) {
|
||||
u32 inc;
|
||||
switch (attr) {
|
||||
case TessConstantAttribute::HsNumPatch:
|
||||
@ -217,14 +220,19 @@ public:
|
||||
}
|
||||
|
||||
for (IR::Use use : read_const_buffer->Uses()) {
|
||||
MarkTessAttributeUsersHelper(use, inc);
|
||||
WalkUsersOfTessConstantHelper(use, inc, false);
|
||||
}
|
||||
|
||||
++seq_num;
|
||||
}
|
||||
|
||||
private:
|
||||
void MarkTessAttributeUsersHelper(IR::Use use, u32 inc) {
|
||||
struct PhiInfo {
|
||||
u32 seq_num;
|
||||
u32 unique_edge;
|
||||
};
|
||||
|
||||
void WalkUsersOfTessConstantHelper(IR::Use use, u32 inc, bool propagateError) {
|
||||
IR::Inst* inst = use.user;
|
||||
|
||||
switch (use.user->GetOpcode()) {
|
||||
@ -232,38 +240,37 @@ private:
|
||||
case IR::Opcode::LoadSharedU64:
|
||||
case IR::Opcode::WriteSharedU32:
|
||||
case IR::Opcode::WriteSharedU64: {
|
||||
u32 counter = inst->Flags<u32>();
|
||||
inst->SetFlags<u32>(counter + inc);
|
||||
// Stop here
|
||||
return;
|
||||
bool is_addr_operand = use.operand == 0;
|
||||
if (is_addr_operand) {
|
||||
u32 counter = inst->Flags<u32>();
|
||||
inst->SetFlags<u32>(counter + inc);
|
||||
ASSERT_MSG(!propagateError, "LDS instruction {} accesses ambiguous attribute type",
|
||||
fmt::ptr(use.user));
|
||||
// Stop here
|
||||
return;
|
||||
}
|
||||
}
|
||||
case IR::Opcode::Phi: {
|
||||
struct PhiCounter {
|
||||
u16 seq_num;
|
||||
u16 unique_edge;
|
||||
};
|
||||
|
||||
PhiCounter count = inst->Flags<PhiCounter>();
|
||||
ASSERT_MSG(count.seq_num == 0 || count.unique_edge == use.operand);
|
||||
auto it = phi_infos.find(use.user);
|
||||
// the point of seq_num is to tell us if we've already traversed this
|
||||
// phi on the current walk. Alternatively we could keep a set of phi's
|
||||
// seen on the current walk. This is to handle phi cycles
|
||||
if (count.seq_num == 0) {
|
||||
// phi on the current walk to handle phi cycles
|
||||
if (it == phi_infos.end()) {
|
||||
// First time we've encountered this phi
|
||||
count.seq_num = seq_num;
|
||||
// Mark the phi as having been traversed originally through this edge
|
||||
count.unique_edge = use.operand;
|
||||
} else if (count.seq_num < seq_num) {
|
||||
count.seq_num = seq_num;
|
||||
phi_infos[inst] = {.seq_num = seq_num,
|
||||
.unique_edge = static_cast<u16>(use.operand)};
|
||||
} else if (it->second.seq_num < seq_num) {
|
||||
it->second.seq_num = seq_num;
|
||||
// For now, assume we are visiting this phi via the same edge
|
||||
// as on other walks. If not, some dataflow analysis might be necessary
|
||||
ASSERT(count.unique_edge == use.operand);
|
||||
if (it->second.unique_edge != use.operand) {
|
||||
propagateError = true;
|
||||
}
|
||||
} else {
|
||||
// count.seq_num == seq_num
|
||||
ASSERT(it->second.seq_num == seq_num);
|
||||
// there's a cycle, and we've already been here on this walk
|
||||
return;
|
||||
}
|
||||
inst->SetFlags<PhiCounter>(count);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -271,10 +278,11 @@ private:
|
||||
}
|
||||
|
||||
for (IR::Use use : inst->Uses()) {
|
||||
MarkTessAttributeUsersHelper(use, inc);
|
||||
WalkUsersOfTessConstantHelper(use, inc, propagateError);
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<const IR::Inst*, PhiInfo> phi_infos;
|
||||
u32 seq_num{1u};
|
||||
};
|
||||
|
||||
@ -690,7 +698,7 @@ void TessellationPreprocess(IR::Program& program, RuntimeInfo& runtime_info) {
|
||||
case TessConstantAttribute::HsNumPatch:
|
||||
case TessConstantAttribute::HsOutputBase:
|
||||
case TessConstantAttribute::PatchConstBase:
|
||||
walker.MarkTessAttributeUsers(&inst, tess_const_attr);
|
||||
walker.WalkUsersOfTessConstant(&inst, tess_const_attr);
|
||||
// We should be able to safely set these to 0 so that indexing happens only
|
||||
// within the local patch in the recompiled Vulkan shader. This assumes
|
||||
// these values only contribute to address calculations for in/out
|
||||
|
||||
Loading…
Reference in New Issue
Block a user