mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-10-12 02:09:17 +00:00
sysutil/audio: Implement shared memory handling (#19467)
This commit is contained in:
@@ -6,6 +6,13 @@
|
||||
#include "Emu/Cell/timers.hpp"
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
#include "Emu/Cell/lv2/sys_event.h"
|
||||
#include "Emu/Cell/lv2/sys_mmapper.h"
|
||||
#include "Emu/Cell/lv2/sys_memory.h"
|
||||
#include "Emu/Memory/vm_var.h"
|
||||
#include "Emu/savestate_utils.hpp"
|
||||
#include "util/asm.hpp"
|
||||
#include "util/vm.hpp"
|
||||
#include "sysPrxForUser.h"
|
||||
#include "cellAudio.h"
|
||||
#include "util/video_provider.h"
|
||||
|
||||
@@ -17,16 +24,6 @@ extern atomic_t<recording_mode> g_recording_mode;
|
||||
|
||||
extern void lv2_sleep(u64 timeout, ppu_thread* ppu = nullptr);
|
||||
|
||||
vm::gvar<char, AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT> g_audio_buffer;
|
||||
|
||||
struct alignas(16) aligned_index_t
|
||||
{
|
||||
be_t<u64> index;
|
||||
u8 pad[8];
|
||||
};
|
||||
|
||||
vm::gvar<aligned_index_t, AUDIO_PORT_COUNT> g_audio_indices;
|
||||
|
||||
template <>
|
||||
void fmt_class_string<CellAudioError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@@ -366,10 +363,8 @@ u64 audio_ringbuffer::update(bool emu_is_paused)
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
void audio_port::tag(s32 offset)
|
||||
void audio_port::tag(be_t<f32>* port_buf)
|
||||
{
|
||||
auto port_buf = get_vm_ptr(offset);
|
||||
|
||||
// This tag will be used to make sure that the game has finished writing the audio for the next audio period
|
||||
// We use -0.0f in case games check if the buffer is empty. -0.0f == 0.0f evaluates to true, but std::signbit can be used to distinguish them
|
||||
const f32 tag = -0.0f;
|
||||
@@ -389,10 +384,72 @@ void audio_port::tag(s32 offset)
|
||||
prev_touched_tag_nr = -1;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void save_audio_memory(utils::serial& ar, const shared_ptr<lv2_memory>& memory)
|
||||
{
|
||||
ar(static_cast<bool>(memory));
|
||||
|
||||
if (!memory)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool registered = memory->exists || memory->key == SYS_MMAPPER_MIO_SHM_KEY;
|
||||
ar(registered);
|
||||
|
||||
if (registered)
|
||||
{
|
||||
ar(memory->key);
|
||||
}
|
||||
else
|
||||
{
|
||||
memory->save_data(ar);
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<lv2_memory> load_audio_memory(utils::serial& ar)
|
||||
{
|
||||
if (!ar.pop<bool>())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (ar.pop<bool>())
|
||||
{
|
||||
return ensure(g_fxo->get<ipc_manager<lv2_memory, u64>>().get(ar.pop<u64>()));
|
||||
}
|
||||
|
||||
auto memory = make_shared<lv2_memory>(stx::exact_t<utils::serial&>(ar));
|
||||
ensure(memory->external_refs);
|
||||
ensure((g_fxo->get<ipc_manager<lv2_memory, u64>>().add(memory->key, [&] { return memory; }).first));
|
||||
return memory;
|
||||
}
|
||||
|
||||
shared_ptr<lv2_memory> acquire_audio_memory(u32 id)
|
||||
{
|
||||
return idm::get<lv2_obj, lv2_memory>(id, [](lv2_memory& memory)
|
||||
{
|
||||
memory.external_refs++;
|
||||
});
|
||||
}
|
||||
|
||||
void release_audio_memory(shared_ptr<lv2_memory>& memory)
|
||||
{
|
||||
if (memory)
|
||||
{
|
||||
std::lock_guard lock(id_manager::g_mutex);
|
||||
ensure(memory->external_refs--);
|
||||
memory->release_memory();
|
||||
memory.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cell_audio_thread::cell_audio_thread(utils::serial& ar)
|
||||
: cell_audio_thread()
|
||||
{
|
||||
ar(init);
|
||||
ar(init, shared_area, shared_address, shared_refs, ports);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
@@ -409,20 +466,26 @@ cell_audio_thread::cell_audio_thread(utils::serial& ar)
|
||||
k.port = lv2_event_queue::load_ptr(ar, k.port, "audio");
|
||||
}
|
||||
|
||||
ar(ports);
|
||||
ar(free_port_count, free_ports, free_indices);
|
||||
shared_memory = load_audio_memory(ar);
|
||||
for (auto& memory : port_memories)
|
||||
{
|
||||
memory = load_audio_memory(ar);
|
||||
}
|
||||
}
|
||||
|
||||
void cell_audio_thread::save(utils::serial& ar)
|
||||
{
|
||||
ar(init);
|
||||
USING_SERIALIZATION_VERSION(cellAudio);
|
||||
|
||||
// closed ports keep their descriptor after audio quits
|
||||
ar(init, shared_area, shared_address, shared_refs, ports);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
USING_SERIALIZATION_VERSION(cellAudio);
|
||||
|
||||
ar(key_count, event_period);
|
||||
ar(static_cast<u64>(keys.size()));
|
||||
|
||||
@@ -432,11 +495,24 @@ void cell_audio_thread::save(utils::serial& ar)
|
||||
lv2_event_queue::save_ptr(ar, k.port.get());
|
||||
}
|
||||
|
||||
ar(ports);
|
||||
ar(free_port_count, free_ports, free_indices);
|
||||
save_audio_memory(ar, shared_memory);
|
||||
for (const auto& memory : port_memories)
|
||||
{
|
||||
save_audio_memory(ar, memory);
|
||||
}
|
||||
}
|
||||
|
||||
be_t<f32>* cell_audio_thread::get_buffer(const audio_port& port, s32 offset) const
|
||||
{
|
||||
// caller holds the audio mutex, port_memories keeps the buffer alive after the game unmaps it
|
||||
return reinterpret_cast<be_t<f32>*>((*port_memories[port.number]->shm.observe())->get()) + port.position(offset) * port.block_size();
|
||||
}
|
||||
|
||||
std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
AUDIT(cfg.buffering_enabled);
|
||||
|
||||
u32 active = 0;
|
||||
@@ -451,7 +527,9 @@ std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
|
||||
if (port.state != audio_port_state::started) continue;
|
||||
active++;
|
||||
|
||||
auto port_buf = port.get_vm_ptr();
|
||||
if (!port.num_channels) continue;
|
||||
|
||||
auto port_buf = get_buffer(port);
|
||||
|
||||
// Find the last tag that has been touched
|
||||
const u32 tag_first_pos = port.num_channels == 2 ? PORT_BUFFER_TAG_FIRST_2CH : PORT_BUFFER_TAG_FIRST_8CH;
|
||||
@@ -568,11 +646,12 @@ void cell_audio_thread::reset_ports(s32 offset)
|
||||
|
||||
if (port.state != audio_port_state::started) continue;
|
||||
|
||||
memset(port.get_vm_ptr(offset), 0, port.block_size() * sizeof(float));
|
||||
auto* buffer = get_buffer(port, offset);
|
||||
memset(buffer, 0, port.block_size() * sizeof(float));
|
||||
|
||||
if (cfg.buffering_enabled)
|
||||
if (cfg.buffering_enabled && port.num_channels)
|
||||
{
|
||||
port.tag(offset);
|
||||
port.tag(buffer);
|
||||
|
||||
// One period is about to elapse for this port. count_port_buffer_tags() zeroes this again as
|
||||
// soon as it sees a tag move, so it counts periods during which none did.
|
||||
@@ -593,6 +672,8 @@ void cell_audio_thread::advance(u64 timestamp)
|
||||
// update ports
|
||||
reset_ports(0);
|
||||
|
||||
be_t<u64>* indices = nullptr;
|
||||
|
||||
for (audio_port& port : ports)
|
||||
{
|
||||
if (port.state != audio_port_state::started) continue;
|
||||
@@ -602,7 +683,13 @@ void cell_audio_thread::advance(u64 timestamp)
|
||||
port.timestamp = timestamp;
|
||||
|
||||
port.cur_pos = port.position(1);
|
||||
*port.index = port.cur_pos;
|
||||
|
||||
if (!indices)
|
||||
{
|
||||
indices = reinterpret_cast<be_t<u64>*>((*shared_memory->shm.observe())->get());
|
||||
}
|
||||
|
||||
indices[port.server_index * 2] = port.cur_pos;
|
||||
}
|
||||
|
||||
if (cfg.buffering_enabled)
|
||||
@@ -1089,22 +1176,109 @@ void cell_audio_thread::operator()()
|
||||
|
||||
audio_port* cell_audio_thread::open_port()
|
||||
{
|
||||
for (u32 i = 0; i < AUDIO_PORT_COUNT; i++)
|
||||
if (!free_port_count)
|
||||
{
|
||||
if (ports[i].state.compare_and_swap_test(audio_port_state::closed, audio_port_state::opened))
|
||||
{
|
||||
m_front_only_reported[i] = false;
|
||||
m_periods_without_tag[i] = 0;
|
||||
return &ports[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
const u32 index = --free_port_count;
|
||||
auto& port = ports[free_ports[index]];
|
||||
port.server_index = free_indices[index];
|
||||
port.state = audio_port_state::opened;
|
||||
m_front_only_reported[port.number] = false;
|
||||
m_periods_without_tag[port.number] = 0;
|
||||
return &port;
|
||||
}
|
||||
|
||||
error_code cell_audio_thread::allocate_port(ppu_thread& ppu, audio_port& port)
|
||||
{
|
||||
const u32 size = std::max<u32>(0x10000, utils::align(port.size, 0x10000));
|
||||
auto* ct = g_ps3_process_info.sdk_ver > 0x21ffff ? &g_fxo->get<lv2_memory_container>() : nullptr;
|
||||
|
||||
for (u64 key = SYS_MMAPPER_MIO_SHM_KEY + 2;; key++)
|
||||
{
|
||||
if (g_fxo->get<ipc_manager<lv2_memory, u64>>().get(key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto result = lv2_obj::create<lv2_memory>(SYS_SYNC_PROCESS_SHARED, key, SYS_SYNC_NEWLY_CREATED, [&]()
|
||||
{
|
||||
return make_shared<lv2_memory>(size, 0x10000, 0x200, key, true, ct);
|
||||
});
|
||||
|
||||
if (result + 0u == CELL_EEXIST)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
close_port(ppu, port);
|
||||
return CELL_AUDIO_ERROR_SHAREDMEMORY;
|
||||
}
|
||||
|
||||
const u32 memory_id = idm::last_id<lv2_memory>();
|
||||
const vm::var<u32> address;
|
||||
auto memory = acquire_audio_memory(memory_id);
|
||||
|
||||
if (!memory || sys_mmapper_search_and_map(ppu, shared_area, memory_id, 0x40000, address))
|
||||
{
|
||||
sys_mmapper_free_shared_memory(ppu, memory_id);
|
||||
release_audio_memory(memory);
|
||||
close_port(ppu, port);
|
||||
return CELL_AUDIO_ERROR_SHAREDMEMORY;
|
||||
}
|
||||
|
||||
port_memories[port.number] = std::move(memory);
|
||||
port.addr = vm::cast(*address);
|
||||
port.mapped = true;
|
||||
port.index = vm::cast(shared_address + port.server_index * 16);
|
||||
std::memset(port.addr.get_ptr(), 0, size);
|
||||
break;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
void cell_audio_thread::close_port(ppu_thread& ppu, audio_port& port)
|
||||
{
|
||||
port.state = audio_port_state::closed;
|
||||
const vm::var<u32> memory_id;
|
||||
|
||||
if (port.mapped && !sys_mmapper_unmap_shared_memory(ppu, port.addr.addr(), memory_id))
|
||||
{
|
||||
// an extra mapping can keep the handle alive after the port closes
|
||||
sys_mmapper_free_shared_memory(ppu, *memory_id);
|
||||
}
|
||||
|
||||
port.mapped = false;
|
||||
release_audio_memory(port_memories[port.number]);
|
||||
free_ports[free_port_count] = port.number;
|
||||
free_indices[free_port_count] = port.server_index;
|
||||
free_port_count++;
|
||||
}
|
||||
|
||||
void cell_audio_thread::release_shared_memory(ppu_thread& ppu)
|
||||
{
|
||||
if (--shared_refs || !shared_address)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const vm::var<u32> memory_id;
|
||||
|
||||
if (!sys_mmapper_unmap_shared_memory(ppu, shared_address, memory_id))
|
||||
{
|
||||
sys_mmapper_free_shared_memory(ppu, *memory_id);
|
||||
}
|
||||
}
|
||||
|
||||
template <AudioChannelCnt channels, AudioChannelCnt downmix>
|
||||
void cell_audio_thread::mix(float* out_buffer, s32 offset)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
AUDIT(out_buffer != nullptr);
|
||||
|
||||
constexpr u32 out_channels = static_cast<u32>(channels);
|
||||
@@ -1120,7 +1294,7 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset)
|
||||
{
|
||||
if (port.state != audio_port_state::started) continue;
|
||||
|
||||
auto buf = port.get_vm_ptr(offset);
|
||||
auto buf = get_buffer(port, offset);
|
||||
|
||||
static constexpr float minus_3db = 0.707f; // value taken from https://www.dolby.com/us/en/technologies/a-guide-to-dolby-metadata.pdf
|
||||
float m = master_volume;
|
||||
@@ -1229,6 +1403,17 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!port.num_channels)
|
||||
{
|
||||
for (u32 out = 0, in = 0; out < out_buffer_sz; out += out_channels, in++)
|
||||
{
|
||||
step_volume(port);
|
||||
|
||||
const float sample = buf[in] * m;
|
||||
out_buffer[out + 0] += sample;
|
||||
out_buffer[out + 1] += sample;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Unknown channel count (port=%u, channel=%d)", port.number, port.num_channels);
|
||||
@@ -1238,6 +1423,8 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset)
|
||||
|
||||
void cell_audio_thread::finish_port_volume_stepping()
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
// part of cellAudioSetPortLevel functionality
|
||||
for (audio_port& port : ports)
|
||||
{
|
||||
@@ -1249,12 +1436,19 @@ void cell_audio_thread::finish_port_volume_stepping()
|
||||
}
|
||||
}
|
||||
|
||||
error_code cellAudioInit()
|
||||
error_code cellAudioInit(ppu_thread& ppu)
|
||||
{
|
||||
cellAudio.warning("cellAudioInit()");
|
||||
|
||||
auto& g_audio = g_fxo->get<cell_audio>();
|
||||
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
|
||||
|
||||
if (!savestate_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& g_audio = g_fxo->get<cell_audio>();
|
||||
std::lock_guard lock(g_audio.mutex);
|
||||
|
||||
if (g_audio.init)
|
||||
@@ -1262,28 +1456,96 @@ error_code cellAudioInit()
|
||||
return CELL_AUDIO_ERROR_ALREADY_INIT;
|
||||
}
|
||||
|
||||
std::memset(g_audio_buffer.get_ptr(), 0, g_audio_buffer.alloc_size);
|
||||
std::memset(g_audio_indices.get_ptr(), 0, g_audio_indices.alloc_size);
|
||||
if (!g_audio.shared_refs++)
|
||||
{
|
||||
g_audio.shared_address = 0;
|
||||
const vm::var<u32> memory_id;
|
||||
const auto result = sys_mmapper_allocate_shared_memory(ppu, SYS_MMAPPER_MIO_SHM_KEY, 0x10000, 0x200, memory_id);
|
||||
|
||||
if (result)
|
||||
{
|
||||
g_audio.release_shared_memory(ppu);
|
||||
|
||||
// mio and audio both release this on an already open handle
|
||||
if (result + 0u == CELL_EEXIST)
|
||||
{
|
||||
g_audio.release_shared_memory(ppu);
|
||||
return CELL_AUDIO_ERROR_TRANS_EVENT;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const vm::var<u32> area;
|
||||
const vm::var<u32> address;
|
||||
auto memory = acquire_audio_memory(*memory_id);
|
||||
|
||||
if (!memory)
|
||||
{
|
||||
g_audio.release_shared_memory(ppu);
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
auto mapping_result = sys_mmapper_get_shared_memory_area(ppu, 0x20full, +area);
|
||||
|
||||
if (!mapping_result)
|
||||
{
|
||||
mapping_result = sys_mmapper_search_and_map(ppu, *area, *memory_id, 0x40000, address);
|
||||
}
|
||||
|
||||
if (mapping_result)
|
||||
{
|
||||
sys_mmapper_free_shared_memory(ppu, *memory_id);
|
||||
release_audio_memory(memory);
|
||||
g_audio.release_shared_memory(ppu);
|
||||
return mapping_result;
|
||||
}
|
||||
|
||||
g_audio.shared_memory = std::move(memory);
|
||||
g_audio.shared_area = *area;
|
||||
g_audio.shared_address = *address;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_audio.release_shared_memory(ppu);
|
||||
return CELL_AUDIO_ERROR_TRANS_EVENT;
|
||||
}
|
||||
|
||||
const u32 port_count = g_ps3_process_info.sdk_ver <= 0x35ffff ? CELL_AUDIO_MAX_PORT : CELL_AUDIO_MAX_PORT_2;
|
||||
g_audio.free_port_count = port_count;
|
||||
|
||||
for (u32 i = 0; i < AUDIO_PORT_COUNT; i++)
|
||||
{
|
||||
g_audio.ports[i].state = audio_port_state::closed;
|
||||
g_audio.ports[i].mapped = false;
|
||||
g_audio.ports[i].number = i;
|
||||
g_audio.ports[i].addr = g_audio_buffer + AUDIO_PORT_OFFSET * i;
|
||||
g_audio.ports[i].index = (g_audio_indices + i).ptr(&aligned_index_t::index);
|
||||
g_audio.ports[i].state = audio_port_state::closed;
|
||||
// closed ports keep their buffer fields and their slot in the new index mapping
|
||||
g_audio.ports[i].index = vm::cast(g_audio.shared_address + g_audio.ports[i].server_index * 16);
|
||||
|
||||
if (i < port_count)
|
||||
{
|
||||
g_audio.free_ports[i] = port_count - i - 1;
|
||||
g_audio.free_indices[i] = port_count - i;
|
||||
}
|
||||
}
|
||||
|
||||
g_audio.init = 1;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellAudioQuit()
|
||||
error_code cellAudioQuit(ppu_thread& ppu)
|
||||
{
|
||||
cellAudio.warning("cellAudioQuit()");
|
||||
|
||||
auto& g_audio = g_fxo->get<cell_audio>();
|
||||
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
|
||||
|
||||
if (!savestate_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& g_audio = g_fxo->get<cell_audio>();
|
||||
std::lock_guard lock(g_audio.mutex);
|
||||
|
||||
if (!g_audio.init)
|
||||
@@ -1291,16 +1553,33 @@ error_code cellAudioQuit()
|
||||
return CELL_AUDIO_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
for (auto& port : g_audio.ports)
|
||||
{
|
||||
if (port.state != audio_port_state::closed)
|
||||
{
|
||||
g_audio.close_port(ppu, port);
|
||||
}
|
||||
}
|
||||
|
||||
g_audio.release_shared_memory(ppu);
|
||||
release_audio_memory(g_audio.shared_memory);
|
||||
// NOTE: Do not clear event queues here. They are handled independently.
|
||||
g_audio.init = 0;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellAudioPortOpen(vm::ptr<CellAudioPortParam> audioParam, vm::ptr<u32> portNum)
|
||||
error_code cellAudioPortOpen(ppu_thread& ppu, vm::ptr<CellAudioPortParam> audioParam, vm::ptr<u32> portNum)
|
||||
{
|
||||
cellAudio.warning("cellAudioPortOpen(audioParam=*0x%x, portNum=*0x%x)", audioParam, portNum);
|
||||
|
||||
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
|
||||
|
||||
if (!savestate_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& g_audio = g_fxo->get<cell_audio>();
|
||||
|
||||
if (!g_audio.init)
|
||||
@@ -1386,13 +1665,10 @@ error_code cellAudioPortOpen(vm::ptr<CellAudioPortParam> audioParam, vm::ptr<u32
|
||||
return CELL_AUDIO_ERROR_PORT_FULL;
|
||||
}
|
||||
|
||||
// TODO: is this necessary in any way? (Based on libaudio.prx)
|
||||
//const u64 num_channels_non_0 = std::max<u64>(1, num_channels);
|
||||
|
||||
port->num_channels = ::narrow<u32>(num_channels);
|
||||
port->num_blocks = ::narrow<u32>(num_blocks);
|
||||
port->attr = attr;
|
||||
port->size = ::narrow<u32>(num_channels * num_blocks * AUDIO_BUFFER_SAMPLES * sizeof(f32));
|
||||
port->size = ::narrow<u32>(std::max<u64>(1, num_channels) * num_blocks * AUDIO_BUFFER_SAMPLES * sizeof(f32));
|
||||
port->cur_pos = 0;
|
||||
port->global_counter = g_audio.m_counter;
|
||||
port->active_counter = 0;
|
||||
@@ -1409,9 +1685,9 @@ error_code cellAudioPortOpen(vm::ptr<CellAudioPortParam> audioParam, vm::ptr<u32
|
||||
|
||||
port->level_set.store({ port->level, 0.0f });
|
||||
|
||||
if (port->level <= -1.0f)
|
||||
if (auto result = g_audio.allocate_port(ppu, *port))
|
||||
{
|
||||
return CELL_AUDIO_ERROR_PORT_OPEN;
|
||||
return result;
|
||||
}
|
||||
|
||||
*portNum = port->number;
|
||||
@@ -1489,19 +1765,32 @@ error_code cellAudioPortStart(u32 portNum)
|
||||
return CELL_AUDIO_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
switch (audio_port_state state = g_audio.ports[portNum].state.compare_and_swap(audio_port_state::opened, audio_port_state::started))
|
||||
auto& port = g_audio.ports[portNum];
|
||||
|
||||
switch (audio_port_state state = port.state)
|
||||
{
|
||||
case audio_port_state::closed: return CELL_AUDIO_ERROR_PORT_NOT_OPEN;
|
||||
case audio_port_state::started: return CELL_AUDIO_ERROR_PORT_ALREADY_RUN;
|
||||
case audio_port_state::opened: return CELL_OK;
|
||||
case audio_port_state::opened:
|
||||
std::memset(port.addr.get_ptr(), 0, port.size);
|
||||
port.state = audio_port_state::started;
|
||||
return CELL_OK;
|
||||
default: fmt::throw_exception("Invalid port state (%d: %d)", portNum, static_cast<u32>(state));
|
||||
}
|
||||
}
|
||||
|
||||
error_code cellAudioPortClose(u32 portNum)
|
||||
error_code cellAudioPortClose(ppu_thread& ppu, u32 portNum)
|
||||
{
|
||||
cellAudio.warning("cellAudioPortClose(portNum=%d)", portNum);
|
||||
|
||||
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
|
||||
|
||||
if (!savestate_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& g_audio = g_fxo->get<cell_audio>();
|
||||
|
||||
std::lock_guard lock(g_audio.mutex);
|
||||
@@ -1516,13 +1805,15 @@ error_code cellAudioPortClose(u32 portNum)
|
||||
return CELL_AUDIO_ERROR_PARAM;
|
||||
}
|
||||
|
||||
switch (audio_port_state state = g_audio.ports[portNum].state.exchange(audio_port_state::closed))
|
||||
auto& port = g_audio.ports[portNum];
|
||||
|
||||
if (port.state == audio_port_state::closed)
|
||||
{
|
||||
case audio_port_state::closed: return CELL_AUDIO_ERROR_PORT_NOT_OPEN;
|
||||
case audio_port_state::started: return CELL_OK;
|
||||
case audio_port_state::opened: return CELL_OK;
|
||||
default: fmt::throw_exception("Invalid port state (%d: %d)", portNum, static_cast<u32>(state));
|
||||
return CELL_AUDIO_ERROR_PORT_NOT_OPEN;
|
||||
}
|
||||
|
||||
g_audio.close_port(ppu, port);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellAudioPortStop(u32 portNum)
|
||||
@@ -1904,7 +2195,7 @@ error_code cellAudioAddData(u32 portNum, vm::ptr<float> src, u32 samples, float
|
||||
|
||||
auto& g_audio = g_fxo->get<cell_audio>();
|
||||
|
||||
std::unique_lock lock(g_audio.mutex);
|
||||
std::lock_guard lock(g_audio.mutex);
|
||||
|
||||
if (!g_audio.init)
|
||||
{
|
||||
@@ -1918,9 +2209,12 @@ error_code cellAudioAddData(u32 portNum, vm::ptr<float> src, u32 samples, float
|
||||
|
||||
const audio_port& port = g_audio.ports[portNum];
|
||||
|
||||
const auto dst = port.get_vm_ptr();
|
||||
if (!port.num_channels)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
const auto dst = port.get_vm_ptr();
|
||||
|
||||
volume = std::isfinite(volume) ? std::clamp(volume, -16.f, 16.f) : 0.f;
|
||||
|
||||
@@ -1938,7 +2232,7 @@ error_code cellAudioAdd2chData(u32 portNum, vm::ptr<float> src, u32 samples, flo
|
||||
|
||||
auto& g_audio = g_fxo->get<cell_audio>();
|
||||
|
||||
std::unique_lock lock(g_audio.mutex);
|
||||
std::lock_guard lock(g_audio.mutex);
|
||||
|
||||
if (!g_audio.init)
|
||||
{
|
||||
@@ -1954,8 +2248,6 @@ error_code cellAudioAdd2chData(u32 portNum, vm::ptr<float> src, u32 samples, flo
|
||||
|
||||
const auto dst = port.get_vm_ptr();
|
||||
|
||||
lock.unlock();
|
||||
|
||||
volume = std::isfinite(volume) ? std::clamp(volume, -16.f, 16.f) : 0.f;
|
||||
|
||||
if (port.num_channels == 2)
|
||||
@@ -1994,7 +2286,7 @@ error_code cellAudioAdd6chData(u32 portNum, vm::ptr<float> src, float volume)
|
||||
|
||||
auto& g_audio = g_fxo->get<cell_audio>();
|
||||
|
||||
std::unique_lock lock(g_audio.mutex);
|
||||
std::lock_guard lock(g_audio.mutex);
|
||||
|
||||
if (!g_audio.init)
|
||||
{
|
||||
@@ -2010,8 +2302,6 @@ error_code cellAudioAdd6chData(u32 portNum, vm::ptr<float> src, float volume)
|
||||
|
||||
const auto dst = port.get_vm_ptr();
|
||||
|
||||
lock.unlock();
|
||||
|
||||
volume = std::isfinite(volume) ? std::clamp(volume, -16.f, 16.f) : 0.f;
|
||||
|
||||
if (port.num_channels == 8)
|
||||
@@ -2131,10 +2421,6 @@ error_code cellAudioUnsetPersonalDevice(s32 iPersonalStream)
|
||||
|
||||
DECLARE(ppu_module_manager::cellAudio)("cellAudio", []()
|
||||
{
|
||||
// Private variables
|
||||
REG_VAR(cellAudio, g_audio_buffer).flag(MFF_HIDDEN);
|
||||
REG_VAR(cellAudio, g_audio_indices).flag(MFF_HIDDEN);
|
||||
|
||||
REG_FUNC(cellAudio, cellAudioInit);
|
||||
REG_FUNC(cellAudio, cellAudioPortClose);
|
||||
REG_FUNC(cellAudio, cellAudioPortStop);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Utilities/Thread.h"
|
||||
#include "Utilities/simple_ringbuf.h"
|
||||
#include "Emu/Memory/vm.h"
|
||||
@@ -10,6 +11,8 @@
|
||||
#include "Emu/system_config_types.h"
|
||||
|
||||
struct lv2_event_queue;
|
||||
struct lv2_memory;
|
||||
class ppu_thread;
|
||||
|
||||
// Error codes
|
||||
enum CellAudioError : u32
|
||||
@@ -153,6 +156,8 @@ struct audio_port
|
||||
atomic_t<audio_port_state> state = audio_port_state::closed;
|
||||
|
||||
u32 number = 0;
|
||||
u32 server_index = 0;
|
||||
bool mapped = false;
|
||||
vm::ptr<char> addr{};
|
||||
vm::ptr<u64> index{};
|
||||
|
||||
@@ -176,7 +181,7 @@ struct audio_port
|
||||
|
||||
u32 block_size() const
|
||||
{
|
||||
return num_channels * AUDIO_BUFFER_SAMPLES;
|
||||
return std::max(num_channels, 1u) * AUDIO_BUFFER_SAMPLES;
|
||||
}
|
||||
|
||||
u32 buf_size() const
|
||||
@@ -190,14 +195,10 @@ struct audio_port
|
||||
return (cur_pos + ofs) % num_blocks;
|
||||
}
|
||||
|
||||
u32 buf_addr(s32 offset = 0) const
|
||||
be_t<f32>* get_vm_ptr() const
|
||||
{
|
||||
return addr.addr() + position(offset) * buf_size();
|
||||
}
|
||||
|
||||
be_t<f32>* get_vm_ptr(s32 offset = 0) const
|
||||
{
|
||||
return vm::_ptr<f32>(buf_addr(offset));
|
||||
const u32 block = static_cast<u16>(*index) & (num_blocks - 1);
|
||||
return vm::_ptr<f32>(addr.addr() + block * buf_size());
|
||||
}
|
||||
|
||||
|
||||
@@ -210,7 +211,7 @@ struct audio_port
|
||||
return tag_nr * PORT_BUFFER_MARK_DELTA_SAMPLE * num_channels + PORT_BUFFER_MARK_CHANNEL;
|
||||
}
|
||||
|
||||
void tag(s32 offset = 0);
|
||||
void tag(be_t<f32>* port_buf);
|
||||
|
||||
audio_port() = default;
|
||||
|
||||
@@ -392,6 +393,7 @@ class cell_audio_thread
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<audio_ringbuffer> ringbuffer{};
|
||||
be_t<f32>* get_buffer(const audio_port& port, s32 offset = 0) const;
|
||||
|
||||
void reset_ports(s32 offset = 0);
|
||||
void advance(u64 timestamp);
|
||||
@@ -410,6 +412,14 @@ public:
|
||||
|
||||
shared_mutex mutex{};
|
||||
atomic_t<u8> init = 0;
|
||||
u32 shared_area = 0;
|
||||
u32 shared_address = 0;
|
||||
u32 shared_refs = 0;
|
||||
shared_ptr<lv2_memory> shared_memory;
|
||||
std::array<shared_ptr<lv2_memory>, AUDIO_PORT_COUNT> port_memories;
|
||||
u32 free_port_count = 0;
|
||||
std::array<u32, AUDIO_PORT_COUNT> free_ports{};
|
||||
std::array<u32, AUDIO_PORT_COUNT> free_indices{};
|
||||
|
||||
u32 key_count = 0;
|
||||
u8 event_period = 0;
|
||||
@@ -457,6 +467,9 @@ public:
|
||||
void save(utils::serial& ar);
|
||||
|
||||
audio_port* open_port();
|
||||
error_code allocate_port(ppu_thread& ppu, audio_port& port);
|
||||
void close_port(ppu_thread& ppu, audio_port& port);
|
||||
void release_shared_memory(ppu_thread& ppu);
|
||||
|
||||
static constexpr auto thread_name = "cellAudio Thread"sv;
|
||||
};
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
|
||||
#include "Emu/Cell/lv2/sys_game.h"
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
#include "Emu/Cell/lv2/sys_mmapper.h"
|
||||
#include "Emu/Cell/lv2/sys_prx.h"
|
||||
#include "Emu/savestate_utils.hpp"
|
||||
#include "cellSysutil.h"
|
||||
#include "sysPrxForUser.h"
|
||||
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "Utilities/lockless.h"
|
||||
@@ -18,6 +22,110 @@
|
||||
|
||||
LOG_CHANNEL(cellSysutil);
|
||||
|
||||
namespace
|
||||
{
|
||||
struct sysutil_module
|
||||
{
|
||||
be_t<u32> start[2];
|
||||
be_t<u32> stop[2];
|
||||
be_t<u32> address;
|
||||
be_t<u32> ref_count;
|
||||
};
|
||||
}
|
||||
|
||||
error_code sysutilModuleStart(ppu_thread& ppu, u32 args, vm::ptr<void> argp)
|
||||
{
|
||||
cellSysutil.notice("module_start(args=%d, argp=*0x%x)", args, argp);
|
||||
|
||||
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
|
||||
|
||||
if (!savestate_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
const vm::ptr<sysutil_module> data = vm::cast(ppu.gpr[2]);
|
||||
|
||||
if (++data->ref_count != 1u)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
const vm::var<u32> memory_id;
|
||||
|
||||
// firmware still returns success if setup fails, a failed map leaves the handle around
|
||||
if (sys_mmapper_allocate_shared_memory(ppu, SYS_MMAPPER_SYSUTIL_SHM_KEY, 0x10000, 0x200, memory_id))
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
const vm::var<u32> area;
|
||||
|
||||
if (!sys_mmapper_get_shared_memory_area(ppu, 0x20full, +area))
|
||||
{
|
||||
sys_mmapper_search_and_map(ppu, *area, *memory_id, 0x40000, data.ptr(&sysutil_module::address));
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sysutilModuleStop(ppu_thread& ppu)
|
||||
{
|
||||
cellSysutil.notice("module_stop()");
|
||||
|
||||
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
|
||||
|
||||
if (!savestate_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
const vm::ptr<sysutil_module> data = vm::cast(ppu.gpr[2]);
|
||||
|
||||
if (--data->ref_count || !data->address)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
const vm::var<u32> memory_id;
|
||||
|
||||
if (!sys_mmapper_unmap_shared_memory(ppu, data->address, memory_id))
|
||||
{
|
||||
data->address = 0;
|
||||
// another mapping can keep this busy, firmware leaves the handle around here too
|
||||
sys_mmapper_free_shared_memory(ppu, *memory_id);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
bool sysutilModuleInit(lv2_prx& prx)
|
||||
{
|
||||
if (!prx.hle_data)
|
||||
{
|
||||
prx.hle_data = vm::alloc(sizeof(sysutil_module), vm::main);
|
||||
|
||||
if (!prx.hle_data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*vm::ptr<sysutil_module>{vm::cast(prx.hle_data)} = {};
|
||||
}
|
||||
|
||||
const vm::ptr<sysutil_module> data = vm::cast(prx.hle_data);
|
||||
const auto& funcs = g_fxo->get<ppu_function_manager>();
|
||||
data->start[0] = funcs.func_addr(FIND_FUNC(sysutilModuleStart), true);
|
||||
data->start[1] = data.addr();
|
||||
data->stop[0] = funcs.func_addr(FIND_FUNC(sysutilModuleStop), true);
|
||||
data->stop[1] = data.addr();
|
||||
prx.start = vm::cast(data.ptr(&sysutil_module::start).addr());
|
||||
prx.stop = vm::cast(data.ptr(&sysutil_module::stop).addr());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<CellSysutilError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@@ -1122,6 +1230,9 @@ extern void cellSysutil_SysCache_init();
|
||||
|
||||
DECLARE(ppu_module_manager::cellSysutil)("cellSysutil", []()
|
||||
{
|
||||
REG_HIDDEN_FUNC(sysutilModuleStart);
|
||||
REG_HIDDEN_FUNC(sysutilModuleStop);
|
||||
|
||||
cellSysutil_SaveData_init(); // cellSaveData functions
|
||||
cellSysutil_GameData_init(); // cellGameData, cellHddGame functions
|
||||
cellSysutil_MsgDialog_init(); // cellMsgDialog functions
|
||||
|
||||
@@ -19,6 +19,8 @@ extern spu_printf_cb_t g_spu_printf_dtcb;
|
||||
|
||||
// Functions
|
||||
|
||||
error_code sys_mmapper_get_shared_memory_area(ppu_thread& ppu, u64 flags, vm::ptr<u32> address);
|
||||
|
||||
vm::ptr<void> _sys_memset(vm::ptr<void> dst, s32 value, u32 size);
|
||||
|
||||
struct sys_lwmutex_t;
|
||||
|
||||
@@ -1,9 +1,61 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Cell/lv2/sys_mmapper.h"
|
||||
#include "Emu/savestate_utils.hpp"
|
||||
#include "sysPrxForUser.h"
|
||||
|
||||
LOG_CHANNEL(sysPrxForUser);
|
||||
|
||||
u32 ppu_get_exported_func_addr(u32 fnid, const std::string& module_name);
|
||||
|
||||
namespace
|
||||
{
|
||||
vm::gvar<u32> s_shared_memory_area;
|
||||
|
||||
struct shared_memory_area_lock
|
||||
{
|
||||
shared_mutex mutex;
|
||||
};
|
||||
}
|
||||
|
||||
error_code sys_mmapper_get_shared_memory_area(ppu_thread& ppu, u64 flags, vm::ptr<u32> address)
|
||||
{
|
||||
if (const u32 entry = ppu_get_exported_func_addr(0xb5d5f64e, "sysPrxForUser"))
|
||||
{
|
||||
return vm::ptr<error_code(u64, vm::ptr<u32>)>{vm::cast(entry)}(ppu, flags, address);
|
||||
}
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sysPrxForUser.notice("sys_mmapper_get_shared_memory_area(flags=0x%llx, address=*0x%x)", flags, address);
|
||||
|
||||
if ((flags & 0xf00) != 0x200 || ((flags & 0xff) != 0 && (flags & 0xff) != 0xf))
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
|
||||
|
||||
if (!savestate_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::lock_guard lock{g_fxo->get<shared_memory_area_lock>().mutex};
|
||||
|
||||
if (!*s_shared_memory_area)
|
||||
{
|
||||
if (const auto ret = sys_mmapper_allocate_address(ppu, 0x10000000, 0x20f, 0x10000000, s_shared_memory_area))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
*address = *s_shared_memory_area;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_mmapper_allocate_memory(ppu_thread& ppu, u32 size, u64 flags, vm::ptr<u32> mem_id)
|
||||
{
|
||||
sysPrxForUser.notice("sys_mmapper_allocate_memory(size=0x%x, flags=0x%llx, mem_id=*0x%x)", size, flags, mem_id);
|
||||
@@ -41,6 +93,9 @@ error_code sys_mmapper_free_memory(ppu_thread& ppu, u32 mem_id)
|
||||
|
||||
extern void sysPrxForUser_sys_mmapper_init()
|
||||
{
|
||||
REG_VAR(sysPrxForUser, s_shared_memory_area).flag(MFF_HIDDEN);
|
||||
// leave the guest import unimplemented for now, LLE sysutil waits for the missing vsh service otherwise
|
||||
|
||||
REG_FUNC(sysPrxForUser, sys_mmapper_allocate_memory);
|
||||
REG_FUNC(sysPrxForUser, sys_mmapper_allocate_memory_from_container);
|
||||
REG_FUNC(sysPrxForUser, sys_mmapper_map_memory);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
#include "Emu/Cell/lv2/sys_prx.h"
|
||||
#include "Emu/Cell/lv2/sys_memory.h"
|
||||
#include "Emu/Cell/lv2/sys_mmapper.h"
|
||||
#include "Emu/Cell/lv2/sys_overlay.h"
|
||||
|
||||
#include "Emu/Cell/Modules/StaticHLE.h"
|
||||
@@ -27,6 +28,7 @@
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <set>
|
||||
#include <shared_mutex>
|
||||
#include <algorithm>
|
||||
#include "util/asm.hpp"
|
||||
|
||||
@@ -698,7 +700,18 @@ bool ppu_form_branch_to_code(u32 entry, u32 target);
|
||||
|
||||
extern u32 ppu_get_exported_func_addr(u32 fnid, const std::string& module_name)
|
||||
{
|
||||
return g_fxo->get<ppu_linkage_info>().modules[module_name].functions[fnid].export_addr;
|
||||
auto& link = g_fxo->get<ppu_linkage_info>();
|
||||
std::shared_lock lock(link.mutex);
|
||||
|
||||
const auto module = link.modules.find(module_name);
|
||||
|
||||
if (module == link.modules.end())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto function = module->second.functions.find(fnid);
|
||||
return function == module->second.functions.end() ? 0 : function->second.export_addr;
|
||||
}
|
||||
|
||||
extern bool ppu_register_library_lock(std::string_view libname, bool lock_lib)
|
||||
@@ -2652,6 +2665,11 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str
|
||||
void init_fxo_for_exec(utils::serial* ar, bool full);
|
||||
init_fxo_for_exec(ar, false);
|
||||
|
||||
if (!ar && !Emu.IsVsh())
|
||||
{
|
||||
init_system_shared_memory();
|
||||
}
|
||||
|
||||
liblv2_begin = 0;
|
||||
liblv2_end = 0;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/lv2/sys_event.h"
|
||||
#include "Emu/savestate_utils.hpp"
|
||||
#include "Emu/Memory/vm_var.h"
|
||||
#include "sys_memory.h"
|
||||
#include "sys_sync.h"
|
||||
@@ -14,6 +15,19 @@
|
||||
|
||||
LOG_CHANNEL(sys_mmapper);
|
||||
|
||||
void init_system_shared_memory()
|
||||
{
|
||||
for (u64 key : {SYS_MMAPPER_SYSUTIL_SHM_KEY, SYS_MMAPPER_MIO_SHM_KEY})
|
||||
{
|
||||
ensure(idm::import<lv2_obj, lv2_memory>([&]() -> shared_ptr<lv2_memory>
|
||||
{
|
||||
auto memory = make_shared<lv2_memory>(0x10000, 0x10000, 0x200, key, true, nullptr);
|
||||
memory->system_handle = idm::last_id<lv2_memory>();
|
||||
return lv2_obj::load(key, std::move(memory));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<lv2_mem_container_id>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@@ -46,23 +60,32 @@ lv2_memory::lv2_memory(utils::serial& ar)
|
||||
, flags(ar)
|
||||
, key(ar)
|
||||
, pshared(ar)
|
||||
, ct(lv2_memory_container::search(ar.pop<u32>()))
|
||||
, shm([&](u32 addr) -> shared_ptr<std::shared_ptr<utils::shm>>
|
||||
, ct([&](u32 id) -> lv2_memory_container*
|
||||
{
|
||||
if (addr)
|
||||
{
|
||||
return make_single_value(ensure(vm::get(vm::any, addr)->peek(addr).second));
|
||||
}
|
||||
|
||||
return null_ptr;
|
||||
return id ? ensure(lv2_memory_container::search(id)) : nullptr;
|
||||
}(ar.pop<u32>()))
|
||||
, counter(ar.pop<u32>())
|
||||
, system_handle(ar)
|
||||
, shm(null_ptr)
|
||||
{
|
||||
const u32 addr{ar};
|
||||
counter = ar.pop<u32>();
|
||||
external_refs = ar.pop<u32>();
|
||||
|
||||
if (addr)
|
||||
{
|
||||
shm = make_single_value(ensure(vm::get(vm::any, addr)->peek(addr).second));
|
||||
}
|
||||
else if (ar.pop<bool>())
|
||||
{
|
||||
auto data = std::make_shared<utils::shm>(size, 1);
|
||||
ar(std::span<u8>(ensure(data->map_self()), size));
|
||||
shm = make_single_value(std::move(data));
|
||||
}
|
||||
}
|
||||
|
||||
CellError lv2_memory::on_id_create()
|
||||
{
|
||||
if (!exists && !ct->take(size))
|
||||
if (!exists && !external_refs && ct && !ct->take(size))
|
||||
{
|
||||
sys_mmapper.error("lv2_memory::on_id_create(): Cannot allocate 0x%x bytes (0x%x available)", size, ct->size - ct->used);
|
||||
return CELL_ENOMEM;
|
||||
@@ -72,9 +95,35 @@ CellError lv2_memory::on_id_create()
|
||||
return {};
|
||||
}
|
||||
|
||||
CellError lv2_memory::on_id_create(u64 requested_size, u32 requested_align, const shared_ptr<lv2_memory>& created)
|
||||
{
|
||||
if (this != created.get() && (size != requested_size || align != requested_align))
|
||||
{
|
||||
return CELL_EPERM;
|
||||
}
|
||||
|
||||
if (exists > (system_handle ? 1u : 0u))
|
||||
{
|
||||
return CELL_EEXIST;
|
||||
}
|
||||
|
||||
return on_id_create();
|
||||
}
|
||||
|
||||
std::function<void(void*)> lv2_memory::load(utils::serial& ar)
|
||||
{
|
||||
auto mem = make_shared<lv2_memory>(stx::exact_t<utils::serial&>(ar));
|
||||
|
||||
if (mem->system_handle)
|
||||
{
|
||||
g_fxo->need<ipc_manager<lv2_memory, u64>>();
|
||||
|
||||
if (auto existing = g_fxo->get<ipc_manager<lv2_memory, u64>>().get(mem->key))
|
||||
{
|
||||
mem = std::move(existing);
|
||||
}
|
||||
}
|
||||
|
||||
mem->exists++; // Disable on_id_create()
|
||||
auto func = load_func(mem, +mem->pshared);
|
||||
mem->exists--;
|
||||
@@ -82,12 +131,48 @@ std::function<void(void*)> lv2_memory::load(utils::serial& ar)
|
||||
}
|
||||
|
||||
void lv2_memory::save(utils::serial& ar)
|
||||
{
|
||||
save_data(ar);
|
||||
}
|
||||
|
||||
void lv2_memory::save_data(utils::serial& ar)
|
||||
{
|
||||
USING_SERIALIZATION_VERSION(lv2_memory);
|
||||
|
||||
ar(size, align, flags, key, pshared, ct->id);
|
||||
ar(counter ? vm::get_shm_addr(*shm.load()) : 0);
|
||||
ar(counter);
|
||||
ar(size, align, flags, key, pshared, ct ? static_cast<u32>(ct->id) : 0, system_handle);
|
||||
const auto data = shm.load();
|
||||
const u32 addr = data ? vm::get_shm_addr(*data) : 0;
|
||||
ar(addr);
|
||||
ar(counter, external_refs);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
ar(static_cast<bool>(data));
|
||||
|
||||
if (data)
|
||||
{
|
||||
ar(std::span<u8>(ensure((*data)->map_self()), size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lv2_memory::release_memory()
|
||||
{
|
||||
// the caller holds the id manager lock
|
||||
if (exists || external_refs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pshared)
|
||||
{
|
||||
g_fxo->get<ipc_manager<lv2_memory, u64>>().remove(key);
|
||||
}
|
||||
|
||||
if (ct)
|
||||
{
|
||||
ct->free(size);
|
||||
}
|
||||
}
|
||||
|
||||
page_fault_notification_entries::page_fault_notification_entries(utils::serial& ar)
|
||||
@@ -104,27 +189,20 @@ template <bool exclusive = false>
|
||||
error_code create_lv2_shm(bool pshared, u64 ipc_key, u64 size, u32 align, u64 flags, lv2_memory_container* ct)
|
||||
{
|
||||
const u32 _pshared = pshared ? SYS_SYNC_PROCESS_SHARED : SYS_SYNC_NOT_PROCESS_SHARED;
|
||||
const s32 create_mode = exclusive || (flags & 0xc000) == 0xc000 ? SYS_SYNC_NEWLY_CREATED
|
||||
: (flags & 0x8000) ? SYS_SYNC_NOT_CARE : SYS_SYNC_NOT_CREATE;
|
||||
|
||||
if (!pshared)
|
||||
{
|
||||
ipc_key = 0;
|
||||
}
|
||||
|
||||
if (auto error = lv2_obj::create<lv2_memory>(_pshared, ipc_key, exclusive ? SYS_SYNC_NEWLY_CREATED : SYS_SYNC_NOT_CARE, [&]()
|
||||
shared_ptr<lv2_memory> created;
|
||||
return lv2_obj::create<lv2_memory>(_pshared, ipc_key, create_mode, [&]()
|
||||
{
|
||||
return make_shared<lv2_memory>(
|
||||
static_cast<u32>(size),
|
||||
align,
|
||||
flags,
|
||||
ipc_key,
|
||||
pshared,
|
||||
ct);
|
||||
}, false))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
created = make_shared<lv2_memory>(static_cast<u32>(size), align, flags, ipc_key, pshared, ct);
|
||||
return created;
|
||||
}, false, size, align, created);
|
||||
}
|
||||
|
||||
error_code sys_mmapper_allocate_address(ppu_thread& ppu, u64 size, u64 flags, u64 alignment, vm::ptr<u32> alloc_addr)
|
||||
@@ -189,6 +267,14 @@ error_code sys_mmapper_allocate_fixed_address(ppu_thread& ppu)
|
||||
|
||||
error_code sys_mmapper_allocate_shared_memory(ppu_thread& ppu, u64 ipc_key, u64 size, u64 flags, vm::ptr<u32> mem_id)
|
||||
{
|
||||
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
|
||||
|
||||
if (!savestate_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_mmapper.warning("sys_mmapper_allocate_shared_memory(ipc_key=0x%x, size=0x%x, flags=0x%x, mem_id=*0x%x)", ipc_key, size, flags, mem_id);
|
||||
@@ -241,6 +327,14 @@ error_code sys_mmapper_allocate_shared_memory(ppu_thread& ppu, u64 ipc_key, u64
|
||||
|
||||
error_code sys_mmapper_allocate_shared_memory_from_container(ppu_thread& ppu, u64 ipc_key, u64 size, u32 cid, u64 flags, vm::ptr<u32> mem_id)
|
||||
{
|
||||
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
|
||||
|
||||
if (!savestate_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_mmapper.warning("sys_mmapper_allocate_shared_memory_from_container(ipc_key=0x%x, size=0x%x, cid=0x%x, flags=0x%x, mem_id=*0x%x)", ipc_key, size, cid, flags, mem_id);
|
||||
@@ -278,6 +372,11 @@ error_code sys_mmapper_allocate_shared_memory_from_container(ppu_thread& ppu, u6
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & 0xc000)
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
const auto ct = idm::get_unlocked<lv2_memory_container>(cid);
|
||||
|
||||
if (!ct)
|
||||
@@ -285,7 +384,7 @@ error_code sys_mmapper_allocate_shared_memory_from_container(ppu_thread& ppu, u6
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (auto error = create_lv2_shm(ipc_key != SYS_MMAPPER_NO_SHM_KEY, ipc_key, size, flags & SYS_MEMORY_PAGE_SIZE_64K ? 0x10000 : 0x100000, flags, ct.get()))
|
||||
if (auto error = create_lv2_shm<true>(ipc_key != SYS_MMAPPER_NO_SHM_KEY, ipc_key, size, flags & SYS_MEMORY_PAGE_SIZE_64K ? 0x10000 : 0x100000, flags, ct.get()))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
@@ -297,6 +396,14 @@ error_code sys_mmapper_allocate_shared_memory_from_container(ppu_thread& ppu, u6
|
||||
|
||||
error_code sys_mmapper_allocate_shared_memory_ext(ppu_thread& ppu, u64 ipc_key, u64 size, u32 flags, vm::ptr<mmapper_unk_entry_struct0> entries, s32 entry_count, vm::ptr<u32> mem_id)
|
||||
{
|
||||
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
|
||||
|
||||
if (!savestate_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_mmapper.todo("sys_mmapper_allocate_shared_memory_ext(ipc_key=0x%x, size=0x%x, flags=0x%x, entries=*0x%x, entry_count=0x%x, mem_id=*0x%x)", ipc_key, size, flags, entries, entry_count, mem_id);
|
||||
@@ -396,6 +503,14 @@ error_code sys_mmapper_allocate_shared_memory_ext(ppu_thread& ppu, u64 ipc_key,
|
||||
|
||||
error_code sys_mmapper_allocate_shared_memory_from_container_ext(ppu_thread& ppu, u64 ipc_key, u64 size, u64 flags, u32 cid, vm::ptr<mmapper_unk_entry_struct0> entries, s32 entry_count, vm::ptr<u32> mem_id)
|
||||
{
|
||||
const std::unique_lock savestate_lock{ g_fxo->get<hle_locks_t>(), std::try_to_lock };
|
||||
|
||||
if (!savestate_lock)
|
||||
{
|
||||
ppu.state += cpu_flag::again;
|
||||
return {};
|
||||
}
|
||||
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_mmapper.todo("sys_mmapper_allocate_shared_memory_from_container_ext(ipc_key=0x%x, size=0x%x, flags=0x%x, cid=0x%x, entries=*0x%x, entry_count=0x%x, mem_id=*0x%x)", ipc_key, size, flags, cid, entries,
|
||||
@@ -581,18 +696,18 @@ error_code sys_mmapper_free_shared_memory(ppu_thread& ppu, u32 mem_id)
|
||||
// Conditionally remove memory ID
|
||||
const auto mem = idm::withdraw<lv2_obj, lv2_memory>(mem_id, [&](lv2_memory& mem) -> CellError
|
||||
{
|
||||
if (mem_id == mem.system_handle)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (mem.counter)
|
||||
{
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
lv2_obj::on_id_destroy(mem, mem.key, +mem.pshared);
|
||||
|
||||
if (!mem.exists)
|
||||
{
|
||||
// Return "physical memory" to the memory container
|
||||
mem.ct->free(mem.size);
|
||||
}
|
||||
lv2_obj::on_id_destroy(mem, mem.key, false);
|
||||
mem.release_memory();
|
||||
|
||||
return {};
|
||||
});
|
||||
@@ -625,6 +740,11 @@ error_code sys_mmapper_map_shared_memory(ppu_thread& ppu, u32 addr, u32 mem_id,
|
||||
|
||||
const auto mem = idm::get<lv2_obj, lv2_memory>(mem_id, [&](lv2_memory& mem) -> CellError
|
||||
{
|
||||
if (mem_id == mem.system_handle)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
const u32 page_alignment = area->flags & SYS_MEMORY_PAGE_SIZE_64K ? 0x10000 : 0x100000;
|
||||
|
||||
if (mem.align < page_alignment)
|
||||
@@ -700,6 +820,11 @@ error_code sys_mmapper_search_and_map(ppu_thread& ppu, u32 start_addr, u32 mem_i
|
||||
|
||||
const auto mem = idm::get<lv2_obj, lv2_memory>(mem_id, [&](lv2_memory& mem) -> CellError
|
||||
{
|
||||
if (mem_id == mem.system_handle)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
const u32 page_alignment = area->flags & SYS_MEMORY_PAGE_SIZE_64K ? 0x10000 : 0x100000;
|
||||
|
||||
if (mem.align < page_alignment)
|
||||
@@ -783,7 +908,7 @@ error_code sys_mmapper_unmap_shared_memory(ppu_thread& ppu, u32 addr, vm::ptr<u3
|
||||
|
||||
const auto mem = idm::select<lv2_obj, lv2_memory>([&](u32 id, lv2_memory& mem) -> u32
|
||||
{
|
||||
if (auto shm0 = mem.shm.load(); shm0 && shm0->get() == shm.second.get())
|
||||
if (auto shm0 = mem.shm.load(); id != mem.system_handle && shm0 && shm0->get() == shm.second.get())
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -25,22 +25,30 @@ struct lv2_memory : lv2_obj
|
||||
const u64 flags;
|
||||
const u64 key; // IPC key
|
||||
const bool pshared; // Process shared flag
|
||||
lv2_memory_container* const ct; // Associated memory container
|
||||
lv2_memory_container* const ct; // null for system memory
|
||||
u32 system_handle = 0; // stands in for the vsh handle
|
||||
atomic_ptr<std::shared_ptr<utils::shm>> shm;
|
||||
|
||||
atomic_t<u32> counter{0};
|
||||
// the game can drop its handle while audio still needs the buffer
|
||||
atomic_t<u32> external_refs{0};
|
||||
|
||||
lv2_memory(u32 size, u32 align, u64 flags, u64 key, bool pshared, lv2_memory_container* ct);
|
||||
|
||||
lv2_memory(utils::serial& ar);
|
||||
static std::function<void(void*)> load(utils::serial& ar);
|
||||
void save(utils::serial& ar);
|
||||
void save_data(utils::serial& ar);
|
||||
void release_memory();
|
||||
|
||||
CellError on_id_create();
|
||||
CellError on_id_create(u64 requested_size, u32 requested_align, const shared_ptr<lv2_memory>& created);
|
||||
};
|
||||
|
||||
enum : u64
|
||||
{
|
||||
SYS_MMAPPER_SYSUTIL_SHM_KEY = 0x8006010000000010ULL,
|
||||
SYS_MMAPPER_MIO_SHM_KEY = 0x80004d494f323211ULL,
|
||||
SYS_MEMORY_PAGE_FAULT_EVENT_KEY = 0xfffe000000000000ULL,
|
||||
};
|
||||
|
||||
@@ -99,6 +107,7 @@ struct mmapper_unk_entry_struct0
|
||||
// Aux
|
||||
class ppu_thread;
|
||||
|
||||
void init_system_shared_memory();
|
||||
error_code mmapper_thread_recover_page_fault(cpu_thread* cpu);
|
||||
|
||||
// SysCalls
|
||||
|
||||
@@ -80,7 +80,20 @@ s32 sys_process_getppid()
|
||||
template <typename T, typename Get>
|
||||
u32 idm_get_count()
|
||||
{
|
||||
return idm::select<T, Get>([&](u32, Get&) {});
|
||||
u32 count = 0;
|
||||
idm::select<T, Get>([&](u32 id, Get& obj)
|
||||
{
|
||||
if constexpr (std::is_same_v<Get, lv2_memory>)
|
||||
{
|
||||
if (id == obj.system_handle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
count++;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
error_code sys_process_get_number_of_object(u32 object, vm::ptr<u32> nump)
|
||||
@@ -123,8 +136,16 @@ error_code sys_process_get_number_of_object(u32 object, vm::ptr<u32> nump)
|
||||
template <typename T, typename Get>
|
||||
void idm_get_set(std::set<u32>& out)
|
||||
{
|
||||
idm::select<T, Get>([&](u32 id, Get&)
|
||||
idm::select<T, Get>([&](u32 id, Get& obj)
|
||||
{
|
||||
if constexpr (std::is_same_v<Get, lv2_memory>)
|
||||
{
|
||||
if (id == obj.system_handle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
out.emplace(id);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -180,6 +180,7 @@ bool ppu_register_library_lock(std::string_view libname, bool lock_lib);
|
||||
|
||||
extern error_code sysmoduleModuleStart(ppu_thread& ppu, u32 args, vm::ptr<void> argp);
|
||||
extern error_code sysmoduleModuleStop(ppu_thread& ppu);
|
||||
extern bool sysutilModuleInit(lv2_prx& prx);
|
||||
|
||||
static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<sys_prx_load_module_option_t> /*pOpt*/, fs::file src = {}, s64 file_offset = 0)
|
||||
{
|
||||
@@ -232,7 +233,7 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
|
||||
ignore = g_prx_list.count(vpath0) && ::at32(g_prx_list, vpath0);
|
||||
}
|
||||
|
||||
auto hle_load = [&]()
|
||||
auto hle_load = [&]() -> error_code
|
||||
{
|
||||
const auto prx = idm::make_ptr<lv2_obj, lv2_prx>();
|
||||
|
||||
@@ -241,6 +242,14 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
|
||||
prx->start = vm::cast(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(sysmoduleModuleStart)));
|
||||
prx->stop = vm::cast(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(sysmoduleModuleStop)));
|
||||
}
|
||||
else if (name == "libsysutil.sprx")
|
||||
{
|
||||
if (!sysutilModuleInit(*prx))
|
||||
{
|
||||
ensure((idm::remove<lv2_obj, lv2_prx>(idm::last_id<lv2_prx>())));
|
||||
return CELL_ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
prx->name = std::move(name);
|
||||
prx->path = std::move(path);
|
||||
@@ -322,6 +331,7 @@ std::function<void(void*)> lv2_prx::load(utils::serial& ar)
|
||||
const std::string path = vfs::get(ar.pop<std::string>());
|
||||
const s64 offset{ar};
|
||||
const u32 state{ar};
|
||||
const u32 hle_data{ar};
|
||||
|
||||
usz seg_count = 0;
|
||||
ar.deserialize_vle<9>(seg_count);
|
||||
@@ -332,7 +342,14 @@ std::function<void(void*)> lv2_prx::load(utils::serial& ar)
|
||||
{
|
||||
prx = make_shared<lv2_prx>();
|
||||
prx->path = path;
|
||||
prx->hle_data = hle_data;
|
||||
prx->name = path.substr(path.find_last_of(fs::delim) + 1);
|
||||
|
||||
if (!seg_count && prx->name == "libsysutil.sprx")
|
||||
{
|
||||
ensure(prx->hle_data);
|
||||
ensure(sysutilModuleInit(*prx));
|
||||
}
|
||||
};
|
||||
|
||||
if (seg_count)
|
||||
@@ -392,7 +409,7 @@ void lv2_prx::save(utils::serial& ar)
|
||||
|
||||
const std::string vpath = vfs::retrieve(path);
|
||||
|
||||
ar(vpath, offset, state);
|
||||
ar(vpath, offset, state, hle_data);
|
||||
|
||||
// Save segments count
|
||||
ar.serialize_vle(segs.size());
|
||||
@@ -765,6 +782,11 @@ error_code _sys_prx_unload_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sy
|
||||
|
||||
prx->mutex.lock_unlock();
|
||||
|
||||
if (prx->hle_data)
|
||||
{
|
||||
ensure(vm::dealloc(std::exchange(prx->hle_data, 0), vm::main));
|
||||
}
|
||||
|
||||
ppu_unload_prx(*prx);
|
||||
|
||||
ppu_finalize(*prx);
|
||||
|
||||
@@ -190,6 +190,7 @@ struct lv2_prx final : ppu_module<lv2_obj>
|
||||
|
||||
atomic_t<u32> state = PRX_STATE_INITIALIZED;
|
||||
shared_mutex mutex;
|
||||
u32 hle_data = 0;
|
||||
|
||||
std::unordered_map<u32, u32> specials;
|
||||
|
||||
|
||||
@@ -308,8 +308,8 @@ public:
|
||||
return (attr.pshared == SYS_SYNC_PROCESS_SHARED ? +attr.ipc_key : 0);
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
static error_code create(u32 pshared, u64 ipc_key, s32 flags, F&& make, bool key_not_zero = true)
|
||||
template <typename T, typename F, typename... Args>
|
||||
static error_code create(u32 pshared, u64 ipc_key, s32 flags, F&& make, bool key_not_zero = true, Args&&... args)
|
||||
{
|
||||
switch (pshared)
|
||||
{
|
||||
@@ -340,8 +340,7 @@ public:
|
||||
default: return CELL_EINVAL;
|
||||
}
|
||||
|
||||
// EAGAIN for IDM IDs shortage
|
||||
CellError error = CELL_EAGAIN;
|
||||
CellError error{};
|
||||
|
||||
if (!idm::import<lv2_obj, T>([&]() -> shared_ptr<T>
|
||||
{
|
||||
@@ -349,7 +348,7 @@ public:
|
||||
|
||||
auto finalize_construct = [&]() -> shared_ptr<T>
|
||||
{
|
||||
if ((error = result->on_id_create()))
|
||||
if ((error = result->on_id_create(std::forward<Args>(args)...)))
|
||||
{
|
||||
result.reset();
|
||||
}
|
||||
@@ -382,6 +381,11 @@ public:
|
||||
bool added = false;
|
||||
std::tie(added, result) = ipc_container.add(ipc_key, finalize_construct, flags != SYS_SYNC_NEWLY_CREATED);
|
||||
|
||||
if (error)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!added)
|
||||
{
|
||||
if (flags == SYS_SYNC_NEWLY_CREATED)
|
||||
@@ -401,7 +405,7 @@ public:
|
||||
return result;
|
||||
}))
|
||||
{
|
||||
return error;
|
||||
return error ? error : CELL_EAGAIN;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
|
||||
+39
-23
@@ -1320,6 +1320,8 @@ namespace vm
|
||||
|
||||
if (m_id.exchange(0))
|
||||
{
|
||||
std::unordered_map<const utils::shm*, s64> mapping_refs;
|
||||
|
||||
// Deallocate all memory
|
||||
for (auto it = m_map.begin(), end = m_map.end(); it != end;)
|
||||
{
|
||||
@@ -1333,7 +1335,40 @@ namespace vm
|
||||
{
|
||||
if (it->second.second.use_count() != 1)
|
||||
{
|
||||
fmt::throw_exception("External memory usage at block 0x%x (addr=0x%x, size=0x%x)", this->addr, it->first, size);
|
||||
if (mapping_refs.empty())
|
||||
{
|
||||
const auto count_refs = [&mapping_refs](const auto& map)
|
||||
{
|
||||
for (const auto& entry : map)
|
||||
{
|
||||
if (entry.second.second)
|
||||
{
|
||||
mapping_refs[entry.second.second.get()]++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// count this block separately, vm::unmap removes it from g_locations before calling us
|
||||
count_refs(m_map);
|
||||
|
||||
for (const auto& block : g_locations)
|
||||
{
|
||||
if (block && block.get() != this)
|
||||
{
|
||||
count_refs((block->m.*block_map)());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (it->second.second.use_count() != mapping_refs.at(it->second.second.get()))
|
||||
{
|
||||
fmt::throw_exception("External memory usage at block 0x%x (addr=0x%x, size=0x%x)", this->addr, it->first, size);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mapping_refs.empty())
|
||||
{
|
||||
mapping_refs.at(it->second.second.get())--;
|
||||
}
|
||||
|
||||
it->second.second.reset();
|
||||
@@ -2294,29 +2329,10 @@ namespace vm
|
||||
|
||||
std::map<utils::shm*, usz> shared_map;
|
||||
|
||||
#ifndef _MSC_VER
|
||||
shared.erase(std::unique(shared.begin(), shared.end(), [](auto& a, auto& b) { return a.first == b.first; }), shared.end());
|
||||
#else
|
||||
// Workaround for bugged std::unique
|
||||
for (auto it = shared.begin(); it != shared.end();)
|
||||
std::erase_if(shared, [&](const auto& memory)
|
||||
{
|
||||
if (shared_map.count(it->first))
|
||||
{
|
||||
it = shared.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
shared_map.emplace(it->first, 0);
|
||||
it++;
|
||||
}
|
||||
|
||||
shared_map.clear();
|
||||
#endif
|
||||
|
||||
for (auto& p : shared)
|
||||
{
|
||||
shared_map.emplace(p.first, &p - shared.data());
|
||||
}
|
||||
return !shared_map.emplace(memory.first, shared_map.size()).second;
|
||||
});
|
||||
|
||||
// TODO: proper serialization of std::map
|
||||
ar(static_cast<usz>(shared_map.size()));
|
||||
|
||||
@@ -40,15 +40,15 @@ static std::array<serial_ver_t, 34> s_serial_versions;
|
||||
return ::s_serial_versions[identifier].current_version;\
|
||||
}
|
||||
|
||||
SERIALIZATION_VER(global_version, 0, 22) // For stuff not listed here
|
||||
SERIALIZATION_VER(global_version, 0, 23) // For stuff not listed here
|
||||
SERIALIZATION_VER(ppu, 1, 1, 2/*PPU sleep order*/, 3/*PPU FNID and module*/)
|
||||
SERIALIZATION_VER(spu, 2, 1)
|
||||
SERIALIZATION_VER(lv2_sync, 3, 1)
|
||||
SERIALIZATION_VER(lv2_vm, 4, 1)
|
||||
SERIALIZATION_VER(lv2_net, 5, 1, 2/*TCP Feign conection loss*/, 3/*P2PS stream_disconnected status*/)
|
||||
SERIALIZATION_VER(lv2_fs, 6, 1, 2/*NPDRM key saving*/)
|
||||
SERIALIZATION_VER(lv2_prx_overlay, 7, 1)
|
||||
SERIALIZATION_VER(lv2_memory, 8, 1)
|
||||
SERIALIZATION_VER(lv2_prx_overlay, 7, 2)
|
||||
SERIALIZATION_VER(lv2_memory, 8, 2)
|
||||
SERIALIZATION_VER(lv2_config, 9, 1)
|
||||
|
||||
namespace rsx
|
||||
@@ -68,7 +68,7 @@ SERIALIZATION_VER(sceNp, 11)
|
||||
#endif
|
||||
|
||||
SERIALIZATION_VER(cellVdec, 12, 1)
|
||||
SERIALIZATION_VER(cellAudio, 13, 1)
|
||||
SERIALIZATION_VER(cellAudio, 13, 2)
|
||||
SERIALIZATION_VER(cellCamera, 14, 1, 2/*gem_camera_shared*/)
|
||||
SERIALIZATION_VER(cellGem, 15, 1, 2/*calibration_status_flags*/, 3/*video_conversion*/)
|
||||
SERIALIZATION_VER(sceNpTrophy, 16, 1)
|
||||
@@ -691,4 +691,3 @@ bool hle_locks_t::try_finalize(std::function<bool()> test)
|
||||
lock_val.notify_all();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -350,14 +350,15 @@ void kernel_explorer::update()
|
||||
{
|
||||
auto& mem = static_cast<lv2_memory&>(obj);
|
||||
const f64 size_mb = mem.size * 1. / (1024 * 1024);
|
||||
const std::string container = mem.ct ? fmt::format("%s", mem.ct->id) : "System";
|
||||
|
||||
if (mem.pshared)
|
||||
{
|
||||
add_leaf(node, QString::fromStdString(fmt::format("Shared Mem 0x%08x: Size: 0x%x (%0.2f MB), Chunk: %s, Mappings: %u, Mem Container: %s, Key: %#llx", id, mem.size, size_mb, mem.align == 0x10000u ? "64K" : "1MB", +mem.counter, mem.ct->id, mem.key)));
|
||||
add_leaf(node, QString::fromStdString(fmt::format("Shared Mem 0x%08x: Size: 0x%x (%0.2f MB), Chunk: %s, Mappings: %u, Mem Container: %s, Key: %#llx", id, mem.size, size_mb, mem.align == 0x10000u ? "64K" : "1MB", +mem.counter, container, mem.key)));
|
||||
break;
|
||||
}
|
||||
|
||||
add_leaf(node, QString::fromStdString(fmt::format("Shared Mem 0x%08x: Size: 0x%x (%0.2f MB), Chunk: %s, Mem Container: %s, Mappings: %u", id, mem.size, size_mb, mem.align == 0x10000u ? "64K" : "1MB", mem.ct->id, +mem.counter)));
|
||||
add_leaf(node, QString::fromStdString(fmt::format("Shared Mem 0x%08x: Size: 0x%x (%0.2f MB), Chunk: %s, Mem Container: %s, Mappings: %u", id, mem.size, size_mb, mem.align == 0x10000u ? "64K" : "1MB", container, +mem.counter)));
|
||||
break;
|
||||
}
|
||||
case SYS_MUTEX_OBJECT:
|
||||
|
||||
Reference in New Issue
Block a user