cellSpurs: Fix HLE workload signalling, taskset fixes

This commit is contained in:
Eladash
2020-10-02 15:15:23 +03:00
committed by Ivan
parent 4cafd5a31c
commit a0b387e0a9
3 changed files with 178 additions and 92 deletions
+81 -59
View File
@@ -2,6 +2,7 @@
#include "Loader/ELF.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Memory/vm_reservation.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/SPURecompiler.h"
#include "Emu/Cell/lv2/sys_lwmutex.h"
@@ -120,24 +121,29 @@ void cellSpursModuleExit(spu_thread& spu)
}
// Execute a DMA operation
bool spursDma(spu_thread& spu, u32 cmd, u64 ea, u32 lsa, u32 size, u32 tag)
bool spursDma(spu_thread& spu, const spu_mfc_cmd& args)
{
spu.set_ch_value(MFC_LSA, lsa);
spu.set_ch_value(MFC_EAH, static_cast<u32>(ea >> 32));
spu.set_ch_value(MFC_EAL, static_cast<u32>(ea));
spu.set_ch_value(MFC_Size, size);
spu.set_ch_value(MFC_TagID, tag);
spu.set_ch_value(MFC_Cmd, cmd);
spu.ch_mfc_cmd = args;
if (cmd == MFC_GETLLAR_CMD || cmd == MFC_PUTLLC_CMD || cmd == MFC_PUTLLUC_CMD)
if (!spu.process_mfc_cmd())
{
const u32 rv = static_cast<u32>(spu.get_ch_value(MFC_RdAtomicStat));
return cmd == MFC_PUTLLC_CMD ? !rv : true;
spu_runtime::g_escape(&spu);
}
if (args.cmd == MFC_GETLLAR_CMD || args.cmd == MFC_PUTLLC_CMD || args.cmd == MFC_PUTLLUC_CMD)
{
return static_cast<u32>(spu.get_ch_value(MFC_RdAtomicStat)) != MFC_PUTLLC_FAILURE;
}
return true;
}
// Execute a DMA operation
bool spursDma(spu_thread& spu, u32 cmd, u64 ea, u32 lsa, u32 size, u32 tag)
{
return spursDma(spu, {MFC(cmd), static_cast<u8>(tag & 0x1f), static_cast<u16>(size & 0x7fff), lsa, static_cast<u32>(ea), static_cast<u32>(ea >> 32)});
}
// Get the status of DMA operations
u32 spursDmaGetCompletionStatus(spu_thread& spu, u32 tagMask)
{
@@ -1402,76 +1408,93 @@ s32 spursTasksetProcessRequest(spu_thread& spu, s32 request, u32* taskId, u32* i
auto ctxt = spu._ptr<SpursTasksetContext>(0x2700);
s32 rc = CELL_OK;
s32 numNewlyReadyTasks;
s32 numNewlyReadyTasks = 0;
//vm::reservation_op(vm::cast(ctxt->taskset.addr(), HERE), 128, [&]()
{
auto taskset = ctxt->taskset.get_ptr();
auto taskset = ctxt->taskset;
v128 waiting = vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::waiting));
v128 running = vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::running));
v128 ready = vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::ready));
v128 pready = vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::pending_ready));
v128 enabled = vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::enabled));
v128 signalled = vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::signalled));
// Verify taskset state is valid
be_t<v128> _0(v128::from32(0));
if ((taskset->waiting & taskset->running) != _0 || (taskset->ready & taskset->pending_ready) != _0 ||
((taskset->running | taskset->ready | taskset->pending_ready | taskset->signalled | taskset->waiting) & ~taskset->enabled) != _0)
if ((waiting & running) != v128{} || (ready & pready) != v128{} ||
(v128::andnot(enabled, running | ready | pready | signalled | waiting) != v128{}))
{
spu_log.error("Invalid taskset state");
spursHalt(spu);
}
// Find the number of tasks that have become ready since the last iteration
auto newlyReadyTasks = (taskset->signalled | taskset->pending_ready) & ~taskset->ready.value();
numNewlyReadyTasks = 0;
for (auto i = 0; i < 128; i++)
{
if (newlyReadyTasks._bit[i])
auto newlyReadyTasks = v128::andnot(ready, signalled | pready);
// TODO: Optimize this shit with std::popcount when it's known to be fixed
for (auto i = 0; i < 128; i++)
{
numNewlyReadyTasks++;
if (newlyReadyTasks._bit[i])
{
numNewlyReadyTasks++;
}
}
}
v128 readyButNotRunning;
u8 selectedTaskId;
v128 running = taskset->running.value();
v128 waiting = taskset->waiting.value();
v128 enabled = taskset->enabled.value();
v128 signalled = (taskset->signalled & (taskset->ready | taskset->pending_ready));
v128 ready = (taskset->signalled | taskset->ready | taskset->pending_ready);
v128 signalled0 = (signalled & (ready | pready));
v128 ready0 = (signalled | ready | pready);
switch (request)
{
case SPURS_TASKSET_REQUEST_POLL_SIGNAL:
rc = signalled._bit[ctxt->taskId] ? 1 : 0;
signalled._bit[ctxt->taskId] = false;
{
rc = signalled0._bit[ctxt->taskId] ? 1 : 0;
signalled0._bit[ctxt->taskId] = false;
break;
}
case SPURS_TASKSET_REQUEST_DESTROY_TASK:
{
numNewlyReadyTasks--;
running._bit[ctxt->taskId] = false;
enabled._bit[ctxt->taskId] = false;
signalled._bit[ctxt->taskId] = false;
ready._bit[ctxt->taskId] = false;
signalled0._bit[ctxt->taskId] = false;
ready0._bit[ctxt->taskId] = false;
break;
}
case SPURS_TASKSET_REQUEST_YIELD_TASK:
{
running._bit[ctxt->taskId] = false;
waiting._bit[ctxt->taskId] = true;
break;
}
case SPURS_TASKSET_REQUEST_WAIT_SIGNAL:
if (signalled._bit[ctxt->taskId] == false)
{
if (signalled0._bit[ctxt->taskId] == false)
{
numNewlyReadyTasks--;
running._bit[ctxt->taskId] = false;
waiting._bit[ctxt->taskId] = true;
signalled._bit[ctxt->taskId] = false;
ready._bit[ctxt->taskId] = false;
signalled0._bit[ctxt->taskId] = false;
ready0._bit[ctxt->taskId] = false;
}
break;
}
case SPURS_TASKSET_REQUEST_POLL:
readyButNotRunning = ready & ~running;
{
readyButNotRunning = v128::andnot(running, ready0);
if (taskset->wkl_flag_wait_task < CELL_SPURS_MAX_TASK)
{
readyButNotRunning = readyButNotRunning & ~(v128::fromBit(taskset->wkl_flag_wait_task));
readyButNotRunning._bit[taskset->wkl_flag_wait_task] = false;
}
rc = readyButNotRunning != _0 ? 1 : 0;
rc = readyButNotRunning != v128{} ? 1 : 0;
break;
}
case SPURS_TASKSET_REQUEST_WAIT_WKL_FLAG:
{
if (taskset->wkl_flag_wait_task == 0x81)
{
// A workload flag is already pending so consume it
@@ -1493,11 +1516,13 @@ s32 spursTasksetProcessRequest(spu_thread& spu, s32 request, u32* taskId, u32* i
rc = CELL_SPURS_TASK_ERROR_BUSY;
}
break;
}
case SPURS_TASKSET_REQUEST_SELECT_TASK:
readyButNotRunning = ready & ~running;
{
readyButNotRunning = v128::andnot(running, ready0);
if (taskset->wkl_flag_wait_task < CELL_SPURS_MAX_TASK)
{
readyButNotRunning = readyButNotRunning & ~(v128::fromBit(taskset->wkl_flag_wait_task));
readyButNotRunning._bit[taskset->wkl_flag_wait_task] = false;
}
// Select a task from the readyButNotRunning set to run. Start from the task after the last scheduled task to ensure fairness.
@@ -1534,7 +1559,9 @@ s32 spursTasksetProcessRequest(spu_thread& spu, s32 request, u32* taskId, u32* i
waiting._bit[selectedTaskId] = false;
}
break;
}
case SPURS_TASKSET_REQUEST_RECV_WKL_FLAG:
{
if (taskset->wkl_flag_wait_task < CELL_SPURS_MAX_TASK)
{
// There is a task waiting for the workload flag
@@ -1549,41 +1576,36 @@ s32 spursTasksetProcessRequest(spu_thread& spu, s32 request, u32* taskId, u32* i
rc = 0;
}
break;
}
default:
spu_log.error("Unknown taskset request");
spursHalt(spu);
}
taskset->pending_ready = _0;
taskset->running = running;
taskset->waiting = waiting;
taskset->enabled = enabled;
taskset->signalled = signalled;
taskset->ready = ready;
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::waiting)) = waiting;
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::running)) = running;
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::ready)) = ready;
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::pending_ready)) = v128{};
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::enabled)) = enabled;
vm::_ref<v128>(ctxt->taskset.addr() + ::offset32(&CellSpursTaskset::signalled)) = signalled;
std::memcpy(spu._ptr<void>(0x2700), taskset, 128);
std::memcpy(spu._ptr<void>(0x2700), spu._ptr<void>(0x100), 128); // Copy data
}//);
// Increment the ready count of the workload by the number of tasks that have become ready
//vm::reservation_op(vm::cast(kernelCtxt->spurs.addr(), HERE), 128, [&]()
if (numNewlyReadyTasks)
{
auto spurs = kernelCtxt->spurs.get_ptr();
auto spurs = kernelCtxt->spurs;
s32 readyCount = kernelCtxt->wklCurrentId < CELL_SPURS_MAX_WORKLOAD ? spurs->wklReadyCount1[kernelCtxt->wklCurrentId].load() : spurs->wklIdleSpuCountOrReadyCount2[kernelCtxt->wklCurrentId & 0x0F].load();
readyCount += numNewlyReadyTasks;
readyCount = readyCount < 0 ? 0 : readyCount > 0xFF ? 0xFF : readyCount;
if (kernelCtxt->wklCurrentId < CELL_SPURS_MAX_WORKLOAD)
auto [res, rtime] = vm::reservation_lock(spurs.addr(), 128, vm::dma_lockb);
spurs->readyCount(kernelCtxt->wklCurrentId).fetch_op([&](u8& val)
{
spurs->wklReadyCount1[kernelCtxt->wklCurrentId] = readyCount;
}
else
{
spurs->wklIdleSpuCountOrReadyCount2[kernelCtxt->wklCurrentId & 0x0F] = readyCount;
}
const s32 _new = val + numNewlyReadyTasks;
val = static_cast<u8>(std::clamp<s32>(_new, 0, 0xFF));
});
std::memcpy(spu._ptr<void>(0x100), spurs, 128);
}//);
res.release(rtime + 128);
}
return rc;
}