diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index 7cffdbc2bf..5a9275dc85 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -55,7 +55,7 @@ bool RawSPUThread::ReadReg(const u32 addr, u32& value) case SPU_MBox_Status_offs: { - value = (ch_out_mbox.get_count() & 0xff) | ((4 - ch_in_mbox.get_count()) << 8 & 0xff) | (ch_out_intr_mbox.get_count() << 16 & 0xff); + value = (ch_out_mbox.get_count() & 0xff) | ((4 - ch_in_mbox.get_count()) << 8 & 0xff00) | (ch_out_intr_mbox.get_count() << 16 & 0xff0000); return true; } diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 63c1b12dd8..84504dd043 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -307,7 +307,7 @@ void SPUThread::process_mfc_cmd(u32 cmd) { if (Ini.HLELogging.GetValue()) { - LOG_NOTICE(SPU, "DMA %s: cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x", get_mfc_cmd_name(cmd), ch_mfc_args.lsa, ch_mfc_args.ea, ch_mfc_args.tag, ch_mfc_args.size, cmd); + LOG_NOTICE(SPU, "DMA %s: cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x", get_mfc_cmd_name(cmd), cmd, ch_mfc_args.lsa, ch_mfc_args.ea, ch_mfc_args.tag, ch_mfc_args.size); } switch (cmd) @@ -398,11 +398,11 @@ void SPUThread::process_mfc_cmd(u32 cmd) // tag may be used here } - break; + return; } } - LOG_ERROR(SPU, "Unknown DMA %s: cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x", get_mfc_cmd_name(cmd), ch_mfc_args.lsa, ch_mfc_args.ea, ch_mfc_args.tag, ch_mfc_args.size, cmd); + LOG_ERROR(SPU, "Unknown DMA %s: cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x", get_mfc_cmd_name(cmd), cmd, ch_mfc_args.lsa, ch_mfc_args.ea, ch_mfc_args.tag, ch_mfc_args.size); throw __FUNCTION__; } @@ -614,9 +614,8 @@ void SPUThread::set_ch_value(u32 ch, u32 value) return; } - queue->events.emplace_back(SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data); + queue->push(SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data); ch_in_mbox.push_uncond(CELL_OK); - queue->cv.notify_one(); return; } else if (code < 128) @@ -654,8 +653,7 @@ void SPUThread::set_ch_value(u32 ch, u32 value) return; } - queue->events.emplace_back(SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data); - queue->cv.notify_one(); + queue->push(SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data); return; } else if (code == 128) @@ -968,14 +966,26 @@ void SPUThread::stop_and_signal(u32 code) LV2_LOCK; - auto found = this->spuq.find(spuq); - if (found == this->spuq.end()) + std::shared_ptr queue; + + for (auto& v : this->spuq) + { + if (spuq == v.first) + { + queue = v.second.lock(); + + if (queue) + { + break; + } + } + } + + if (!queue) { ch_in_mbox.push_uncond(CELL_EINVAL); // TODO: check error value return; } - - std::shared_ptr queue = found->second; // protocol is ignored in current implementation queue->waiters++; @@ -1046,7 +1056,7 @@ void SPUThread::stop_and_signal(u32 code) group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED; group->exit_status = value; - group->join_state |= STGJSF_GROUP_EXIT; + group->join_state |= SPU_TGJSF_GROUP_EXIT; group->join_cv.notify_one(); return; } diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index eec3c5af40..db395bb770 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -504,9 +504,9 @@ public: spu_interrupt_tag_t int0; // SPU Class 0 Interrupt Management spu_interrupt_tag_t int2; // SPU Class 2 Interrupt Management - std::weak_ptr tg; // SPU Thread Group Id + std::weak_ptr tg; // SPU Thread Group - std::unordered_map> spuq; // Event Queue Keys for SPU Thread + std::array>, 32> spuq; // Event Queue Keys for SPU Thread std::weak_ptr spup[64]; // SPU Ports void write_snr(bool number, u32 value) diff --git a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp index a5245772e1..2f45e35988 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp @@ -393,19 +393,18 @@ s32 cellAudioInit() keys.resize(g_audio.keys.size()); memcpy(keys.data(), g_audio.keys.data(), sizeof(u64) * keys.size()); } - for (u32 i = 0; i < keys.size(); i++) { - auto eq = Emu.GetEventManager().GetEventQueue(keys[i]); + LV2_LOCK; - if (eq) + for (u32 i = 0; i < keys.size(); i++) { - LV2_LOCK; - - // TODO: check event source - eq->events.emplace_back(0x10103000e010e07, 0, 0, 0); - eq->cv.notify_one(); + if (std::shared_ptr queue = Emu.GetEventManager().GetEventQueue(keys[i])) + { + queue->push(0, 0, 0, 0); // TODO: check arguments + } } } + //const u64 stamp3 = get_system_time(); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_event.cpp b/rpcs3/Emu/SysCalls/lv2/sys_event.cpp index 8d80c691bd..72852ef01c 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_event.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_event.cpp @@ -314,6 +314,7 @@ s32 sys_event_port_disconnect(u32 eport_id) //} port->queue.reset(); + return CELL_OK; } @@ -343,7 +344,7 @@ s32 sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3) const u64 source = port->name ? port->name : ((u64)process_getpid() << 32) | (u64)eport_id; - queue->events.emplace_back(source, data1, data2, data3); - queue->cv.notify_one(); + queue->push(source, data1, data2, data3); + return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/sys_event.h b/rpcs3/Emu/SysCalls/lv2/sys_event.h index 7542dd871f..e1e10faada 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_event.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_event.h @@ -92,6 +92,12 @@ struct event_queue_t , waiters(0) { } + + void push(u64 source, u64 data1, u64 data2, u64 data3) + { + events.emplace_back(source, data1, data2, data3); + cv.notify_one(); + } }; struct event_port_t diff --git a/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp b/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp index 03ea5e701a..148a7fa4e6 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp @@ -320,6 +320,13 @@ s32 sys_spu_thread_group_start(u32 id) } } + // because SPU_THREAD_GROUP_STATUS_READY is not possible, run event is delivered immediately + + if (std::shared_ptr queue = group->ep_run.lock()) + { + queue->push(SYS_SPU_THREAD_GROUP_EVENT_RUN_KEY, id, 0, 0); // TODO: check data2 and data3 + } + for (auto& t : group->threads) { if (t) @@ -485,7 +492,7 @@ s32 sys_spu_thread_group_terminate(u32 id, s32 value) group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED; group->exit_status = value; - group->join_state |= STGJSF_TERMINATED; + group->join_state |= SPU_TGJSF_TERMINATED; group->join_cv.notify_one(); return CELL_OK; } @@ -507,13 +514,13 @@ s32 sys_spu_thread_group_join(u32 id, vm::ptr cause, vm::ptr status) return CELL_ESTAT; } - if (group->join_state.fetch_or(STGJSF_IS_JOINING) & STGJSF_IS_JOINING) + if (group->join_state.fetch_or(SPU_TGJSF_IS_JOINING) & SPU_TGJSF_IS_JOINING) { // another PPU thread is joining this thread group return CELL_EBUSY; } - while ((group->join_state & ~STGJSF_IS_JOINING) == 0) + while ((group->join_state & ~SPU_TGJSF_IS_JOINING) == 0) { bool stopped = true; @@ -545,19 +552,19 @@ s32 sys_spu_thread_group_join(u32 id, vm::ptr cause, vm::ptr status) group->join_cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); } - switch (group->join_state & ~STGJSF_IS_JOINING) + switch (group->join_state & ~SPU_TGJSF_IS_JOINING) { case 0: { if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_ALL_THREADS_EXIT; break; } - case STGJSF_GROUP_EXIT: + case SPU_TGJSF_GROUP_EXIT: { if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_GROUP_EXIT; break; } - case STGJSF_TERMINATED: + case SPU_TGJSF_TERMINATED: { if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_TERMINATED; break; @@ -570,7 +577,7 @@ s32 sys_spu_thread_group_join(u32 id, vm::ptr cause, vm::ptr status) *status = group->exit_status; } - group->join_state &= ~STGJSF_IS_JOINING; + group->join_state &= ~SPU_TGJSF_IS_JOINING; group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED; // hack return CELL_OK; } @@ -729,14 +736,111 @@ s32 sys_spu_thread_write_snr(u32 id, u32 number, u32 value) s32 sys_spu_thread_group_connect_event(u32 id, u32 eq, u32 et) { - sys_spu.Todo("sys_spu_thread_group_connect_event(id=%d, eq=%d, et=0x%x)", id, eq, et); + sys_spu.Warning("sys_spu_thread_group_connect_event(id=%d, eq=%d, et=%d)", id, eq, et); + + LV2_LOCK; + + std::shared_ptr group; + std::shared_ptr queue; + + if (!Emu.GetIdManager().GetIDData(id, group) || !Emu.GetIdManager().GetIDData(eq, queue)) + { + return CELL_ESRCH; + } + + switch (et) + { + case SYS_SPU_THREAD_GROUP_EVENT_RUN: + { + if (!group->ep_run.expired()) + { + return CELL_EBUSY; + } + + group->ep_run = queue; + break; + } + case SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION: + { + if (!group->ep_exception.expired()) + { + return CELL_EBUSY; + } + + group->ep_exception = queue; + break; + } + case SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE: + { + if (!group->ep_sysmodule.expired()) + { + return CELL_EBUSY; + } + + group->ep_sysmodule = queue; + break; + } + default: + { + sys_spu.Error("sys_spu_thread_group_connect_event(): unknown event type (%d)", et); + return CELL_EINVAL; + } + } return CELL_OK; } s32 sys_spu_thread_group_disconnect_event(u32 id, u32 et) { - sys_spu.Todo("sys_spu_thread_group_disconnect_event(id=%d, et=0x%x)", id, et); + sys_spu.Warning("sys_spu_thread_group_disconnect_event(id=%d, et=%d)", id, et); + + LV2_LOCK; + + std::shared_ptr group; + + if (!Emu.GetIdManager().GetIDData(id, group)) + { + return CELL_ESRCH; + } + + switch (et) + { + case SYS_SPU_THREAD_GROUP_EVENT_RUN: + { + if (group->ep_run.expired()) + { + return CELL_ENOTCONN; + } + + group->ep_run.reset(); + break; + } + case SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION: + { + if (group->ep_exception.expired()) + { + return CELL_ENOTCONN; + } + + group->ep_exception.reset(); + break; + } + case SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE: + { + if (group->ep_sysmodule.expired()) + { + return CELL_ENOTCONN; + } + + group->ep_sysmodule.reset(); + break; + } + default: + { + sys_spu.Error("sys_spu_thread_group_disconnect_event(): unknown event type (%d)", et); + return CELL_EINVAL; + } + } return CELL_OK; } @@ -839,20 +943,29 @@ s32 sys_spu_thread_bind_queue(u32 id, u32 spuq, u32 spuq_num) return CELL_EINVAL; } - if (spu.spuq.size() >= 32) + for (auto& v : spu.spuq) { - return CELL_EAGAIN; + if (auto q = v.second.lock()) + { + if (v.first == spuq_num || q == queue) + { + return CELL_EBUSY; + } + } } - auto found = spu.spuq.find(spuq_num); - if (found != spu.spuq.end()) + for (auto& v : spu.spuq) { - return CELL_EBUSY; + if (v.second.expired()) + { + v.first = spuq_num; + v.second = queue; + + return CELL_OK; + } } - spu.spuq[spuq_num] = queue; - - return CELL_OK; + return CELL_EAGAIN; } s32 sys_spu_thread_unbind_queue(u32 id, u32 spuq_num) @@ -870,15 +983,17 @@ s32 sys_spu_thread_unbind_queue(u32 id, u32 spuq_num) auto& spu = static_cast(*t); - auto found = spu.spuq.find(spuq_num); - if (found == spu.spuq.end()) + for (auto& v : spu.spuq) { - return CELL_ESRCH; + if (v.first == spuq_num && !v.second.expired()) + { + v.second.reset(); + + return CELL_OK; + } } - spu.spuq.erase(found); - - return CELL_OK; + return CELL_ESRCH; } s32 sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq, u64 req, vm::ptr spup) diff --git a/rpcs3/Emu/SysCalls/lv2/sys_spu.h b/rpcs3/Emu/SysCalls/lv2/sys_spu.h index 9879ecbd7b..cfa67f082c 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_spu.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_spu.h @@ -1,5 +1,7 @@ #pragma once +struct event_queue_t; + enum : s32 { SYS_SPU_THREAD_GROUP_TYPE_NORMAL = 0x00, @@ -132,9 +134,9 @@ struct spu_arg_t // SPU Thread Group Join State Flag enum : u32 { - STGJSF_IS_JOINING = (1 << 0), - STGJSF_TERMINATED = (1 << 1), // set if SPU Thread Group is terminated by sys_spu_thread_group_terminate - STGJSF_GROUP_EXIT = (1 << 2), // set if SPU Thread Group is terminated by sys_spu_thread_group_exit + SPU_TGJSF_IS_JOINING = (1 << 0), + SPU_TGJSF_TERMINATED = (1 << 1), // set if SPU Thread Group is terminated by sys_spu_thread_group_terminate + SPU_TGJSF_GROUP_EXIT = (1 << 2), // set if SPU Thread Group is terminated by sys_spu_thread_group_exit }; struct spu_group_t @@ -144,9 +146,9 @@ struct spu_group_t const s32 type; // SPU Thread Group Type const u32 ct; // Memory Container Id - std::array, 256> threads; + std::array, 256> threads; // SPU Threads + std::array, 256> images; // SPU Images std::array args; // SPU Thread Arguments - std::array, 256> images; // SPU Thread Images s32 prio; // SPU Thread Group Priority u32 state; // SPU Thread Group State @@ -155,6 +157,10 @@ struct spu_group_t std::atomic join_state; // flags used to detect exit cause std::condition_variable join_cv; // used to signal waiting PPU thread + std::weak_ptr ep_run; // port for SYS_SPU_THREAD_GROUP_EVENT_RUN events + std::weak_ptr ep_exception; // TODO: SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION + std::weak_ptr ep_sysmodule; // TODO: SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE + spu_group_t(std::string name, u32 num, s32 prio, s32 type, u32 ct) : name(name) , num(num)